Обнаружение скорости движения 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);
}
Ответы
Ответ 1
Вы можете попробовать (нулевое расстояниеSinceStart и timeSinceStart в touchhesBegan):
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, если вы хотите увеличить вес к последним значениям.
Ответ 2
Основная проблема заключается в том, что вычисление скорости будет очень неточным, если timeSincePrevious
очень мало (несколько миллисекунд). Чтобы увидеть это, скажем, что timeSincePrevious
составляет 1 мс. Тогда расчетная скорость будет равна 0, если distanceFromPrevious
равно 0, а 1000, если distanceFromZero
равно 1.
По этой причине я предлагаю следующее значение лямбда:
const float labmda = (timeSincePrevious>0.2? 1: timeSincePrevious/0.2);
То есть мы используем крошечную лямбда, когда timeSincePrevious
мала.
Ответ 3
Предложение фильтра может быть в порядке, но оно не решает проблему: пик будет сглажен, но останется.
Если вы вышли из событий касания, эти пики будут выглядеть как прикосновение с очень маленькой временной дельтами от предыдущей (0.001215 мс), предшествующей касанию с большой временной дельтами.
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
То, что я делаю, это вычислять среднюю временную дельта между недавними событиями касания, и если есть прикосновение к аномальной временной дельта (± 30%), я игнорирую его скорость (сохраняя скорость предыдущего события)