示范工程代码:
?
6.通过2个上下对位点,计算dieszie高,比实际xml文件的diesize精度更高。
初始化:通过预设的wafer中心坐标(镜头视觉显示wafer的中心位置)与xml文件路径,完成mapping的初始化
private void button_initia_Click(object sender, EventArgs e)
{
//Mapping mapping = new Mapping();
mapping2.CloseWindow();
int x_axis_pulse = int.Parse(textBox_motor_value_x.Text); //wafer中心坐标x
int y_axis_pulse = int.Parse(textBox_motor_value_y.Text); //wafer中心坐标y
Point2d[] points_die_size = new Point2d[] { new Point2d(x_axis_pulse, y_axis_pulse), new Point2d(27, 106), new Point2d(1238, 102) } ;
Point2d motor_xy_loc = points_die_size[0]; //wafer中心坐标(x与y轴电机脉冲),确定实际值后,一般不会变化。wafer在卡环中心,卡环每次放置的位置固定不变
double theta = mapping2.GetTheta(points_die_size[1], points_die_size[2]);//默认,初始校准可忽略
mapping2.Calibrate(motor_xy_loc, default(OpenCvSharp.Point)/*new Point(55, 82)*/, theta, textBox_xml_path.Text);
AddToLogInfo("已完成初始化");
button_set_result.Enabled = true;
//button_to_txt.Enabled = true;
button_to_XML.Enabled = true;
button_get_result.Enabled = true;
button_get_index_motor_loc.Enabled = true;
}
?
校准:
?传入参数:
鼠标在mapping视图中移动到对位点,显示xy的索引与初始的电机坐标
/// <summary>
/// duiwei_mapping指的行列
/// </summary>
/// <param name="duiwei_coord ">移动x,y轴,相机显示对位点后,对准到芯片中心位置,记录电机的实际值编码值脉冲值</param>
/// <param name="duiwei_mapping">mapping视图的显示比如32,225</param>
/// <param name="theta">默认</param>
/// <param name="芯片间距">xml的芯片尺寸,宽盒高</param>
/// <param name="path_xml">xml文件的路径</param>
public void Calibrate(Point2d duiwei_coord, Point duiwei_mapping, double theta, Size2d 芯片间距, string path_xml)
{
mapping.Calibrate(duiwei_coord, duiwei_mapping, theta, 芯片间距, path_xml);
}
相机镜头下看到的对位点,移动xy轴把红色十字对准芯片中心,记录电机编码脉冲值。?
?依次传入参数:(电机实际值,mapping图索引值,默认角度,xml文件的diesize,xml文件路径)
即可完成校准
private void button_cali_Click(object sender, EventArgs e)
{
var p1 = new Point2d(27, 92);
var p2 = new Point2d(1238, 73);
var theta = mapping2.GetTheta(p1, p2);
// mapping.Show();
// mapping.MouseMiddleClick += (coord) =>
//{
//mapping.SetResult(new Point(coord[0], coord[1]), 5);
// };
string die_size_w = textBox_die_size_w.Text; //已知实际尺寸-宽
string die_size_h = textBox_die_size_h.Text; 已知实际尺寸-高
string duiwei_x = textBox_duiwei_motor_loc_x.Text; //第一个对位点x //电机x轴实际脉冲,通过读取PLC得到
string duiwei_y = textBox_duiwei_motor_loc_y.Text; //第一个对位点y //电机y轴实际脉冲,通过读取PLC得到
int xx_loc = Convert.ToInt32(duiwei_x);
int yy_loc = Convert.ToInt32(duiwei_y);
//mapping文件的x,y坐标
OpenCvSharp.Point point_xy_row_colomn = new OpenCvSharp.Point(int.Parse(textBox_duiwei_index_x.Text), int.Parse(textBox_duiwei_index_y.Text));//获取xy,行列
OpenCvSharp.Point2d motor_point2D_loc = new OpenCvSharp.Point2d(xx_loc, yy_loc);
mapping2.Calibrate(motor_point2D_loc, point_xy_row_colomn, 0.1/*jog_mapping.d_wafer_angle*/, new Size2d(double.Parse(die_size_w), double.Parse(die_size_h)), textBox_xml_path.Text);
AddToLogInfo("已校准");
}
显示mapping图
private void button_show_mapping_Click(object sender, EventArgs e)
{
// CloseMappingProcess();
//Task.Run(() =>
// {
try
{
mapping2.mapping_show();
}
catch (Exception ex)
{
AddToLogInfo("请先初始化,再执行校准");
MessageBox.Show("请先初始化,再执行校准");
MessageBox.Show("请标定" + ex.Message + ex.StackTrace);
}
// });
}
批量设置结果:
?指定修改对应值:mapping中:NG对应05,OK对应01
private void button_modify_Click(object sender, EventArgs e)
{
// FindAndKillWindow("mapping");
// mapping2.GetImgQualityScore("test11.jpg",0);
Thread.Sleep(500);
int ind_x = int.Parse(textBox_x.Text);
int ind_y = int.Parse(textBox_y.Text);
bool b_value = false;
if(comboBox_result.Text.Trim()=="")
{
AddToLogInfo("请选择结果");
MessageBox.Show("请选择结果","tips",MessageBoxButtons.OK,MessageBoxIcon.Information);
return;
}
if(comboBox_result.Text == "OK")
{
mapping2.SetResult(new Point(ind_x, ind_y), 01);
}
else
{
mapping2.SetResult(new Point(ind_x, ind_y), 05);
}
AddToLogInfo("设置结果:" + comboBox_result.Text);
button_show_mapping.PerformClick();
}
?获取指定坐标点结果:
private void button_get_result_Click(object sender, EventArgs e)
{
//FindAndKillWindow("mapping");
// mapping2.GetImgQualityScore("test11.jpg",0);
Thread.Sleep(500);
int ind_x = int.Parse(textBox_x.Text);
int ind_y = int.Parse(textBox_y.Text);
textBox_get_result.Text = mapping2.GetBinCode(ind_x,ind_y).ToString();
AddToLogInfo("已获取结果:"+textBox_get_result.Text);
}
计算所有芯片的电机坐标:
private void button_calc_all_loc_Click(object sender, EventArgs e)
{
point2Ds_calc = GetMotorLocation(0, 0, 0, 0);
}
private OpenCvSharp.Point2d[,] GetMotorLocation(int xx, int yy, int width2, int height2)
{
// int xx = Convert.ToInt32(loc_x);
// int yy = Convert.ToInt32(loc_y);
//OpenCvSharp.Point point_loc = new OpenCvSharp.Point(xx, yy);
//Size size2 = new Size(width2, height2);
OpenCvSharp.Point2d[,] motor_loc = mapping2.AllCoord();//.GetMotoCoord(point_loc, size2);
return motor_loc;
}
通过索引获取电机坐标:
得到了电机坐标,则可进行电机移动,对每颗芯片进行图像采集与分析,修改对应的结果
private void button_get_index_motor_loc_Click(object sender, EventArgs e)
{
button_calc_all_loc.PerformClick();
int x = int.Parse(textBox_x.Text);
int y = int.Parse(textBox_y.Text);
string motor_loc_x = "";
string motor_loc_y = "";
motor_loc_x = point2Ds_calc[x, y].X.ToString();
motor_loc_y = point2Ds_calc[x, y].Y.ToString();
textBox_get_motor_loc_x.Text = int.Parse( motor_loc_x).ToString();
textBox_get_motor_loc_y.Text = int.Parse(motor_loc_y).ToString();
}
或许会出现使用xml默认的diesize 宽和高计算的电机坐标进行移动,每颗芯片并没有再芯片中心位置。此时需要校准dieszie的宽和高
左右偏移有偏差则计算宽:
private void button_recal_w_Click(object sender, EventArgs e)
{
try
{
OpenCvSharp.Point map1 = new OpenCvSharp.Point(int.Parse(textBox_middle_loc_x.Text), int.Parse(textBox_middle_loc_y.Text));
var s = ",".Split(',');
s[0] = textBox_recal_loc_x1.Text;
s[1] = textBox_recal_loc_y1.Text;
OpenCvSharp.Point2d coord1 = new OpenCvSharp.Point(double.Parse(s[0]), double.Parse(s[1]));
OpenCvSharp.Point map2 = new OpenCvSharp.Point(int.Parse(textBox_right_loc_x.Text), int.Parse(textBox_right_loc_y.Text));
var s2 = ",".Split(',');
s2[0] = textBox_recal_loc_x2.Text;
s2[1] = textBox_recal_loc_y2.Text;
OpenCvSharp.Point2d coord2 = new OpenCvSharp.Point(double.Parse(s2[0]), double.Parse(s2[1]));
var dieSizeX = mapping2.CalDieSize(map1, coord1, map2, coord2);
// label_DieSizeX.Text = (dieSizeX / 1000).ToString();//0.259425476439132
textBox_die_size_w.Text = (dieSizeX / 1000).ToString();//0.259425476439132
AddToLogInfo("校准后的diesize_宽:" + textBox_die_size_w.Text);
}
catch(Exception ex)
{
AddToLogInfo(ex.Message+ "在mapping视图第一个对位点点击中键(左边),然后移动鼠标在mapping视图第二个对位点点击右键(右边)");
MessageBox.Show(ex.Message+ "在mapping视图第一个对位点点击中键(左边),然后移动鼠标在mapping视图第二个对位点点击右键(右边)");
}
}
鼠标移动到第一个对位点,点击中键,这里是32,225,(不同xml不是值),会记录到编辑中
?在32,225位置点击左键,回传对位点的电机坐标,这个可以写入PLC寄存器,执行移动到实际位置附近。此时通过相机查看,控制电机左右微调到芯片中心,并记录电机值
鼠标移动到第二个对位点,点击右键,这里是380,225,(不同xml不是值),会记录到编辑中
在38,225位置点击左键,回传对位点的电机坐标,这个可以写入PLC寄存器,执行移动到实际位置附近。此时通过相机查看,控制电机左右微调到芯片中心,并记录电机值到编辑框?
?
完成芯片宽校准。
更新到此处
上下偏移有偏差则计算高:
private void button_recal_h_Click(object sender, EventArgs e)
{
try
{
OpenCvSharp.Point map1 = new OpenCvSharp.Point(int.Parse(textBox_middle_loc_x.Text), int.Parse(textBox_middle_loc_y.Text));
var s = ",".Split(',');
s[0] = textBox_recal_loc_x1.Text;
s[1] = textBox_recal_loc_y1.Text;
OpenCvSharp.Point2d coord1 = new OpenCvSharp.Point(double.Parse(s[0]), double.Parse(s[1]));
OpenCvSharp.Point map2 = new OpenCvSharp.Point(int.Parse(textBox_right_loc_x.Text), int.Parse(textBox_right_loc_y.Text));
var s2 = ",".Split(',');
s2[0] = textBox_recal_loc_x2.Text;
s2[1] = textBox_recal_loc_y2.Text;
OpenCvSharp.Point2d coord2 = new OpenCvSharp.Point(double.Parse(s2[0]), double.Parse(s2[1]));
var dieSizeX = mapping2.CalDieSize(map1, coord1, map2, coord2);
// label_DieSizeX.Text = (dieSizeX / 1000).ToString();//0.259425476439132
textBox_die_size_h.Text = (dieSizeX / 1000).ToString();//0.259425476439132
AddToLogInfo("校准后的diesize_高:" + textBox_die_size_h.Text);
}
catch (Exception ex)
{
AddToLogInfo(ex.Message + "在mapping视图第一个对位点点击中键(上边),然后移动鼠标在mapping视图第二个对位点点击右键(下边)");
MessageBox.Show(ex.Message+ "在mapping视图第一个对位点点击中键(上边),然后移动鼠标在mapping视图第二个对位点点击右键(下边)");
}
}
鼠标移动到第一个Y方向对位点,点击中键,这里是188,29,(不同xml不是值),会记录到编辑中
?在188,29位置点击左键,回传对位点的电机坐标,这个可以写入PLC寄存器,执行移动到实际位置附近。此时通过相机查看,控制电机左右微调到芯片中心,并记录电机值,
如下示范,实际采集图像略有不同
鼠标移动到第二个Y方向对位点,点击右键,这里是188,432,(不同xml不是值),会记录到编辑中
在188,432位置点击左键,回传对位点的电机坐标,这个可以写入PLC寄存器,执行移动到实际位置附近。此时通过相机查看,控制电机左右微调到芯片中心,并记录电机值?
示范工程代码: