4. 触摸校准数学原理:线性校准模型、三点校准法、最小二乘法拟合
做触摸屏开发,迟早要面对一个问题:我点下去的位置,和系统认为我点的位置,对不上。
这不是硬件坏了。说白了,是触摸屏的物理坐标和 LCD 的显示坐标之间,存在一个映射偏差。这个偏差来自屏幕贴合误差、ADC 采样非线性、甚至安装时的微小旋转。我早年做第一个带触摸的项目时,就因为这个偏差被折腾了好几天——点一个按钮,光标跑到旁边去了,用户体验极差。
所以,我们需要一套数学方法,把触摸坐标“翻译”成正确的显示坐标。这就是触摸校准的核心。
4.1 线性校准模型:最基础的映射关系
先想一个问题:触摸屏和 LCD 之间,到底是什么关系?
理想情况下,它们是线性对应的。也就是说,触摸坐标 (Xt, Yt) 和显示坐标 (Xd, Yd) 之间,可以用一个简单的线性方程来描述:
Xd = A · Xt + B · Yt + C
Yd = D · Xt + E · Yt + F
这里 A、B、C、D、E、F 就是我们要找的校准参数。一旦确定了这 6 个参数,任何触摸坐标都能被准确映射到显示坐标上。
关键理解:线性模型假设触摸屏和 LCD 之间是仿射变换关系——包含缩放、旋转、平移和轻微的倾斜。大多数嵌入式触摸屏(电阻式、电容式)都满足这个假设。
为什么是 6 个参数?因为每个坐标轴不仅受自身影响,还可能受另一个轴的影响(比如屏幕旋转时)。我见过一些初学者只做 X 和 Y 的独立缩放,结果屏幕旋转 90 度后就完全乱套了。
4.2 三点校准法:最经典的工程方案
6 个未知数,理论上需要 6 个方程才能解。但仔细看上面的公式——X 轴和 Y 轴是解耦的。Xd 只依赖 A、B、C 三个参数,Yd 只依赖 D、E、F 三个参数。
所以,每个轴只需要 3 个方程。换句话说,3 个校准点就够了。
这就是三点校准法的由来。具体步骤是这样的:
- 在屏幕上显示 3 个已知坐标的点(比如左上、右下、中心)。
- 让用户依次点击这 3 个点,记录下触摸屏返回的物理坐标。
- 代入线性方程,解出 6 个参数。
举个例子,假设三个校准点的显示坐标分别是:
| 点 | 显示坐标 (Xd, Yd) | 触摸坐标 (Xt, Yt) |
|---|---|---|
| P1 | (50, 50) | (120, 130) |
| P2 | (200, 50) | (580, 140) |
| P3 | (50, 200) | (110, 590) |
对于 X 轴,我们有:
50 = A·120 + B·130 + C
200 = A·580 + B·140 + C
50 = A·110 + B·590 + C
解这个三元一次方程组,就能得到 A、B、C。Y 轴同理。实际代码中,我一般用克莱姆法则或高斯消元法来解,计算量很小,在单片机上跑毫无压力。
我的经验:三个点不要选得太靠近。我见过有人把三个点都选在屏幕中央附近,结果校准参数对边缘区域的补偿效果很差。建议选在屏幕的左上、右下和中心,或者左上、右上、左下,尽量覆盖整个显示区域。
4.3 最小二乘法拟合:当精度要求更高时
三点校准法简单可靠,但它有一个前提:用户点击的精度足够高。
现实是,人的手指是有宽度的,触摸屏本身也有噪声。你让用户点同一个点 10 次,每次得到的触摸坐标可能都不一样。这种情况下,只用 3 个点来拟合,误差会直接传递到校准参数中。
怎么办?多采几个点,用最小二乘法来拟合。
最小二乘法的核心思想是:我们不要求拟合曲线精确穿过每一个点,而是让所有点的误差平方和最小。
对于 X 轴,我们定义误差函数:
E(A,B,C) = Σ [ Xd_i - (A·Xt_i + B·Yt_i + C) ]²
然后对 A、B、C 分别求偏导,令导数为 0,得到正规方程组。写成矩阵形式就是:
[ ΣXt² ΣXtYt ΣXt ] [ A ] [ ΣXtXd ]
[ ΣXtYt ΣYt² ΣYt ] [ B ] = [ ΣYtXd ]
[ ΣXt ΣYt n ] [ C ] [ ΣXd ]
解这个 3×3 的线性方程组,就能得到最优的 A、B、C。Y 轴同理。
注意:最小二乘法需要采集至少 4 个点(实际上 6-9 个点效果更好)。点越多,拟合结果越稳定,但用户操作时间也越长。我一般取 5 个点——四个角加中心,既覆盖全面,又不会让用户觉得烦。
4.4 三种方法的对比与选择
说了这么多,到底该用哪种?我整理了一个对比表:
| 方法 | 所需点数 | 计算复杂度 | 抗噪声能力 | 适用场景 |
|---|---|---|---|---|
| 线性模型(理论) | 2 点(仅缩放) | 极低 | 差 | 已知无旋转、无偏移的完美贴合屏 |
| 三点校准法 | 3 点 | 低 | 中等 | 大多数嵌入式产品,量产校准 |
| 最小二乘法 | ≥4 点(推荐 5-9) | 中等 | 强 | 高精度要求、用户环境差异大 |
我个人习惯是:开发阶段用最小二乘法,因为可以反复采集数据,调试参数;量产阶段用三点校准法,因为速度快,用户操作简单。如果产品对精度要求极高(比如绘图应用),我会在出厂前做一次最小二乘法校准,把参数写进 Flash。
4.5 校准流程总览
下面这张图展示了从采集点到生成校准参数的完整流程:
流程看起来不复杂,但每一步都有坑。我曾经在一个项目里,校准点显示和触摸采集之间没有加延时,结果用户还没点下去,系统就记录了上一个点的坐标——校准出来的参数完全是乱的。
4.6 在 LVGL 中集成校准参数
拿到 6 个参数后,怎么用到 LVGL 里?LVGL 的触摸输入驱动有一个 lv_indev_drv_t 结构体,里面有个 read_cb 回调函数。我们在这个回调里做坐标转换:
static void touchpad_read(lv_indev_drv_t *drv, lv_indev_data_t *data)
{
// 1. 从硬件读取原始触摸坐标
uint16_t raw_x, raw_y;
touch_hw_read(&raw_x, &raw_y);
// 2. 应用校准参数(假设已从 Flash 加载到全局变量)
int32_t cal_x = (int32_t)(A * raw_x + B * raw_y + C);
int32_t cal_y = (int32_t)(D * raw_x + E * raw_y + F);
// 3. 限制在屏幕范围内
data->point.x = LV_CLAMP(0, cal_x, LV_HOR_RES - 1);
data->point.y = LV_CLAMP(0, cal_y, LV_VER_RES - 1);
data->state = (touch_hw_is_pressed()) ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
}
注意数据类型:触摸坐标通常是 12 位 ADC 值(0-4095),而显示坐标可能是 0-480。乘法运算时建议用 int32_t 或 float,避免溢出。我习惯用定点数(比如 Q16.16)来避免浮点运算,在无 FPU 的 MCU 上性能更好。
嗯,到这里,触摸校准的数学原理就讲清楚了。从线性模型到三点校准法,再到最小二乘法拟合,每一步都有它的适用场景。你想想看,如果你的产品只是做个简单的菜单选择,三点校准法完全够用;但如果是做手写输入或绘图,那就得上最小二乘法了。