C#Halcon九点标定自动标定插件
通常所说的相机标定分为两种,相机参数的标定与相机和机械手之间的标定
第一种是相机参数的标定,这一般用到张氏标定法,标定的作用是校正相机自身的畸变,利用校正得到的参数对图形进行处理后再呈现出来。一般的机械手定位也不会进行这个标定,因为现在的相机畸变还是很小的,精度可以满足大多数要求。网上原理理论很多,有兴趣的小伙伴可以自行参考。插件封装可以参考之前的Demo C#仿Halcon Calibration插件
本文封装的Demo是第二种,相机和机械手之间的标定,相机知道的是像素坐标,机械手是空间坐标系,所以手眼标定就是得到像素坐标系和空间机械手坐标系的坐标转化关系。手眼标定作用:建立相机坐标系和机械手坐标系之间的关系,即给机械手装上眼睛,让它去哪就去哪。
九点标定直接建立相机和机械手之间的坐标变换关系。让机械手的末端去走这9个点得到在机器人坐标系中的坐标,同时还要用相机识别9个点得到像素坐标。这样就得到了9组对应的坐标。
核心原理算子vector_to_hom_mat2d 更多原理可以参考小编之前文章 Halcon九点标定与旋转标定
本文主要针对C#UI布局和功能给大家提供自己的封装思路
实现效果
UI布局(后期调用可以以XML方式/Ini文件方式/添加按键以Halcon数据方式保存)
相对坐标/绝对坐标的作用为机械/图像坐标textBox是否只读,在相对坐标模式下根据步幅确定按键自动生成机械坐标数据,在绝对坐标模式下需手动输入机械坐标,可以输入对应的机械坐标,方便后期与机械手核对坐标。
实现方式为:RadioButton
private void radioButton1_Click(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
label11.Visible = true;
tbx_step.Visible = true;
btn_CreateRobotPoint.Visible = true;
for (int i = 0; i < 9; i++)
{
_worldTextBoxArray[i].ReadOnly=true;
_pixelTextBoxArray[i].ReadOnly = true;
}
}
}
图像坐标通过加载图像文件夹读取图像自动获取图像坐标方式实现
private void btn_loadImage_Click(object sender, EventArgs e)
{
try
{
//创建文件夹浏览对话框
var dialog = new FolderBrowserDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
//获取选择的路径
var calibImageRootPath = dialog.SelectedPath;
//获取当前目录下所有的图像信息
var dicInfo = new DirectoryInfo(calibImageRootPath);
//筛选图像
files = dicInfo.GetFiles("*.bmp");
//遍历并填充到图像列表listview中
foreach (var file in files)
{
//如果图像不为孔
if (file != null)
{
//创建Listview的列表项 并添加图像名称
var lvItem = new ListViewItem(file.Name);
//添加状态列的数据
lvItem.SubItems.Add("完成");
//添加lvItem到listView1控件列表中
listView1.Items.Add(lvItem);
}
}
//循环显示图片
for (int i = 0; i < dicInfo.GetFiles().Length; i++)//遍历文件夹
{
Application.DoEvents();
HIMage = new HImage(files[i].FullName);
labTicps.Text = "当前正在加载第" + (i + 1) + "张图像";
GetPoint();
Double R = hv_RowCheck.D;
Double C = hv_ColumnCheck.D;
hSW.SetColor("red");
hSW.SetDraw("margin");
hSW.DispObj(HIMage);
hSW.DispObj(ho_Circle);
hSW.DispObj(ho_Cross);
_pixelTextBoxArray[i].Text = R.ToString("0.000") + "," + C.ToString("0.000");
//自适应图片
hSmartWindowControl1.SetFullImagePart();
Thread.Sleep(1000);
}
}
}
catch (Exception)
{
MessageBox.Show("导入图像失败");
}
}
执行标定
private void calibResultPot()
{
for (int i = 0; i < 9; i++)
{
string[] sArray = _pixelTextBoxArray[i].Text.Split(',');
hv_Bx[i] = Convert.ToSingle(sArray[0]);
hv_By[i] = Convert.ToSingle(sArray[1]);
}
for (int i = 0; i < 9; i++)
{
string[] sArray = _worldTextBoxArray[i].Text.Split(',');
hv_Ax[i] = Convert.ToSingle(sArray[0]);
hv_Ay[i] = Convert.ToSingle(sArray[1]);
}
hv_HomMat2D.Dispose();
HOperatorSet.VectorToHomMat2d(hv_Bx, hv_By, hv_Ax, hv_Ay, out hv_HomMat2D);
float A = hv_HomMat2D[0].F;
float B = hv_HomMat2D[1].F;
float C = hv_HomMat2D[2].F;
float D = hv_HomMat2D[3].F;
float E = hv_HomMat2D[4].F;
float F = hv_HomMat2D[5].F;
_paramTextBoxArray[0].Text = A.ToString();
_paramTextBoxArray[1].Text = B.ToString();
_paramTextBoxArray[2].Text = C.ToString();
_paramTextBoxArray[3].Text = D.ToString();
_paramTextBoxArray[4].Text = E.ToString();
_paramTextBoxArray[5].Text = F.ToString();
hv_tx = Convert.ToDouble(_beginTextBoxArray[0].Text);
hv_ty = Convert.ToDouble(_beginTextBoxArray[1].Text); ;
HOperatorSet.AffineTransPoint2d(hv_HomMat2D, hv_tx, hv_ty, out hv_Qx, out hv_Qy);
_beginTextBoxArray[3].Text = hv_Qx.D.ToString();
_beginTextBoxArray[4].Text = hv_Qy.D.ToString();
_beginTextBoxArray[5].Text = _beginTextBoxArray[2].Text;
}