4、三点校准法:三个校准点的选取策略、建立方程组、求解转换矩阵
三点校准法,说白了就是触摸屏校准里最经典、最实用的方法。你想想看,两点校准只能解决平移和缩放,但实际触摸屏的安装、贴合过程中,难免会有旋转和倾斜。这时候,三点校准就派上用场了。
我个人习惯把三点校准叫做“全参数校准”。因为它能同时修正X轴和Y轴的缩放、旋转、平移,以及两轴之间的正交误差。嗯,这里要注意——正交误差是什么?就是触摸屏的X轴和LCD的X轴不完全平行,有个小夹角。这在批量生产中很常见,两点校准根本搞不定。
4.1 三个校准点的选取策略
选点不是随便点的。我在项目中踩过坑,一开始图省事,把三个点选在屏幕左上角附近,结果校准完,右下角偏差大得离谱。后来才明白,选点要讲究“覆盖”和“分散”。
具体来说,有这几个原则:
- 覆盖显示区域:三个点应该尽量分散在屏幕的不同角落,形成一个三角形,覆盖大部分有效显示区。
- 避免共线:三个点绝对不能在同一条直线上。一旦共线,方程组就解不出来了,矩阵会奇异。说白了就是“无解”。
- 远离边缘:我个人建议离屏幕边缘留出5%-10%的余量。因为边缘区域的触摸线性度往往最差,选在边缘反而引入误差。
- 对称分布:最经典的选法是左上、右上、左下(或右下),形成一个近似等边三角形。这样各方向的权重均衡。
下面这张图展示了三个校准点的典型分布策略:
4.2 建立方程组——从物理坐标到显示坐标
好,点选好了。接下来要干什么?建立数学关系。说白了,就是找到一个转换矩阵,能把触摸屏上报的物理坐标(x, y)映射到LCD的显示坐标(X, Y)。
三点校准用的是仿射变换模型。它比两点校准多了一个旋转项。数学表达式是这样的:
X = A * x + B * y + C
Y = D * x + E * y + F
这里有6个未知数:A、B、C、D、E、F。每个点提供两个方程(一个X,一个Y),三个点正好提供6个方程。方程组刚好可解。
你想想看,如果只有两个点,只能解出4个参数,旋转和正交误差就丢了。这就是为什么三点校准更准的根本原因。
把三个点的数据代入,得到完整的方程组:
对于点1 (x1, y1) → (X1, Y1):
X1 = A * x1 + B * y1 + C
Y1 = D * x1 + E * y1 + F
对于点2 (x2, y2) → (X2, Y2):
X2 = A * x2 + B * y2 + C
Y2 = D * x2 + E * y2 + F
对于点3 (x3, y3) → (X3, Y3):
X3 = A * x3 + B * y3 + C
Y3 = D * x3 + E * y3 + F
4.3 求解转换矩阵——两种实用方法
方程组有了,怎么解?我一般用两种方法,看项目资源来选。
方法一:矩阵求逆法(适合有数学库的平台)
把方程组写成矩阵形式:
| X1 | | x1 y1 1 0 0 0 | | A |
| Y1 | | 0 0 0 x1 y1 1 | | B |
| X2 | = | x2 y2 1 0 0 0 | * | C |
| Y2 | | 0 0 0 x2 y2 1 | | D |
| X3 | | x3 y3 1 0 0 0 | | E |
| Y3 | | 0 0 0 x3 y3 1 | | F |
简写为:V = M * P
那么:P = M⁻¹ * V
只要M可逆(三个点不共线就能保证),直接求逆矩阵,乘一下,6个参数就出来了。我在Cortex-M4上跑过,浮点运算大概几十微秒,完全够用。
方法二:高斯消元法(适合裸机、无数学库)
如果平台没有矩阵库,或者你不想引入依赖,那就手写高斯消元。把6个方程当成6元一次方程组,逐行消去。代码量不大,我贴一个我常用的实现片段:
// 三点校准 - 高斯消元求解6个参数
// 输入:三个点的触摸坐标 (tx[], ty[]) 和显示坐标 (lx[], ly[])
// 输出:参数 A, B, C, D, E, F
void calc_3point_calib(float *tx, float *ty, float *lx, float *ly,
float *A, float *B, float *C,
float *D, float *E, float *F) {
// 构造增广矩阵 (6行 x 7列)
float mat[6][7];
for (int i = 0; i < 3; i++) {
int r = i * 2;
mat[r][0] = tx[i]; mat[r][1] = ty[i]; mat[r][2] = 1;
mat[r][3] = 0; mat[r][4] = 0; mat[r][5] = 0;
mat[r][6] = lx[i];
mat[r+1][0] = 0; mat[r+1][1] = 0; mat[r+1][2] = 0;
mat[r+1][3] = tx[i]; mat[r+1][4] = ty[i]; mat[r+1][5] = 1;
mat[r+1][6] = ly[i];
}
// 高斯消元(列主元)
for (int col = 0; col < 6; col++) {
// 找主元
int max_row = col;
for (int row = col + 1; row < 6; row++) {
if (fabs(mat[row][col]) > fabs(mat[max_row][col]))
max_row = row;
}
// 交换行
for (int j = col; j <= 6; j++) {
float tmp = mat[col][j];
mat[col][j] = mat[max_row][j];
mat[max_row][j] = tmp;
}
// 消去
for (int row = col + 1; row < 6; row++) {
float factor = mat[row][col] / mat[col][col];
for (int j = col; j <= 6; j++) {
mat[row][j] -= factor * mat[col][j];
}
}
}
// 回代求解
for (int i = 5; i >= 0; i--) {
mat[i][6] /= mat[i][i];
for (int j = i - 1; j >= 0; j--) {
mat[j][6] -= mat[j][i] * mat[i][6];
}
}
*A = mat[0][6]; *B = mat[1][6]; *C = mat[2][6];
*D = mat[3][6]; *E = mat[4][6]; *F = mat[5][6];
}
4.4 参数验证与常见问题
参数算出来就完事了?当然不是。我建议做两步验证:
- 回代验证:把三个点的触摸坐标代回公式,算出来的显示坐标应该和原始显示坐标几乎一致。偏差超过1个像素就要警惕了。
- 随机点验证:在屏幕上随机点几个位置,看看校准后的坐标是否准确。这一步能发现“过拟合”问题——三个点很准,但中间区域反而偏了。
下面这个表格总结了三点校准的常见问题和对策:
| 问题现象 | 可能原因 | 我的建议 |
|---|---|---|
| 校准后边缘偏差大 | 校准点太靠近边缘,或分布不均匀 | 重新选点,保证三角形覆盖中心区域 |
| 校准结果偶尔跳变 | 触摸坐标噪声大,矩阵病态 | 多次采样取平均,或增加滤波 |
| 三个点准,中间不准 | 触摸屏本身非线性严重 | 考虑四点或九点校准,用更复杂的模型 |
| 矩阵求逆失败 | 三个点共线或太接近 | 检查选点,确保三角形面积足够大 |
最后说一句,三点校准法虽然经典,但不是万能的。如果触摸屏的畸变是桶形或枕形(比如一些曲面屏),线性模型就扛不住了。那时候得用更高阶的模型——不过那是后面章节的内容了。