Still on the BigNerdRanch iOS Development book.
In the Accelerometer chapter, they first implement accelerometer tracking but it's fairly jumpy. They then suggest to apply a low pass filter to it by changing the original code:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
HypnosisView *hv = (HypnosisView *)[self view];
[hv setXShift:10.0 * [acceleration x]];
[hv setYShift:10.0 * [acceleration y]];
[hv setNeedsDisplay];
}
to this:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
HypnosisView *hv = (HypnosisView *)[self view];
float xShift = [hv xShift] * 0.8 + [accel x] * 2.0;
float yShift = [hv yShift] * 0.8 + [accel y] * 2.0;
[hv setXShift:xShift];
[hv setYShift:yShift];
[hv setNeedsDisplay];
}
Fairly simple question: where do they get these values from? I've been looking through the documentation and I found something about low pass filters, which suggests the following code:
#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.
}
However, I first tried with that code and I got an error (by analyzing my app) saying 'the left value of '*' is a garbage value'. My accelerometer tracking didn't work either.
I'm fairly confused as to what these values mean. For example, in the first part of the code, why do they multiply the acceleration values by 10? To get a 'bigger' movement? I could make some sense out of that, but the second code with the low pass filter makes absolutely no sense to me.
accelX = (acceleration.x * kFilteringFactor) + (accelX * (1.0 - kFilteringFactor));
Whats happening in this code you are multiplying the acceleration at the moment by the Filtering factor 0.1 and then adding it to the filtered acceleration of the last time an update was called by 0.9.
This is pretty much getting the new value and adding it as 10% of the total accelX the other 90% is made up of the previous value which depends on the value before that, which depends on the value before that and so on. This cuts out high frequency values as only allows 10% of any change to go through to the new accelX value.
The KFilteringFactor of 0.1 makes this filter cut out all high frequencies. You will definitely want to experiment by changing this value to suit your particular application.
Since you're working through the Big Nerd Ranch Book - a good idea would be to go on to the Book's discussion forum.
For more information have a look at the Wikepedia article about low pass filters.
And for another example of filtering have a look at Apple's AccelerometerGraph example
Also - think if you take kFilteringFactor to be 0.2 which gives the multipliers for the current value to be 0.8 which is 1 - 0.2, and the multiplier for the new value is 2.0 because it's 0.2 x 10
I suppose 10 is the scaling factor to give reasonable values.
Related
I am trying to calculate Jerk (http://en.wikipedia.org/wiki/Jerk_(physics)) and jounce (http://en.wikipedia.org/wiki/Jounce) with the acceleration data from the accelerometer. I think I have Jerk figured out, but I am not sure what I am doing for jounce is correct. Can anyone confirm or deny what I am doing is giving me correct values (Do I need to take into consideration time?)
#define kFilteringFactor 0.4
float prevAccelerationX;
float prevAccelerationY;
float prevAccelerationZ;
float prevJerkX;
float prevJerkY;
float prevJerkZ;
- (void)viewDidLoad
{
[super viewDidLoad];
prevAccelerationX = 0;
prevAccelerationY = 0;
prevAccelerationZ = 0;
prevJerkX = 0;
prevJerkY = 0;
prevJerkZ = 0;
[self changeFilter:[LowpassFilter class]];
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:1.0 / kUpdateFrequency];
[[UIAccelerometer sharedAccelerometer] setDelegate:self];
}
// UIAccelerometerDelegate method, called when the device accelerates.
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
float pax = prevAccelerationX;
float pay = prevAccelerationY;
float paz = prevAccelerationZ;
float pjx = prevJerkX;
float pjy = prevJerkY;
float pjz = prevJerkZ;
prevAccelerationX = acceleration.x - ( (acceleration.x * kFilteringFactor) +
(prevAccelerationX * (1.0 - kFilteringFactor)) );
prevAccelerationY = acceleration.y - ( (acceleration.y * kFilteringFactor) +
(prevAccelerationY * (1.0 - kFilteringFactor)) );
prevAccelerationZ = acceleration.z - ( (acceleration.z * kFilteringFactor) +
(prevAccelerationZ * (1.0 - kFilteringFactor)) );
// Compute the derivative (which represents change in acceleration).
float jerkX = ABS((prevAccelerationX - pax));
float jerkY = ABS((prevAccelerationY - pay));
float jerkZ = ABS((prevAccelerationZ - paz));
prevJerkX = jerkX - ( ( jerkX * kFilteringFactor) +
(prevJerkX * (1.0 - kFilteringFactor)));
prevJerkY = jerkY- ( (jerkY * kFilteringFactor) +
(prevJerkY* (1.0 - kFilteringFactor)) );
prevJerkZ = jerkZ - ( (jerkZ * kFilteringFactor) +
(prevJerkZ * (1.0 - kFilteringFactor)) );
// Compute the derivative (which represents change in acceleration).
float jounceX = ABS((prevJerkX - pjx));
float jounceY = ABS((prevJerkY - pjy));
float jounceZ = ABS((prevJerkZ - pjz));
}
In order to calculate derivatives, yes, you need to take time into consideration. Basically you can estimate jerk with just (a2-a1)/samplingtime. Its time derivative is similar. Your way of using kFilteringFactor seems weird to me but might work for your particular sampling time. You should not take ABS(), as it is perfectly valid for the derivative to be negative.
However, one big issue is probably going to be low sampling frequency. Sampling frequencies in phones are usually around 60 Hz. That means your actual bandwidth for acceleration is 30 Hz (the Nyquist frequency). Halve that and that's your jerk bandwidth. Halve that and your bandwidth for jounce, namely 7.5 Hz. Roughly speaking. All jerks (still a funny word) over 15 Hz and jounces over 7.5 Hz do not disappear but instead are aliased on top of your results. So not only you miss some information, the information you miss actually causes even more damage to your results. Properly done, you'd need low pass filtering before each derivative.
take several time series points from the acclerometer and perform B-Spline Interpolation and find the control points.
Take those points and utilize a a 3rd degree Berstein polynomial, take its first derivative and feed the control points from the B-Spline solution into the derived polynomial where t is between 0 and 1 (assuming 1 Hz sampling rate .. 0 to 1 interpolates everything in that second of time). Those values will be the Jerk/Jounce. You'll be computing this for front and side and acceleration values.
I am working on app which is used to display the current G-Force value of a moving car on iPhone. I don't know how to calculate the G-force value using the iPhone accelerometer values. Also, I have to calculate distance and speed using these values.
Have your class (ViewController, or whatever) implement the UIAccelerometerDelegate protocol. Then
-(void)startListening {
UIAccelerometer *meter = [UIAccelerometer sharedAccelerometer];
meter.updateInterval = 1.0; // One second
meter.delegate = self;
}
Your delegate can then use the UIAcceleration object given it by the UIAccelerometer to do whatever it is you need. For instance, if you only need the magnitude of the iPhone's acceleration, you could, with a double accelMagnitude instance variable, have:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
accelMagnitude = sqrt(acceleration.x * acceleration.x
+ acceleration.y * acceleration.y
+ acceleration.z * acceleration.z);
[self refreshDisplay: accelMagnitude];
}
where refreshDisplay does whatever displaying you need.
If you can calibrate your accelerometer while the car is at rest, you can later get the horizontal force component by simple vector arithmetics:
Fhorizontal = Ftotal - Fvertical
I have read the accelerometer is not that accurate, so I think empirical fine tuning will be needed.
Once you calibrate, meassure/filter (and convert to the proper unit) the accelerometer data, speed can be aproximated by (Trapezoid Rule)
Speed_i+1 = Speed_i + (interval_time/2)* (Accel_i+Accel_i+1)
Same fashion for the distance. If you feel like having more accuracy you could try higher order quadrature formulas (like SimpsonĀ“s).
I doing a sound application on accelerometer.Its play different sound for movement by calculating accelerometer value.But how can i find the accelerometer direction that the user move x-axis plus or minus and y-axis plus or minus.How can i find this value on accelerometer.
Please give some instruction or helping code or project.
You need to perform a vector addition and calculate the Summation of 2 vectors to get the resultant vector. The above article explains all the common methods of calculating it. But doing it in Programmatically you just have to apply Pythagoras theorem and Tan theta = b/a
I think you would need the magnetometer direction (to at least give you a bearing you could always compare against), as well as using the vector math mentioned above. This article does a better job of explaining how to add vectors (the first one glosses over the most likely case by just saying it's "hard")...
http://blog.dotphys.net/2008/09/basics-vectors-and-vector-addition/
You have to represent it using vectors, there is a delegate method below which details what you need to do.
Now I haven't taken a look at the API too much yet, but it I believe your direction vector is returned to you from the accelerometer.
There is a delegate method which returns the values you will need.
The following code may help from a tutorial you should take a look at here:
- (void)acceleratedInX:(float)xx Y:(float)yy Z:(float)zz
{
// Create Status feedback string
NSString *xstring = [NSString stringWithFormat:
#"X (roll, %4.1f%%): %f\nY (pitch %4.1f%%): %f\nZ (%4.1f%%) : %f",
100.0 - (xx + 1.0) * 100.0, xx,
100.0 - (yy + 1.0) * 100.0, yy,
100.0 - (zz + 1.0) * 100.0, zz
];
self.textView.text = xstring;
// Revert Arrow and then rotate to new coords
float angle = atan2(xx, yy);
angle += M_PI / 2.0;
CGAffineTransform affineTransform = CGAffineTransformIdentity;
affineTransform = CGAffineTransformConcat( affineTransform, CGAffineTransformMakeRotation(angle));
self.xarrow.transform = affineTransform;
}
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
[self acceleratedInX:acceleration.x Y:acceleration.y Z:acceleration.z];
}
There is also an easy to read article which explains it clearly here along with sample code.
I'm asking them at 50Hz / 50 times per second for data. When I suddenly flip the device on the x-axis by 90 degrees while the device was flat on a table with display facing up bevore, the values move pretty slowly to the "target" value for that position.
Now the weird thing is: If I increase the measurement-rate, the value will move faster to that new value upon suddenly flipping the device by 90 degrees. But if I just ask once per second for the new value, it take's very long until the value reaches the target. What can be the reason for this?
I don't do any kind of data aggregation, and don't accumulate anything. I just do some simple filtering to get rid of the noise. My method looks like this:
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
// Use a basic low-pass filter to only keep the gravity in the accelerometer values for the X and Y axes
// accelerationX is an instance variable
accelerationX = acceleration.x * 0.05 + accelerationX * (1.0 - 0.05);
// round
int i = accelerationX * 100;
float clippedAccelerationValue = i;
clippedAccelerationValue /= 100;
[self moveViews:clippedAccelerationValue];
}
later on, in my -moveViews: method, I do this:
-(IBAction)moveSceneForPseudo3D:(float)accelerationValue {
if(fabs(lastAccelerationValue - accelerationValue) > 0.02) { // some little treshold to prevent flickering when it lays on a table
float viewAccelerationOffset = accelerationValue * 19 * -1;
newXPos = initialViewOrigin + viewAccelerationOffset;
myView.frame = CGRectMake(newXPos, myView.frame.origin.y, myView.frame.size.width, myView.frame.size.height);
lastAccelerationValue = accelerationValue;
}
}
As a result, of the device gets turned 90 degrees on the x-achsis, or 180 degrees, the view just moves pretty slowly to it's target position. I don't know if that's because of the physics of the accelerometers, or if it's a bug in my filtering code. I only know that there are fast paced games where the accelerometers are used for steering, so I almost can't imagine that's a hardware problem.
This line:
accelerationX = acceleration.x * 0.05 + accelerationX * (1.0 - 0.05);
is a low-pass filter, which works by computing a moving average of the x acceleration. In other words, each time that callback is called, you're only moving the accelerationX by 5% towards the new accelerometer value. That's why it takes many iterations before accelerationX reflects the new orientation.
What you should do is increase the 0.05 value, to say 0.2. I'd make a global #define and play around with different values along with different refresh rates.
I remember seeing the code for a Highpass filter a few days back somewhere in the samples, however I can't find it anywhere now! Could someone remember me where the Highpass filter implementation code was?
Or better yet post the algorithm?
Thanks!
From the idevkit.com forums:
#define kFilteringFactor 0.1
static UIAccelerationValue rollingX=0, rollingY=0, rollingZ=0;
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
// Calculate low pass values
rollingX = (acceleration.x * kFilteringFactor) + (rollingX * (1.0 - kFilteringFactor));
rollingY = (acceleration.y * kFilteringFactor) + (rollingY * (1.0 - kFilteringFactor));
rollingZ = (acceleration.z * kFilteringFactor) + (rollingZ * (1.0 - kFilteringFactor));
// Subtract the low-pass value from the current value to get a simplified high-pass filter
float accelX = acceleration.x - rollingX;
float accelY = acceleration.y - rollingY;
float accelZ = acceleration.z - rollingZ;
// Use the acceleration data.
}
Just in case someone wants to know, the highpass filter can be found in the Accelerometer Graph sample.
Here's the link, i was looking for this one too. This is an example for adaptive / non adaptive highpass and lowpass filter: Apple iOS Reference Library - AccelerometerGraph Example