Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UITouch movement speed detection

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);

}

like image 407
dizy Avatar asked Mar 20 '09 22:03

dizy


3 Answers

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.

like image 154
Jim Avatar answered Nov 08 '22 02:11

Jim


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.

like image 35
ragnarius Avatar answered Nov 08 '22 04:11

ragnarius


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)

like image 1
Pavel Alexeev Avatar answered Nov 08 '22 02:11

Pavel Alexeev