iOS: Accurately determining energy of a bump from accelerometer output
Asked Answered
T

3

2

I am creating a tuning fork app, where you pat the iPhone into the palm of your other hand, or against a soft surface in order to set the fork zinging.

So I would like to detect the energy contained in each 'bump'

(EDIT: Removed a ton of gumpf)

Can anyone help me crack this one?

Tann answered 30/6, 2011 at 15:8 Comment(1)
#5531333Tann
T
5

Thanks to one of the wizards on freenode's #math channel (thanks Igor), I have a really good working solution.

You use the standard method to fire a callback at the maximum frequency possible (100Hz), which will contain the instantaneous acceleration x,y,z values.

I will let the code speak for itself, good code should always speak for itself.

typedef 
struct {
    double x,y,z;
}
vec_d3;

#define RECENT_COUNT 10

#define SMOOTH_IP( x, x_new, fac )  x  =  fac * x  +  ( 1. - fac ) * x_new

- (void) accelerometer: (UIAccelerometer *) accelerometer 
          didAccelerate: (UIAcceleration *) acceleration 
{
    // smooth incoming acceleration values
    static vec_d3 smooth = { DOUBLE_EMPTY, 0, 0 };

    {
        if ( smooth.x == DOUBLE_EMPTY )
        {
            smooth.x = acceleration.x;
            smooth.y = acceleration.y;
            smooth.z = acceleration.z;

            return;
        }

        SMOOTH_IP( smooth.x, acceleration.x, 0.9 );
        SMOOTH_IP( smooth.y, acceleration.y, 0.9 );
        SMOOTH_IP( smooth.z, acceleration.z, 0.9 );
    }

    // keep track of last k smoothed acceleration values
    static vec_d3 recent[ RECENT_COUNT ];
    {
        static int ptr = 0;
        static BOOL gotEnoughData = NO;

        recent[ ptr ] = smooth;

        ptr++;
        if ( ptr == RECENT_COUNT )
        {
            ptr = 0;
            gotEnoughData = YES;
        }

        // return if array not filled yet
        if ( ! gotEnoughData )
            return;
    }

    // get the resultant variation in acceleration over the whole array
    double variation;
    {
        vec_d3 min = smooth, max = smooth;

        for ( int i=0; i < RECENT_COUNT; i++ )
        {
            min.x = MIN( min.x, recent[ i ].x );
            min.y = MIN( min.y, recent[ i ].y );
            min.z = MIN( min.z, recent[ i ].z );

            max.x = MAX( max.x, recent[ i ].x );
            max.y = MAX( max.y, recent[ i ].y );
            max.z = MAX( max.z, recent[ i ].z );
        }

        vec_d3 V = (vec_d3) 
        {
            .x = max.x - min.x,
            .y = max.y - min.y,
            .z = max.z - min.z
        };

        variation = sqrt(
                         V.x * V.x  +
                         V.y * V.y  +
                         V.z * V.z
                         );
    }

    // smooth it
    static double var_smoothed = DOUBLE_EMPTY;
    {
        if ( var_smoothed == DOUBLE_EMPTY )
        {
            var_smoothed = variation;
            return;
        }
        SMOOTH_IP( var_smoothed, variation, 0.9 );
    }


    // see if it's just passed a peak
    {
        static double varSmoothed_last = DOUBLE_EMPTY;
        if ( varSmoothed_last == DOUBLE_EMPTY )
        {
            varSmoothed_last = var_smoothed;
            return;
        }

        static double varSmoothed_preLast = DOUBLE_EMPTY;
        if ( varSmoothed_preLast == DOUBLE_EMPTY )
        {
            varSmoothed_preLast = varSmoothed_last;
            varSmoothed_last = var_smoothed;
            return;
        }

#define THRESHOLD_IMPULSE .15

        if ( varSmoothed_last > varSmoothed_preLast  
            &&  varSmoothed_last > var_smoothed  
            &&  varSmoothed_last > THRESHOLD_IMPULSE )
        {
            LOG ( @"PotPeak @ %f", varSmoothed_last );

            // hit a peak at imp_last
            [self peakedWithImpulse: varSmoothed_last ];
        }

        varSmoothed_preLast = varSmoothed_last;
        varSmoothed_last = var_smoothed;
    }
}
Tann answered 10/7, 2011 at 15:55 Comment(5)
Note: this outputs typically a value between 0 and 0.9 for tapping against my palm. however, swishing it in the air gets up to 1.5.Tann
I'm trying to have a go of this code, however I get errors for DOUBLE_EMPTY. Can you please explain what DOUBLE_EMPTY is? ThanksAshley
DOUBLE_EMPTY is a value that indicates that the variable is empty. Use something like DBL_MAX, i.e.: #define DOUBLE_EMPTY DBL_MAXSneck
@Pi can you please explain what your code is doing, i am trying your code to detect road bumps but i am facing some difficulty. Will you please help me.Whistler
@Pi can you please help in coding this algorithm in my app . open-sci.net/wiki/sealappexamplepotholesWhistler
S
3

First, the questions you asked:

A) Just calibrate. The standing acceleration is the steady pull of gravity, and any sudden deviation is the addition of acceleration due to hitting something. You'll also have to keep track of changes in orientation as reported by the gyroscope, so that you can disregard gravity's sudden shift to a new direction.

B) Integrate the acceleration to get the change in velocity. That squared, times mass of the phone, divided by two, is the energy (in the resting frame).

C) This one's basically unsolvable. Theoretically, if the case is flexible, the acceleration from an impact will tend to appear as a spike-like curve with a characteristic shape, so that if you have a couple of points that aren't on the peak you can estimate the whole shape. But I suspect the device is too rigid and the sampling too sparse. There's nothing you can do about this, unless the hardware will integrate acceleration for you, which I doubt (I don't know the iPhone).

But energy probably isn't the best measure to use anyway; you can deliver as much energy hitting it with a pillow as tapping it with a hammer, but you wouldn't expect a tuning fork to ring as loud. Peak acceleration might be better, but that still relies on good data from the accelerometer.

Could you use the microphone? Try recording what it hears when you tap it against you knee, and look for features that aren't present in ordinary sound, like, I don't know, big low-frequency amplitude, wide spectrum, maybe even a characteristic resonance of the case. It may still respond a little bit to a loud noise in the environment, but then that's quite realistic.

Stern answered 1/7, 2011 at 19:5 Comment(1)
Apologies for re-engineering my question, leaving this answering something that is no longer there. I did it so as to create a better resource ( by way of being more concise ) for anyone searching this in the future. thanks for answering!Tann
G
0

What you want is a Kalman Filter. Google that phrase; writing about one is beyond the scope of an internet forum such as this. There are books and books and books on Kalman Filters.

One issue: The iphone interface isn't really built for what you need. My employer had to work with Apple to access and munge with internals precisely because of this problem. And no, I can't say much about what they did or what the project is.

Regarding the accelerometer reading when held flat: It should register 1G up, not down. There is no way to sense gravity. (Newtonian POV: There's no such thing as a gravity shield. Relativistic POV: Gravity is a fictitious force.) The accelerometer isn't sensing gravity. It is sensing the Earth or table pushing the accelerometer upward.

Grassland answered 2/7, 2011 at 9:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.