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 个校准点就够了。

这就是三点校准法的由来。具体步骤是这样的:

  1. 在屏幕上显示 3 个已知坐标的点(比如左上、右下、中心)。
  2. 让用户依次点击这 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 校准流程总览

下面这张图展示了从采集点到生成校准参数的完整流程:

触摸校准流程总览 显示校准点 采集触摸坐标 (Xₜ, Yₜ) 选择校准方法 三点校准法(3点) 最小二乘法(≥4点) 解方程组 → 得到6个参数 参数存储到 Flash/EEPROM,每次触摸时调用线性模型进行坐标转换

流程看起来不复杂,但每一步都有坑。我曾经在一个项目里,校准点显示和触摸采集之间没有加延时,结果用户还没点下去,系统就记录了上一个点的坐标——校准出来的参数完全是乱的。

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 上性能更好。

嗯,到这里,触摸校准的数学原理就讲清楚了。从线性模型到三点校准法,再到最小二乘法拟合,每一步都有它的适用场景。你想想看,如果你的产品只是做个简单的菜单选择,三点校准法完全够用;但如果是做手写输入或绘图,那就得上最小二乘法了。