Tuesday, November 22, 2011

Gyroscope & Accelerometer - iOS SDK


Gyroscope : A gyroscope measures the rate at which a device rotates around each of the three spatial axes
Accelerometer : measures the acceleration of the device along each of the three spatial axes.

Access Accelerometer using UIAccelerometer

Configuring the accelerometer
#define kAccelerometerFrequency        50.0 //Hz
-(void)configureAccelerometer
{
     UIAccelerometer*  theAccelerometer = [UIAccelerometer sharedAccelerometer];
     theAccelerometer.updateInterval = 1 / kAccelerometerFrequency;
     theAccelerometer.delegate = self;
     // Delegate events begin immediately.
}


Receiving an accelerometer event
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
    UIAccelerationValue x, y, z;
    x = acceleration.x;
    y = acceleration.y;
    z = acceleration.z;
    // Do something with the values.
}

It shows a simplified version of a low-pass filter.

This example uses a low-value filtering factor to generate a value that uses 10 percent of the
unfiltered acceleration data and 90 percent of the previously filtered value


Isolating the effects of gravity from accelerometer data
#define kFilteringFactor 0.1
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
     // Use a basic low-pass filter to keep only the gravity component of each axis.
       accelX = (acceleration.x * kFilteringFactor) + (accelX * (1.0 - kFilteringFactor));
       accelY = (acceleration.y * kFilteringFactor) + (accelY * (1.0 - kFilteringFactor));
    accelZ = (acceleration.z * kFilteringFactor) + (accelZ * (1.0 - kFilteringFactor));
       // Use the acceleration data.
}


shows a simplified high-pass filter computation with constant effect of gravity.
Getting the instantaneous portion of movement from accelerometer data

#define kFilteringFactor 0.1
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
// Subtract the low-pass value from the current value to get a simplified high-pass filter
accelX = acceleration.x - ( (acceleration.x * kFilteringFactor) + (accelX * (1.0 - kFilteringFactor)) );
accelY = acceleration.y - ( (acceleration.y * kFilteringFactor) + (accelY * (1.0 - kFilteringFactor)) );
accelZ = acceleration.z - ( (acceleration.z * kFilteringFactor) + (accelZ * (1.0 - kFilteringFactor)) );
// Use the acceleration data.
}
Handling Accelerometer and Gycometer using COREMOTION

Core Motion is a system framework that obtains motion data from sensors on a device and presents that data to applications for processing.


Core Motion defines a manager class, CMMotionManager, and three classes whose instances encapsulate measurements of motion data of various types:
A CMAccelerometerData object encapsulates a data structure that records a measurement of device acceleration along the three spatial axes. This data derives from the accelerometer.
A CMGyroData object encapsulates a data structure that records a biased estimate of a device’s rate of rotation along the three spatial axes.
A CMDeviceMotion 
object encapsulates processed device-motion data that derives from both the accelerometer and the gyroscope. 


************Handling Accelerometer using CoreMotion************

To start receiving and handling accelerometer data, create an instance of the CMMotionManager class and call one of the following two methods on it:
startAccelerometerUpdates
After this method is called, Core Motion continuously updates the accelerometerData property of CMMotionManager with the latest measurement of accelerometer activity. 
startAccelerometerUpdatesToQueue:withHandler:
Before calling this method, the application assigns an update interval to the accelerometerUpdateInterval property. It also creates an instance of NSOperationQueue and implements a block of the CMAccelerometerHandler type that handles the accelerometer updates.


Configuring the motion manager and starting updates
- (void)startAnimation {
if (!animating) {
// code that configures and schedules CADisplayLink or timer here ...
}
motionManager = [[CMMotionManager alloc] init]// motionManager is an instance variable
motionManager.accelerometerUpdateInterval = 0.01// 100Hz
memset(filteredAcceleration, 0sizeof(filteredAcceleration));
[motionManager startAccelerometerUpdates];
}


- (void)stopAnimation {
if (animating) {
// code that invalidates CADisplayLink or timer here...
}
     [motionManager stopAccelerometerUpdates];
}


Below code shows how the application, in this same method, gets the latest accelerometer data and runs it through a low-pass filter. It then updates the drawing model with the filtered acceleration values and renders its view.
Listing 4-8  Sampling and filtering accelerometer data

- (void)drawView {
// alpha is the filter value (instance variable)
CMAccelerometerData *newestAccel = motionManager.accelerometerData;
filteredAcceleration[0] = filteredAcceleration[0] * (1.0-alpha) + newestAccel.acceleration.x * alpha;
filteredAcceleration[1] = filteredAcceleration[1] * (1.0-alpha) + newestAccel.acceleration.y * alpha;
filteredAcceleration[2] = filteredAcceleration[2] * (1.0-alpha) + newestAccel.acceleration.z * alpha;
[self updateModelsWithAcceleration:filteredAcceleration];
[renderer render];
}
Note: You can apply a low-pass or high-pass filter to acceleration values and thereby isolate the gravity and user-acceleration components:
To apply a low-pass filter, thereby isolating the gravity component, 
To apply a high-pass filter, thereby isolating the user-acceleration component


*******Handling Gyroscope Using CORE MOTION*******

To start receiving and handling rotation-rate data, create an instance of the CMMotionManager class and call one of the following two methods on it:

startGyroUpdates
After this method is called, Core Motion continuously updates the gyroData property of CMMotionManager with the latest measurement of gyroscope activity. 

startGyroUpdatesToQueue:withHandler:
Before calling this method, the application assigns an update interval to the gyroUpdateInterval property.

It also creates an instance of NSOperationQueue and implements a block of the CMGyroHandler type that handles the gyroscope updates.


Creating the CMMotionManager object and setting up for gyroscope updates
- (void)viewDidLoad {
    [super viewDidLoad];
    motionManager = [[CMMotionManager alloc] init];
    motionManager.gyroUpdateInterval = 1.0/60.0;
    if (motionManager.gyroAvailable) {
        opQ = [[NSOperationQueue currentQueue] retain];
        gyroHandler = ^ (CMGyroData *gyroData, NSError *error) {
            CMRotationRate rotate = gyroData.rotationRate;
            // handle rotation-rate data here......
        };
    } else {
        NSLog(@"No gyroscope on device.");
        toggleButton.enabled = NO;
        [motionManager release];
    }
}


Starting and stopping gyroscope updates 
- (IBAction)toggleGyroUpdates:(id)sender {
    if ([[(UIButton *)sender currentTitle] isEqualToString:@"Start"]) {
        [motionManager startGyroUpdatesToQueue:opQ withHandler:gyroHandler];
    } else {
        [motionManager stopGyroUpdates];
    }
 
}