Calculate g-force from acceleration for 1 second interval
Asked Answered
J

4

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!
Jacintha answered 25/4, 2018 at 13:20 Comment(4)
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.Dynamometer
I'm not sure if this is programming related, you just need to find right physical formula and understand what data you haveLifeboat
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.Assort
@Assort i've edited my questionJacintha
M
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!

Midday answered 4/5, 2018 at 10:56 Comment(6)
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.Nepheline
@Nepheline 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.Midday
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=.066GNepheline
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.Midday
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.Nepheline
I have edited my answer and included you, thanks for your help!Midday
D
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);
Dynamometer answered 25/4, 2018 at 13:46 Comment(0)
N
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);
Nepheline answered 4/5, 2018 at 1:41 Comment(9)
I already have a working javascript app that shows the graph and the video side-by-side, only that the values are wrong. I have edited my question to include this info.Jacintha
So what does your graph look like? is it just a x,y graph? You only want to show the g's for the AcclX values with respect to time?Nepheline
the x, y and z g-force values in repect with timeJacintha
A question to clarify. Do you want three lines on the graph each representing the x,y,z planes or do you want just want one line representing all planes?Nepheline
separated, three linesJacintha
Is that what your expectingNepheline
Let us continue this discussion in chat.Nepheline
@Binar Web Please see updated answer. I think it's gonna do what you want it to do.Nepheline
@Binar Web Check out the more updated version, I think you will like it a lot better.Nepheline
K
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.

Kirsch answered 5/5, 2018 at 18:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.