Sunday, June 21, 2009

Accelerometer calibration

Several weeks have passed and I'm really busy with my day job so things are going really slow on the prototype. I've got some basic weapon pickups code, and weapon placement similar to xenon2's mechanism. Will see how it feels later on. I have also migrated some bezier path code I had done for Swing 'n Strike, where I can follow a bezier path with constant speed.

The spaceship is controlled with the accelerometer. I would like to keep the finger off from the screen as much as possible since the game will be quite hectic and a finger on the screen would would obstruct the user's view.

However I would like to calibrate, so to speak, the iPhone's position, i.e. record the offset when the game is launched and assume that position to be the neutral center position of the spaceship. It turned out to be very easy to do, when you know how the accelerometer works. I was puzzled at first and thought that the acceloremeter would only give me values when I moved the iPhone around. It is interesting to know that it will always give you a reading for each axis, roughly between -1 and 1 when it's not moving. Why? Well, gravity (which is acceleration... approx 9.8m/sec^2) is acting on the device). So that 1g acting on it will be represented as a vector acting on the accelerometers. You just record them in some offsets and then subtract that during the game. I'm guessing that astronauts in space are screwed since there will be no gravity acting on the device :)

So in your accelerometer:didAccelerate: method you need to record the starting offsets if you haven't already done so, and the subtract them from the actual readings later on during the game. I also discovered that the very first view readings of the accelerometer are bogus, so I skip the first few frames. (Averaging a bunch of frames would probably be better)


- (void)accelerometer:(UIAccelerometer*)accelerometer didAccelerate:(UIAcceleration*)acceleration {
static int framesLeftToRecord = 30; //the number of frames to skip to record the offset
static BOOL recordOffset = YES; //whether we should record the offset or not
//What we are doing is that we let some time pass to get valid acceleration values as offsets
if (framesLeftToRecord == 0)
{
if (recordOffset)
{
NSLog(@"setting start acceloremeter %f,%f,%f",acceleration.x, acceleration.y, acceleration.z);
_startAccelerometer[0] = acceleration.x;
_startAccelerometer[1] = acceleration.y;
_startAccelerometer[2] = acceleration.z;
recordOffset = NO;
}
} else
{
framesLeftToRecord--;
}

//Use a basic low-pass filter to only keep the gravity in the accelerometer values
_accelerometer[0] = (acceleration.x - _startAccelerometer[0]) * kFilteringFactor + (_accelerometer[0]) * (1.0 - kFilteringFactor);
_accelerometer[1] = (acceleration.y - _startAccelerometer[1]) * kFilteringFactor + (_accelerometer[1]) * (1.0 - kFilteringFactor);
_accelerometer[2] = (acceleration.z - _startAccelerometer[2]) * kFilteringFactor + (_accelerometer[2]) * (1.0 - kFilteringFactor);
}

No comments: