3

I was working on a project where I get analog values from a resistive touchscreen and turn them into intersection points.

Here is an example:enter image description here

Here is my code for the data collection using an Arduino Uno and construction of the points using tool called processing.

#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();
}

This is the Processing code for the construction of the graph.

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]);
      }
    }
  }
}

I was wondering how I could convert the intersection of those points to a coordinate? Example: in the image, I showed you, I set the parameters for the dimensions to be (600,600). Is it possible to change that intersection are to a coordinate value? Currently, my code is printing out coordinates however they are diagonals such at the x and y values are equal. I want the coordinates of x and y to have different quantities so that I can get coordinates for different sides in the square. Can somebody help?

vallentin
  • 23,478
  • 6
  • 59
  • 81
  • So you basically want to do [trilateration](https://en.wikipedia.org/wiki/Trilateration) with four sites? – Nico Schertler Mar 16 '17 at 07:27
  • 1
    Hello, I looked into trilateration and I think that's how I am going to solve the problem. Can you please help out with this? – marshmellooooooos Mar 16 '17 at 19:43
  • To answer your question we need more details. What exactly values you get in Arduino mean (`sensorValue1` - `sensorValue4`)? Is this a pressure? If so at which points? What is the scale? Not just [0-1] but what 0.5 means (is it linear, logarifmic, etc)? Maybe you have some reference/manual for the hardware (touch screen) you use. Without knowing what those value **exactly** mean it is impossible to aggregate them properly to whatever you want. – SergGr Mar 18 '17 at 07:22
  • 1
    so sensorValue1,sensorValue2, sensorValue3, sensorValue4 are sides digitalWrite pins that are connected to the corners of my touchpad. My touchpad is made up of VeloStat and I have tried to mimic [link](https://www.youtube.com/watch?v=j3b68vZQyIc). – marshmellooooooos Mar 18 '17 at 22:42
  • 1
    The scale (are you referring to scaleValue in the processing code) I am using is basically used to increase the sensorValue data as it is needs to be amplified. – marshmellooooooos Mar 18 '17 at 22:44
  • For a better reference, this guy created something similar but not exactly what I am looking for. [Link](http://www.plusea.at/?p=3929) – marshmellooooooos Mar 18 '17 at 22:46
  • @marshmellooooooos, Unfortunatelly I think you didn't understand my question about the "scale" and from this I imply that you underestimate complexity of the tasks and thus it would be very hard for anyone to help you. What I mean is something similar to what http://www.plusea.at/?p=3929 describes as "Linearizing sensor data". That link implies that on that paritcular "hardware"the scale is close to logariphmic. Do you know if this is true for your hardware? Or re-stating the problem, do you already know how to properly "linearize sensor data" in your case? We can't do it without your hardware – SergGr Mar 19 '17 at 03:30
  • Is there a method I could use to check if the scale is logarithmic – marshmellooooooos Mar 19 '17 at 03:31
  • @marshmellooooooos, It is hard to fit in a comment but I'll try. AFAIU your original sensor values are in range (0,1) and you map them to range (0, 1023). I call this "theoretical" range. There are a few important questions: when you measure touches in the corner close to the sensor and in the corner diagonally-away from the sensor what are values? Is it really whole (0,1023) range or something smaller? this is what I call "practical range". Now you touch in the middle, what is the value? Is it half between practical range? If no (which most probably so), what is it? – SergGr Mar 19 '17 at 03:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/138435/discussion-between-serggr-and-marshmellooooooos). I've added a few more comments in the chat as they are far too big for comments – SergGr Mar 19 '17 at 03:38

2 Answers2

2

By reading your code I'm assuming that you know the position of all n sensors and the distance from each n sensor to a target. So what you're essentially trying to do is trilateration (as mentioned by Nico Schertler). In other words determining a relative position based on the distance between n points.

Just a quick definition note in case of confusion:

Trilateration requires at least 3 points and distances.

  • 1 sensor gives you the distance the target is away from the sensor
  • 2 sensors gives you 2 possible locations the target can be
  • 3 sensors tells you which of the 2 locations the target is at

The first solution that probably comes to mind is calculating the intersections between 3 sensors treating them as circles. Given that there might be some error in the distances this means that the circles might not always intersect. Which rules out this solution.

The following code has all been done in Processing.

I took the liberty of making a 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;
    }
}

Now to calculate and approximate the intersection point between the sensors/circles, do the following:

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);
}

Where s1, s2, s3 is any of your 3 sensors, do the following to calculate the the intersection point between the given sensors:

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

While it is possible to calculate the intersection between any amount of sensors. It becomes more and more complex the more sensors you want to include. Especially since you're doing it yourself. If you're able to use external Java libraries, then it would be a lot easier.

If you're able to use external Java libraries, then I highly recommend using com.lemmingapex.trilateration. Then you'd be able to calculate the intersection point between 4 sensors by doing:

Considering s1, s2, s3, s4 as instances of the previously mentioned 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];

The following examples, are examples of the trilateration() method I wrote and not an example of the library above.

Example 1 - No Sensor Error

The 3 big circles being any 3 sensors and the single red circle being the approximated point.

Example 1

Example 2 - With Sensor Error

The 3 big circles being any 3 sensors and the single red circle being the approximated point.

Example 2

vallentin
  • 23,478
  • 6
  • 59
  • 81
  • 1
    Thanks for this comment but the problem is I have 4 sensors. Also when I am getting values, the x and y values are exactly the same which is the part I am stuck on. I am only able to get diagonal values. So basically on my touchpad its almost impossible to get up, down, left right. – marshmellooooooos Mar 18 '17 at 22:48
  • 1
    I will incorporate your code and see if it works. Meanwhile can you take a look this video I have created? It shows you how my touchpad works currently. [Video of my touchpad](https://www.youtube.com/watch?v=q4ptAQQpeBo) and [the second part of the video](https://www.youtube.com/watch?v=Oq1ik9o-Zbs) – marshmellooooooos Mar 19 '17 at 17:13
  • 1
    I was looking at the code you sent me. Was a little confused at the beginning so I looked at examples online to check. Correct me if I am wrong but do I have to call the sensor class in the serialEvent class? – marshmellooooooos Mar 19 '17 at 20:44
  • The `Sensor` class is just a small container for the data. So considering this `ellipse(height,0, scaleValue*sensorValues[0], scaleValue*sensorValues[0])`. Then you'd make a sensor representing that like this `Sensor s1 = new Sensor(height,0, scaleValue*sensorValues[0])`. Now take try of those `ellipse(...)`. Make `Sensor s1, s2, s3`, and call `PVector target = trilateration(s1, s2, s3)`. – vallentin Mar 20 '17 at 00:52
  • Meant "take three" and not "take try". Also note that you need to do it in `draw()` and not `setup()`, as you of course need to update all sensors values. – vallentin Mar 20 '17 at 01:33
  • 1
    can you check my code? I am having problems [link pastebin](http://pastebin.com/CMCd9XTQ) – marshmellooooooos Mar 20 '17 at 16:53
  • 1
    I was having problems with the sensor class. – marshmellooooooos Mar 20 '17 at 16:54
  • I had a quick glance at it, and can't see what the problem would be, besides you not doing `PVector target = trilateration(s1, s2, s3)` after creating the sensors. Then after that do `ellipse(target.x, target.y, 10,10);` to draw the intersection point. – vallentin Mar 21 '17 at 07:57
  • 1
    In the program it is giving me problems when I use sensor class – marshmellooooooos Mar 21 '17 at 11:13
  • 1
    So if u look at my sensor class and in draw when I am calling sensor, it is giving me a syntax error. – marshmellooooooos Mar 21 '17 at 11:32
  • 1
    it says in the error when I initialize sensors s1,s2,s3 that 'constructor sensor(int, int, float) does not exist' – marshmellooooooos Mar 21 '17 at 15:13
  • 1
    thankyou so much! It works well know! So If I try to incorporate my s1, s2, s3, s4, do you think it would work? I am thinking of using the eclipse plugin for it. – marshmellooooooos Mar 22 '17 at 16:36
  • You're welcome! I don't have your device so I couldn't directly test it. However simulating it on my computer and it worked. So I can't see why it wouldn't work. – vallentin Mar 22 '17 at 16:38
  • 1
    I guess I have to use the eclipse plugin. One more thing, In the code you sent me about the sensors, I changed one thing. The target.x and target.y I put an abs() in front of them to get positive values. Is that correct? – marshmellooooooos Mar 22 '17 at 16:43
  • If you're able to, then it would probably be easier to use the plugin. If you need positive values, then yes use `abs()`. However you shouldn't need to, as `target`'s `x` and/or `y` would only be negative if it's at a negative position. – vallentin Mar 22 '17 at 16:45
  • It's almost finished however I still need to import the java library but I don't know how to do so. Thank you for your help! If I have any questions I will ask you shortly. – marshmellooooooos Mar 23 '17 at 17:13
  • If your project has a POM file, then you can [add this](https://github.com/lemmingapex/trilateration#getting-trilateration). However if you don't, then you could [download the jar](https://github.com/lemmingapex/trilateration/releases), and add it to your project. I don't know which IDE you're using, but it's usually just a right-click on the project and adding an (external) library. – vallentin Mar 23 '17 at 17:24
  • I'm commenting this for marshmellooos, he can't connect right now. he says "I tried using your code in processing for the 4 points. I am not sure in what method I should implement the 4 point trilateration? Can you please help? " – BigSpicyPotato Mar 28 '17 at 17:23
  • I highly recommend he actually continued using the library. As having to implement it yourself gets more and more complex, the more points you introduce. Thus why I recommended only using 3 points. – vallentin Mar 28 '17 at 22:13
0

What you need to compute is the point that it nearest to the a set of circles, let denote their centers by (x1,y1), (x2,y2), (x3,y3), (x4,y4) and their radii by r1,r2,r3,r4.

You want to find (x,y) that minimizes

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

This can be achieved by using Newton's algorithm. Newton's algorithm works from an "initial guess" (let's say at the center of the screen), improved iteratively by solving a series of linear systems (in this case, with 2 variables, easy to solve). M P = -G

where M is the (2x2) matrix of the second order derivatives of F with respect to x and y (called the Hessian), and G the vector of the first order derivatives of F with respect to x and y (the gradient). This gives the "update" vector P, that tells how to move the coordinates:

Then (x,y) is updated by x = x + Px, y = y + Py, and so on and so forth (recompute M and G, solve for P, update x and y, recompute M and G, solve for P, update x and y). In your case it will probably converge in a handful of iterations.

Since you got two variables only, the 2x2 linear solve is trivial, and the expression of F and its derivatives is simple, thus you can implement it without needing an external library.

Note1: the Levenberg-Marquardt algorithm mentioned in the other answer is a variant of Newton's algorithm (specialized for sum of squares, like here, and that neglects some terms, and that regularizes the matrix M by adding small numbers to its diagonal coefficients). More on this here.

Note2: a simple gradient descent will also probably work (a bit simpler to implement, since it only uses first order derivatives), but given that you only got two variables to implement, the 2x2 linear solve is trivial, so Newton is probably worth it (requires a much much smaller number of iterations for convergence, may be critial if your system is interactive).

BrunoLevy
  • 2,495
  • 17
  • 30
  • 1
    how do I implement this? Is there a built in function? – marshmellooooooos Mar 21 '17 at 02:07
  • 1
    So far I have this much calculated however having syntax issues. I am using the levenberg-marquardt algorithm suggested however with 3 points at this time. http://pastebin.com/CMCd9XTQ – marshmellooooooos Mar 21 '17 at 02:07
  • There is no need for a builtin function, it is just a couple of lines of code (once the formal computations are done), I'll try to do the computations and expand my answer with a simple C function.. – BrunoLevy Mar 21 '17 at 16:38