11

I'm trying to detect the speed of touch movement and i'm not always getting the results I'd expect. (added: Speed spikes around too much) Can anyone spot if i'm doing something funky or suggest a better way of doing it ?


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    self.previousTimestamp = event.timestamp;
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
    UITouch *touch = [touches anyObject];
    CGPoint location = [touch locationInView:self.view];
    CGPoint prevLocation = [touch previousLocationInView:self.view];
    CGFloat distanceFromPrevious = distanceBetweenPoints(location,prevLocation);
    NSTimeInterval timeSincePrevious = event.timestamp - self.previousTimestamp;
    CGFloat speed = distanceFromPrevious/timeSincePrevious;
    self.previousTimestamp = event.timestamp;
    NSLog(@"dist %f | time %f | speed %f",distanceFromPrevious, timeSincePrevious, speed);

}

dizy
  • 7,951
  • 10
  • 53
  • 54

3 Answers3

10

You could try (zero out distanceSinceStart and timeSinceStart in touchesBegan):

distanceSinceStart = distanceSinceStart + distanceFromPrevious;
timeSinceStart = timeSincestart + timeSincePrevious;
speed = distanceSinceStart/timeSinceStart;

which will give you the average speed since you started the touch (total distance/total time).

Or you could do a moving average of the speed, perhaps an exponential moving average:

const float lambda = 0.8f; // the closer to 1 the higher weight to the next touch

newSpeed = (1.0 - lambda) * oldSpeed + lambda* (distanceFromPrevious/timeSincePrevious);
oldSpeed = newSpeed;

You can adjust lambda to values near 1 if you want to give more weight to recent values.

jjxtra
  • 20,415
  • 16
  • 100
  • 140
Jim
  • 4,691
  • 1
  • 25
  • 23
  • Hey... i'm having trouble implementing this. Is lambda function part of objective-c ? What do I need to implement it ? tia – dizy Apr 17 '09 at 00:53
  • 2
    Nope...it's a constant you specify yourself. The closer it is to 1, the more weight you place on the newest value. Compare to an arithmetic average of n values. Each new value gets a weight of 1/n. For exponential, set lambda = 2/(n+1) where n is the equivalent arithmetic value. So the new value is weighted 2/(n+1) instead of 1/n, and then the existing moving average is scaled back by (1-lambda) = (n-1)/(n+1) and the two are added. Clearer? – Jim Apr 17 '09 at 13:21
3

The main problem is that the speed calculation will be very inaccurate when timeSincePrevious is very small (a few milliseconds). To see this, let's say that timeSincePrevious is 1ms. Then the calculated speed will be 0 if the distanceFromPrevious is 0, and 1000 if the distanceFromZero is 1.

For this reason I suggest the following value of lambda:

const float labmda = (timeSincePrevious>0.2? 1: timeSincePrevious/0.2);

That is to say, we use a tiny lambda when the timeSincePrevious is small.

ragnarius
  • 5,642
  • 10
  • 47
  • 68
  • This is the only answer using a proper variable sample rate filter. And to do it like a standard first-order low-pass: k = ; a = exp(-dt / k); filteredSpeed = a * (dx / dt) + (1-a) * filteredSpeed; – kylefinn Jul 18 '14 at 05:53
1

The filter suggestion might be ok, but it doesn't solve the problem: the peak will be smoothed out, but remain.

If you logged out the touch events, these peaks will look like a touch with very little time delta from previous (0.001215 ms), preceding by touch with large time delta.

distance = 17.269917, timeDelta = 0.016132, speed = 1070.504639 
distance = 15.206906, timeDelta = 0.017494, speed = 869.251709 
distance = 15.882380, timeDelta = 0.017583, speed = 903.297546 
distance = 14.983324, timeDelta = 0.030101, speed = 497.771088      //low peak
distance = 15.435349, timeDelta = 0.001215, speed = 12703.991211    //high peak!
distance = 15.882380, timeDelta = 0.017343, speed = 915.795898 
distance = 15.890248, timeDelta = 0.016302, speed = 974.742249 
distance = 16.560495, timeDelta = 0.016468, speed = 1005.606445 
distance = 16.101242, timeDelta = 0.017291, speed = 931.201050 

What I do is compute an average time delta between recent touch events, and if there is a touch with abnormal time delta (±30%), I ignore its speed (keeping the speed of the previous event)

Pavel Alexeev
  • 6,026
  • 4
  • 43
  • 51