Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
tutorials:accelerometer_setup_and_easy_fine-tuning [2011/08/23 05:05]
menehune17
tutorials:accelerometer_setup_and_easy_fine-tuning [2013/03/05 10:19] (current)
Line 1: Line 1:
 +==== 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:
 +
 +  * [[accelerometer_setup_and_easy_fine-tuning#​setup_and_retrieval_code|Setup and Retrieval Code]]
 +  * [[accelerometer_setup_and_easy_fine-tuning#​fine-tuning_accelerometer_data|Fine-tuning Accelerometer Data]]
 +  * [[accelerometer_setup_and_easy_fine-tuning#​movement_using_accelerometer_data:​_an_example|Movement Using Accelerometer Data: An Example]]
 +  * [[accelerometer_setup_and_easy_fine-tuning#​appendix:​_mathematics_behind_fine-tuning|Appendix:​ Mathematics Behind Fine-tuning]]
 +
 +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).
 +
 +<code objc>
 +UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];​
 +accelerometer.updateInterval = 1.0/60.0;
 +accelerometer.delegate = self;
 +</​code>​
 +
 +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:
 +
 +<code objc>
 +@interface MyClass : MySuperClass <​UIAccelerometerDelegate>​
 +.
 +.
 +.
 +@end
 +</​code>​
 +
 +== 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:
 +<code objc>
 +- (void) accelerometer:​(UIAccelerometer *)accelerometer didAccelerate:​(UIAcceleration *)acceleration
 +{
 +   float accelX = acceleration.x;​
 +   float accelY = acceleration.y;​
 +   float accelZ = acceleration.z;​
 +}
 +</​code>​
 +
 +=== 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:
 +<code objc>
 +@property (nonatomic) float accel;
 +</​code>​
 +
 +and we will set its value in the accelerometer:​ didAccelerate:​ method by the following:
 +
 +<code objc>
 +- (void) accelerometer:​(UIAccelerometer *)accelerometer didAccelerate:​(UIAcceleration *)acceleration
 +{
 +   accel = acceleration.y;​
 +}
 +</​code>​
 +
 +== 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:
 +
 +<code objc>
 +#define ACCEL_FACTOR 100  // this value can vary depending on the application
 +</​code>​
 +
 +and use it in the accelerometer:​ didAccelerate:​ method to amplify the raw data:
 +<code objc>
 +- (void) accelerometer:​(UIAccelerometer *)accelerometer didAccelerate:​(UIAcceleration *)acceleration
 +{
 +   accel = ACCEL_FACTOR * acceleration.y;​
 +}
 +</​code>​
 +
 +== 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  [[accelerometer_setup_and_easy_fine-tuning#​appendix:​_mathematics_behind_fine-tuning|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:
 +<code objc>
 +#define NUM_FILTER_POINTS 10  // number of recent points to use in average
 +
 +.
 +.
 +.
 +
 +@property (nonatomic, retain) NSMutableArray rawAccel; ​ // will hold raw samples
 +</​code>​
 +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):
 +<code objc>
 +self.rawAccel = [NSMutableArray arrayWithCapacity:​NUM_FILTER_POINTS];​
 +for (int i = 0; i < NUM_FILTER_POINTS;​ i++)
 +{
 +    [rawAccel addObject:​[NSNumber numberWithFloat:​0.0]];​
 +}
 +</​code>​
 +
 +Then, the accelerometer:​ didAccelerate:​ method must be updated to handle the new filtering:
 +<code objc>
 +// 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;​
 +}
 +</​code>​
 +
 +=== Movement Using Accelerometer Data: An Example ===
 +
 +For realistic movement, it is helpful to use the following code in an SPEnterFrameEvent callback:
 +
 +<code objc>
 +- (void) onEnterFrameEvent:​(SPEnterFrameEvent *)event
 +{
 +   float dt = event.passedTime;​
 +   
 +   ​myObject.x += accel * dt * dt;
 +}
 +</​code>​
 +
 +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":​
 +<​code>​
 +y[n] = h[0] * x[n] + h[1] * x[n-1] + ... + h[M] * x[n-M]
 +</​code>​
 +
 +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:
 +<​code>​
 +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}
 +</​code>​
 +
 +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.
 
 
Powered by DokuWiki