3、校正坐标系建立:振镜坐标系、场镜坐标系、工件坐标系的转换关系
做振镜校正,说白了就是跟三个坐标系打交道。我刚开始接触这行的时候,总觉得坐标系转换是个纯数学问题,套公式就行了。后来在产线上被现实狠狠教育了一回——你算得再漂亮,装调一偏,全白搭。
今天咱们就把这三个坐标系的关系彻底捋清楚。你想想看,激光束从振镜出来,经过场镜聚焦,最后打到工件上。每一步都涉及坐标变换。搞不懂这个,校正就是空中楼阁。
3.1 三个坐标系,各管各的事
先给它们一个明确的定义:
- 振镜坐标系 (G):以振镜转轴中心为原点。X、Y轴对应两个镜片的偏转角度。说白了,这就是你给振镜发的指令坐标。
- 场镜坐标系 (F):以场镜的光轴为基准。原点在场镜的入射光瞳中心。这个坐标系描述的是光束经过场镜后的理想位置。
- 工件坐标系 (W):以工件台面为参考。原点通常设在工件的一个角上或中心。这是你最终要加工的位置。
核心认知: 振镜控制的是角度,场镜负责把角度映射成位置,工件坐标系才是你真正关心的加工坐标。三者之间是串联转换关系。
3.2 振镜坐标系 → 场镜坐标系
这一步转换,我习惯叫它「角度到位置的映射」。为什么这么说?因为振镜输出的本质是两个角度值 (θx, θy),而场镜的输入是光束的入射位置和角度。
对于理想场镜(f-θ 透镜),转换关系很简单:
X_f = f * θx
Y_f = f * θy
其中 f 是场镜的焦距。嗯,这里要注意——这个公式只在理想情况下成立。我在项目中遇到过一批场镜,实际焦距跟标称值差了0.3%。你说大不大?对于微米级加工,这误差能让你直接报废一批产品。
我的经验: 别迷信场镜的标称焦距。每次换新场镜,我都会用标准网格板实测一遍,反算出实际焦距。这个习惯帮我避免了好几次批量报废。
3.3 场镜坐标系 → 工件坐标系
这一步转换,说白了就是「理想位置到实际位置的映射」。场镜坐标系里的点 (X_f, Y_f) 是理论上的理想位置,但到了工件上,因为装调误差、工件倾斜、场镜畸变等因素,实际落点会偏移。
转换关系一般用一个仿射变换来描述:
X_w = a11 * X_f + a12 * Y_f + tx
Y_w = a21 * X_f + a22 * Y_f + ty
这里的 a11, a12, a21, a22 是旋转和缩放参数,tx, ty 是平移参数。你可能会问:这不就是个2D仿射变换吗?对,但实际应用中,我还会加入高阶项来补偿场镜的桶形畸变。
避坑指南: 我曾经遇到过一台设备,振镜和场镜的装调角度差了0.5度。仿射变换的参数怎么算都对不上。后来发现是机械装配时场镜绕Z轴转了个小角度。所以,做校正前,先确认机械装调没问题,否则你就是在用算法弥补机械的锅。
3.4 完整的转换链路
把上面两步串起来,完整的转换链路就是:
振镜角度 (θx, θy)
→ 场镜理想位置 (X_f, Y_f)
→ 工件实际位置 (X_w, Y_w)
反过来,校正的时候我们做的是逆向映射:
工件目标位置 (X_w, Y_w)
→ 场镜理想位置 (X_f, Y_f)
→ 振镜角度 (θx, θy)
这个逆向映射,就是校正算法的核心。说白了,你测出一堆网格点的实际位置,然后反算出振镜该给什么角度。
3.5 坐标系转换的SVG框架图
下面这张图,把三个坐标系的关系和转换路径画清楚了。我建议你把它存下来,调试的时候对照着看。
3.6 实战中的坐标系对齐
理论说完了,聊点实际的。坐标系对齐这件事,我踩过的坑比走过的路还多。
第一个坑:原点定义不一致。 振镜的原点在转轴中心,场镜的原点在光瞳,工件的原点在台面。这三个原点在物理空间里根本不重合。怎么办?我的做法是:先找一个公共参考点,比如用一个十字标记,让三个坐标系都认这个点。
第二个坑:旋转方向。 振镜的X轴正方向跟工件的X轴正方向可能相反。我记得有一次,校正完发现图形是镜像的,找了半天才发现是振镜驱动器的接线反了。嗯,这种低级错误,犯过一次就记住了。
第三个坑:单位统一。 振镜输出的是角度(度或弧度),场镜输出的是长度(毫米),工件也是长度(毫米)。转换的时候别忘了单位换算。我习惯把所有中间量都转成毫米,最后再转回角度。
我的小技巧: 在代码里用一个结构体把三个坐标系的原点、方向、单位都存起来。每次转换前先检查一遍。这个习惯帮我省了不少调试时间。
3.7 代码示例:坐标系转换类
下面是一个简单的Python类,实现了三个坐标系之间的转换。实际项目中我会加上畸变模型,这里先给个基础版本:
class CoordinateTransformer:
def __init__(self, focal_length):
self.f = focal_length # 场镜焦距 (mm)
# 仿射变换参数 (从场镜到工件)
self.a11 = 1.0
self.a12 = 0.0
self.a21 = 0.0
self.a22 = 1.0
self.tx = 0.0
self.ty = 0.0
def g_to_f(self, theta_x, theta_y):
"""振镜角度 → 场镜理想位置"""
x_f = self.f * theta_x # theta 单位:弧度
y_f = self.f * theta_y
return x_f, y_f
def f_to_w(self, x_f, y_f):
"""场镜理想位置 → 工件实际位置"""
x_w = self.a11 * x_f + self.a12 * y_f + self.tx
y_w = self.a21 * x_f + self.a22 * y_f + self.ty
return x_w, y_w
def g_to_w(self, theta_x, theta_y):
"""振镜角度 → 工件位置(完整链路)"""
x_f, y_f = self.g_to_f(theta_x, theta_y)
return self.f_to_w(x_f, y_f)
def set_affine(self, a11, a12, a21, a22, tx, ty):
"""设置仿射变换参数(由校正算法计算得出)"""
self.a11 = a11
self.a12 = a12
self.a21 = a21
self.a22 = a22
self.tx = tx
self.ty = ty
这个类看着简单,但它是整个校正系统的基础。你想想看,所有的校正算法最终都要落到这个转换关系上。参数怎么来?靠实测网格点,用最小二乘法拟合出来。
3.8 小结
三个坐标系的关系,说白了就是:振镜给角度,场镜做映射,工件看结果。校正的本质,就是把这个映射关系反过来算清楚。
我个人习惯在每次校正前,先手动验证一下坐标系的对齐。拿一个已知位置的标记点,让振镜打一下,看看实际落点跟预期差多少。这一步花不了几分钟,但能避免后面白忙活半天。
下一节咱们会深入讲校正数据的采集方法。到时候你会看到,坐标系转换的精度直接决定了校正效果的好坏。