Low-Pass Filter Coefficients


#1

(Oops! I originally posted this on the iPhone Programming board; I meant to post it here. Sorry about that.)

I’m a bit confused about the coefficients used in the low-pass filter on page 152. Here’s the code as printed in the book:

float xShift = [hv xShift] * 0.8 + [accel x] * 2.0;
float yShift = [hv yShift] * 0.8 - [accel y] * 2.0;

So the coefficient of the [hv *Shift] term is 0.8 and the coefficient of the [accel *] term is 2.0. But according to the “for the more curious” discussion on filtering, the sum of the coefficients should be 1.0, right? Should the coefficient on the [accel *] term be 0.2 instead of 2.0?


#2

That bugged me for a bit, too, until I realized that the original version multiplied the accelerometer value by 10 - so one point of accelerometer is worth 10 points of x/yShift.

So the general discussion on page 156 (iOS version of the book, section “For the More Curious: Filtering and Frequency”) leaves out the scaling factor - what you’re really looking for is (with a scalingFactor of 10, because the original version of accelerometer:didAccelerate: in HypnosisView.m, under the section “Using the Accelerometer,” was x/yShift = 10.0 * [accel x/y]).

So what was originally, before the low pass filter:

// outputValue = inputValue * scalingFactor;
xShift = [accel x] * 10.0;

with the low pass filter becomes:

// lowPassed = newValue * filteringFactor * scalingFactor + lowPassed * (1.0 - filteringFactor);
xShift = [accel x] * 10.0 * 0.2 + [hv xShift] * 0.8;

Which works out with a scalingFactor of 10 and a filteringFactor of .2.

I’m not sure how clear that is, put another way the key thing is that the value of lowPassed left from your last iteration was already multiplied by 10, while the incoming value from the accelerometer, while dampened, still needs to be multiplied by 10.

That make sense?


#3

That does make sense, especially your last sentence. I, too, noticed the scaling by 10 and thought that might explain why one of the coefficients was 2.0 instead of 0.2. But then I couldn’t figure out why the other coefficient wouldn’t also be multiplied by 10 and therefore be 8.0 instead of 0.8. But you’re right, that factor has already been multiplied by 10 from the last iteration, so I guess it shouldn’t be re-scaled.

Thanks for the explanation!


#4

Correct, a scale is being applied:

float xShift = [hv xShift] * 0.8 + [accel x] * 2.0;

is equivalent to:

float xShift = ([hv xShift] * 0.286 + [accel x] * 0.714) * 2.8;
// 0.286 + 0.714 = 1.0


#5

I’m sorry, but I can’t seem to understand what’s going on with [hv xShift] and [hv yShift] here:
On line 2, we set a new local variable xShift to be equal to [hv xShift] * 0.8 + [accel x] *2.0
but where is [hv Shift] set? why isn’t it 0, and why is it responding to the acceleration?

It was easy to understand this method before we set the low pass filter, where we set [hv xShift] to be equal to 10 * accel.x, but we never do that here…
(also, the book says to add the lines for the low pass filter, not replace lines, but the text for the method does not include the previous code where we set [hv xShift] to be equal to 10.0 * [accel x], so its unclear whether we’re to remove those lines or not)

[code]- (void)accelerometer:(UIAccelerometer *)meter
didAccelerate:(UIAcceleration *)accel
{
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];

// Redraw the view
[hv setNeedsDisplay];

}
[/code]

I added an NSLog to check the behavior of [hv xShift] before [hv setXShift:xShift], and it mysteriously responds to acceleration.


#6

I believe that it’s just working with the current value of xShift and yShift and updating them with the acceleration data. So the first time through this method, xShift and yShift have a value of zero, so they will be set to [acceleration x] * 2.0 and -[acceleration y] * 2.0 The next time around, the current values are factored into the equation, hence why the data gets smoothed.


#7

that makes sense. Thanks

So now the question I have is more obj-c I guess:
Here, we’re creating a new variable xShift with the scope of this method, and then setting the class variable xShift to it. Why name it the same in both cases, I don’t know, it seems confusing.

float xShift = [hv xShift] * 0.8 + [accel x] * 2.0; [hv setXShift:xShift];

why not write it as

or

and forgo the creation of a new variable entirely?


#8

I would guess the answer is by making a new local variable, the code is easier to read. Probably the fewer nested square brackets, the better!


#9

Could be. I don’t know why this little bit of code gave me such trouble last week.

This monday after a good night’s sleep, it seems perfectly obvious and simple. I guess sometimes you just need to leave it and come back with a refreshed brain :slight_smile:

Thanks for your help and input!