问题 UITouch移动速度检测


我试图检测触摸运动的速度,我并不总是得到我期望的结果。 (补充说:速度太快了) 任何人都可以发现,如果我正在做一些时髦或建议更好的方式吗?


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

}


1758
2018-03-20 22:37


起源



答案:


你可以尝试(在touchesBegan中将distanceSinceStart和timeSinceStart归零):

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

这将为您提供自开始触摸以来的平均速度(总距离/总时间)。

或者你可以做一个移动平均速度,也许是一个指数移动平均线:

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;

如果要为最近的值赋予更多权重,可以将lambda调整为接近1的值。


11
2018-03-21 02:46



嘿......我在实现这个方面遇到了麻烦。 lambda函数是objective-c的一部分吗?我需要什么来实现它? TIA - dizy
不,这是你自己指定的常数。它越接近1,您对最新值的重量就越大。与n值的算术平均值比较。每个新值的权重为1 / n。对于指数,设置lambda = 2 /(n + 1),其中n是等效算术值。所以新值加权2 /(n + 1)而不是1 / n,然后现有的移动平均值按(1-lambda)=(n-1)/(n + 1)缩小,两者是添加。更清晰? - Jim


答案:


你可以尝试(在touchesBegan中将distanceSinceStart和timeSinceStart归零):

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

这将为您提供自开始触摸以来的平均速度(总距离/总时间)。

或者你可以做一个移动平均速度,也许是一个指数移动平均线:

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;

如果要为最近的值赋予更多权重,可以将lambda调整为接近1的值。


11
2018-03-21 02:46



嘿......我在实现这个方面遇到了麻烦。 lambda函数是objective-c的一部分吗?我需要什么来实现它? TIA - dizy
不,这是你自己指定的常数。它越接近1,您对最新值的重量就越大。与n值的算术平均值比较。每个新值的权重为1 / n。对于指数,设置lambda = 2 /(n + 1),其中n是等效算术值。所以新值加权2 /(n + 1)而不是1 / n,然后现有的移动平均值按(1-lambda)=(n-1)/(n + 1)缩小,两者是添加。更清晰? - Jim


主要问题是速度计算会非常 不准确 什么时候 timeSincePrevious 非常小(几毫秒)。为了看到这一点,让我们说 timeSincePrevious 是1毫秒。然后计算出的速度将为0 distanceFromPrevious 是0,如果是1000则 distanceFromZero 是1。

出于这个原因,我建议使用lambda的以下值:

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

也就是说,当我们使用一个小小的lambda时 timeSincePrevious 是小。


3
2017-07-31 11:36



这是使用适当的可变采样率滤波器的唯一答案。并且像标准的一阶低通一样:k = <响应时间>; a = exp(-dt / k); filteredSpeed = a *(dx / dt)+(1-a)* filteredSpeed; - kylefinn


过滤器建议可能没问题,但它没有解决问题:峰值将被平滑,但保持不变。

如果您注销了触摸事件,那么这些峰值看起来就像是一个触摸,其前一个时间点(0.001215毫秒)的时间差非常小,之前触摸的是大时间增量。

距离= 17.269917,timeDelta = 0.016132,速度= 1070.504639
距离= 15.206906,timeDelta = 0.017494,速度= 869.251709
距离= 15.882380,timeDelta = 0.017583,速度= 903.297546
距离= 14.983324,timeDelta = 0.030101,速度= 497.771088 //低峰值
距离= 15.435349,timeDelta = 0.001215,速度= 12703.991211 //高峰!
距离= 15.882380,timeDelta = 0.017343,速度= 915.795898
距离= 15.890248,timeDelta = 0.016302,速度= 974.742249
距离= 16.560495,timeDelta = 0.016468,速度= 1005.606445
距离= 16.101242,timeDelta = 0.017291,速度= 931.201050

我所做的是计算最近触摸事件之间的平均时间增量,如果触摸有异常时间增量(±30%),我会忽略它的速度(保持前一事件的速度)


1
2017-11-26 12:30