最小二乘法计算触摸事件速度

作者:别怪我很水日期:2026/8/1

最小二乘法计算触摸事件速度

引言:为什么需要计算触摸速度?在触控设备(如手机、平板、触摸屏)上,我们经常需要根据用户的触摸滑动速度来调整交互行为。例如,在滚动列表时,快速滑动应该让列表继续惯性滚动一段距离,而慢速滑动则应该立即停止。这些功能的核心在于准确计算触摸事件的速度。然而,由于触摸采样率的限制和手指抖动的噪声干扰,直接使用相邻两点的位移除以时间间隔会得到不稳定的速度值。最小二乘法通过拟合多个采样点的数据,能够有效平滑噪声,估算出更准确的速度。## 基础概念:最小二乘法的数学原理最小二乘法是一种数学优化技术,它通过最小化误差的平方和来寻找数据的最佳函数匹配。在速度计算中,我们假设触摸位置随时间线性变化(匀速运动),即:position(t) = velocity * t + initial_position给定 n 个采样点 (t_i, x_i)(时间与位置),我们希望找到一条直线 x = v * t + b,使得所有点到直线的垂直距离的平方和最小。数学表达式为:S = Σ(x_i - (v * t_i + b))²通过求偏导并令其为零,可以得到速度 v 和截距 b 的最优解:v = (n * Σ(t_i * x_i) - Σt_i * Σx_i) / (n * Σt_i² - (Σt_i)²)b = (Σx_i - v * Σt_i) / n## 初级实现:一维滑动速度计算首先,我们实现一个最简单的一维情况:用户只在 x 方向上滑动。pythonimport mathfrom typing import List, Tupleclass TouchVelocityCalculator: """触摸速度计算器,使用最小二乘法""" def __init__(self, window_size: int = 5): """ 初始化计算器 Args: window_size: 用于计算的采样点数量 """ self.window_size = window_size self.points: List[Tuple[float, float]] = [] # (时间戳, x位置) def add_point(self, timestamp: float, x_position: float): """添加一个触摸点""" self.points.append((timestamp, x_position)) # 保持窗口大小 if len(self.points) > self.window_size: self.points.pop(0) def calculate_velocity(self) -> float: """使用最小二乘法计算当前速度""" if len(self.points) < 2: return 0.0 n = len(self.points) # 提取时间和位置数据 times = [p[0] for p in self.points] positions = [p[1] for p in self.points] # 计算各项求和 sum_t = sum(times) sum_x = sum(positions) sum_tt = sum(t * t for t in times) sum_tx = sum(t * x for t, x in zip(times, positions)) # 计算速度(最小二乘法公式) denominator = n * sum_tt - sum_t * sum_t if denominator == 0: return 0.0 velocity = (n * sum_tx - sum_t * sum_x) / denominator return velocity# 使用示例calculator = TouchVelocityCalculator(window_size=5)# 模拟触摸采样数据(时间戳从0开始,x位置从0开始,速度约为3单位/秒)test_data = [ (0.0, 0.0), (0.02, 0.06), (0.04, 0.12), (0.06, 0.18), (0.08, 0.24), (0.10, 0.30)]print("模拟匀速运动测试:")for timestamp, x in test_data: calculator.add_point(timestamp, x) speed = calculator.calculate_velocity() print(f"时间: {timestamp:.2f}s, 位置: {x:.2f}, 计算速度: {speed:.2f} 单位/秒")## 高级应用:二维触摸速度与加速度在实际触摸事件中,我们需要同时处理 x 和 y 两个方向的速度,甚至需要计算加速度来预测未来位置。下面是一个更完整的实现。pythonimport mathfrom typing import List, Tuple, Optionalclass TouchMotionAnalyzer: """触摸运动分析器,支持二维速度和加速度计算""" def __init__(self, window_size: int = 5, acceleration_enabled: bool = False): """ 初始化分析器 Args: window_size: 用于拟合的采样点数量 acceleration_enabled: 是否启用加速度计算(使用二次拟合) """ self.window_size = window_size self.acceleration_enabled = acceleration_enabled self.points: List[Tuple[float, float, float]] = [] # (时间戳, x位置, y位置) self.last_velocity_x = 0.0 self.last_velocity_y = 0.0 def add_point(self, timestamp: float, x: float, y: float): """添加一个触摸点""" self.points.append((timestamp, x, y)) if len(self.points) > self.window_size: self.points.pop(0) def _calculate_linear_fit(self, times: List[float], positions: List[float]) -> float: """线性拟合计算速度""" n = len(times) if n < 2: return 0.0 sum_t = sum(times) sum_p = sum(positions) sum_tt = sum(t * t for t in times) sum_tp = sum(t * p for t, p in zip(times, positions)) denominator = n * sum_tt - sum_t * sum_t if denominator == 0: return 0.0 velocity = (n * sum_tp - sum_t * sum_p) / denominator return velocity def _calculate_quadratic_fit(self, times: List[float], positions: List[float]) -> Tuple[float, float]: """ 二次拟合计算速度和加速度 模型: p = 0.5 * a * t² + v * t + c 返回: (速度, 加速度) """ n = len(times) if n < 3: # 点数不足时回退到线性拟合 return self._calculate_linear_fit(times, positions), 0.0 # 构造最小二乘法的正规方程 # 使用矩阵求解,这里简化为实现核心逻辑 sum_t = sum(times) sum_t2 = sum(t * t for t in times) sum_t3 = sum(t ** 3 for t in times) sum_t4 = sum(t ** 4 for t in times) sum_p = sum(positions) sum_tp = sum(t * p for t, p in zip(times, positions)) sum_t2p = sum(t * t * p for t, p in zip(times, positions)) # 构建矩阵和向量(忽略c的求解,只求速度和加速度) # 这里使用简化版本:先中心化时间数据,再求解 t_mean = sum_t / n centered_times = [t - t_mean for t in times] # 重新计算中心化后的各项 sum_ct = sum(centered_times) sum_ct2 = sum(t * t for t in centered_times) sum_ct3 = sum(t ** 3 for t in centered_times) sum_ct4 = sum(t ** 4 for t in centered_times) sum_p = sum(positions) sum_ctp = sum(t * p for t, p in zip(centered_times, positions)) sum_ct2p = sum(t * t * p for t, p in zip(centered_times, positions)) # 求解 (这里假设数据已经中心化,简化计算) # 实际上需要解2x2线性方程组,这里为了演示使用简化公式 # 注意:实际应用需要完整的线性代数求解 velocity = sum_ctp / sum_ct2 if sum_ct2 != 0 else 0.0 acceleration = 2 * (sum_ct2p - velocity * sum_ct3) / sum_ct4 if sum_ct4 != 0 else 0.0 return velocity, acceleration def analyze(self) -> dict: """分析当前触摸运动状态""" if len(self.points) < 2: return { "velocity_x": 0.0, "velocity_y": 0.0, "acceleration_x": 0.0, "acceleration_y": 0.0, "speed": 0.0, "direction": 0.0 } times = [p[0] for p in self.points] xs = [p[1] for p in self.points] ys = [p[2] for p in self.points] if self.acceleration_enabled and len(self.points) >= 3: vx, ax = self._calculate_quadratic_fit(times, xs) vy, ay = self._calculate_quadratic_fit(times, ys) else: vx = self._calculate_linear_fit(times, xs) vy = self._calculate_linear_fit(times, ys) ax = ay = 0.0 # 计算合速度和方向 speed = math.sqrt(vx ** 2 + vy ** 2) direction = math.atan2(vy, vx) # 弧度,-π 到 π self.last_velocity_x = vx self.last_velocity_y = vy return { "velocity_x": vx, "velocity_y": vy, "acceleration_x": ax, "acceleration_y": ay, "speed": speed, "direction": direction }# 使用示例:模拟一个曲线运动analyzer = TouchMotionAnalyzer(window_size=5, acceleration_enabled=True)print("\n模拟曲线运动测试(抛物线轨迹):")# 模拟 x 匀速,y 加速的运动for i in range(10): t = i * 0.05 # 50ms间隔 x = 2.0 * t # x方向速度2单位/秒 y = 0.5 * t * t # y方向加速度1单位/秒² (二次项系数0.5) analyzer.add_point(t, x, y) result = analyzer.analyze() print(f"时间: {t:.2f}s, 位置: ({x:.2f}, {y:.2f})") print(f" 速度: ({result['velocity_x']:.2f}, {result['velocity_y']:.2f})") print(f" 加速度: ({result['acceleration_x']:.2f}, {result['acceleration_y']:.2f})") print(f" 合速度: {result['speed']:.2f} 单位/秒, 方向: {math.degrees(result['direction']):.1f}°")## 性能优化与实践技巧### 1. 滑动窗口选择- 小窗口(3-5个点):响应快,但对噪声敏感,适合快速交互- 大窗口(10-20个点):平滑效果好,但有延迟,适合惯性滚动### 2. 权重机制可以给较新的点更高的权重,提高响应速度:pythondef calculate_weighted_velocity(self): """带权重的速度计算""" n = len(self.points) if n < 2: return 0.0 # 使用指数权重:越新的点权重越大 weights = [math.exp(0.5 * i) for i in range(n)] # 简单指数权重 sum_w = sum(weights) sum_wt = sum(w * p[0] for w, p in zip(weights, self.points)) sum_wx = sum(w * p[1] for w, p in zip(weights, self.points)) sum_wtt = sum(w * p[0] * p[0] for w, p in zip(weights, self.points)) sum_wtx = sum(w * p[0] * p[1] for w, p in zip(weights, self.points)) denominator = sum_w * sum_wtt - sum_wt * sum_wt if denominator == 0: return 0.0 velocity = (sum_w * sum_wtx - sum_wt * sum_wx) / denominator return velocity### 3. 异常值处理在触摸事件中,偶尔会出现异常跳点,需要过滤:pythondef add_point_with_filter(self, timestamp, x, y, max_jump=100): """带过滤的点添加""" if self.points: last_x = self.points[-1][1] last_y = self.points[-1][2] dx = abs(x - last_x) dy = abs(y - last_y) # 如果跳变过大,忽略该点 if dx > max_jump or dy > max_jump: return False self.points.append((timestamp, x, y)) if len(self.points) > self.window_size: self.points.pop(0) return True## 总结最小二乘法在触摸速度计算中是一个强大的工具,它通过统计平滑有效地解决了触摸数据的噪声问题。本文从基础数学原理出发,逐步展示了如何实现一维和二维的速度计算,并进一步扩展到加速度估计。关键要点:1. 窗口大小影响性能:选择合适的窗口大小需要在响应速度和平滑度之间平衡2. 维度扩展:从一维到二维,核心思想一致,只是需要分别对 x 和 y 方向进行拟合3. 高级应用:通过二次拟合可以估算加速度,用于更精确的预测4. 实际优化:权重机制和异常值过滤能够显著提升真实场景下的效果在实际开发中,建议先使用线性拟合(最小二乘法)作为基准,然后根据具体应用场景(如惯性滚动、手势识别、游戏控制等)调整窗口大小和是否启用加速度计算。通过不断测试和调优,你就能构建出流畅自然的触摸交互体验。


最小二乘法计算触摸事件速度》 是转载文章,点击查看原文


相关推荐


QML 文字波动与视觉干扰:波浪、倒影、故障
Quz2026/7/24

目录 Demo 1 波浪跳动 演示代码 关键逻辑解析 Demo 2 倒影镜像 演示代码 关键逻辑解析 Demo 3 故障风效果 演示代码 关键逻辑解析 运行验证 扩展复用方向 工程下载


Android window属性全解析
solo_992026/7/16

Android Theme XML 属性如何影响 PhoneWindow:从 TypedArray 到 LayoutParams 的完整链路 每天写布局、配主题,但你有没有想过——android:windowIsTranslucent="true" 这行 XML 到底是怎么一路传递,最终影响了 Window 的像素格式?为什么 statusBarColor 有时候怎么设都不生效?FLAG_LAYOUT_IN_SCREEN 到底是干什么的?本文将带你深入 Android Framework 源码


GitHub 热榜项目 - 周榜(2026-07-04)
CoderJia_2026/7/8

GitHub 热榜项目 - 周榜(2026-07-04) 生成于:2026-07-04 共发现热门项目: 21 个 Token赞助:siliconflow 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 本期热点趋势总结 本期 GitHub 热榜明显聚焦 AI Agent 工程化:多智能体协作、MCP / 知识图谱记忆、代码库索引与任务编排成为核心热点,代码生成、网页克隆、视频编辑、招聘评估等场景快速落地;


【从零开始大模型开发与微调:基于PyTorch与ChatGLM】(一学就会的深度学习基础算法详解)
承渊政道2026/6/30

🔥承渊政道:个人主页 ❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》 《C++知识内容》 《Linux系统知识》 《算法刷题指南》 《测评文章活动推广》 《大模型语言路线学习》 《MySQL数据库学习》 ✨逆境不吐心中苦,顺境不忘来时路!✨ 🎬 博主简介: 神经网络看起来由大量矩阵、激活函数和参数组成,但训练过程可以浓缩成四句话:先用当前参数完成一次预测;再用损失函数衡量预测有多差;接着通过链式法则把误差逐层传回;最后沿着能让损


栈和队列的实现
纪念 2292026/6/21

༺ 个人主页 · 纪念229 ༻ 🏠我的博客主页🏠 ༒专栏目录:《数据结构》༒ ༒其它有趣的计算机知识༒ ༺世上本没有路,走的人多了自然就有了༻ 这篇文章讲述的是用顺序表实现栈、用单链表实现队列,本文主要讲述栈和队列的定义,希望对大家有所帮助! 文章目录 1.Stack.h2.Stack.c2.1 STackDesTroy(ST* ps)2.2STackPush(ST* ps, STDataType x)2.3 STackEmpty(ST* ps)2.4 S


【云计算】华为公有云构建高可用Redis集群
Harvy_没救了2026/6/13

华为公有云构建高可用Redis集群(3主3从 + AS弹性扩容)详细方案 一、方案概述 本方案基于华为云服务(ECS、VPC、AS、IMS、ELB),手动搭建一个 Redis Cluster(3主3从),并通过 弹性伸缩AS 实现自动扩容(增加节点),但不配置自动缩容。扩容时新节点自动加入集群,并通过脚本完成集群拓扑更新。整体架构满足 高可用、负载均衡、水平扩展 的需求。 核心技术栈 服务作用VPC + 安全组隔离网络,保证内网互通ECS运行Redis服务的云服务器(6台基础节点)IMS


从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮)三角洲自动跑刀教程
深度学习教程,2026/6/6

从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮 从零实现《三角洲行动》手游自动跑刀脚本:ADB 直控 + OpenCV 视觉识别 + 固定点位搜刮一、前言二、整体架构与技术栈三、ADB 控制层:截图、点击、滑动四、核心难点一:怎么判断"真进游戏了"?五、核心难点二:识别"这把刷在哪个出生点"5.1 用圆检测把小地图"圆形抠出来"5.2 模板匹配识别出生点 六、核心难点三:自动拾取物资七、把它串起来:固定路线八、一个诚实的工程结论九、


React Native + RNOH:跨页面数据回传的最佳实践与避坑指南
皮蛋小精灵2026/5/31

从内存闭包到官方路由通信的深度对比 技术栈: React Native 0.77 + React Navigation v7 + RNOH(React Native OpenHarmony) 适用场景: 表单录入、列表选择器、跨页面数据回传通信 一句话结论: 内存闭包 Bridge 体验虽好但存在“热重载失效、系统回收丢回调、嵌套覆盖”三大致命缺陷;官方 route.params 需配合 setParams 清洗;在嵌套/鸿蒙 JS Stack 下,使用 CommonActions.setPa


aardio从惊喜到失望到被拉黑二三事
1681692026/5/9

aardio从惊喜到失望到被拉黑二三事 前情 其实我非常喜欢开发一些小工具,用于解决工作中和生活中的问题,我个人也有开发一些小程序和一些cli,但是对于桌面端一直没的找到好的开发方案,其中有试过autohotkey,但是它的语法和学习资料非常少,带你开发GUI就更少了,平时也就用用热词热键什么的,electron我也用过,但是它太重了,一直想找一个轻量点的,我也知道c#开发桌面工具非常不错,但是那要重新学一门语言,有想过学,但是一看c#教程就犯困也就没什么尝试了,直到某一天看到了aardio,真


一觉醒来,大模型就帮我排查完页面性能问题
candyTong2026/4/29

最近遇到一个性能问题,我让大模型自己去处理,然后就去午休了,醒来之后,它还真的把问题找出来并修复了 先说结果 最终定位出来的根因是: 这个业务工作台的查询表单,把 Formily 的 form 实例塞进了带 devtools 的 Zustand store。 这件事在开发环境里会非常要命,因为 form 不是一个轻量对象,它里面带着大量字段状态、reaction、effect、schema 和联动信息。 一旦进了 store,又被 devtools 观察、快照、序列化,内存就会被瞬间放大。

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 聚合阅读