龙空技术网

机器人与视觉标定

EasyProgram 254

前言:

现时你们对“拟合圆是什么意思”大致比较重视,看官们都想要了解一些“拟合圆是什么意思”的相关资讯。那么小编同时在网络上收集了一些有关“拟合圆是什么意思””的相关文章,希望我们能喜欢,小伙伴们快快来了解一下吧!

机器人引导项目大致可分为以下几种情况:

1.相机固定不动, 上往下看引导机器人移动

2.相机固定不动, 下往上看

3.相机固定在机器人上,离旋转中心较近

4.相机固定在机器人上,离旋转中心很远

5.特殊固定方式 – 分离轴

下面通过以下几种情况分别进行说明机器人与视觉的标定步骤及每种情况下优缺点及适用的条件:

一:相机固定不动, 上往下看引导机器人移动

a.相机非线性校正

使用标定板做非线性校正

b.相机与机器人做9点标定

可以使用机器人扎9个点,或者机器人抓住工件摆放9个位置,得到9个

机械坐标,相机也得到9个像素坐标,然后标定

c.计算机器人的旋转中心

机器人抓取工件分别旋转三个角度摆放到相机视野内,相机可以得到三

个坐标值,通过三个坐标值拟合圆获得圆心坐标即为旋转中心

d.相机通过公式计算得出最终的输出结果

(rx0, ry0)为旋转中心,( x, y)为被旋转的点,(x0,y0)旋转后的点

x0= cos (a) * (x-rx0) – sin (a) * (y-ry0) +rx0

y0= cos (a) * (y-ry0) + sin (a) * (x-rx0) +ry0

该公式(文章下面提到的公式都是指此公式)推导过程已经在前面介绍过,大家不熟悉可以翻到之前的内容去看一下。机器人视觉引导关键的数学公式推导

二:相机固定不动, 下往上看

a.相机非线性校正

使用机器人吸起标定板做非线性校正

b.相机与机器人做9点标定

可以使用实物标定,机器人抓住工件摆放9个位置,得到9个

机械坐标,相机也得到9个像素坐标,然后标定。

c.计算机器人的旋转中心

d.相机通过公式计算得出最终的输出结果

三:相机固定在机器人上,离旋转中心较近

a.相机非线性校正

b.相机与机器人做9点标定

可以使用实物标定,机器人抓住工件摆放9个位置,得到9个机器人坐标,相机也得到9个像素坐(相机每次需要回到固定位置拍照),然后标定。

c.计算机器人的旋转中心

机器人抓取工件分别旋转三个角度摆放到相机视野内,拍照可以得到三个坐标值,通过三个坐标值拟合圆(三点确定一个圆)获得圆心坐标即为旋转中心。此方法操作简单,但是实际应用中机器人的旋转中心离工件较远,旋转已经的角度后,工件便移出了视野,而迫使只能旋转很小的一个角度来使工件不出视野,这样导致的问题就是,三点拟合的圆心并不准确(如下图所示),导致定位精度度达不到要求甚至项目的失败。因此我们就有相机固定在机器人上,离旋转中心很远的情况:

图1

四:相机固定在机器人上,离旋转中心很远

1.相机非线性校正

2.相机与机器人做9点标定

3.计算机器人的旋转中心(文章后面着重讲机器人旋转中心的计算过程)

4.相机通过公式计算得出最终的输出结果

注:由于旋转中心距离视野很远,通常拟合出来的旋转中心存在比较大的误差,给定位精度造成影响

下面进入重点,如何计算机器人的旋转中心。如下图

图2

计算某个点绕另外一点旋转一定角度后的坐标,如图:A(x,y)绕B(rx0,ry0)旋转

a度后的位置为C(x0,y0),则有如下关系式:

x0= cos (a) * (x-rx0) – sin (a) * (y-ry0) +rx0

y0= cos (a) * (y-ry0) + sin (a) * (x-rx0) +ry0

看公式可能有点熟悉,没错就是文章开头提过的那个公式:下面计算所有的旋转和偏移量均是基于上面的公式,公式比较重要,如果大家不清楚其推导过程,或者觉得难记不清楚其原理大家可以翻到之前的内容去看一下。公式推导过程

下面引用康耐视内部培训的一个图片进行说明:

图3

如上图3左所示(机器人的旋转中心位置坐标(Cx,Cy),我们可看作是做模板时机器人示教的标准抓取工件的位置)

当工件发生偏移和旋转后,如上图3右所示,此时机器人的抓取位置应该在上图所示的红色点位,我们标记为(Cx1,Cy1),这样才能保证每次抓取的位置是一致的。此时机器人的X、Y轴偏移量分别为Cx1-Cx、Cy1-Cy,而视觉拍照得出的偏移量为(Mdx,Mdy)显然是不相等的。因此我们得根据公式计算出机器人的抓取位置。要想计算出机器人实际的抓取位置,我们要是知道R,那就好办了,但是实际应用中R很难确定,我们必须性其他的办法,也就是我们之前所说的机器人旋转中心 的计算(如果离旋转中心近,则直接三点拟合圆,得到圆心完事。如果离旋转中心远,那么..............)。废话不多说,下面我们一步步来解决万一离中心远如何计算机器人的旋转中心的问题:如下图4所示:机器人以标准姿态抓取工件移到视野内拍照得出(X,Y)坐标,机器人旋转θ角度后再拍一次照,得出坐标(X’,Y’)套用公式:

X’ = cos θ * (X-Xo) – sin θ *(Y-Yo) + Xo;

Y’ = cos θ* (Y-Yo) + sin θ* (X-Xo) + Yo;

此时:X’,Y’,X,Y,θ均一致,解方程 便可求出Xo,Yo。

回到图3,即我们已经求出坐标Cx,Cy。我们已知模板位置图三红色五角星,我们标记坐标为(Mx0,My0),套用公式我们可以计算出补偿了角度后的新位置后机器人的坐标(图3中未画出)我们标记为(CX’,CY’)。

CX’ = cos θ * (Cx-Mxo) – sin θ *(Cy-Myo) + Mxo;

CY’ = cos θ* (Cy-Myo) + sin θ* (Cx-Mxo) + Myo;

然后再加上视觉给出的偏移量便是机器人抓取位置要补偿的偏移量

图4

下面粘贴两段,第一段代码是:已知点位坐标(X,Y)和饶旋转中心转了A角度后的坐标(X1,Y1),求旋转中心的坐标的代码。第二段代码是:绕某点旋转后的坐标计算的代码。代码的运行环境是Congex VisionPro,我已经做成了一个完整的ToolBlock可以直接用,有需要的同学关注我,免费赠送 ToolBlock,最后希望工程人少走坑:

代码1:

#region namespace imports

using System;

using System.Collections;

using System.Drawing;

using System.IO;

using System.Windows.Forms;

using Cognex.VisionPro;

using Cognex.VisionPro.ToolBlock;

using Cognex.VisionPro3D;

#endregion

public class CogToolBlockAdvancedScript : CogToolBlockAdvancedScriptBase

{

#region Private Member Variables

private Cognex.VisionPro.ToolBlock.CogToolBlock mToolBlock;

private double X1,Y1,X2,Y2,A,W,W1,W2;//W3;

private double d,R,Xt,Yt,X0,Y0;

#endregion

/// <summary>

/// Called when the parent tool is run.

/// Add code here to customize or replace the normal run behavior.

/// </summary>

/// <param name="message">Sets the Message in the tool's RunStatus.</param>

/// <param name="result">Sets the Result in the tool's RunStatus</param>

/// <returns>True if the tool should run normally,

/// False if GroupRun customizes run behavior</returns>

public override bool GroupRun(ref string message, ref CogToolResultConstants result)

{

// To let the execution stop in this script when a debugger is attached, uncomment the following lines.

// #if DEBUG

// if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();

// #endif

// Run each tool using the RunTool function

//foreach(ICogTool tool in mToolBlock.Tools)

//mToolBlock.RunTool(tool, ref message, ref result);

X1 = (double) mToolBlock.Inputs["PX1"].Value;

Y1 = (double) mToolBlock.Inputs["PY1"].Value;

X2 = (double) mToolBlock.Inputs["PX2"].Value;

Y2 = (double) mToolBlock.Inputs["PY2"].Value;

A = (double) mToolBlock.Inputs["A"].Value;

d = System.Math.Sqrt(System.Math.Pow((X2 - X1), 2) + System.Math.Pow((Y2 - Y1), 2));

//d = System.Math.Cos(A / 180 * System.Math.PI) * (X1 - X2) - System.Math.Sin(A / 180 * System.Math.PI) * (Y1 - Y2) + X2;

//d=System.Math.

R = (d / 2) / (System.Math.Sin(A / 2 / 180 * System.Math.PI));

//R = System.Math.Cos(A / 180 * System.Math.PI) * (Y1 - Y2) + System.Math.Sin(A / 180 * System.Math.PI) * (X1 - X2) + Y2;

//if(A < 0)

//{

if((X2 >= X1) && (Y2 >= Y1))//四象限

{

Xt = (1 - (R / d)) * X1 + (R / d) * X2;

Yt = (1 - (R / d)) * Y1 + (R / d) * Y2;

X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

}

else if((X2 <= X1) && (Y2 >= Y1))//一象限

{

Xt = X1 - R / d * (X1 - X2);

Yt = (1 - (R / d)) * Y1 + (R / d) * Y2;

X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

}

else if((X2 <= X1) && (Y2 <= Y1))//二

{

Xt = X1 - R / d * (X1 - X2);

Yt = Y1 - R / d * (Y1 - Y2);

X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

}

else if((X2 >= X1) && (Y2 <= Y1))//三

{

Xt = (1 - (R / d)) * X1 + (R / d) * X2;

Yt = Y1 - R / d * (Y1 - Y2);

X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

}

//}

//else

//{

//if((X2 > X1) && (Y2 > Y1))//四象限

//{

//Xt = (1 - (R / d)) * X1 + (R / d) * X2;

//Yt = (1 - (R / d)) * Y1 + (R / d) * Y2;

//X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

//Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

//}

//else if((X2 < X1) && (Y2 > Y1))//一象限

//{

//Xt = X1 - R / d * (X1 - X2);

//Yt = (1 - (R / d)) * Y1 + (R / d) * Y2;

//X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

//Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

//}

//else if((X2 < X1) && (Y2 < Y1))//二

//{

//Xt = X1 - R / d * (X1 - X2);

//Yt = Y1 - R / d * (Y1 - Y2);

//X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

//Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

//}

//else if((X2 > X1) && (Y2 < Y1))//三

//{

//Xt = (1 - (R / d)) * X1 + (R / d) * X2;

//Yt = Y1 - R / d * (Y1 - Y2);

//X0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) - System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + X1;

//Y0 = System.Math.Cos((90 - A / 2) / 180 * System.Math.PI) * (Yt - Y1) + System.Math.Sin((90 - A / 2) / 180 * System.Math.PI) * (Xt - X1) + Y1;

//}

//}

//W = 1 - System.Math.Cos(A/180*System.Math.PI);

//W1 = W * X2 - W * System.Math.Cos(A/180*System.Math.PI) * X1 + W * System.Math.Sin(A/180*System.Math.PI) * Y1 - System.Math.Sin(A/180*System.Math.PI) * Y2 +

//System.Math.Sin(A/180*System.Math.PI) * System.Math.Cos(A/180*System.Math.PI) * Y1 + System.Math.Sin(A/180*System.Math.PI) * System.Math.Sin(A/180*System.Math.PI) * X1;

//W2 = A + System.Math.Sin(A/180*System.Math.PI) * System.Math.Sin(A/180*System.Math.PI) - W * System.Math.Cos(A/180*System.Math.PI);

mToolBlock.Outputs["OX"].Value = X0;

mToolBlock.Outputs["OY"].Value = Y0;

return false;

}

#region When the Current Run Record is Created

/// <summary>

/// Called when the current record may have changed and is being reconstructed

/// </summary>

/// <param name="currentRecord">

/// The new currentRecord is available to be initialized or customized.</param>

public override void ModifyCurrentRunRecord(Cognex.VisionPro.ICogRecord currentRecord)

{

}

#endregion

#region When the Last Run Record is Created

/// <summary>

/// Called when the last run record may have changed and is being reconstructed

/// </summary>

/// <param name="lastRecord">

/// The new last run record is available to be initialized or customized.</param>

public override void ModifyLastRunRecord(Cognex.VisionPro.ICogRecord lastRecord)

{

}

#endregion

#region When the Script is Initialized

/// <summary>

/// Perform any initialization required by your script here

/// </summary>

/// <param name="host">The host tool</param>

public override void Initialize(Cognex.VisionPro.ToolGroup.CogToolGroup host)

{

// DO NOT REMOVE - Call the base class implementation first - DO NOT REMOVE

base.Initialize(host);

// Store a local copy of the script host

this.mToolBlock = ((Cognex.VisionPro.ToolBlock.CogToolBlock)(host));

}

#endregion

}

ToolBlock1运行界面

代码2:

#region namespace imports

using System;

using System.Collections;

using System.Drawing;

using System.IO;

using System.Windows.Forms;

using Cognex.VisionPro;

using Cognex.VisionPro.ToolBlock;

using Cognex.VisionPro3D;

#endregion

public class CogToolBlockAdvancedScript : CogToolBlockAdvancedScriptBase

{

#region Private Member Variables

private Cognex.VisionPro.ToolBlock.CogToolBlock mToolBlock;

private double X1,Y1,X2,Y2,A;//W,W1,W2;//W3;

private double d,R,Xt,Yt,X0,Y0;

#endregion

/// <summary>

/// Called when the parent tool is run.

/// Add code here to customize or replace the normal run behavior.

/// </summary>

/// <param name="message">Sets the Message in the tool's RunStatus.</param>

/// <param name="result">Sets the Result in the tool's RunStatus</param>

/// <returns>True if the tool should run normally,

/// False if GroupRun customizes run behavior</returns>

public override bool GroupRun(ref string message, ref CogToolResultConstants result)

{

// To let the execution stop in this script when a debugger is attached, uncomment the following lines.

// #if DEBUG

// if (System.Diagnostics.Debugger.IsAttached) System.Diagnostics.Debugger.Break();

// #endif

//坐标系中(X向右为正方向,Y向上为正,第一象限X>0且Y>0,第二象限X<0且Y>0,第三象限X<0且Y<0,第四象限X>0且Y<0),

//逆时针旋转角度取正直,逆时针角度取负值。(实际可以通过旋转后,

//与旋转前的坐标在坐标系中的相对位置判断正转逆转,)

// Run each tool using the RunTool function

//foreach(ICogTool tool in mToolBlock.Tools)

//mToolBlock.RunTool(tool, ref message, ref result);

X1 = (double)mToolBlock.Inputs["Before_Rotation_X"].Value;

Y1 = (double)mToolBlock.Inputs["Before_Rotation_Y"].Value;

X2 = (double)mToolBlock.Inputs["Center_of_Rotation_X"].Value;

Y2 = (double)mToolBlock.Inputs["Center_of_Rotation_Y"].Value;

A = (double)mToolBlock.Inputs["Rotation_Angle"].Value;

//d = System.Math.Sqrt(System.Math.Pow((X2 - X1), 2) + System.Math.Pow((Y2 - Y1), 2));

d = System.Math.Cos(A / 180 * System.Math.PI) * (X1 - X2) - System.Math.Sin(A / 180 * System.Math.PI) * (Y1 - Y2) + X2;

//d=System.Math.

//R = d/2/System.Math.Sin(A/ 180 * System.Math.PI);

R = System.Math.Cos(A / 180 * System.Math.PI) * (Y1 - Y2) + System.Math.Sin(A / 180 * System.Math.PI) * (X1 - X2) + Y2;

//Xt = (1 - (R / d)) * X1 + (R / d) * X2;

//Yt = (1 - (R / d)) * Y1 + (R / d) * Y2;

//X0 = System.Math.Cos((90 - A / 2)/ 180 * System.Math.PI ) * (Xt - X1) - System.Math.Sin((90 - A / 2)/ 180 * System.Math.PI ) * (Yt - Y1) + X1;

//Y0 = System.Math.Cos((90 - A / 2)/ 180 * System.Math.PI ) * (Yt - Y1) + System.Math.Sin((90 - A / 2)/ 180 * System.Math.PI ) * (Xt - X1) + Y1;

//W = 1 - System.Math.Cos(A/180*System.Math.PI);

//W1 = W * X2 - W * System.Math.Cos(A/180*System.Math.PI) * X1 + W * System.Math.Sin(A/180*System.Math.PI) * Y1 - System.Math.Sin(A/180*System.Math.PI) * Y2 +

//System.Math.Sin(A/180*System.Math.PI) * System.Math.Cos(A/180*System.Math.PI) * Y1 + System.Math.Sin(A/180*System.Math.PI) * System.Math.Sin(A/180*System.Math.PI) * X1;

//W2 = A + System.Math.Sin(A/180*System.Math.PI) * System.Math.Sin(A/180*System.Math.PI) - W * System.Math.Cos(A/180*System.Math.PI);

mToolBlock.Outputs["After_Rotation_X"].Value = d;

mToolBlock.Outputs["After_Rotation_Y"].Value = R;

return false;

}

#region When the Current Run Record is Created

/// <summary>

/// Called when the current record may have changed and is being reconstructed

/// </summary>

/// <param name="currentRecord">

/// The new currentRecord is available to be initialized or customized.</param>

public override void ModifyCurrentRunRecord(Cognex.VisionPro.ICogRecord currentRecord)

{

}

#endregion

#region When the Last Run Record is Created

/// <summary>

/// Called when the last run record may have changed and is being reconstructed

/// </summary>

/// <param name="lastRecord">

/// The new last run record is available to be initialized or customized.</param>

public override void ModifyLastRunRecord(Cognex.VisionPro.ICogRecord lastRecord)

{

}

#endregion

#region When the Script is Initialized

/// <summary>

/// Perform any initialization required by your script here

/// </summary>

/// <param name="host">The host tool</param>

public override void Initialize(Cognex.VisionPro.ToolGroup.CogToolGroup host)

{

// DO NOT REMOVE - Call the base class implementation first - DO NOT REMOVE

base.Initialize(host);

// Store a local copy of the script host

this.mToolBlock = ((Cognex.VisionPro.ToolBlock.CogToolBlock)(host));

}

#endregion

}

ToolBlock2运行界面

标签: #拟合圆是什么意思