Creating a (vector) path from acceleration values derived from txt file

Hello everyone and thank you in advance for your help!!
I have data collected from a sensor attached on Arduino which have been saved on an SD card as a txt file. Now i’m trying to visualize those values into Processing parsing the text and turning them into floats from Strings creating an array of 6 values seperated by commas for every line of my txt. I am creating an Object (class Body) for every line and I’m adding the location to the acceleration x and y which I have on my txt file. In order to create the path that the person has walked while wearing the accelerometer sensor, I am adding acceleration to location and then trying to find the direction between the locations that change with each loop, since acceleration values change, so I am using the difference between the location vector which has added acceleration to itself and acceleration(Vec2D dif=loc.addSelf(a).sub(a):wink:
When i am creating a point using the x and y of location it’s fine but I’m not sure if i have used the right way to join with lines each new position with the previous one. the code is this (i haven’t yet used the other variables temp, myo and vibr for my vizualization only accx and accy) Please take a look if you want :

import toxi.geom.*;
Body body;

String[] data;
//PeasyCam cam;
int index=0;

void setup()
{
  frameRate(3);
  size(1200, 1200);
  smooth();
  data = loadStrings("katerina an thessaloniki paralia 16.30 2_6_2018.TXT");
  
  body= new Body (0, 0, 0.1, -0.01, 0, -0.001);
}
void draw()
{
  if (index<data.length) 
  {
        float[] values = float(splitTokens(data[index], ",:/"));
        body.update(values[0], values[1], values[2], values[3], values[4], values[5]);
        body.display();

}
  index++;
}

class Body
{

  Vec2D loc, c, a;

  Vec2D velocity;
  Vec2D acceleration;
  Vec2D nrm, scl;
  Vec2D pos1, pos2, locNew, dif, nrm1, nrm2, scl1, scl2;
  int tot=200;
  float step=50;
  float temp;
  float myo;
  float vibr;
  float accx;
  float accy;
  float accz;

  float mag;

  Body (float temp, float myo, float vibr, float accx, float accy, float accz)
  {
    this.temp=temp;
    this.myo=myo;
    this.vibr=vibr;

    this.accx = accx;
    this.accy = accy;
    this.accz= accz;
    loc=new Vec2D(50, 50);
    
  }

  void update(float temp, float myo, float vibr, float accx, float accy, float accz)
  {
    this.temp=temp;
    this.myo=myo;
    this.vibr=vibr;
    this.accx = accx;
    this.accy = accy;
    this.accz= accz;

    acceleration= new Vec2D (0, 0);
    a=acceleration.addSelf(accx, accy);
    mag= acceleration.magnitude();

    loc.addSelf(a);
   
    locNew=loc.copy();
    dif= loc.addSelf(a).sub(a);
    
    
    println(a,loc,loc.addSelf(a),dif);
    
  }

  void display()
  {


    strokeWeight(3);
    stroke(255);
    strokeWeight(5);
    point(loc.addSelf(a).x, loc.addSelf(a).y);
    stroke(255, 0, 0);
    strokeWeight(2);
    line(dif.x, dif.y,loc.x, loc.y);
  
  }
}

the .txt file which has to be in the data folder of the script is:

30.00,0.07,0.81,0.351,0.602,0.493
30.00,0.13,0.87,0.429,0.896,0.199
30.00,0.41,1.44,0.896,1.999,0.627
30.00,2.87,0.90,0.418,0.906,0.202
30.00,2.14,1.45,0.950,1.869,0.729
30.00,0.38,1.70,0.063,1.438,-0.006
30.00,1.24,1.16,0.549,1.215,0.079
30.00,0.64,1.10,0.572,1.288,0.500
30.00,1.06,0.98,0.639,1.663,1.279
30.00,0.92,0.97,0.750,1.681,0.938
30.00,0.42,0.70,0.665,1.717,0.959
30.00,0.94,1.15,0.409,1.017,0.119
30.00,0.43,1.80,-0.062,0.685,0.195
30.00,0.54,1.38,0.379,1.062,0.238
30.00,0.89,1.40,0.430,0.864,0.086
30.00,0.66,1.72,0.616,1.840,-0.314
30.00,1.15,0.63,1.201,1.683,0.047
30.00,0.65,1.35,0.326,0.951,0.001
30.00,2.16,0.86,0.452,0.921,0.160
30.00,1.36,0.97,0.102,1.555,0.283
31.00,2.49,1.45,0.378,1.932,-0.100
31.00,1.26,1.95,0.741,-0.109,0.036
31.00,0.29,1.82,1.058,1.572,0.623
31.00,3.64,1.13,0.561,1.045,0.053
31.00,0.53,0.93,0.488,0.881,0.167
31.00,0.96,0.91,0.479,0.885,0.183
31.00,0.12,1.26,0.468,0.882,0.160
31.00,3.03,1.63,0.457,0.890,0.148
31.00,1.02,1.41,0.451,0.896,0.156
31.00,0.57,1.14,0.458,0.893,0.137
31.00,0.60,1.12,0.475,0.875,0.140
31.00,0.53,1.07,0.453,0.893,0.155
31.00,0.23,1.02,0.439,0.906,0.125
31.00,0.46,1.42,0.509,1.024,0.343
31.00,1.30,1.06,0.479,1.062,0.310
31.00,0.85,0.96,0.535,1.005,0.126
31.00,0.70,0.95,0.437,0.887,0.049
31.00,0.69,1.69,0.488,0.890,0.116
31.00,0.38,0.92,-0.463,1.503,-0.063
30.00,1.44,0.81,-0.328,1.334,-0.233
30.00,0.97,3.23,0.462,1.795,0.332
30.00,0.16,1.09,0.374,0.655,-0.561
30.00,0.48,1.49,0.658,1.074,0.285
30.00,1.08,1.12,0.510,1.045,0.190
30.00,0.47,1.02,0.565,0.887,-0.011
31.00,1.29,0.76,1.275,0.896,-1.261
31.00,0.30,1.85,0.479,1.755,0.263
31.00,1.19,0.97,0.239,0.327,-0.025
31.00,0.22,1.49,0.468,0.824,-0.544
31.00,1.37,1.32,1.363,1.560,-0.086
31.00,1.39,1.72,0.584,1.085,0.232
31.00,1.31,0.73,0.257,1.026,-0.044
31.00,0.10,0.89,0.397,0.915,0.144
31.00,1.00,0.97,0.421,0.907,0.181
31.00,0.67,0.73,-0.097,1.033,0.075
31.00,1.98,1.87,0.051,0.775,0.632

I suggest you change the initial value of loc in your Body’s constructor from loc=new Vec2D(50,50); to this loc=new Vec2D(width/2,height/2);. This will ensure your line starts at the center of the sketch. If you do a test with your arduino, if instead of going forward, you travel backwards, you should be able to see it in your sketch.

I am not familiar with the library So I am not sure if it is right. I would think you can infer orientation from the acc vector normalized to unity. I did a quick search and this might be useful http://www.chrobotics.com/library/accel-position-velocity On a side note, I notice than in update(), you modify the vector loc and diff. In display(), you are still modifying loc, since you are calling loc.addSelf(a).x. This affect the rendering of your final product. You can easily see that the following two codes provide different results:

CODE1: points

  void display()
  {


    strokeWeight(3);
    stroke(255);
    strokeWeight(5);
    point(loc.addSelf(a).x, loc.addSelf(a).y);
    stroke(255, 0, 0);
    strokeWeight(2);
    //line(dif.x, dif.y, loc.x, loc.y);
  }

CODE2: lines

  void display()
  {


    strokeWeight(3);
    stroke(255);
    strokeWeight(5);
    //point(loc.addSelf(a).x, loc.addSelf(a).y);
    stroke(255, 0, 0);
    strokeWeight(2);
    line(dif.x, dif.y, loc.x, loc.y);
  }

I suggest you check the documentation to addSelf() and make sure you are calling this function properly. I think you should call it only once. However I am not sure at the moment. When you reference a vector, you access its components directly. No need to call addSelf().

Related to the acceleration question, There are two ways to do this, the right way and the what-it-seems-easy and fun way but not the so-right way.
The right way is to study a bit of physics, if you haven’t done so, and understand how you can get position from acceleration. It requires to perform a double integral. I believe this is what you are doing but I am not convinced it is totally right. One needs to look at the documentation of the library you are using to verify if you are doing this properly or what would be the proper way to do it.

The fun way is for you to run two simple experiments where you collect data. Start at the center of an empty room. You start your device and you run forward for a some distance… say 10 meters. A second experience is that you start from the same position and you run backwards 10 meters. If you plot both data sets (this is why I suggested to move the center of your start point in your constructor to the middle of the sketch), is one in opposite direction to the other one? Are they both the same length?

Another different exercise is to run an experiment moving in the same direction, starting from the same position, but moving different distances, say 6, 8 and 12m. If the length of the line that you plot for the first data set half of the third data set? Does the length of the like for the 8 m fall between the length of the line for the 6 m and the 12m experiences?

Kf

1 Like

First of all thank you for your answer! I have placed the lines in the center of my sketch! The code overall finally worked as I wanted,connecting the lines with one another and not with (0,0)- because generally Vec2D vectors from toxiclibs are placed in 0,0 -I don’t know if this is the same with the PVector class of Processing. The truth is the vizualisation is not totally accurate since all the x,y values of the location are positive but the people the accelerometer was on also did backward movements… so as you say it should also go backwards. But after also reading some of this article I understand that it’s quite difficult to derive precise measurements from these cheap acelerometers and I can’t find a way to find the orientation more accurately. Now the only thing I did was to do calculations between vectors because I’m not aware of all the math equations that lie behind them but i understand the calculations between vectors, that’s why i use the functions of the library assuming they do all the calculations for me. Since i haven’t measured velocity , which might have been more correct to add it first to location and then add acceleration to velocity, i add directly acceleration to location. It might not be the most correct way but what I want is to trace the path even thought it doesn’t have the correct orientation. Since it’s only for reaching conclusions about the acceleration and it’s relationships with the other environmental parameters I have measured like temperature and so on, it doesn’t have to be that accurate - i need to continue with visualizing all the parameters in accordance with this acceleration “path”. I also used loc.x,y in the display function as you suggested.
What would be nice if possible is if someone can suggest me a way that I can multiply those vectors with a factor or maybe the magnitude of acceleration since now I hvent’t consedered it and the line that is produced is very small and not easy to see.
Any ideas are welcome!
Thanks a lot :slight_smile:
This is a screenshot of what is produced from one of the txts with data:

Hi cathytstir,

Am I understanding correctly that your questions are

  • How to connect the points which represent the body’s position at step n with a line?

  • How to scale the display of the data correctly?

I came from a Processing Facebook group, and it looked like there was another question about how to best update the body’s location given the acceleration data. I have no better answers than those already given there - I referred to Dan Shiffman’s Nature of Code ch.2 when making this example.

A side question, just to doublecheck: when your sensor records the data, which axis points up, toward the sky, z or y? For each axis – which direction does a positive value travel, which a negative? [EDIT: Sorry, I believe you already answered this above.] Which graphics renderer do you want to use long-term (JAVA2D, P3D, P2D)? I ask because you have PeasyCam commented out. Processing’s 3D world coordinates are odd; or, if your sketch is 2D, obviously one axis has to be left out.

Even if I don’t understand your questions right, I think it would be helpful to break the task into smaller sub-tasks.

  • How to represent your data in working memory. I created a class for that, then created an array of instances of that class from the text data.

  • How to leave the original data unchanged, but let it be translated, rotated and scaled; and, of course, supplied to other functions. For that, I made my own camera function and some key controls.

  • What reference marks could be used to compare against the displayed data. For that, I drew circles.

  • The aforementioned issue of how to interpret and apply the data to the body. I followed Shiffman’s lead. I was more comfortable with PVectors, so I used them instead of Vec3Ds.

  • How to plot the data. I used beginShape, vertex and endShape.

I hope that can be of use to you. Best!

613

DataPoint[] parsedData;
Body body = new Body();
PMatrix2D camMat = new PMatrix2D();
int progress = 1;
float progressIncr = 0.25;
float cx, cy, zoom = 1.0, invzoom = 1.0, mvSpd = 2.0, mvZoom;
int refRings = 46, ringSkip = 2;

boolean[] pressed = new boolean[256];

String instructions = "Pan with UP, LEFT, RIGHT, DOWN\n" +
  "Zoom with > and ?\n" +
  "Mouse X controls progress through data.";

void setup() {
  size(500, 500);
  ellipseMode(RADIUS);

  String[] data = loadStrings("vecpath.txt");
  int sz = data.length;
  parsedData = new DataPoint[sz];
  for (int i = 0; i < sz; ++i) {
    float[] p = float(splitTokens(data[i], ",:/"));
    parsedData[i] = new DataPoint(
      p[0], p[1], p[2], p[3], p[4], p[5]);
    println(parsedData[i]);
  }
  getMatrix(camMat);
}

void draw() {
  mvZoom = mvSpd * invzoom;
  body.reset();
  int sz = parsedData.length;
  progress = int(map(mouseX, 0, width, 0, sz));

  controls();
  camera(camMat, cx, cy, 0, zoom, zoom);
  background(255);
  noFill();
  
  drawRings();

  strokeWeight(2.0);
  stroke(0xffff3f00);
  beginShape();
  for (int i = 0; i < progress; ++i) {
    DataPoint dp = parsedData[i];
    body.acc.set(dp.acc.x, dp.acc.y, dp.acc.z);
    body.update();
    vertex(body.loc.x, body.loc.y);
  }
  endShape();

  drawGui();

  surface.setTitle(String.format("%.0f", frameRate));
}

void keyPressed() {
  pressed[keyCode] = true;
}

void keyReleased() {
  pressed[keyCode] = false;
}

void drawRings() {
  strokeWeight(1.0);
  stroke(0xff00c0c0);
  for (int i = 0; i < refRings; i += ringSkip) {
    float scl = sq(i + 2);
    ellipse(0, 0, scl, scl);
  }
}

void drawGui() {
  pushStyle();
  resetMatrix();
  textAlign(LEFT, TOP);
  noStroke();
  fill(0);
  text(instructions, 0, 0);
  popStyle();
}

void controls() {
  if (pressed[DOWN]) {
    cy += mvZoom;
  }
  if (pressed[UP]) {
    cy -= mvZoom;
  }
  if (pressed[RIGHT]) {
    cx += mvZoom;
  }
  if (pressed[LEFT]) {
    cx -= mvZoom;
  }
  if (pressed[46]) { /* . > key */
    zoom += 0.01;
    invzoom = 1.0 / zoom;
  }
  if (pressed[47]) { /* ? / key */
    zoom = zoom >= 0.01 ? zoom - 0.01 : zoom;
    invzoom = 1.0 / zoom;
  }
}

static class Body {
  PVector loc = new PVector();
  PVector vel = new PVector();
  PVector acc = new PVector();

  Body() {
  }

  void update() {
    vel.add(acc);
    loc.add(vel);
    acc.set(0, 0, 0);
  }

  void reset() {
    vel.set(0, 0, 0);
    loc.set(0, 0, 0);
    acc.set(0, 0, 0);
  }
}

static class DataPoint {
  float myo;
  float temp;
  float vibr;
  PVector acc = new PVector();

  DataPoint(float myo, float temp, float vibr, 
    float ax, float ay, float az) {
    this.myo = myo;
    this.temp = temp;
    this.vibr = vibr;
    this.acc.set(ax, ay, az);
  }

  String toString() {
    return String.format("myo: %.2f\ntemp: %.2f\nvibr: %.2f\n%s", 
      myo, temp, vibr, acc.toString());
  }
}

PMatrix2D camera(PMatrix2D cameraMatrix, 
  float tx, float ty, float angle, 
  float zoomW, float zoomH) {
  cameraMatrix.set(
    1.0, 0.0, width * 0.5, 
    0.0, 1.0, height * 0.5);
  cameraMatrix.rotate(angle);
  cameraMatrix.scale(zoomW, zoomH);
  cameraMatrix.translate(-tx, -ty);
  setMatrix(cameraMatrix);
  return cameraMatrix;
}

Hello Jeremy and thank you very much for your answer and kind help!
I think with your code what i wanted looks really decent and correct!
First of all to answer to your questions: according to the code of the Arduino the first of the 3 variables is x, the second y and the third z. I looked on the accelerometer [SparkFun Triple Axis Accelerometer Breakout - MMA8452Q (with Headers) - BOB-13926 - SparkFun Electronics] and it shows y as the vertical axis looking up (actually down) towards the sky. so as i have placed the whole mechanism on the human body the z axis is the one which moves forwards or backwards. It also shows the orientation and during my experiment it was showing “Portarait Down” if this helps. After seeing it all again, i couldn’t define which one was the negative and the positive direction of each axis… but i don’t know if it really matters since my vizualisation is more abstract just trying to come to conclusions mostly counting on the magnitude of acceleration so the orientation doesn’t really matter. I just want to see how fast or slow a person moves according to environmental parameters like temperature and vibrations and how him/her muscles react (counted by the muscle sensor). i’m not going to use any of these renderers since working on 3 axes is maybe way to difficult and doesn’t help (and i also unfortunately don’t have the time to do it in 3 axes) i’m only going to use 2 dimensional vectors, as you did it also in the sketch you sent above.

I guess I’m not so advanced in programming as you and I still have a lot to learn in Processing of course,so I just want to make sure I understood correctly your version of the code: instead of scaling the vectors, you used this camera object which zooms in and out so that we can better see the vizualisation right?
Also those circles are for showing how much one dispaces himself comparing each location for every different line of data from the initial txt?
Do you have any ideas how i could draw lines vertical to each of these displacement points so that i can map and vizualise the other data (myo,temp,vibr) too? i mean making a line which shows how big temperature or vibration is,accoriding to their values (temp and myo) or making an ellipse starting from each of these points and not from 0,0 , whose diameter is as big as the value of the muscle sensor?
Also another question: the controls are only working once when i first run the sketch-if i stop it and run it again the picture doesn’t move up or down…why is that happening?

thank you very much!hoping to hear from you soon!! :slight_smile:

Hi cathytsir,

Whoofta, that’s a bundle, so I’ll just touch on two of the smaller topics for now.

On key press controls:

  • Does the sketch lose focus when it is stopped and restarted?
  • Will clicking on the sketch window with the mouse restore focus?
  • When the variable focused == true, do the key controls work or not work?

I’m not too certain on this, so searching this forum, its previous incarnations, and Processing’s issues page on the topic ‘requesting focus’ may give more info. The straightforward way could work better, too – with if-else statements in draw that use the keyPressed, key and keyCode variables.

On mapping data to a shape and/or color:

For the variables myo, temp and vibr, do you know the expected lower and upper bounds (a range) of each? For example, if I am working with an angle of rotation, I can expect a value in either [-180, 180] or [0, 360] degrees.

If you don’t, you could either eyeball it. One range looks like [30, 31]. Or, you could loop through your array of data and find the minimum and maximum values.

Once you have a range, you can use the map function to convert a temperature at step n from its original range to an equivalent value in a target range. The target range, in this case, would be the radius of an ellipse or the height of a bar in a bar graph.

Cheers for now,

Hello again Jeremy and thank you for your answer :slight_smile:
The problem with the image going up and down is solved: as you said it only required clicking inside the window when running the sketch. As for the visualization for each txt file and for each value I found the minimum and maximum value and used this range to map the diamter of ellipses, the size of rectangles , the colours etc. So it was a lot easier than drawing lines perpendicular to each location point !
Thank you a lot again for your kind help!!!the visualization that came out were really close to what I wanted and really cool :slight_smile:

2 Likes