像素坐标到PLC脉冲的坐标变换与标定:从图像到运动的桥梁
一、第一性原理:为什么需要坐标变换?
1.1 从"两个坐标系"出发
在3D轮廓仪上位机中,存在两个完全不同的坐标系:
- 图像坐标系:以像素为单位,原点在图像左上角,X轴向右,Y轴向下。定位算法找到的打标区域中心,坐标是像素值(如x_dis=500像素)。
- 运动坐标系:以PLC脉冲为单位,控制运动平台移动。PLC需要知道"移动多少脉冲"才能把打标头移动到目标位置。
第一性原理:坐标变换的本质是"把图像坐标系中的位置,换算成运动坐标系中的位置"。因为图像和运动平台是两个独立的系统,它们之间需要一个"翻译"——把像素距离翻译成物理距离,再把物理距离翻译成脉冲数。
1.2 变换的链条
从像素坐标到PLC脉冲,需要经过两个关键换算:
像素坐标 → 物理距离(毫米) → PLC脉冲数
- 像素 → 毫米:需要知道"单像素精度"(Accuracy),即每个像素对应多少毫米。这是标定得到的。
- 毫米 → 脉冲:需要知道"脉冲当量",即每个脉冲对应多少毫米。这是运动系统的固有参数。
第一性原理:整个变换的核心是"比例换算"。像素坐标乘以像素精度得到毫米,毫米除以脉冲当量得到脉冲数。这个"乘精度、除当量"的过程,就是坐标变换的本质。
二、标定:确定像素精度
2.1 什么是标定?
标定(Calibration)是确定"像素与物理单位之间换算关系"的过程。在3D轮廓仪上位机中,核心标定参数是单像素精度(Accuracy)——每个像素对应多少毫米。
第一性原理:标定的本质是"建立像素与物理世界的映射"。相机看到的图像是像素,但运动平台需要的是毫米,两者之间需要一个比例系数。这个系数不能凭空设定,必须通过实际测量得到。
2.2 标定的方法
常见的标定方法是N点标定(项目中的2_N_point_calibration模块):
Accuracy = 物理距离(毫米) / 像素距离(像素)
例如,如果标定物实际长75毫米,在图像中占1000像素,则Accuracy = 75 / 1000 = 0.075毫米/像素。
2.3 精度在代码中的体现
在MainView.cs中,Accuracy被用于ROI框尺寸的计算:
Size BoxSize = new Size((CConfig.RoiWidth*1000/CConfig.Accuracy), (CConfig.RoiHeight*1000/ CConfig.Accuracy));
这里CConfig.RoiWidth是ROI框的物理尺寸(毫米),CConfig.Accuracy是单像素精度(毫米/像素)。第一性原理:物理尺寸 / 精度 = 像素尺寸,即把毫米换算成像素。因为定位算法在像素空间工作,所以ROI框的物理尺寸必须换算成像素尺寸。
注意这里有个细节:RoiWidth*1000/Accuracy。如果RoiWidth单位是毫米,Accuracy单位是毫米/像素,那么RoiWidth/Accuracy就是像素数。乘以1000可能是为了处理小数精度(用整数运算避免浮点误差)。
三、项目实例讲解:像素坐标到PLC脉冲的换算
3.1 定位结果:Box结构体
定位算法(DLL)返回一个Box结构体,包含打标区域的信息:
public int x_dis;//与右下角的横向距离
public int y_dis;//与右下角的纵向距离
x_dis和y_dis是打标区域中心到图像右下角的像素距离。这是定位算法输出的关键结果——它告诉系统"打标区域在图像的哪个位置"。
3.2 核心换算代码
在driveBtn_Click方法中,定位完成后进行坐标变换:
//拿box中x_dis,y_dis乘以像素精度再转换为plc脉冲
int x_pulse = (int)((box.x_dis * CConfig.Accuracy + startPoint * CConfig.Accuracy – 345.65 * 1000)/10);
int y_pulse = (int)((box.y_dis * CConfig.Accuracy – 56.12 * 1000) / 10);
这段代码是坐标变换的核心,我们逐项拆解:
第一项:box.x_dis * CConfig.Accuracy
把打标区域中心的像素距离(x_dis)乘以单像素精度(Accuracy),得到物理距离(毫米)。这是"像素→毫米"的换算。
第二项:startPoint * CConfig.Accuracy
startPoint是扫描的起始像素位置。因为x_dis是相对于图像右下角的距离,而实际打标区域可能不在图像右下角,所以需要加上起始位置的物理偏移。这也是"像素→毫米"的换算。
第三项:- 345.65 * 1000
这是一个标定偏移量。345.65是某个基准位置的物理坐标(毫米),乘以1000可能是为了统一单位(毫米→微米,或处理小数)。这个偏移量表示"图像坐标系原点"与"运动坐标系原点"之间的固定偏差,通过标定得到。
第四项:/10
这是脉冲当量换算。除以10表示"每10个物理单位对应1个脉冲",即脉冲当量是10。这是运动系统的固有参数。
综合理解:
x_pulse = (打标区域物理位置 – 基准偏移) / 脉冲当量
即:先算出打标区域在运动坐标系中的物理位置,再除以脉冲当量得到脉冲数。这就是完整的"像素→毫米→脉冲"变换链。
3.3 方向判断与PLC写入
if (x_pulse>0)
{
WritePlc("M103", 1);
WritePlc("D108", Math.Abs(x_pulse));
}
else
{
WritePlc("M103", 0);
WritePlc("D108", Math.Abs(x_pulse));
}
这段代码处理运动方向和脉冲写入:
- x_pulse > 0:判断运动方向。正数表示正向运动,负数表示反向运动。
- WritePlc("M103", 1/0):写入方向标志。M103是PLC的方向寄存器,1表示正向,0表示反向。
- WritePlc("D108", Math.Abs(x_pulse)):写入脉冲数。D108是PLC的脉冲寄存器,写入脉冲数的绝对值(因为方向由M103控制,脉冲数只取绝对值)。
第一性原理:运动控制需要两个信息——方向和距离。方向用标志位(M103)表示,距离用脉冲数(D108)表示。这种"方向寄存器 + 脉冲寄存器"的模式,是PLC运动控制的通用做法。
同理,Y轴方向:
if(y_pulse>0)
{
WritePlc("M102", 1);
WritePlc("D100", Math.Abs(y_pulse));
}
else
{
WritePlc("M102", 0);
WritePlc("D100", Math.Abs(y_pulse));
}
Y轴用M102控制方向,D100存脉冲数。
3.4 触发运动
WritePlc("M100", 1);
WritePlc("M101", 1);
写入M100和M101触发运动。第一性原理:PLC运动控制通常需要"先设置参数,再触发执行"。M100、M101是触发信号,置1后PLC开始执行运动。这种"参数就绪 → 触发执行"的模式,保证了运动的安全性和确定性。
四、坐标变换的完整流程
通过分析driveBtn_Click,我们可以总结出从图像到运动的完整流程:
1. 定位算法返回打标区域中心(像素坐标 x_dis, y_dis)
2. 像素坐标 × 像素精度 → 物理距离(毫米)
3. 物理距离 + 起始偏移 – 基准偏移 → 运动坐标系中的物理位置
4. 物理位置 / 脉冲当量 → 脉冲数
5. 判断方向,写入方向寄存器(M102/M103)
6. 写入脉冲数(D100/D108)
7. 触发运动(M100/M101)
这个流程是"图像定位 → 坐标变换 → 运动控制"的完整链路,也是机器视觉+运动控制系统的核心范式。
五、坐标变换的关键经验
5.1 标定是基础
坐标变换的准确性完全取决于标定参数(Accuracy、基准偏移、脉冲当量)。标定不准,变换结果必然不准。**“标定决定精度”**是坐标变换的第一原则。
5.2 单位统一
坐标变换中最容易出错的是单位混乱。像素、毫米、微米、脉冲,各种单位混在一起。代码中*1000、/10这些魔法数字,本质上是单位换算。建议用常量或配置项明确单位,避免魔法数字。
5.3 方向与距离分离
运动控制中,方向(正负)和距离(绝对值)分开处理。方向用标志位,距离用脉冲数。这样逻辑清晰,也便于PLC端处理。
5.4 参数就绪再触发
先设置所有运动参数(方向、脉冲数),再触发执行。避免"边设置边运动"导致的错误。
六、结语
坐标变换的本质是"把图像坐标系中的位置,换算成运动坐标系中的位置"。通过"像素×精度→毫米,毫米÷当量→脉冲"的比例换算,加上标定偏移量的修正,实现了从图像到运动的精确映射。
在3D轮廓仪上位机中,box.x_dis * CConfig.Accuracy + startPoint * CConfig.Accuracy – 345.65 * 1000这段代码,浓缩了坐标变换的全部精髓:像素精度换算、起始偏移、基准偏移、脉冲当量。理解这段代码,就理解了机器视觉+运动控制系统的核心——如何把"看到的"变成"做到的"。
至此,15篇技术文章全部完成。从系统架构、深度图格式、相机SDK、定位算法、P/Invoke互操作、多线程、图像拼接、PLC通信、TCP协议、配置管理、边缘检测、3D点云、自定义控件、软键盘到坐标变换,完整覆盖了3D轮廓仪上位机的核心技术点。




