7

I have extracted a CSV file with accelerometer data (in m/s2) from GoPro metadata file (github library).

One second of accelerometer contains ~200 samples of data on 3 axis. A sample of this file looks like this:

accelerometer data sample

In PHP, for each instantaneous value on X axis, I convert m/s2 like this:

function convert_meters_per_second_squared_to_g($ms2) {
    // 1g = 9.80665 m/s2
    return $ms2 * 0.101971621297793; // 1 / 9.80665 == 0.101971621297793
}

Sample code for 200 rows (1 second) of CSV file:

$acc_x_summed_up = 0;
if (($handle = fopen($filepath, "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        list ($millis, $acc_x, $acc_y, $acc_z) = $data;

        $acc_x_summed_up += $acc_x;
    }
}

$g_force = convert_meters_per_second_squared_to_g($acc_x_summed_up);

But how do I show the g-force value for each second on X axis? I tried to sum up the values and convert them, but the result is clearly wrong, as I get values up to 63 G.

[ UPDATE: ]

  1. The instant g-force values (all 3 axis, separated) are displayed on a graph (using highcharts). The gopro video file is displayed (using YouTube javascript API) side-by-side with the graph and played real time.
  2. The graph and video are already working fine side by side. Only the g-force values are wrong.
    Note: The video file has a g-force overlay (embeded in it) showing 2 axis (x,y).
  3. I have rewarded @Joseph_J just because it seemed a good solution and because I'm forced to give the reward (over the weekend) by SO system. Thanks everyone for your answers!
Binar Web
  • 867
  • 1
  • 11
  • 26
  • Instead of sum I think you should actually calculate the average, i.e. the average x, y, and z acceleration over the course of a second. – Terry Apr 25 '18 at 13:43
  • I'm not sure if this is programming related, you just need to find right physical formula and understand what data you have – mleko Apr 25 '18 at 13:49
  • I'm not clear what you are asking for. The gyro data from the same metadata file should indicate the orientation of the camera with respect to the plane of gravity, If your camera is motionless, you should be able to (within the limits of error and noise), convert the gyro data to and from the accelerometer data using the G constant. What do you ultimately want from this data?. You really need to combine the gyro, acceleration (running both through a noise filter) and GPS info (to correct for drift) to do anything useful with a moving camera. – wordragon May 01 '18 at 04:41
  • @wordragon i've edited my question – Binar Web May 01 '18 at 08:20

4 Answers4

2

I believe you are treating each instantaneous value as if it has occurred over 1 second, rather than instantaneously.

I'd say your best bet is to do each calculation by multiplying $acc_x by the resolution of your data divided by gravity's acceleration. So in your case, the resolution of your data is 5ms or one two-hundredth of a second, meaning your calculation should be $acc_x * 0.005/9.80665.

Using the information you provided, the 63G result that you got should be more like 0.315G. This seems more appropriate, though I'm not sure the context of the data.

EDIT: I forgot to mention that you should still sum all values that you receive from $acc_x * 0.005/9.80665 over 200 values, (you can choose to do this in blocks, or do it in running, doing in blocks will be less taxing on the system, but running will be more accurate). Pointed out by @Joseph_J

EDIT 2: As per your request of a source, I could not find much from calculating the average acceleration (and therefore g-force), but you can use the same principal behind average velocity from velocity over time graphs, however, I did find a scenario similar to yours here: Source and Source 2

Hope this helps!

  • 1
    I'm confused by this. I don't see how you can just arbitrarily reduce an acceleration value by a percentage equal to a time interval. What if my resolution was 2 seconds. That would scale my Ax by 2. How did you obtain the .315G value? By your method if I take Ax @ t = 5 I would get .72*.005/9.8 = .000367G. – Joseph_J May 05 '18 at 06:51
  • @Joseph_J Firstly, It should be more so thought of as an average of the acceleration values over 1 second. In that sense, what Terry said appears to be more accurate in terms of explanation, however, they have the same result. Also, remember you obtain g-force by looking at average acceleration over 1 second, not over the resolution time of the data. Finally, I arrived at the 0.315G value by simply reversing the operations that he did on 63G i.e. I multiplied by gravitational acceleration, then multiplied by 5ms over gravitational acceleration. – Joshua Nicholl May 05 '18 at 07:05
  • Following your formula: I take Ax @ t = 5 I would get .72*.005/9.8 = .000367G which != .315G. The 63G value was done by taking the sum of all the values then converting. Terry's solution takes the literal average of the acceleration over 1 sec. To get G's all you have to do it divide the Ax/9.8 @ t=5 .65/9.8=.066G – Joseph_J May 05 '18 at 07:17
  • The 63G value that Binar Web obtained was given from the summation of the values of the acceleration per 5ms. This effectively gets the net acceleration, or at least as close as 5ms resolution allows, however, Binar's mistake was that he treated each data point as if it occurred over 1 second. To get this result I do the summation of the accelerations, convert to g-force, then multiply by the the percentage of a second that the resolution is. So, in this case, since I can't see all of the data, I used 63G, then divided by 200 since 1s/0.005s = 200. – Joshua Nicholl May 05 '18 at 07:33
  • That makes more sense now that I know your reverse engineering the 63G total. Your answer made it sound as though that is what you are doing per Ax value. – Joseph_J May 05 '18 at 07:41
  • 1
    I have edited my answer and included you, thanks for your help! – Joshua Nicholl May 05 '18 at 07:42
1

As per my comment, summing it up doesn't work because force is not additive over time. What you want is to calculate the average acceleration:

function convert_meters_per_second_squared_to_g($acc_array) {
    $acc_average = array_sum($acc_array)/count($acc_array);
    return $acc_average * 0.101971621297793;
}

$acc_x_array = [];
if (($handle = fopen($filepath, "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
        list ($millis, $acc_x, $acc_y, $acc_z) = $data;

        $acc_x_array[] = $acc_x;
    }
}

$g_force = convert_meters_per_second_squared_to_g($acc_x_array);
Terry
  • 63,248
  • 15
  • 96
  • 118
1

THE UPDATED UPDATED SOLUTION

This solution will take your CSV and create an array containing your time, Ax, Ay, & Az values after they have been converted to G's. You should be able to take this array and feed it right into your graph.

The value displayed at each interval will be the average acceleration "at" the interval no before or after it.

I added a parameter to the function to allow for you to define how many intervals per second that you want to display on your graph. This will help smooth out your graph a bit.

I also set the initial and final values. Since this finds the average acceleration at the interval it needs data on both sides of the interval. Obviously at 0 we are missing the left hand side and on the last interval we are missing the right hand side.

I chose to use all the data from one interval to the next, this overlaps half the values from one interval to the next. This will smooth out(reduce the noise) of the averages instead of pickup up from one interval where the other left off. I added a parameter where you can toggle the overlap on and off.

Hope it works for you!

function formatAccelData($data, $split, $scale, $overlap = TRUE){

  if(!$data || !$split || !$scale || !is_int($split) || !is_int($scale)){

    return FALSE;

  }

  $g = 9.80665;
  $round = 3;

  $value1 = 1;
  $value2 = 2;

  if(!$overlap){ //Toggle overlapping data.

    $value1 = 2;
    $value2 = 1;

  }

  //Set the initial condition at t=0;
  $results =  array();
  $results[0]['seconds'] = 0;
  $results[0]['Ax'] = round(($data[0][1])/$g, $round);
  $results[0]['Ay'] = round(($data[0][2])/$g, $round);
  $results[0]['Az'] = round(($data[0][3])/$g, $round);

  $count = 1;
  $interval = (int)(1000/$split)/$scale;

  for($i = $interval; $i < count($data); $i += $interval){

      $Ax = $Ay = $Az = 0;

      for($j = $i - ($interval/$value1); $j < $i + ($interval/$value1); $j++){

        $Ax += $data[$j][1];
        $Ay += $data[$j][2];
        $Az += $data[$j][3];

      }

    $results[$count]['seconds'] = round($count/$scale, $round);
    $results[$count]['Ax'] = round(($Ax/($interval * $value2))/$g, $round);
    $results[$count]['Ay'] = round(($Ay/($interval * $value2))/$g, $round);
    $results[$count]['Az'] = round(($Az/($interval * $value2))/$g, $round);

    $count++;

  }


array_pop($results); //We do this because the last interval
//will not have enought data to be calculated.

//Set the final condition with the data from the end of the last complete interval.
$results[$count - 1]['seconds'] = round(($count - 1)/$scale, $round);
$results[$count - 1]['Ax'] = round(($data[$i - $interval][1])/$g, $round);
$results[$count - 1]['Ay'] = round(($data[$i - $interval][2])/$g, $round);
$results[$count - 1]['Az'] = round(($data[$i - $interval][3])/$g, $round);

return $results;

}

To use:

$data = array_map('str_getcsv', file($path));

$split = 5; //(int) - # of milliseconds inbetween datapoints.
$scale = 4; // (int) # of data points per second you want to display.
$overlap = TRUE;  //(Bool) - Overlap data from one interval to the next.
$results = formatAccelData($data, $split, $scale, $overlap);

print_r($results);  

THE OLD UPDATED SOLUTION

Remember, this function takes the average leading up to the interval. So it's really a half an interval behind.

function formatAccelData($data, $step){

  $fps = 1000/$step;

  $second = 1;
  $frame = 0;
  $count = 0;

  for($i = 0; $i < count($data); $i += $fps){

      $Ax = $Ay = $Az = 0;

      for($j = 0; $j < $fps; $j++){

        $Ax += $data[$frame][1];
        $Ay += $data[$frame][2];
        $Az += $data[$frame][3];

        $frame++;

      }

    $results[$count]['seconds'] = $second;
    $results[$count]['Ax'] = ($Ax/$fps) * 0.101971621297793;
    $results[$count]['Ay'] = ($Ay/$fps) * 0.101971621297793;
    $results[$count]['Az'] = ($Az/$fps) * 0.101971621297793;

    $second++;
    $count++;
  }

return $results;

}

How to use:

$data = array_map('str_getcsv', file($path));

$step = 5; //milliseconds

$results = formatAccelData($data, $step);

print_r($results);
Joseph_J
  • 3,654
  • 2
  • 13
  • 22
1

Maybe your question can be seen as equivalent to asking for the net change in velocity between samples at one-second intervals?

In that sense, what you need to do is to integrate-up all the small accelerations in your 5ms intervals, so as to compute the net change in velocity over a period of one second (i.e. 200 samples). That change in velocity, divided by the 1-second interval, represents an average acceleration during that 1-second period.

So, in your case, what you'd need to do is to add up all the AcclX, AcclY & AcclZ values over a one-second period, and multiply by 0.005 to get the vector representing the change in velocity (in units of metres per second). If you then divide that by the one-second total extent of the time window, and by 9.80665m/s^2, you'll end up with the (vector) acceleration in units of G. If you want the (scalar) acceleration you can then just compute the magnitude of that vector, as sqrt(ax^2+ay^2+az^2).

You could apply the same principle to get an average acceleration over a different time-window, so long as you divide the sum of AcclX,AcclY,AcclY (after multiplying by the 0.005s inter-sample time) by the duration of the time window over which you've integrated. This is just like approximating the time-derivative of a function f(t) by (f(t+d) - f(t))/d. In fact, this is a better approximation to the derivative at the midpoint of the time-interval, namely t+d/2. For example, you could sum up the values over a 2s window, to get an average value at the centre of that 2s timespan. There's no need to just report these average accelerations every two seconds; instead you could simply move the window along 0.5s to get the next reported average acceleration 0.5s later.

rwp
  • 1,786
  • 2
  • 17
  • 28