从零开始:如何通过VM3.4二次开发进行手眼标定
本文主要介绍什么是手眼标定、如何通过VM二次开发进行一键手眼标定以及最终的效果展示。

一、什么是手眼标定

在做手眼标定之前,我们一定要弄清楚什么是标定。

1. 标定的本质

标定的目的是统一坐标系,将图像坐标系中的点转换到物理坐标系中,然后在物理坐标系中进行数值处理。从数学的角度来看标定:




标定:已知像素坐标系A有一系列点Px1,Py1;物理坐标系B有一系列点Wx1,Wy1;通过标定运算获得坐标系A到坐标系B的转换关系Matrix,即我们生成的标定文件。

生产:已知坐标系A的一个点currentPtA;求解该点在坐标系B中的对应点currentPtAMapB。

求解:currentPtAMapB = currentPtA*Matrix


2. 标定的分类

标定的目的是统一坐标系,但是标定的过程不一样,标定的流程也会有所不同。下图是外参标定的常用方法,本文我们以手眼标定中12点标定为例讲解。



3. 12点相对标定

12点标定是9点标定和旋转标定的总和。主要用于处理不共轴的问题。不共轴:即运行点和基准点不重合,当基准点发生旋转时,运行点会发送变化的现象。常见的不共轴原因有:①图像处理引起,②机构引起,③图像定位点与机构定位点不重合引起。



9点标定的结果是个3×3的矩阵Matrix,旋转标定的结果是点Center,而12点标定则是将前者统一起来,做了原点归一化,将旋转中心当做原点(零点)。



将旋转中心(CenterX,CenterY)与12点标定矩阵相乘,即做标定转换,可发现输出结果为(0,0),由此可见12点标定是将原点定在了旋转中心上,旋转中心即为零点。
注意:由上面的关系可知,当基准拍照位置变化后,需要重新做12点标定,因为旋转中心发生了移动。所以12点标定的拍照点必须与生产时的拍照点完全一致


4. 一键标定流程中标定与通讯的配合



一键标定可分为三个阶段:
1)开始标定:机构移动到拍照位,如上图0的位置(和后续的5,10位置相同),吸取标定对象(可以是产品),然后给视觉软件发送开始标定指令
2)XY平移标定:机构按设定的偏移值依次移动到上图9个位置,然后给视觉发送到位信号。
3)旋转标定:机构依次移动到上图的3个位置(此时XY要保持不动,只能选择R轴),然后给视觉发送到位信号。

二、VM二次开发进行一键手眼标定

1. 标定方案的搭建

这里我们以VM算法平台例程中的N点标定方案为例,方案默认路径为C:\Program Files\VisionMaster3.4.0\Applications\Samples\CH\软件功能展示\标定\N点标定
通过特征匹配模块获取图像Mark点作为图像点传给N点标定模块。




2. 二次开发的一键标定流程
首先获取到N点标定模块的基本参数。


//清空N点标定数据点
int nRet = ImvsPlatformSDK_API.IMVS_PF_SetParamValue_CS(m_handle, 3, "ClearPoint", "null");

//标定结束标志位
CalibEnd = false;

//获取参数偏移量X
string MoveX = string.Empty;
string MoveY = string.Empty;
string MoveAngle = string.Empty;

nRet = ImvsPlatformSDK_API.IMVS_PF_GetParamValue_CS(m_handle, 3, "MoveAlignX", 1024, ref MoveX);
nRet = ImvsPlatformSDK_API.IMVS_PF_GetParamValue_CS(m_handle, 3, "MoveAlignY", 1024, ref MoveY);
nRet = ImvsPlatformSDK_API.IMVS_PF_GetParamValue_CS(m_handle, 3, "MoveAngle", 1024, ref MoveAngle);

//获取参数基准点
string BasicX = string.Empty;
string BasicY = string.Empty;
nRet = ImvsPlatformSDK_API.IMVS_PF_GetParamValue_CS(m_handle, 3, "BasePointX", 1024, ref BasicX);
nRet = ImvsPlatformSDK_API.IMVS_PF_GetParamValue_CS(m_handle, 3, "BasePointY", 1024, ref BasicY);

然后根据获取的基准点,偏移值,计算出要走的12点点位,然后开始走9宫格。


//计算要走到的工位
PointF[] absoluteFList = new PointF[9];
for (int i = 0 ; i < 9 ; i++)
{
absoluteFList[i] = new PointF();

if (i == 0 || i == 5 || i == 6)
{
absoluteFList[i].fX = Convert.ToSingle(BasicX) - Convert.ToSingle(MoveX);
}
else if (i == 1 || i == 4 || i == 7)
{
absoluteFList[i].fX = Convert.ToSingle(BasicX);
}
else
{
absoluteFList[i].fX = Convert.ToSingle(BasicX) + Convert.ToSingle(MoveX);
}

if (i < 3)
{
absoluteFList[i].fY = Convert.ToSingle(BasicY) - Convert.ToSingle(MoveY);
}
else if (i < 6)
{
absoluteFList[i].fY = Convert.ToSingle(BasicY);
}
else if (i < 9)
{
absoluteFList[i].fY = Convert.ToSingle(BasicY) + Convert.ToSingle(MoveY);
}
}

float fDeltaX = 0F;
float fDeltaY = 0F;
float fDeltaTheta = 0F;
float fPreX = 0F;
float fPreY = 0F;
float fPreTheta = 0F;

//开始走九宫格
for (int i = 0 ; i < 9 ; i++)
{
fDeltaX = absoluteFList[i].fX - fPreX;
fDeltaY = absoluteFList[i].fY - fPreY;

//走到对应位置
GoOnePos(fDeltaX, fDeltaY, 0F);
fPreX = absoluteFList[i].fX;
fPreY = absoluteFList[i].fY;
this.AddToMessage(string.Format("第" + (i + 1) + "点"+ absoluteFList[i].fX+ "," +absoluteFList[i].fY + "," +0));


//睡眠300ms
Thread.Sleep(300);

//触发相机拍照
nRet = ImvsPlatformSDK_API.IMVS_PF_ExecuteOnce_V30_CS(m_handle, 10000, "calib");
Thread.Sleep(20);

//循环得到返回结果
int waitForReceived = 0;
while (true)
{
if (waitForReceived > 150)
{
throw new Exception();
}

if (!IsRecData)
{
waitForReceived++;
Thread.Sleep(100);
continue;
}
else
{
IsRecData = false;
waitForReceived = 0;
break;
}
}
}


注意:

1)上面代码中的GoOnePos(fDeltaX, fDeltaY, 0F)为通讯的函数,可使用自己习惯的通讯库开发,其中需要有机构运动到位信号,即发送给机构偏移坐标后,需收到到位回复再进行下一步动作。

2)fDeltaX, fDeltaY, 0F为即将运动到的下一点坐标与当前坐标的偏移值。


走位9点平移标定后,再回到原点,开始旋转标定。


//回到初始位置
fDeltaX = absoluteFList[4].fX - absoluteFList[8].fX;
fDeltaY = absoluteFList[4].fY - absoluteFList[8].fY;
GoOnePos(fDeltaX, fDeltaY, 0F);

float[] fAngle = new float[3];
fAngle[0] = 0F - Convert.ToSingle(MoveAngle);
fAngle[1] = 0F;
fAngle[2] = 0F + Convert.ToSingle(MoveAngle);

for (int i = 0 ; i < 3 ; i++)
{
fDeltaTheta = fAngle[i] - fPreTheta;
fPreTheta = fAngle[i];


//移动到位
GoOnePos(0F, 0F, fDeltaTheta);
this.AddToMessage(string.Format("第" + (i + 10) + "点" + 0 + "," + 0+ ","+fDeltaTheta));

//睡眠300ms
Thread.Sleep(300);
IsRecData = false;


//触发相机拍照
nRet = ImvsPlatformSDK_API.IMVS_PF_ExecuteOnce_V30_CS(m_handle, 10000, "calib");
Thread.Sleep(20);

//循环得到返回结果
int waitForReceived = 0;
while (true)
{
if (waitForReceived > 150)
{
throw new Exception();
}
if (!IsRecData)
{
waitForReceived++;
Thread.Sleep(100);
continue;
}
else
{
IsRecData = false;
waitForReceived = 0;
break;
}
}
}


最后回到原点,开始生成标定矩阵。


//回到初始位置
fDeltaTheta = fAngle[1] - fAngle[2];
GoOnePos(0F, 0F, fDeltaTheta);
Thread.Sleep(500);
Thread.Sleep(20);
nRet = ImvsPlatformSDK_API.IMVS_PF_ExecuteOnce_V30_CS(m_handle, 10000, "calib");
Thread.Sleep(200);


// 判断结果是否可用
if (!CalibEnd)
throw new Exception("标定失败!");

try
{
//生成标定文件
string strMatPath = AppDomain.CurrentDomain.BaseDirectory + @"\Calib\" + DateTime.Now.ToString("yyyyMMddhhmmss_") + "CalibFile.iwcal";

//生成标定文件
nRet = ImvsPlatformSDK_API.IMVS_PF_SetParamValue_CS(m_handle, 3, "SaveCalibPath", strMatPath);
if (ImvsSdkPFDefine.IMVS_EC_OK == nRet)
{
AddToMessage("标定成功!");
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}


上面代码中的IsRecData和CalibEnd为是否收到标准图像坐标和是否标定完成的bool标志位,IsRecData主要为确认执行一次后,N点标定模块成功收到本次的图像坐标点;CalibEnd为N点标定的标定状态,当标定点不满12点时每次返回0,满12点后返回1,因此可以通过判断该状态来确定N点标定模块是否成功接收到12组数据。这两个标志位可在回调中如下面设置。


private void UpdateDataModuResultOutput(ImvsSdkPFDefine.IMVS_PF_MODU_RES_INFO struResultInfo)
{
if (null == struResultInfo.pData)
{
return;
}
switch (struResultInfo.strModuleName)
{
case ImvsSdkPFDefine.MODU_NAME_ALIGNCALIBMODU:
ImvsSdkPFDefine.IMVS_PF_ALIGNCALIB_MODU_INFO stCalibInfo = (ImvsSdkPFDefine.
IMVS_PF_ALIGNCALIB_MODU_INFO)Marshal.PtrToStructure(struResultInfo.pData,
typeof(ImvsSdkPFDefine.IMVS_PF_ALIGNCALIB_MODU_INFO));
if (1 == stCalibInfo.iModuStatu)
{
CalibEnd = true;
}

if(stCalibInfo.pstImagePt[stCalibInfo.iCalibIndex-1].fPtX !=0 || stCalibInfo.pstImagePt[stCalibInfo.iCalibIndex - 1].fPtY != 0)
{
IsRecData = true;
}
break;

default: break;
}
}

三、效果展示

这里本文使用调试助手模拟PLC和软件通讯:




最终生成标定文件:

版权声明:本文为V社区用户原创内容,转载时必须标注文章的来源(V社区),文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:tech_support@hikrobotics.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
评论请先登录 登录
全部评论 0
7
创作
11
粉丝
18
获赞
相关阅读
  • 海康机器视觉工程师认证相关内容
    2021-11-04
  • 深度学习的前沿研究与应用
    2022-01-04
  • 集成OpenCV、Halcon算子进VM算法平台的方法
    2021-11-15
  • 轻松学会镜头选型
    2021-10-27
  • 【VM的视觉应用】IPad表面划痕缺陷检测
    2021-11-04

请升级浏览器版本

您正在使用的浏览器版本过低,请升级最新版本以获得更好的体验。

推荐使用以下浏览器