转换交叉区域以生成坐标

时间:2017-03-16 03:18:21

标签: arduino processing computational-geometry coordinate-systems trilateration

我正在开展一个项目,我从电阻式触摸屏获取模拟值并将其转换为交叉点。

以下是一个示例:enter image description here

这是我使用Arduino Uno进行数据收集的代码,并使用称为处理的工具构建点。

#define side1 2
#define side2 3
#define side3 4
#define side4 5
#define contact A0

void setup() {
  pinMode(contact, INPUT); 
  pinMode(side1, OUTPUT);  
  pinMode(side2, OUTPUT);  
  pinMode(side3, OUTPUT);  
  pinMode(side4, OUTPUT);  
  Serial.begin(9600);
}

void loop() {
  int sensorValue1;
  int sensorValue2;
  int sensorValue3;
  int sensorValue4;

  // SENSOR VALUE 1:
  digitalWrite(side1, LOW);
  digitalWrite(side2, HIGH);
  digitalWrite(side3, HIGH);
  digitalWrite(side4, HIGH);
  delay(5);
  for (int i = 0; i < 10; i++){
    sensorValue1 = analogRead(contact);
  }


  // SENSOR VALUE 2:
  digitalWrite(side2, LOW);
  digitalWrite(side3, HIGH);
  digitalWrite(side4, HIGH);
  digitalWrite(side1, HIGH);
  delay(5);
  for (int i = 0; i < 10; i++){
    sensorValue2 = analogRead(contact);
  }


  // SENSOR VALUE 3:
  digitalWrite(side3, LOW);
  digitalWrite(side2, HIGH);
  digitalWrite(side4, HIGH);
  digitalWrite(side1, HIGH);
  delay(5);
  for (int i = 0; i < 10; i++){
    sensorValue3 = analogRead(contact);
  }


  // SENSOR VALUE 2:
  digitalWrite(side4, LOW);
  digitalWrite(side3, HIGH);
  digitalWrite(side2, HIGH);  
  digitalWrite(side1, HIGH);
  delay(5);
  for (int i = 0; i < 10; i++){
    sensorValue4 = analogRead(contact);
  }

  Serial.print(sensorValue1);
  Serial.print(",");
  Serial.print(sensorValue2);
  Serial.print(",");
  Serial.print(sensorValue3);
  Serial.print(",");
  Serial.print(sensorValue4);
  Serial.println();
}

这是构建图表的Processing代码。

import processing.serial.*;


Serial myPort;  // The serial port
int maxNumberOfSensors = 4;   
float[] sensorValues = new float[maxNumberOfSensors];
float sensorValueX;
float sensorValueX1;
float sensorValueY;
float sensorValueY1;
int scaleValue = 2;

void setup () { 
  size(600, 600);  // set up the window to whatever size you want
  //println(Serial.list());  // List all the available serial ports
  String portName = "COM5";
  myPort = new Serial(this, portName, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');  // don't generate a serialEvent() until you get a newline (\n) byte
  background(255);    // set inital background
  smooth();  // turn on antialiasing
}


void draw () {
  //background(255);
  //noFill();
  fill(100,100,100,100);
  ellipse(height,0, scaleValue*sensorValues[0], scaleValue*sensorValues[0]);

  ellipse(0,width, scaleValue*sensorValues[1], scaleValue*sensorValues[1]);
  ellipse(height,width, scaleValue*sensorValues[2], scaleValue*sensorValues[2]);
  ellipse(0,0, scaleValue*sensorValues[3], scaleValue*sensorValues[3]);
  //ellipse(sensorValueY, sensorValueX, 10,10);
  //println(sensorValueY,sensorValueX);
  sensorValueX = ((sensorValues[3]*sensorValues[3])-(sensorValues[2]*sensorValues[2])+600*600)/2000;
  sensorValueX1 = ((sensorValues[0]*sensorValues[0])-(sensorValues[1]*sensorValues[1])+600*600)/2000;
sensorValueY = ((sensorValues[3]*sensorValues[3])-(sensorValues[2]*sensorValues[2])+(600*600))/2000;
  sensorValueY1 = ((sensorValues[1]*sensorValues[1])-(sensorValues[0]*sensorValues[0])+(600*600))/2000;

  line(0, scaleValue*sensorValueX, height,scaleValue* sensorValueX);
  line(scaleValue*sensorValueY, 0, scaleValue*sensorValueY, width);
  ellipse(scaleValue*sensorValueY, scaleValue*sensorValueX, 20,20);
  line(0, scaleValue*sensorValueX1, height,scaleValue* sensorValueX1);
  line(scaleValue*sensorValueY1, 0, scaleValue*sensorValueY1, width);
  ellipse(scaleValue*sensorValueY1, scaleValue*sensorValueX1, 20,20);
  println(scaleValue*sensorValueX,scaleValue*sensorValueY);
}


void serialEvent (Serial myPort) {
  String inString = myPort.readStringUntil('\n');  // get the ASCII string

  if (inString != null) {  // if it's not empty
    inString = trim(inString);  // trim off any whitespace
    int incomingValues[] = int(split(inString, ","));  // convert to an array of ints

    if (incomingValues.length <= maxNumberOfSensors && incomingValues.length > 0) {
      for (int i = 0; i < incomingValues.length; i++) {
        // map the incoming values (0 to  1023) to an appropriate gray-scale range (0-255):

        sensorValues[i] = map(incomingValues[i], 0, 1023, 0, width);
        //println(incomingValues[i]+ " " + sensorValues[i]);
      }
    }
  }
}

我想知道如何将这些点的交点转换为坐标?示例:在图像中,我向您展示了,我将尺寸参数设置为(600,600)。是否可以将该交点更改为坐标值?目前,我的代码打印出坐标,但它们是对角线,例如x和y值相等。我希望x和y的坐标具有不同的量,以便我可以获得正方形中不同边的坐标。有人可以帮忙吗?

2 个答案:

答案 0 :(得分:2)

通过阅读您的代码,我假设您知道所有n个传感器的位置以及每个传感器到目标的距离。所以你基本上要做的是trilateration(如Nico Schertler所述)。换句话说,根据n个点之间的距离确定相对位置。

如果出现混淆,请快速定义一下:

三边测量需要至少3个点和距离。

  • 1个传感器为您提供目标远离传感器的距离
  • 2个传感器为您提供目标可能的两个位置
  • 3个传感器告诉您目标所在的2个位置中的哪个位置

可能想到的第一个解决方案是计算交叉点 3个传感器之间将它们视为圆圈。鉴于距离可能存在一些误差,这意味着圆圈可能并不总是相交。这排除了这个解决方案。

以下代码全部在Processing。

中完成

我冒昧地制作class Sensor

class Sensor {
    public PVector p; // position
    public float d; // distance from sensor to target (radius of the circle)

    public Sensor(float x, float y) {
        this.p = new PVector(x, y);
        this.d = 0;
    }
}

现在计算并近似传感器/圆圈之间的交叉点,执行以下操作:

PVector trilateration(Sensor s1, Sensor s2, Sensor s3) { 
    PVector s = PVector.sub(s2.p, s1.p).div(PVector.sub(s2.p, s1.p).mag());
    float a = s.dot(PVector.sub(s3.p, s1.p));

    PVector t = PVector.sub(s3.p, s1.p).sub(PVector.mult(s, a)).div(PVector.sub(s3.p, s1.p).sub(PVector.mult(s, a)).mag());
    float b = t.dot(PVector.sub(s3.p, s1.p));
    float c = PVector.sub(s2.p, s1.p).mag();

    float x = (sq(s1.d) - sq(s2.d) + sq(c)) / (c * 2);
    float y = ((sq(s1.d) - sq(s3.d) + sq(a) + sq(b)) / (b * 2)) - ((a / b) * x);

    s.mult(x);
    t.mult(y);

    return PVector.add(s1.p, s).add(t);
}

s1s2s3是您的3个传感器中的任何一个,请执行以下操作来计算给定传感器之间的交叉点:

PVector target = trilateration(s1, s2, s3);

虽然可以计算任意数量的传感器之间的交叉点。您希望包含的传感器越多,它就越复杂。特别是因为你自己做了。如果您能够使用外部Java库,那么它将变得更加容易。

如果您能够使用外部Java库,那么强烈建议您使用com.lemmingapex.trilateration。然后,您可以通过执行以下操作来计算4个传感器之间的交点:

s1s2s3s4视为前面提到的class Sensor的实例。

double[][] positions = new double[][] { { s1.x, s1.y }, { s2.x, s2.y }, { s3.x, s3.y }, { s4.x, s4.y } };
double[] distances = new double[] { s1.d, s2.d, s3.d, s4.d };

NonLinearLeastSquaresSolver solver = new NonLinearLeastSquaresSolver(
            new TrilaterationFunction(positions, distances),
            new LevenbergMarquardtOptimizer());
Optimum optimum = solver.solve();

double[] target = optimum.getPoint().toArray();
double x = target[0];
double y = target[1];

以下示例是我编写的trilateration()方法的示例,而不是上述库的示例。

示例1 - 无传感器错误

3个大圆圈是任意3个传感器,单个红色圆圈是近似点。

Example 1

示例2 - 传感器错误

3个大圆圈是任意3个传感器,单个红色圆圈是近似点。

Example 2

答案 1 :(得分:0)

你需要计算的是它离一组圆最近的点, let用(x1,y1),(x2,y2),(x3,y3),(x4,y4)表示它们的中心,用r1,r2,r3,r4表示它们的半径。

你想找到最小化的(x,y)

F(x,y)= Sum_i [square(d2((x,y),(xi,yi)) - ri)]

这可以通过使用Newton's algorithm来实现。牛顿算法的工作原理是&#34;初始猜测&#34; (让我们说在屏幕的中心),通过求解一系列线性系统(在这种情况下,有2个变量,易于解决)迭代改进。 M P = -G

其中M是F的二阶导数相对于x和y的(2x2)矩阵(称为 Hessian ),G是F的一阶导数的向量 尊重x和y(渐变)。这给了&#34;更新&#34;向量P,告诉如何移动坐标:

然后(x,y)更新x = x + Px,y = y + Py,依此类推(重新计算M和G,求解P,更新x和y,重新计算M和G,求解P,更新x和y)。在你的情况下,它可能会在少数迭代中收敛。

由于只有两个变量,2x2线性求解是微不足道的,F及其导数的表达式很简单,因此无需外部库就可以实现它。

注1:另一个答案中提到的Levenberg-Marquardt算法是牛顿算法的一种变体(专门用于平方和,就像这里一样,忽略了一些术语,并且通过在其对角系数上加上小数来使矩阵M正则化。 More on this here

注2:一个简单的gradient descent也可能有效(实现起来有点简单,因为它只使用一阶导数),但考虑到你只有两个变量要实现, 2x2线性求解是微不足道的,所以Newton可能是值得的(需要更少的迭代次数才能收敛,如果你的系统是交互式的话,可能会很关键)。