C# 上位机实战:视觉检测与九点标定、旋转中心拟合完整实现(下篇)
上篇打通了 PLC 通信。这篇讲机器的"眼睛"——Cognex VisionPro 视觉:怎么加载 VPP 作业、一次/二次拍照怎么算角度和 XY 偏移、九点标定怎么把像素坐标映射到机械坐标、三点拟合旋转中心怎么算圆心。(上篇见《C# 上位机实战:PLC(S7-1200) 以太网通信 + 机器视觉系统架构》)
一、Vision 类:封装 VisionPro 视觉
Vision也是单例,负责加载 VPP 作业(ToolBlock)和取相机:
internalclassVision{privatestaticreadonlyLazy<Vision>_instance=newLazy<Vision>(()=>newVision());publicstaticVisionInstance{get{return_instance.Value;}}// 两个 VPP 作业路径publicconststringIdentificationTbPath="VPP/Identification_tb.vpp";// 识别publicconststringNPointCalibrationTbPath="VPP/NPointCalibration_tb.vpp";// N点标定// 后台加载 VPPpublicTaskLoadVpp(){returnTask.Run(()=>{IdentificationTb=CogSerializer.LoadObjectFromFile(IdentificationTbPath)asCogToolBlock;NPointCalibrationTb=CogSerializer.LoadObjectFromFile(NPointCalibrationTbPath)asCogToolBlock;});}// 取识别 TB 的相机(从工具里按类型找 AcqFifo)publicICogAcqFifoIdentificationFifo{get{returnIdentificationTb.Tools.OfType<CogAcqFifoTool>().First().Operator;}}// N点标定 TB 的相机同理publicICogAcqFifoNPointCalibrationFifo{get{returnNPointCalibrationTb.Tools.OfType<CogAcqFifoTool>().First().Operator;}}// 保存作业publicvoidSave(CogToolBlocktb,stringpath){CogSerializer.SaveObjectToFile(tb,path);}}关键 API:
OfType<CogAcqFifoTool>().First()按类型筛出所有相机工具、取第一个。两个作业共用一套加载逻辑。
二、主窗体 Form1:连接 PLC + 加载 VPP
1. 启动流程
privatevoidForm1_Load(objectsender,EventArgse){LoadVppAsync();// 异步加载视觉作业connectPlc_Click(null,null);// 连接 PLC}privateasyncvoidLoadVppAsync(){log("开始加载VPP");try{awaitvision.LoadVpp();log("VPP加载完成");// 监听检测识别到位事件(PLC 发拍照编号触发)plcOperator.MeasureNoChange-=testing;plcOperator.MeasureNoChange+=testing;// 菜单/按钮启用startLiveBtn.Enabled=true;photo1Btn.Enabled=true;photo2Btn.Enabled=true;}catch(Exceptionex){log("VPP加载失败:"+ex.Message);}}2. 连接 PLC
privateasyncvoidconnectPlc_Click(objectsender,EventArgse){try{awaitplcOperator.Connect(CpuType.S71200,globalConfig.PlcIp,0,1);log("PLC连接成功");}catch(Exceptionex){log("PLC连接失败, msg"+ex.Message);}}三、检测逻辑:一次/二次拍照算偏移(核心)
PLC 触发MeasureNoChange事件后,进入testing(num)。第一次拍照算角度偏移 R,第二次拍照算 XY 偏移:
privatevoidtesting(intnum){log($"开始{num}次检测");Invoke(newAction(()=>vision.IdentificationTb.Run()));// 在 UI 线程跑作业// 取识别结果图cogIdentificationDisplay.Record=vision.IdentificationTb.CreateLastRunRecord().SubRecords["CogCalibNPointToNPointTool1.OutputImage"];if(num==1)// 第一次拍照:算角度偏移{floatr=Convert.ToSingle(vision.IdentificationTb.Outputs["Angle"].Value);r=(float)(180/Math.PI)*r;// 弧度转角度floatoffsetR=-(globalConfig.BaseA-r);// 识别角度和设备角度相反,取负号log($"一次检测完成,角度:{r}偏移角度:{offsetR}");plcOperator.OffsetR=offsetR;// 让机器旋转到需要的角度}if(num==2)// 第二次拍照:算 XY 偏移{floatx=Convert.ToSingle(vision.IdentificationTb.Outputs["X"].Value);floaty=Convert.ToSingle(vision.IdentificationTb.Outputs["Y"].Value);floatoffsetX=globalConfig.BaseX-x;floatoffsetY=globalConfig.BaseY-y;log($"二次检测完成,x偏移:{offsetX}y偏移:{offsetY}");plcOperator.OffsetX=offsetX;// 先发偏移plcOperator.OffsetY=offsetY;}plcOperator.MeasureCheckNo=num;// 再发确认编号,通知 PLC 继续log($"{num}次偏移发送完成");}核心思路:识别到当前工件的位置/角度 → 和基准点(Config 里的 BaseX/BaseY/BaseA)做差 → 得到偏移量 →先写偏移、再写确认编号,PLC 据此补偿。
四、九点标定:像素坐标 ↔ 机械坐标
标定的目的:把相机看到的像素坐标,换算成设备机械坐标。用 VisionPro 的CogCalibNPointToNPoint九点工具:
// 获取主程序中的九点标定工具calib=(vision.IdentificationTb.Tools["CogCalibNPointToNPointTool1"]asCogCalibNPointToNPointTool).Calibration;收到"九点到位"事件后
privateasyncvoidNPointCalibrationNoChange(intno){log(no+"次到位, 开始识别");awaitTask.Delay(1000);// 等机械到位Invoke(newAction(()=>vision.NPointCalibrationTb.Run()));// 识别doublex=(double)vision.NPointCalibrationTb.Outputs["X"].Value;// 像素Xdoubley=(double)vision.NPointCalibrationTb.Outputs["Y"].Value;// 像素Ylog($"识别成功,像素坐标为x:{x}y:{y}");// 添加点对:前两个=像素坐标,后两个=机械坐标calib.AddPointPair(x,y,plcOperator.PosX,plcOperator.PosY);log($"已添加点对:像素{x}{y}与 机械坐标:{plcOperator.PosX}{plcOperator.PosY}");plcOperator.NPointCalibrationCheckNo=no;// 通知 PLC 拍照完成,共 9 次if(no>=9)// 9 个点对齐了,开始计算标定{calib.Calibrate();// 相当于 VP 九点工具的"计算校正"按钮if(calib.Calibrated){log("校准完成, RMS:"+calib.ComputedRMSError);// 误差一般 10 以内最佳if(calib.ComputedRMSError>30)log("标定结果不理想,建议重新标定");vision.Save(vision.IdentificationTb,Vision.IdentificationTbPath);// 保存作业}}}RMS 误差:
calib.ComputedRMSError是标定精度指标,一般10 以内最佳,超过 30 建议重标。
五、三点拟合旋转中心:算圆心
设备要绕一个点旋转,这个旋转中心(圆心)怎么找?让机械转三次,分别识别同一特征点,得到三个坐标,用三点求圆心:
floatx1,y1,x2,y2,x3,y3,cx,cy;privatevoidPLC_CenterCalibrationNoChange(intnum){BeginInvoke(newAction(()=>{vision.IdentificationTb.Run();floatx=Convert.ToSingle(vision.IdentificationTb.Outputs["X"].Value);floaty=Convert.ToSingle(vision.IdentificationTb.Outputs["Y"].Value);// 按次数存下三个点if(num==1){x1=x;y1=y;}elseif(num==2){x2=x;y2=y;}elseif(num==3){x3=x;y3=y;}plcOperator.CenterCalibrationCheckNo=num;// 通知 PLC 拍完,发起下次旋转if(num==3)// 三次旋转完成,计算圆心{calcCircleCenter(x1,y1,x2,y2,x3,y3,outcx,outcy);label7.Text=$"X:{cx:F2}, Y:{cy:F2}";}}));}// 三点求圆心(垂直平分线交点)privatevoidcalcCircleCenter(floatx1,floaty1,floatx2,floaty2,floatx3,floaty3,outfloatx,outfloaty){doublea=x1-x2;doubleb=y1-y2;doublec=x1-x3;doubled=y1-y3;doublee=((x1*x1-x2*x2)-(y2*y2-y1*y1))/2;doublef=((x1*x1-x3*x3)-(y3*y3-y1*y1))/2;x=(float)((e*d-b*f)/(a*d-b*c));y=(float)((a*f-e*c)/(a*d-b*c));}把算出的圆心存进
globalConfig.RotateCenterX/Y,下次旋转就能绕这个中心转了。
六、相机 / 作业 / 系统配置窗体
上位机还要能改配置,用了三个配置窗体:
1. 相机配置(CogFifoEditFrm)
publicCogFifoEditFrm(ICogAcqFifofifo){InitializeComponent();cogAcqFifoEditV21.Subject.Operator=fifo;// 绑定相机}publiceventAction<ICogAcqFifo>OnSave;privatevoidCogFifoEditFrm_FormClosing(objectsender,FormClosingEventArgse){OnSave?.Invoke(cogAcqFifoEditV21.Subject.Operator);// 关闭时触发保存}2. 作业配置(CogToolBlockEditFrm)
publicCogToolBlockEditFrm(CogToolBlocktb){InitializeComponent();cogToolBlockEditV21.Subject=tb;// 绑定 ToolBlock}主窗体菜单里用事件
OnSave += SaveIdentificationTb把改动保存回 VPP 文件,实现"相机/作业在线配置"。
3. 系统配置(SettingFrm)
privatevoidSetting_Load(objectsender,EventArgse){textBox1.Text=globalConfig.BaseX.ToString();// 基准点XtextBox2.Text=globalConfig.BaseY.ToString();textBox3.Text=globalConfig.BaseA.ToString();textBox4.Text=globalConfig.RotateCenterX.ToString();// 旋转中心textBox5.Text=globalConfig.RotateCenterY.ToString();textBox6.Text=globalConfig.PlcIp;// PLC IP}// 关闭时写回 GlobalConfig(析构自动存 INI)七、下篇小结
| 知识点 | 一句话 |
|---|---|
| Vision 类 | 加载 VPP ToolBlock,OfType<CogAcqFifoTool>()取相机 |
| 检测流程 | 一次拍算角度R,二次拍算XY,先写偏移再写确认 |
| 角度处理 | 弧度→角度,识别角度与设备相反要取负 |
| 九点标定 | AddPointPair(像素X,像素Y, 机械X,机械Y)建点对 |
| 标定精度 | ComputedRMSError,10 以内最佳、超 30 重标 |
| 旋转中心 | 三点求圆心(垂直平分线交点) |
| 配置窗体 | 相机/作业用OnSave事件存 VPP,系统参数存 INI |
结语
到这里,整套PLC 通信 + 机器视觉上位机就闭环了:PLC 发指令 → 相机拍照识别 → 计算 X/Y/R 偏移 → 回写 PLC 补偿 → 九点标定建立像素↔机械映射 → 三点拟合旋转中心。这套架构就是工业视觉引导定位(视觉对位、上下料引导)的通用模板,掌握了它,换个设备也能照着搭。觉得有用就点赞收藏,一起进阶!
参考:结合 S7.Net 通信、Cognex VisionPro 视觉、WinForm 上位机教学讲义整理的学习笔记。