Introduction

Using the accelerometer in applications has become an intuitive and popular way of allowing user input, especially for gaming applications. This tutorial will cover the following in regard to accelerometer use:

Objective-C proficiency is assumed.

Setup and Retrieval Code

Setup Code

Place the following code in the initialization method for whichever class will handle accelerometer input (usually Game).

UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.updateInterval = 1.0/60.0;
accelerometer.delegate = self;

This code handles the initialization of the accelerometer and sets the retrieval rate (60 times/sec in second line of code above).

The third line tells the class that it is a delegate for the accelerometer. This specification is also required in the interface by placing <UIAccelerometerDelegate> in the class's .h file, as shown:

@interface MyClass : MySuperClass <UIAccelerometerDelegate>
.
.
.
@end
Retrieval Code

In order to retrieve data (X, Y, and Z axis measures of force, rated in Gs) from the accelerometer, the following method must be added to the handling class:

- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
   float accelX = acceleration.x;
   float accelY = acceleration.y;
   float accelZ = acceleration.z;
}

Fine-tuning Accelerometer Data

The above code works for retrieving raw data from the accelerometer. However, the raw data is many times not very useful without some sort of processing. For simplicity, the code to-be-presented will represent fine-tuning for one component of the accelerometer data (the Y component), however it can easily be extended to incorporate all three axes.

Class-wide Access to Accelerometer Data

One improvement over the above code would be to allow acceleration data to be accessed by the entire handling class, not just from within the accelerometer: didAccelerate: method. The solution is simple: add a property or ivar to the class (since it will not be a pointer, it does not matter which you use). In this tutorial, we will use:

@property (nonatomic) float accel;

and we will set its value in the accelerometer: didAccelerate: method by the following:

- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
   accel = acceleration.y;
}
Amplify the Acceleration Data

Since acceleration is rated in Gs (one G is 9.88 m/s^2), the raw data may not get to be much higher than 3.0 or 4.0 (although this varies with use). Consequently, we will introduce a #define constant in the handling class's interface to amplify the raw accelerometer data:

#define ACCEL_FACTOR 100  // this value can vary depending on the application

and use it in the accelerometer: didAccelerate: method to amplify the raw data:

- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
   accel = ACCEL_FACTOR * acceleration.y;
}
Filter the Acceleration Data

The final stage of fine-tuning the accelerometer data is to perform what is called low-pass filtering. In short, a low-pass filter allows one to diminish the effect of high-frequency noise (a.k.a. jitter) when reading from a sensor. Since most sensors contain noise, this technique is common.

A simple example of a low-pass filter is a moving averager, and is explained further in the appendix. Simply put, a moving averager computes the average of the last M samples of a sensor and uses that instead of the most recent raw sample. We will introduce one new #define constant and an NSMutableArray in the handling class's interface:

#define NUM_FILTER_POINTS 10  // number of recent points to use in average
 
.
.
.
 
@property (nonatomic, retain) NSMutableArray rawAccel;  // will hold raw samples

Experimenting with NUM_FILTER_POINTS can cause varying effects, including a lag effect, so have fun toying with this! Setting it to a value of 1 will result in no filtering (because the average over 1 value is the value itself), which can be helpful for comparing results.

To initialize the rawAccel array, the following code must be placed in the handling class's initialization method (or in another appropriate place):

self.rawAccel = [NSMutableArray arrayWithCapacity:NUM_FILTER_POINTS];
for (int i = 0; i < NUM_FILTER_POINTS; i++)
{
    [rawAccel addObject:[NSNumber numberWithFloat:0.0]];
}

Then, the accelerometer: didAccelerate: method must be updated to handle the new filtering:

// accelerometer handler
// implement a low-pass filter to extract stable acceleration value
 
- (void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    // insert newest value
    // will push current values over by 1 spot, extending length by 1
 
    [rawAccel insertObject:[NSNumber numberWithFloat: acceleration.y] atIndex:0];
 
 
    // remove oldest value, returning length to NUM_FILTER_POINTS
 
    [rawAccel removeObjectAtIndex:NUM_FILTER_POINTS];
 
 
    // perform averaging
 
    accel = 0.0;
    for (NSNumber *raw in rawAccel)
    {
        accel += [raw floatValue];
    }
    accel *= ACCEL_FACTOR / NUM_FILTER_POINTS;
}

Movement Using Accelerometer Data: An Example

For realistic movement, it is helpful to use the following code in an SPEnterFrameEvent callback:

- (void) onEnterFrameEvent:(SPEnterFrameEvent *)event
{
   float dt = event.passedTime;
 
   myObject.x += accel * dt * dt;
}

The above code will cause the object to move left/right with increasing or decreasing speed (a.k.a the physics definition of acceleration). This is achieved by using “dt * dt” versus just “dt”. (Google basic physics if this is confusing and you are interested).

Note that because dt is much less than 1.0, dt * dt will be even smaller. Because of this, ACCEL_FACTOR must usually be very large. Experiment with it – it is not uncommon for the required ACCEL_FACTOR to reach 50,000 or even 100,000.

Hopefully, this tutorial will get Sparrow users started on using the accelerometer in applications without many prerequisites. Below is an appendix that explains the mathematics of the low-pass filter, for those interested.

Appendix: Mathematics Behind Fine-tuning

The running averager low-pass filter described in this tutorial works by “smoothing” out the sequence of data points read in from the accelerometer. This smoothing results in less prominent spikes or dips in the acceleration data. In Electrical Engineering & Signal Processing, a filter can be described by a difference equation, like the one shown below, for input data “x” and output “y”:

y[n] = h[0] * x[n] + h[1] * x[n-1] + ... + h[M] * x[n-M]

where the values of the array “h” characterize the filter.

In this tutorial, the filter we used was characterized by the h array: h[n] = ACCEL_FACTOR / NUM_FILTER_POINTS, for all n. Mathematically, this is equivalent to:

accel = (ACCEL_FACTOR / NUM_FILTER_POINTS) * rawAccel[0] + (ACCEL_FACTOR / NUM_FILTER_POINTS) * rawAccel[1] + ... + (ACCEL_FACTOR / NUM_FILTER_POINTS) * rawAccel[NUM_FILTER_POINTS-1]

=> accel = (ACCEL_FACTOR / NUM_FILTER_POINTS) * sum{rawAccel}

It should now be apparent why our filter is an averager: summing data and dividing by the number of data points results in an average, exactly what is described by the filter above, with the exception of the amplification by ACCEL_FACTOR.

  tutorials/accelerometer_setup_and_easy_fine-tuning.txt · Last modified: 2013/03/05 10:19 by 127.0.0.1
 
Powered by DokuWiki