How to calculate correct velocity values from accerometer

Aft3rmath picture Aft3rmath · May 14, 2012 · Viewed 9.5k times · Source

I need to calculate iphone velocity in space, punch speed for example.
My first question is: Are the acceleration values accelerometer:didAcceleratewith this filtering actions:

gravity_x = acceleration.x * kFilteringFactor + gravity_x * (1.0 - kFilteringFactor);
gravity_y = acceleration.y * kFilteringFactor + gravity_y * (1.0 - kFilteringFactor);
gravity_z = acceleration.z * kFilteringFactor + gravity_z * (1.0 - kFilteringFactor);
float gravityNorm = sqrt(gravity_x * gravity_x + gravity_y * gravity_y + gravity_z * gravity_z);
accelX = acceleration.x - gravity_x / gravityNorm;
accelY = acceleration.y - gravity_y / gravityNorm;
accelZ = acceleration.z - gravity_z / gravityNorm;

the same as using CoreMotion's
motionManager.deviceMotion.userAcceleration.x; motionManager.deviceMotion.userAcceleration.y; motionManager.deviceMotion.userAcceleration.z; ?

Next, according to same questions, I do the following

    const float accuracy=10;
    float accx = (float) ((int)  (accelX * accuracy))/ accuracy;
    float accy=  (float) ((int)  (accelY * accuracy))/ accuracy;
    float accz=  (float) ((int)  (accelZ * accuracy))/ accuracy;

for rounding values, then, as I think, I obtain current speed

float dx = accx*9.81+prevX*9.81;
float dy = accy*9.81+prevY*9.81;
float dz = accz*9.81+prevZ*9.81;

speed_after_x = dx/2*myAccelerometer.updateInterval+speed_before_x;
speed_after_y = dy/2*myAccelerometer.updateInterval+speed_before_y;
speed_after_z = dz/2*myAccelerometer.updateInterval+speed_before_z;

according to iphone accelerometer speed and distance
then

    prevX=accx;
    prevY=accy;
    prevZ=accz;

    speed_before_x=speed_after_x;
    speed_before_y=speed_after_y;
    speed_before_z=speed_after_z;

and finally calculating speed as vector's length

    float speed = sqrt(speed_after_x*speed_after_x+speed_after_y*speed_after_y+speed_after_z*speed_after_z);
    if (max_speed<speed)  max_speed = speed;

But speed value, that is containing in label, is always increasing. I mean if i moved device and then stopped, speed value doesn't become to 0. Doesn't acceleration compensate itself at 0?

Answer

Diziet picture Diziet · May 14, 2012

Here's the example code I managed to hash out. I'll just put it here for now:

//
//  ViewController.m
//  Acce
//
//  Created by Diziet on 14/05/2012.
//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//

#import "ViewController.h"

@interface ViewController () {
    UIAccelerationValue gravX;
    UIAccelerationValue gravY;
    UIAccelerationValue gravZ;
    UIAccelerationValue prevVelocity;
    UIAccelerationValue prevAcce;
}

@property (strong) UIAccelerometer *sharedAcc;

@end

@implementation ViewController

@synthesize sharedAcc = _sharedAcc;

#define kAccelerometerFrequency        50.0 //Hz
#define kFilteringFactor 0.1

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.sharedAcc = [UIAccelerometer sharedAccelerometer];
    self.sharedAcc.delegate = self;
    self.sharedAcc.updateInterval = 1 / kAccelerometerFrequency; 

    gravX = gravY = gravZ = prevVelocity = prevAcce = 0.f;
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

- (UIAccelerationValue)tendToZero:(UIAccelerationValue)value {
    if (value < 0) {
        return ceil(value);
    } else {
        return floor(value);
    }
}

#define kAccelerometerFrequency        50.0 //Hz
#define kFilteringFactor 0.1
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
    gravX = (acceleration.x * kFilteringFactor) + (gravX * (1.0 - kFilteringFactor));
    gravY = (acceleration.y * kFilteringFactor) + (gravY * (1.0 - kFilteringFactor));
    gravZ = (acceleration.z * kFilteringFactor) + (gravZ * (1.0 - kFilteringFactor));

    UIAccelerationValue accelX = acceleration.x - ( (acceleration.x * kFilteringFactor) + (gravX * (1.0 - kFilteringFactor)) );

    UIAccelerationValue accelY = acceleration.y - ( (acceleration.y * kFilteringFactor) + (gravY * (1.0 - kFilteringFactor)) );
    UIAccelerationValue accelZ = acceleration.z - ( (acceleration.z * kFilteringFactor) + (gravZ * (1.0 - kFilteringFactor)) );
    accelX *= 9.81f;
    accelY *= 9.81f;
    accelZ *= 9.81f;
    accelX = [self tendToZero:accelX];
    accelY = [self tendToZero:accelY];
    accelZ = [self tendToZero:accelZ];

    UIAccelerationValue vector = sqrt(pow(accelX,2)+pow(accelY,2)+pow(accelZ, 2));
    UIAccelerationValue acce = vector - prevVelocity;
    UIAccelerationValue velocity = (((acce - prevAcce)/2) * (1/kAccelerometerFrequency)) + prevVelocity;

    NSLog(@"X %g Y %g Z %g, Vector %g, Velocity %g",accelX,accelY,accelZ,vector,velocity);

    prevAcce = acce;
    prevVelocity = velocity;
}

@end

It'll need modifying for your needs, especially as I'm just throwing the values away afterwards. The resultant value 'velocity' logged as 'Velocity' tends back towards extremely small negative values after acceleration events have ceased, e.g. *10^-17. So yeah, that's practically zero in my book. You'll want to do some rounding in there and probably even scale the values up. I don't think I could get it higher than 0.2 or 0.3 but then again I don't want to hurl my phone across the room (yet).