update toolblock for get circle center

This commit is contained in:
2025-11-21 14:48:46 +08:00
parent 091ce9b21f
commit cf0dfd1619
14 changed files with 825 additions and 370 deletions

View File

@@ -161,7 +161,6 @@ namespace WaferAlignment
_dev.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE); _dev.MV_CC_SetEnumValue_NET("TriggerSource", (uint)MyCamera.MV_CAM_TRIGGER_SOURCE.MV_TRIGGER_SOURCE_SOFTWARE);
_dev.MV_CC_StartGrabbing_NET(); _dev.MV_CC_StartGrabbing_NET();
_dev.MV_CC_TriggerSoftwareExecute_NET(); _dev.MV_CC_TriggerSoftwareExecute_NET();
Thread.Sleep(100);
} }
public void Stop() public void Stop()
@@ -195,7 +194,7 @@ namespace WaferAlignment
try try
{ {
_startAcqToken.Cancel(); _startAcqToken.Cancel();
Thread.Sleep(20); Thread.Sleep(200);
_imageAcquisitionTask.Wait(_stopAcqToken.Token); _imageAcquisitionTask.Wait(_stopAcqToken.Token);
_imageAcquisitionTask.Dispose(); _imageAcquisitionTask.Dispose();
} }

View File

@@ -16,7 +16,7 @@ namespace ConVX.VXData
/// <summary> /// <summary>
/// 判断线程状态 /// 判断线程状态
/// </summary> /// </summary>
private AutoResetEvent _autoReset = null; //private AutoResetEvent _autoReset = null;
/// <summary> /// <summary>
/// 线程 /// 线程
/// </summary> /// </summary>
@@ -53,7 +53,7 @@ namespace ConVX.VXData
public DataPostbox() public DataPostbox()
{ {
_queue = new ConcurrentQueue<CogImage8Grey>(); _queue = new ConcurrentQueue<CogImage8Grey>();
_autoReset = new AutoResetEvent(false);//线程非终止状态 //_autoReset = new AutoResetEvent(false);//线程非终止状态
} }
/// <summary> /// <summary>
/// 启动派发 /// 启动派发
@@ -72,7 +72,7 @@ namespace ConVX.VXData
try try
{ {
RuningToken.Cancel(); RuningToken.Cancel();
_autoReset.Set();//为了避免线程正在执行或等待状态 //_autoReset.Set();//为了避免线程正在执行或等待状态
await _task; await _task;
} }
catch (OperationCanceledException ex) catch (OperationCanceledException ex)
@@ -98,7 +98,7 @@ namespace ConVX.VXData
//} //}
_queue.Enqueue(envelope); _queue.Enqueue(envelope);
_autoReset.Set(); //_autoReset.Set();
} }
#endregion #endregion
@@ -110,7 +110,8 @@ namespace ConVX.VXData
{ {
if (_queue.IsEmpty) if (_queue.IsEmpty)
{ {
_autoReset.WaitOne(); //_autoReset.WaitOne();
Thread.Sleep(1);
} }
else else
{ {

View File

@@ -0,0 +1,101 @@
using Cognex.VisionPro;
using Cognex.VisionPro.PMAlign;
using Cognex.VisionPro.ToolBlock;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Documents;
namespace WaferAdjust
{
public delegate void OnFitCircleResult(double x, double y, double r, double rms);
internal class FitCircleToolBlock
{
private CogToolBlock cogToolBlock;
private bool initialized = false;
public event OnToolReady OnToolReady;
public event OnFitCircleResult OnFitCircleResult;
public CogImage8Grey FitCircleImage;
public void Initialize(string vpp)
{
try
{
if (cogToolBlock != null)
{
cogToolBlock.Ran -= CogToolBlock_Ran;
cogToolBlock.Dispose();
cogToolBlock = null;
}
initialized = false;
cogToolBlock = CogSerializer.LoadObjectFromFile(vpp) as CogToolBlock;
cogToolBlock.Ran += CogToolBlock_Ran;
initialized = true;
OnToolReady?.Invoke(initialized);
LogHelper.LogInfo("FitCircleToolBlock initialized successfully: " + vpp);
}
catch (Exception ex)
{
LogHelper.LogError(ex.Message);
}
}
private void CogToolBlock_Ran(object sender, EventArgs e)
{
try
{
FitCircleImage = cogToolBlock.Outputs["OutputImage"].Value as CogImage8Grey;
OnFitCircleResult?.Invoke(Math.Round((double)cogToolBlock.Outputs["CenterX"].Value, 3),
Math.Round((double)cogToolBlock.Outputs["CenterY"].Value, 3),
Math.Round((double)cogToolBlock.Outputs["Radius"].Value, 3),
Math.Round((double)cogToolBlock.Outputs["RMSError"].Value, 3));
}
catch (Exception ex)
{
LogHelper.LogError(ex.Message + ex.StackTrace);
}
}
public void Run(Bitmap bmp, List<PointInfo> pointInfos)
{
try
{
CogImage8Grey image8Grey = new CogImage8Grey(bmp);
Run(image8Grey, pointInfos);
}
catch (Exception ex)
{
LogHelper.LogError("ScanToolBlock Run Error: " + ex.Message);
}
}
public void Run(CogImage8Grey image8Grey, List<PointInfo> pointInfos)
{
try
{
cogToolBlock.Inputs["OutputImage"].Value = image8Grey;
CogFitCircleTool fitCircleTool = cogToolBlock.Tools["CogFitCircleTool1"] as CogFitCircleTool;
fitCircleTool.RunParams.NumPoints = 0;
foreach (var item in pointInfos)
fitCircleTool.RunParams.AddPoint(item.X, item.Y);
cogToolBlock.Run();
}
catch (Exception ex)
{
LogHelper.LogError("ScanToolBlock Run Error: " + ex.Message);
}
}
public bool Ready()
{
return initialized;
}
public void Stop()
{
if (cogToolBlock != null)
{
cogToolBlock.Ran -= CogToolBlock_Ran;
cogToolBlock.Dispose();
cogToolBlock = null;
}
}
}
}

View File

@@ -68,6 +68,10 @@
this.nud_SizeX = new System.Windows.Forms.NumericUpDown(); this.nud_SizeX = new System.Windows.Forms.NumericUpDown();
this.btn_Action = new System.Windows.Forms.Button(); this.btn_Action = new System.Windows.Forms.Button();
this.btn_ToZero = new System.Windows.Forms.Button(); this.btn_ToZero = new System.Windows.Forms.Button();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.textBox3 = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout(); this.splitContainer1.Panel2.SuspendLayout();
@@ -93,6 +97,10 @@
// //
// splitContainer1.Panel2 // splitContainer1.Panel2
// //
this.splitContainer1.Panel2.Controls.Add(this.textBox2);
this.splitContainer1.Panel2.Controls.Add(this.label1);
this.splitContainer1.Panel2.Controls.Add(this.textBox3);
this.splitContainer1.Panel2.Controls.Add(this.label2);
this.splitContainer1.Panel2.Controls.Add(this.lbl_Time); this.splitContainer1.Panel2.Controls.Add(this.lbl_Time);
this.splitContainer1.Panel2.Controls.Add(this.txt_Time); this.splitContainer1.Panel2.Controls.Add(this.txt_Time);
this.splitContainer1.Panel2.Controls.Add(this.lbl_Times); this.splitContainer1.Panel2.Controls.Add(this.lbl_Times);
@@ -167,7 +175,7 @@
// lbl_Times // lbl_Times
// //
this.lbl_Times.AutoSize = true; this.lbl_Times.AutoSize = true;
this.lbl_Times.Location = new System.Drawing.Point(234, 15); this.lbl_Times.Location = new System.Drawing.Point(182, 17);
this.lbl_Times.Name = "lbl_Times"; this.lbl_Times.Name = "lbl_Times";
this.lbl_Times.Size = new System.Drawing.Size(89, 12); this.lbl_Times.Size = new System.Drawing.Size(89, 12);
this.lbl_Times.TabIndex = 30; this.lbl_Times.TabIndex = 30;
@@ -175,7 +183,7 @@
// //
// txt_Times // txt_Times
// //
this.txt_Times.Location = new System.Drawing.Point(326, 12); this.txt_Times.Location = new System.Drawing.Point(274, 14);
this.txt_Times.Name = "txt_Times"; this.txt_Times.Name = "txt_Times";
this.txt_Times.ReadOnly = true; this.txt_Times.ReadOnly = true;
this.txt_Times.Size = new System.Drawing.Size(129, 21); this.txt_Times.Size = new System.Drawing.Size(129, 21);
@@ -383,9 +391,9 @@
// //
// btn_RunTest // btn_RunTest
// //
this.btn_RunTest.Location = new System.Drawing.Point(481, 12); this.btn_RunTest.Location = new System.Drawing.Point(409, 14);
this.btn_RunTest.Name = "btn_RunTest"; this.btn_RunTest.Name = "btn_RunTest";
this.btn_RunTest.Size = new System.Drawing.Size(161, 23); this.btn_RunTest.Size = new System.Drawing.Size(119, 23);
this.btn_RunTest.TabIndex = 9; this.btn_RunTest.TabIndex = 9;
this.btn_RunTest.Text = "启动检测"; this.btn_RunTest.Text = "启动检测";
this.btn_RunTest.UseVisualStyleBackColor = true; this.btn_RunTest.UseVisualStyleBackColor = true;
@@ -444,6 +452,7 @@
0, 0,
0, 0,
-2147483648}); -2147483648});
this.nud_SizeY.ValueChanged += new System.EventHandler(this.nud_SizeY_ValueChanged);
// //
// lbl_X // lbl_X
// //
@@ -458,8 +467,13 @@
// //
this.nud_SizeX.DecimalPlaces = 2; this.nud_SizeX.DecimalPlaces = 2;
this.nud_SizeX.Location = new System.Drawing.Point(80, 137); this.nud_SizeX.Location = new System.Drawing.Point(80, 137);
this.nud_SizeX.Maximum = new decimal(new int[] {
6,
0,
0,
0});
this.nud_SizeX.Minimum = new decimal(new int[] { this.nud_SizeX.Minimum = new decimal(new int[] {
100, 8,
0, 0,
0, 0,
-2147483648}); -2147483648});
@@ -471,6 +485,7 @@
0, 0,
0, 0,
0}); 0});
this.nud_SizeX.ValueChanged += new System.EventHandler(this.nud_SizeX_ValueChanged);
// //
// btn_Action // btn_Action
// //
@@ -484,7 +499,7 @@
// //
// btn_ToZero // btn_ToZero
// //
this.btn_ToZero.Location = new System.Drawing.Point(17, 99); this.btn_ToZero.Location = new System.Drawing.Point(17, 95);
this.btn_ToZero.Name = "btn_ToZero"; this.btn_ToZero.Name = "btn_ToZero";
this.btn_ToZero.Size = new System.Drawing.Size(161, 23); this.btn_ToZero.Size = new System.Drawing.Size(161, 23);
this.btn_ToZero.TabIndex = 0; this.btn_ToZero.TabIndex = 0;
@@ -492,6 +507,42 @@
this.btn_ToZero.UseVisualStyleBackColor = true; this.btn_ToZero.UseVisualStyleBackColor = true;
this.btn_ToZero.Click += new System.EventHandler(this.btn_ToZero_Click); this.btn_ToZero.Click += new System.EventHandler(this.btn_ToZero_Click);
// //
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(534, 22);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(55, 21);
this.textBox2.TabIndex = 33;
this.textBox2.Text = "82";
this.textBox2.TextChanged += new System.EventHandler(this.textBox2_TextChanged);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(532, 7);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(53, 12);
this.label1.TabIndex = 34;
this.label1.Text = "相机计数";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(595, 22);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(55, 21);
this.textBox3.TabIndex = 35;
this.textBox3.Text = "4096";
this.textBox3.TextChanged += new System.EventHandler(this.textBox3_TextChanged);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(593, 7);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(41, 12);
this.label2.TabIndex = 36;
this.label2.Text = "总计数";
//
// Form1 // Form1
// //
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
@@ -556,7 +607,10 @@
private System.Windows.Forms.DataGridViewTextBoxColumn Column4; private System.Windows.Forms.DataGridViewTextBoxColumn Column4;
private System.Windows.Forms.Label lbl_Time; private System.Windows.Forms.Label lbl_Time;
private System.Windows.Forms.TextBox txt_Time; private System.Windows.Forms.TextBox txt_Time;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox3;
private System.Windows.Forms.Label label2;
} }
} }

View File

@@ -19,6 +19,7 @@ using System.Windows.Forms;
using System.Xml.Serialization; using System.Xml.Serialization;
using System.Diagnostics; using System.Diagnostics;
using Cognex.VisionPro.PMAlign; using Cognex.VisionPro.PMAlign;
using WaferAdjust;
namespace WaferAlignment namespace WaferAlignment
@@ -54,20 +55,12 @@ namespace WaferAlignment
/// </summary> /// </summary>
string arAngle = "DB7.DBD76";//设置触发拍照的偏转角度 string arAngle = "DB7.DBD76";//设置触发拍照的偏转角度
GetCircleToolBlock getCircleMachineToolBlock;
FitCircleToolBlock FitCircleMachineToolBlock;
TranslateCirclePoint translateCirclePoint;
CogGraphicCollection m_graphics;
CogGraphicCollection m_graphics2;
string vppPath;
CogToolBlock mTB;
CogToolBlock mTB_1;
CogToolBlock mTB_2;
float R = 11.25F;//1024->12
//CogImage8Grey OutputImg;
//旋转中心的坐标应将标定的数据保存后获取
double circleCenterX;
double circleCenterY;
//XML文件读取与写入 //XML文件读取与写入
ConfigStore _ConfigStore = new ConfigStore(); ConfigStore _ConfigStore = new ConfigStore();
CenterOfRotation _center = new CenterOfRotation(); CenterOfRotation _center = new CenterOfRotation();
@@ -123,7 +116,6 @@ namespace WaferAlignment
_s7.SetValue(arSizeX, (float)0); _s7.SetValue(arSizeX, (float)0);
_s7.SetValue(arSizeY, (float)0); _s7.SetValue(arSizeY, (float)0);
_s7.SetValue(arSizeR, (float)0); _s7.SetValue(arSizeR, (float)0);
_s7.SetValue(arAngle, Convert.ToSingle(R));//角度为10
lbl_CommunState.BackColor = Color.Lime; lbl_CommunState.BackColor = Color.Lime;
lbl_CommunState.Text = "已连接"; lbl_CommunState.Text = "已连接";
} }
@@ -150,9 +142,16 @@ namespace WaferAlignment
try try
{ {
// 加载VPP文件 // 加载VPP文件
vppPath = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "vpp", "ToolBlock.vpp"); getCircleMachineToolBlock = new GetCircleToolBlock();
getCircleMachineToolBlock.OnGetCircleResult += GetCircleMachineToolBlock_OnGetCircleResult;
getCircleMachineToolBlock.OnGetNashResult += GetCircleMachineToolBlock_OnGetNashResult;
getCircleMachineToolBlock.OnToolReady += GetCircleMachineToolBlock_OnToolReady;
mTB = CogSerializer.LoadObjectFromFile(vppPath) as CogToolBlock; FitCircleMachineToolBlock = new FitCircleToolBlock();
FitCircleMachineToolBlock.OnFitCircleResult += FitCircleMachineToolBlock_OnFitCircleResult;
FitCircleMachineToolBlock.OnToolReady += FitCircleMachineToolBlock_OnToolReady;
translateCirclePoint = new TranslateCirclePoint();
m_graphics2 = new CogGraphicCollection();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -171,15 +170,117 @@ namespace WaferAlignment
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show(ex.Message); MessageBox.Show(ex.Message);
}
} }
// 指定XML文件路径 private void FitCircleMachineToolBlock_OnToolReady(bool ready)
string filePath = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "vpp", "CenterOfRotation.xml"); //@"D:\晶圆寻边机\XML文件\CenterOfRotation.xml"; {
_center = _ConfigStore.ReadConfigFromFile(_center.GetType(), filePath) as CenterOfRotation; LogHelper.LogInfo("FitCircleMachineToolBlock_OnToolReady");
circleCenterX = _center.Center_X;
circleCenterY = _center.Center_Y;
} }
private void FitCircleMachineToolBlock_OnFitCircleResult(double x, double y, double r, double rms)
{
TCP_X = x.ToString("F4");
TCP_Y = y.ToString("F4");
TCP_RMS = rms.ToString("F4");
TCP_R = r.ToString("F4");
deviationX = Math.Round((x - translateCirclePoint.GetRotateX()), 3);
deviationY = Math.Round((y - translateCirclePoint.GetRotateY()), 3);
AddPointMarker(m_graphics2, x, y);
AddPointMarker(m_graphics2, translateCirclePoint.GetRotateX(), translateCirclePoint.GetRotateY(), CogColorConstants.Red);
PointInfo nPoint = translateCirclePoint.GetNashPoint();
if (nPoint != null)
{
AddPointMarker(m_graphics2, nPoint.X, nPoint.Y, CogColorConstants.Yellow);
AddLineMarker(m_graphics2, x, y, x, y - r);
AddLineMarker(m_graphics2, x, y, nPoint.X, nPoint.Y, CogColorConstants.Yellow);
angle = GetCounterClockwiseAngle360(x, y, x, y - r, nPoint.X, nPoint.Y);
AddLabelMarker(m_graphics2, angle.ToString(), x, y, CogColorConstants.Yellow);
LogHelper.LogInfo($"nash degree:{angle}");
}
LogHelper.LogInfo($"X:{x}, Y:{y}, RMS:{rms}");
LogHelper.LogInfo($"DiffX:{deviationX}, DiffY:{deviationY}, RMS:{rms}{Environment.NewLine}");
ImageDisplay(FitCircleMachineToolBlock.FitCircleImage);
ImageDisplay(m_graphics);
ImageDisplay(m_graphics2);
ImageDisplayFit();
}
private void AddPointMarker(CogGraphicCollection graphicCollection, double x, double y, CogColorConstants color = CogColorConstants.Green)
{
CogPointMarker marker = new CogPointMarker();
marker.X = x;
marker.Y = y;
marker.Color = color;
marker.Interactive = false;
graphicCollection.Add(marker);
}
private void AddLineMarker(CogGraphicCollection graphicCollection, double x1, double y1, double x2, double y2, CogColorConstants color = CogColorConstants.Green)
{
CogLineSegment line = new CogLineSegment();
line.SetStartEnd((int)x1, (int)y1, (int)x2, (int)y2);
line.Color = color;
line.Interactive = false;
graphicCollection.Add(line);
}
private void AddLabelMarker(CogGraphicCollection graphicCollection, string text, double x, double y, CogColorConstants color = CogColorConstants.Green)
{
CogGraphicLabel label = new CogGraphicLabel();
label.Text = text;
label.X = x;
label.Y = y;
label.Color = color;
graphicCollection.Add(label);
}
/// <summary>
/// 计算从 lineA 到 lineB 的逆时针有向角度0-360度
/// 坐标系:向右为正,向下为正(标准屏幕坐标系)
/// </summary>
public double GetCounterClockwiseAngle360(double x, double y, double x1, double y1, double x2, double y2)
{
// 2. 获取方向向量dy 向下为正)
double dxA = x1 - x;
double dyA = y1 - y;
double dxB = x2 - x;
double dyB = y2 - y;
// 3. 将Y轴翻转转换为标准数学坐标系dy向上为正
// 这样 Math.Atan2 返回的就是标准数学角度(逆时针为正)
double angleA = Math.Atan2(-dyA, dxA); // 注意y取负号
double angleB = Math.Atan2(-dyB, dxB); // 注意y取负号
// 4. 计算逆时针角度差(弧度)
double angleDiff = angleB - angleA;
// 5. 转换为角度并规范化为 [0, 360)
double angleDegrees = angleDiff * 180.0 / Math.PI;
angleDegrees = angleDegrees % 360.0;
if (angleDegrees < 0) angleDegrees += 360.0;
return Math.Round(angleDegrees, 3);
}
private void GetCircleMachineToolBlock_OnToolReady(bool ready)
{
LogHelper.LogInfo("GetCircleMachineToolBlock_OnToolReady");
}
private void GetCircleMachineToolBlock_OnGetNashResult(double x, double y)
{
translateCirclePoint.AddNashPoint(x, y);
LogHelper.LogInfo($"Update Nash X:{x}, Y:{y}");
}
private void GetCircleMachineToolBlock_OnGetCircleResult(double x, double y, double r)
{
translateCirclePoint.AddCircleCenter(x, y, r);
translateCirclePoint.AddCirclePoint(getCircleMachineToolBlock.PointInfos);
LogHelper.LogInfo($"X:{x}, Y:{y}, R:{r}");
}
/// <summary> /// <summary>
/// 接收到TCP数据时根据接收到的数据进行不同操作 /// 接收到TCP数据时根据接收到的数据进行不同操作
/// </summary> /// </summary>
@@ -217,17 +318,11 @@ namespace WaferAlignment
_tcp.SendData(e.ClientIp, "DetectionTime" + TCP_time + " " + "X" + TCP_X + " " + "Y" + TCP_Y + " " + "Angle" + TCP_R + " " + "RMS" + TCP_RMS); _tcp.SendData(e.ClientIp, "DetectionTime" + TCP_time + " " + "X" + TCP_X + " " + "Y" + TCP_Y + " " + "Angle" + TCP_R + " " + "RMS" + TCP_RMS);
} }
} }
} }
void _cam_OutputImageEvent(Bitmap obj) void _cam_OutputImageEvent(Bitmap obj)
{ {
lstNasR.Add((acqNum + 1) * R);
acqNum++; acqNum++;
//lstNasR.Add((testNum) * R);
//保存图像
//SaveImage(new CogImage8Grey(obj));
ImageData.Mailing(new CogImage8Grey(obj)); ImageData.Mailing(new CogImage8Grey(obj));
Images.Add(new CogImage8Grey(obj)); Images.Add(new CogImage8Grey(obj));
@@ -235,9 +330,8 @@ namespace WaferAlignment
void ImageData_ErrorEvent(string obj) void ImageData_ErrorEvent(string obj)
{ {
//throw new NotImplementedException();
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{ {
@@ -300,7 +394,6 @@ namespace WaferAlignment
this.splitContainer1.Invoke(new Action(() => this.splitContainer1.Panel2.Enabled = true)); this.splitContainer1.Invoke(new Action(() => this.splitContainer1.Panel2.Enabled = true));
}); });
} }
public string ToZero() public string ToZero()
{ {
if (_s7 != null) if (_s7 != null)
@@ -393,15 +486,7 @@ namespace WaferAlignment
} }
} }
List<double> lstRotation;
List<double> lstX;
List<double> lstY;
List<double> lstRadius;
List<double> lstNasX;
List<double> lstNasY;
List<double> lstNasR;
List<CogImage8Grey> Images; List<CogImage8Grey> Images;
int acqNum = 0; int acqNum = 0;
int testNum = 0; int testNum = 0;
double angle = 0; double angle = 0;
@@ -411,9 +496,11 @@ namespace WaferAlignment
private void btn_RunTest_Click(object sender, EventArgs e) private void btn_RunTest_Click(object sender, EventArgs e)
{ {
SavePath = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "SaveImage", DateTime.Now.ToString("yyyyMMdd_HHmmss")); SavePath = Path.Combine(System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "SaveImage", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
m_cameraCount = Convert.ToDouble(textBox2.Text);
m_codeCount = Convert.ToDouble(textBox3.Text);
m_roateX = Convert.ToDouble(nud_SizeX.Value);
m_roateY = Convert.ToDouble(nud_SizeY.Value);
ClearDisplay(); ClearDisplay();
////---------------------------------------
Task.Run(() => Task.Run(() =>
{ {
this.splitContainer1.Invoke(new Action(() => this.splitContainer1.Panel2.Enabled = false)); this.splitContainer1.Invoke(new Action(() => this.splitContainer1.Panel2.Enabled = false));
@@ -423,8 +510,6 @@ namespace WaferAlignment
this.splitContainer1.Invoke(new Action(() => this.splitContainer1.Panel2.Enabled = true)); this.splitContainer1.Invoke(new Action(() => this.splitContainer1.Panel2.Enabled = true));
}); });
//---------------------------------------
} }
public string RunTest() public string RunTest()
@@ -436,7 +521,6 @@ namespace WaferAlignment
} }
else else
{ {
//R轴回0 //R轴回0
if (_s7 != null) if (_s7 != null)
{ {
@@ -480,13 +564,6 @@ namespace WaferAlignment
angle = 0; angle = 0;
acqNum = 0; acqNum = 0;
testNum = 0; testNum = 0;
lstRotation = new List<double>();
lstX = new List<double>();
lstY = new List<double>();
lstRadius = new List<double>();
lstNasX = new List<double>();
lstNasY = new List<double>();
lstNasR = new List<double>();
Images = new List<CogImage8Grey>(); Images = new List<CogImage8Grey>();
sw_1 = new Stopwatch();//检测时间 sw_1 = new Stopwatch();//检测时间
@@ -495,6 +572,7 @@ namespace WaferAlignment
deviationY = 0; deviationY = 0;
times = 0; times = 0;
//启动拍照 //启动拍照
translateCirclePoint.SetRotateXY(m_roateX, m_roateY, 360 * m_cameraCount / m_codeCount);
if (_s7 != null) if (_s7 != null)
{ {
@@ -529,9 +607,7 @@ namespace WaferAlignment
} }
} }
//Thread.Sleep(100); //Thread.Sleep(100);
} }
} }
_cam.SoftwareGrab(); _cam.SoftwareGrab();
@@ -644,327 +720,62 @@ namespace WaferAlignment
return "NG_RUN"; return "NG_RUN";
} }
} }
private CogImage8Grey m_lastImage;
//所有点 //所有点
// 调整vpp中CogCaliperTool工具数据需要同时调整Y值与 for (double i = Y; i < 12; i = i + 0.5)中12的值 // 调整vpp中CogCaliperTool工具数据需要同时调整Y值与 for (double i = Y; i < 12; i = i + 0.5)中12的值
private void MonitorSpot(CogImage8Grey outputImg) private void MonitorSpot(CogImage8Grey outputImg)
{ {
sw_1 = new Stopwatch();//检测时间 sw_1 = new Stopwatch();//检测时间
sw_1.Start(); sw_1.Start();
//double Y = -17;
//double X = 0;
double X = -15;
double Y = 0;
CogImage8Grey inputImg = new CogImage8Grey();
CogGraphicCollection graphics = new CogGraphicCollection();
CogImage8Grey _OutputImg = new CogImage8Grey();
inputImg = outputImg; translateCirclePoint.AddCircleIndex();
mTB_1 = mTB.Tools["CogToolBlock1"] as CogToolBlock; m_lastImage = outputImg;
mTB_2 = mTB.Tools["CogToolBlock2"] as CogToolBlock; getCircleMachineToolBlock.Run(outputImg);
mTB_1.Inputs["InputImage"].Value = inputImg;
mTB_1.Run();
_OutputImg = mTB_1.Outputs["OutputImage"].Value as CogImage8Grey;
CogCaliperTool mCT = mTB_1.Tools["CogCaliperTool1"] as CogCaliperTool;
//x= cx + (xcx)⋅cos(r)(ycy)⋅sin(r)
//y= cy + (xcx)⋅sin(r) + (ycy)⋅cos(r)
int _count = 0;
for (double i = X; i < 14; i = i + 0.5)
{
mCT.InputImage = _OutputImg;
mCT.Region.CenterX = i;
mCT.Run();
_count = mCT.Results.Count;
if (_count != 0)
{
Y = mCT.Results[0].Edge0.PositionY;
//旋转角度
double r = lstNasR[testNum] / 180 * Math.PI;
//旋转移动到实际位置
lstNasX.Add(i * Math.Cos(r) - Y * Math.Sin(r) + circleCenterX * (1 - Math.Cos(r)) + circleCenterY * Math.Sin(r));
lstNasY.Add(i * Math.Sin(r) + Y * Math.Cos(r) - circleCenterX * Math.Sin(r) + circleCenterY * (1 - Math.Cos(r)));
CogPointMarker marker_1 = new CogPointMarker();//圆
marker_1.X = i;
marker_1.Y = Y;
marker_1.Color = CogColorConstants.Blue;
marker_1.Interactive = false;
graphics.Add(marker_1);
}
else
{
OKorNG = false;
testNum++;
return;
}
if (i == X)
{
double r = lstNasR[testNum] / 180 * Math.PI;
lstX.Add(i * Math.Cos(r) - Y * Math.Sin(r) + circleCenterX * (1 - Math.Cos(r)) + circleCenterY * Math.Sin(r));
lstY.Add(i * Math.Sin(r) + Y * Math.Cos(r) - circleCenterX * Math.Sin(r) + circleCenterY * (1 - Math.Cos(r)));
}
}
sw_1.Stop(); sw_1.Stop();
TimeSpan ts = sw_1.Elapsed; TimeSpan ts = sw_1.Elapsed;
ImageDisplay(_OutputImg, graphics); PointInfo pp = translateCirclePoint.GetFirstPoint();
DataDisplay(lstNasR[testNum], lstNasX[0], lstNasY[0], ts.TotalMilliseconds); if (pp != null)
DataDisplay(translateCirclePoint.GetCurrentDegree(), pp.X, pp.Y, ts.TotalMilliseconds);
else
DataDisplay(translateCirclePoint.GetCurrentDegree(), 0, 0, ts.TotalMilliseconds);
testNum++; testNum++;
times += ts.TotalMilliseconds; times += ts.TotalMilliseconds;
OKorNG = true; OKorNG = true;
} }
//比较半径和点间距,拟合圆
private void FitCircle() private void FitCircle()
{ {
if (OKorNG) if (OKorNG)
{ {
sw_2 = new Stopwatch();//检测时间 sw_2 = new Stopwatch();//检测时间
sw_2.Start(); sw_2.Start();
int Image_i = -1;
// 调整vpp中CogCaliperTool工具数据需要同时调整step为x点数*2
int step = 58;
CogPMAlignTool mPM = mTB_2.Tools["CogPMAlignTool2"] as CogPMAlignTool;
LogHelper.LogInfo("转换机械坐标系下圆弧各点的原始坐标:\n");
var points = translateCirclePoint.DoTranslatePoint();
translateCirclePoint.DoTranslateNashPoint();
LogHelper.LogInfo("拟合机械坐标系下的圆中心:\n");
CogImage8Grey outputImg = mTB_1.Outputs["OutputImage"].Value as CogImage8Grey; m_graphics = new CogGraphicCollection();
//添加找圆工具的 忽略点数 和 RMS偏差量 m_graphics2 = new CogGraphicCollection();
CogFitCircleTool fitCircle_1 = new CogFitCircleTool(); foreach (PointInfo pointInfo in points)
CogFitCircleTool fitCircle_2 = new CogFitCircleTool();
CogGraphicCollection graphics = new CogGraphicCollection();
fitCircle_1.InputImage = outputImg;
for (int i = 0; i < lstX.Count; i++)
{ {
fitCircle_1.RunParams.AddPoint(lstX[i], lstY[i]); AddPointMarker(m_graphics, pointInfo.X, pointInfo.Y);
//CogPointMarker marker_1 = new CogPointMarker();//圆
//marker_1.X = lstNasX[i];
//marker_1.Y = lstNasY[i];
//marker_1.Color = CogColorConstants.White;
//marker_1.Interactive = false;
//graphics.Add(marker_1);
} }
//fitCircle_1.RunParams.NumToIgnore = 0; FitCircleMachineToolBlock.Run(m_lastImage, points);
fitCircle_1.Run();
if (fitCircle_1.Result != null)
{
double _RMS = fitCircle_1.Result.RMSError;
CogCircle circle_1 = new CogCircle(fitCircle_1.Result.GetCircle());
circle_1.Color = CogColorConstants.Cyan;
circle_1.Interactive = false;
CogFitLineTool fitLine = new CogFitLineTool();
//晶圆圆心位置
double _centerX = circle_1.CenterX;
double _centerY = circle_1.CenterY;
double _radius = circle_1.Radius;
fitCircle_2.InputImage = outputImg;
CogDistancePointPointTool pTp = new CogDistancePointPointTool();
pTp.InputImage = outputImg;
pTp.StartX = _centerX;
pTp.StartY = _centerY;
//double[] _DistanceMin = new double[lstNasX.Count];
//int _distanceMin_i = 0;
for (int i = 0; i < lstNasX.Count; i += step)
{
for (int j = i; j < i + step && j < lstNasX.Count; j++)
{
if (j < (i + step - 15))//忽略重合数据的末尾的15个点
{
pTp.EndX = lstNasX[j];
pTp.EndY = lstNasY[j];
pTp.Run();
double Distance = pTp.Distance;
if (Distance - _radius < 0.1 && Distance - _radius > -0.1)
{
fitCircle_2.RunParams.AddPoint(lstNasX[j], lstNasY[j]);
CogPointMarker marker_1 = new CogPointMarker();//圆
marker_1.X = lstNasX[j];
marker_1.Y = lstNasY[j];
marker_1.Color = CogColorConstants.Green;
marker_1.Interactive = false;
graphics.Add(marker_1);
}
else//忽略的点数
{
if (!(Distance - _radius < 1 && Distance - _radius > -1))
{
if (Image_i == -1)
{
Image_i = (int)(j / step);
}
}
CogPointMarker marker_1 = new CogPointMarker();//圆
marker_1.X = lstNasX[j];
marker_1.Y = lstNasY[j];
marker_1.Color = CogColorConstants.Yellow;
marker_1.Interactive = false;
graphics.Add(marker_1);
}
//_DistanceMin[i] = Distance;
}
else
{
CogPointMarker marker_1 = new CogPointMarker();//圆
marker_1.X = lstNasX[j];
marker_1.Y = lstNasY[j];
marker_1.Color = CogColorConstants.Red;
marker_1.Interactive = false;
//graphics.Add(marker_1);
}
}
}
//忽略的拟合点数
fitCircle_2.RunParams.NumToIgnore = 0;
fitCircle_2.Run();
double RMS = fitCircle_2.Result.RMSError;
CogCircle circle_2 = new CogCircle(fitCircle_2.Result.GetCircle());
circle_2.Color = CogColorConstants.Cyan;
circle_2.Interactive = false;
//晶圆圆心位置
double centerX = circle_2.CenterX;
double centerY = circle_2.CenterY;
double radius = circle_2.Radius;
double PM_X = 0;
double PM_Y = 0;
int _count = 0;
if (Image_i != -1)
{
mTB_2.Inputs["InputImage"].Value = Images[Image_i];
mTB_2.Run();
_count = mPM.Results.Count;
if (_count != 0)
{
double r = lstNasR[Image_i] / 180 * Math.PI;
PM_X = mPM.Results[0].GetPose().TranslationX * Math.Cos(r) - mPM.Results[0].GetPose().TranslationY * Math.Sin(r) + circleCenterX * (1 - Math.Cos(r)) + circleCenterY * Math.Sin(r);
PM_Y = mPM.Results[0].GetPose().TranslationX * Math.Sin(r) + mPM.Results[0].GetPose().TranslationY * Math.Cos(r) - circleCenterX * Math.Sin(r) + circleCenterY * (1 - Math.Cos(r));
}
}
//_distanceMin_i = DistanceMin(_DistanceMin);
////纳什口
//CogLine line_ = new CogLine();
//line_.SetFromStartXYEndXY(centerX, centerY, lstNasX[_distanceMin_i], lstNasY[_distanceMin_i]);
//line_.Color = CogColorConstants.Green;
//line_.Interactive = false;
//graphics.Add(line_);
//纳什口
CogLine line_ = new CogLine();
line_.SetFromStartXYEndXY(centerX, centerY, PM_X, PM_Y);
line_.Color = CogColorConstants.Green;
line_.Interactive = false;
graphics.Add(line_);
//角度值为0
CogLine line_0 = new CogLine();
line_0.SetXYRotation(centerX, centerY, 0);
line_0.Color = CogColorConstants.Blue;
line_0.Interactive = false;
graphics.Add(line_0);
CogAngleLineLineTool llTool = new CogAngleLineLineTool();
llTool.InputImage = outputImg;
llTool.LineA = line_0;
llTool.LineB = line_;
llTool.Run();
angle = CogMisc.RadToDeg(llTool.Angle);
CogPointMarker marker_2 = new CogPointMarker();//圆心
marker_2.X = centerX;
marker_2.Y = centerY;
marker_2.Color = CogColorConstants.Green;
marker_2.Interactive = false;
graphics.Add(marker_2);
CogPointMarker marker_3 = new CogPointMarker();//旋转圆心
marker_3.X = circleCenterX;
marker_3.Y = circleCenterY;
marker_3.Color = CogColorConstants.Orange;
marker_3.Interactive = false;
graphics.Add(marker_3);
graphics.Add(circle_2);
deviationX = centerX - circleCenterX;
deviationY = centerY - circleCenterY;
sw_2.Stop(); sw_2.Stop();
TimeSpan ts = sw_2.Elapsed; TimeSpan ts = sw_2.Elapsed;
times += ts.TotalMilliseconds; times += ts.TotalMilliseconds;
ImageDisplay(outputImg, graphics);
TCP_time = ts.TotalMilliseconds.ToString(); TCP_time = ts.TotalMilliseconds.ToString();
TCP_X = centerX.ToString("F4");
TCP_Y = centerY.ToString("F4");
TCP_RMS = RMS.ToString("F4");
TCP_R = angle.ToString("F4");
ResultDisplay(times, ts.TotalMilliseconds, centerX, centerY, radius, RMS, deviationX, deviationY, angle);
OKorNG = true; ResultDisplay(times, ts.TotalMilliseconds, TCP_X, TCP_Y, TCP_R, TCP_RMS, deviationX, deviationY, angle);
}
else
{
//MessageBox.Show("检测结果为空。");
OKorNG = false;
} }
} }
private void ResultDisplay(double Times,double Time, string X, string Y, string R, string RMS, double dX, double dY, double dR)
}
//private int DistanceMin(double[] numbers)
//{
// double number = numbers[0];
// int num1 = 0;
// double num2 = ((IEnumerable<double>)numbers).Sum() / (double)((IEnumerable<double>)numbers).Count<double>();
// for (int index = 1; index < numbers.Length; ++index)
// {
// if (numbers[index] < number)
// {
// number = numbers[index];
// num1 = index;
// }
// }
// //return Math.Abs(number - num2) > 0.0 ? num1 : -1;
// return num1;
//}
private void ResultDisplay(double Times,double Time, double X, double Y, double R, double RMS, double dX, double dY, double dR)
{ {
if (this.InvokeRequired) if (this.InvokeRequired)
{ {
@@ -1013,7 +824,6 @@ namespace WaferAlignment
return; return;
} }
this.cogRecordDisplay1.StaticGraphics.AddList(graphics, ""); this.cogRecordDisplay1.StaticGraphics.AddList(graphics, "");
this.cogRecordDisplay1.Fit(true);
} }
private void ImageDisplay(CogImage8Grey img) private void ImageDisplay(CogImage8Grey img)
{ {
@@ -1023,9 +833,17 @@ namespace WaferAlignment
return; return;
} }
this.cogRecordDisplay1.Image = img; this.cogRecordDisplay1.Image = img;
this.cogRecordDisplay1.Fit(true); cogRecordDisplay1.StaticGraphics.Clear();
}
private void ImageDisplayFit()
{
if (InvokeRequired)
{
BeginInvoke(new Action(() => ImageDisplayFit()));
return;
}
cogRecordDisplay1.Fit(true);
} }
private void DataDisplay(double Rotation, double X, double Y, double Radius) private void DataDisplay(double Rotation, double X, double Y, double Radius)
{ {
if (this.InvokeRequired) if (this.InvokeRequired)
@@ -1041,18 +859,6 @@ namespace WaferAlignment
} }
string SavePath; string SavePath;
private void SaveImage(ICogImage img)
{
if (!Directory.Exists(SavePath))
{
Directory.CreateDirectory(SavePath);
}
using (CogImageFile imgFile = new CogImageFile())
{
imgFile.Open(Path.Combine(SavePath, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".bmp"), CogImageFileModeConstants.Write);
imgFile.Append(img);
}
}
private void Save() private void Save()
{ {
if (!Directory.Exists(SavePath)) if (!Directory.Exists(SavePath))
@@ -1068,5 +874,27 @@ namespace WaferAlignment
} }
} }
} }
private double m_cameraCount = 82;
private double m_codeCount = 4096;
private void textBox2_TextChanged(object sender, EventArgs e)
{
m_cameraCount = Convert.ToDouble(textBox2.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
m_codeCount = Convert.ToDouble(textBox3.Text);
}
private double m_roateX = 1;
private double m_roateY = -1;
private void nud_SizeX_ValueChanged(object sender, EventArgs e)
{
m_roateX = Convert.ToDouble(nud_SizeX.Value);
}
private void nud_SizeY_ValueChanged(object sender, EventArgs e)
{
m_roateY = Convert.ToDouble(nud_SizeY.Value);
}
} }
} }

View File

@@ -140,4 +140,16 @@
<metadata name="Column4.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="Column4.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value> <value>True</value>
</metadata> </metadata>
<metadata name="Column1.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Column2.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Column3.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
<metadata name="Column4.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root> </root>

View File

@@ -0,0 +1,117 @@
using Cognex.VisionPro;
using Cognex.VisionPro.Caliper;
using Cognex.VisionPro.ToolBlock;
using QWhale.Common;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WaferAdjust
{
public delegate void OnGetCircleResult(double x, double y, double r);
public delegate void OnToolReady(bool ready);
public delegate void OnGetNashResult(double x, double y);
internal class GetCircleToolBlock
{
private CogToolBlock cogToolBlock;
private bool initialized = false;
public event OnToolReady OnToolReady;
public event OnGetCircleResult OnGetCircleResult;
public event OnGetNashResult OnGetNashResult;
public List<PointInfo> PointInfos;
public void Initialize(string vpp)
{
try
{
if (cogToolBlock != null)
{
cogToolBlock.Ran -= CogToolBlock_Ran;
cogToolBlock.Dispose();
cogToolBlock = null;
}
initialized = false;
PointInfos = new List<PointInfo>();
cogToolBlock = CogSerializer.LoadObjectFromFile(vpp) as CogToolBlock;
cogToolBlock.Ran += CogToolBlock_Ran;
initialized = true;
OnToolReady?.Invoke(initialized);
LogHelper.LogInfo($"{vpp} initialized successfully: " + vpp);
}
catch (Exception ex)
{
LogHelper.LogError(ex.Message);
}
}
private void CogToolBlock_Ran(object sender, EventArgs e)
{
try
{
CogFindCircleTool cogFindCircleTool = cogToolBlock.Tools["CogFindCircleTool1"] as CogFindCircleTool;
PointInfos.Clear();
if (cogFindCircleTool.Results != null)
{
foreach (CogFindCircleResult item in cogFindCircleTool.Results)
{
if (item.Found && item.Used)
PointInfos.Add(new PointInfo(item.X, item.Y, item.DistanceToCircle));
}
}
OnGetCircleResult?.Invoke(Math.Round((double)cogToolBlock.Outputs["CenterX"].Value, 3),
Math.Round((double)cogToolBlock.Outputs["CenterY"].Value, 3),
Math.Round((double)cogToolBlock.Outputs["Radius"].Value, 3));
if ((int)cogToolBlock.Outputs["Results_Count"].Value == 1)
{
OnGetNashResult?.Invoke(Math.Round((double)cogToolBlock.Outputs["TranslationX"].Value, 3),
Math.Round((double)cogToolBlock.Outputs["TranslationY"].Value, 3));
}
}
catch (Exception ex)
{
LogHelper.LogError(ex.Message + ex.StackTrace);
}
}
public void Run(Bitmap bmp)
{
try
{
CogImage8Grey image8Grey = new CogImage8Grey(bmp);
Run(image8Grey);
}
catch (Exception ex)
{
LogHelper.LogError("ScanToolBlock Run Error: " + ex.Message);
}
}
public void Run(CogImage8Grey image8Grey)
{
try
{
cogToolBlock.Inputs["OutputImage"].Value = image8Grey;
cogToolBlock.Run();
}
catch (Exception ex)
{
LogHelper.LogError("ScanToolBlock Run Error: " + ex.Message);
}
}
public bool Ready()
{
return initialized;
}
public void Stop()
{
if (cogToolBlock != null)
{
cogToolBlock.Ran -= CogToolBlock_Ran;
cogToolBlock.Dispose();
cogToolBlock = null;
}
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;
namespace WaferAdjust
{
public class LogHelper
{
private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();
public static void LogInfo(string message)
{
Logger.Info(message);
}
public static void LogError(string message)
{
Logger.Error(message);
}
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
autoReload="true"
throwConfigExceptions="true">
<variable name="generic" value="${time} | ${level} | ${message} | ${all-event-properties} ${exception:format=tostring}"/>
<targets async="true">
<target xsi:type="File" name="logfile" fileName="${basedir}/logs/${shortdate}.log"
layout="${generic}" encoding="utf-8"/>
</targets>
<rules>
<logger name="*" minlevel="Info" writeTo="logfile" />
</rules>
</nlog>

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WaferAdjust
{
internal class PointInfo
{
public double X;
public double Y;
public double Radius;
public PointInfo(double x, double y, double radius)
{
X = x;
Y = y;
Radius = radius;
}
}
}

View File

@@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WaferAdjust
{
/// <summary>
/// 二维坐标旋转计算器(支持自定义坐标系方向)
/// </summary>
public class Rotation2D
{
#region
/// <summary>
/// 点A绕点B逆时针旋转指定角度角度制
/// </summary>
/// <param name="ax">点A的X坐标</param>
/// <param name="ay">点A的Y坐标</param>
/// <param name="bx">旋转中心B的X坐标</param>
/// <param name="by">旋转中心B的Y坐标</param>
/// <param name="angleDegrees">旋转角度(角度制,逆时针)</param>
/// <param name="isRight">X轴正方向是否向右默认true</param>
/// <param name="isUp">Y轴正方向是否向上默认true</param>
/// <returns>旋转后的点A'坐标 (x', y')</returns>
public static (double x, double y) RotatePointDegrees(
double ax, double ay,
double bx, double by,
double angleDegrees,
bool isRight = true,
bool isUp = true)
{
return RotatePointRadians(ax, ay, bx, by,
DegreesToRadians(angleDegrees), isRight, isUp);
}
/// <summary>
/// 点A绕点B逆时针旋转指定角度弧度制
/// </summary>
/// <param name="ax">点A的X坐标</param>
/// <param name="ay">点A的Y坐标</param>
/// <param name="bx">旋转中心B的X坐标</param>
/// <param name="by">旋转中心B的Y坐标</param>
/// <param name="angleRadians">旋转角度(弧度制,逆时针)</param>
/// <param name="isRight">X轴正方向是否向右默认true</param>
/// <param name="isUp">Y轴正方向是否向上默认true</param>
/// <returns>旋转后的点A'坐标 (x', y')</returns>
public static (double x, double y) RotatePointRadians(
double ax, double ay,
double bx, double by,
double angleRadians,
bool isRight = true,
bool isUp = true)
{
// 1. 转换为标准数学坐标系(向右为正,向上为正)
double mathAx = isRight ? ax : -ax;
double mathAy = isUp ? ay : -ay;
double mathBx = isRight ? bx : -bx;
double mathBy = isUp ? by : -by;
// 2. 在标准坐标系中执行旋转
double cosR = Math.Cos(angleRadians);
double sinR = Math.Sin(angleRadians);
double dx = mathAx - mathBx;
double dy = mathAy - mathBy;
double rotatedDx = dx * cosR - dy * sinR;
double rotatedDy = dx * sinR + dy * cosR;
double mathNewX = mathBx + rotatedDx;
double mathNewY = mathBy + rotatedDy;
// 3. 转换回用户指定的坐标系
double outputX = isRight ? mathNewX : -mathNewX;
double outputY = isUp ? mathNewY : -mathNewY;
return (outputX, outputY);
}
#endregion
#region
/// <summary>
/// 已知旋转后的点A'求绕点B逆时针旋转前的原始点A角度制
/// </summary>
/// <param name="rotatedX">旋转后的点A'的X坐标</param>
/// <param name="rotatedY">旋转后的点A'的Y坐标</param>
/// <param name="bx">旋转中心B的X坐标</param>
/// <param name="by">旋转中心B的Y坐标</param>
/// <param name="angleDegrees">旋转角度(角度制,逆时针)</param>
/// <param name="isRight">X轴正方向是否向右默认true</param>
/// <param name="isUp">Y轴正方向是否向上默认true</param>
/// <returns>原始的未旋转点A坐标 (x, y)</returns>
public static (double x, double y) GetOriginalPointDegrees(
double rotatedX, double rotatedY,
double bx, double by,
double angleDegrees,
bool isRight = true,
bool isUp = true)
{
return GetOriginalPointRadians(rotatedX, rotatedY, bx, by,
DegreesToRadians(angleDegrees), isRight, isUp);
}
/// <summary>
/// 已知旋转后的点A'求绕点B逆时针旋转前的原始点A弧度制
/// </summary>
/// <param name="rotatedX">旋转后的点A'的X坐标</param>
/// <param name="rotatedY">旋转后的点A'的Y坐标</param>
/// <param name="bx">旋转中心B的X坐标</param>
/// <param name="by">旋转中心B的Y坐标</param>
/// <param name="angleRadians">旋转角度(弧度制,逆时针)</param>
/// <param name="isRight">X轴正方向是否向右默认true</param>
/// <param name="isUp">Y轴正方向是否向上默认true</param>
/// <returns>原始的未旋转点A坐标 (x, y)</returns>
public static (double x, double y) GetOriginalPointRadians(
double rotatedX, double rotatedY,
double bx, double by,
double angleRadians,
bool isRight = true,
bool isUp = true)
{
// 1. 转换为标准数学坐标系
double mathRotatedX = isRight ? rotatedX : -rotatedX;
double mathRotatedY = isUp ? rotatedY : -rotatedY;
double mathBx = isRight ? bx : -bx;
double mathBy = isUp ? by : -by;
// 2. 在标准坐标系中执行反向旋转
double cosR = Math.Cos(angleRadians);
double sinR = Math.Sin(angleRadians);
double dx = mathRotatedX - mathBx;
double dy = mathRotatedY - mathBy;
// 反向旋转(相当于顺时针旋转)
double originalDx = dx * cosR + dy * sinR;
double originalDy = -dx * sinR + dy * cosR;
double mathOriginalX = mathBx + originalDx;
double mathOriginalY = mathBy + originalDy;
// 3. 转换回用户指定的坐标系
double outputX = isRight ? mathOriginalX : -mathOriginalX;
double outputY = isUp ? mathOriginalY : -mathOriginalY;
return (outputX, outputY);
}
#endregion
#region
/// <summary>
/// 角度转弧度
/// </summary>
private static double DegreesToRadians(double degrees)
{
return degrees * Math.PI / 180.0;
}
/// <summary>
/// 弧度转角度(公共方法,供外部使用)
/// </summary>
public static double RadiansToDegrees(double radians)
{
return radians * 180.0 / Math.PI;
}
#endregion
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WaferAdjust
{
internal class TranslateCirclePoint
{
private double rotateX;
private double rotateY;
List<PointInfo> totalPoints;
List<PointInfo> totalCenters;
private int circleIndex;
private double perAngle;
PointInfo nashPoint;
PointInfo firstPoint;
public void SetRotateXY(double x, double y, double angle)
{
rotateX = x; rotateY = y;
totalPoints = new List<PointInfo>();
totalCenters = new List<PointInfo>();
circleIndex = 0;
perAngle = angle;
nashPoint = null;
firstPoint = null;
}
public double GetRotateX()
{
return rotateX;
}
public double GetRotateY()
{
return rotateY;
}
public void AddCirclePoint(List<PointInfo> pointInfos)
{
if (pointInfos == null || pointInfos.Count == 0) return;
foreach (var item in pointInfos)
{
totalPoints.Add(new PointInfo(item.X, item.Y, circleIndex * perAngle));
}
firstPoint = pointInfos[0];
}
public PointInfo GetFirstPoint()
{
return firstPoint;
}
public void AddCircleIndex()
{
circleIndex++;
}
public double GetCurrentDegree()
{
return circleIndex * perAngle;
}
public void AddNashPoint(double x, double y)
{
nashPoint = new PointInfo(x, y, circleIndex * perAngle);
}
public void AddCircleCenter(double x, double y, double r)
{
totalCenters.Add(new PointInfo(x, y, r));
}
public List<PointInfo> DoTranslatePoint()
{
List<PointInfo> trans = new List<PointInfo>();
foreach (var item in totalPoints)
{
var res = Rotation2D.GetOriginalPointDegrees(item.X, item.Y,
rotateX, rotateY, item.Radius, true, false);
trans.Add(new PointInfo(res.x, res.y, item.Radius));
}
return trans;
}
public void DoTranslateNashPoint()
{
if (nashPoint == null)
return;
var res = Rotation2D.GetOriginalPointDegrees(nashPoint.X, nashPoint.Y,
rotateX, rotateY, nashPoint.Radius, true, false);
nashPoint = new PointInfo(res.x, res.y, nashPoint.Radius);
}
public PointInfo GetNashPoint()
{
return nashPoint;
}
}
}

View File

@@ -79,6 +79,9 @@
<Reference Include="MvCameraControl.Net"> <Reference Include="MvCameraControl.Net">
<HintPath>..\..\..\..\..\..\Program Files (x86)\MVS\Development\DotNet\win64\net40\MvCameraControl.Net.dll</HintPath> <HintPath>..\..\..\..\..\..\Program Files (x86)\MVS\Development\DotNet\win64\net40\MvCameraControl.Net.dll</HintPath>
</Reference> </Reference>
<Reference Include="NLog, Version=6.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\packages\NLog.6.0.6\lib\net46\NLog.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" /> <Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
<Reference Include="QWhale.Common, Version=1.62.4104.36375, Culture=neutral, PublicKeyToken=da632fd1713dff10" /> <Reference Include="QWhale.Common, Version=1.62.4104.36375, Culture=neutral, PublicKeyToken=da632fd1713dff10" />
@@ -86,6 +89,7 @@
<Reference Include="QWhale.Syntax, Version=1.62.4104.36376, Culture=neutral, PublicKeyToken=da632fd1713dff10" /> <Reference Include="QWhale.Syntax, Version=1.62.4104.36376, Culture=neutral, PublicKeyToken=da632fd1713dff10" />
<Reference Include="QWhale.Syntax.Parsers, Version=1.62.4104.36376, Culture=neutral, PublicKeyToken=da632fd1713dff10" /> <Reference Include="QWhale.Syntax.Parsers, Version=1.62.4104.36376, Culture=neutral, PublicKeyToken=da632fd1713dff10" />
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@@ -104,15 +108,21 @@
<Compile Include="CenterOfRotation.cs" /> <Compile Include="CenterOfRotation.cs" />
<Compile Include="ConfigStore.cs" /> <Compile Include="ConfigStore.cs" />
<Compile Include="DataPostbox.cs" /> <Compile Include="DataPostbox.cs" />
<Compile Include="FitCircleToolBlock.cs" />
<Compile Include="Form1.cs"> <Compile Include="Form1.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
<Compile Include="Form1.Designer.cs"> <Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon> <DependentUpon>Form1.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="GetCircleToolBlock.cs" />
<Compile Include="LogHelper.cs" />
<Compile Include="PointInfo.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rotation2D.cs" />
<Compile Include="TCP.cs" /> <Compile Include="TCP.cs" />
<Compile Include="TranslateCirclePoint.cs" />
<EmbeddedResource Include="Form1.resx"> <EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon> <DependentUpon>Form1.cs</DependentUpon>
</EmbeddedResource> </EmbeddedResource>
@@ -126,6 +136,10 @@
<DependentUpon>Resources.resx</DependentUpon> <DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
</Compile> </Compile>
<None Include="Nlog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NLog" version="6.0.6" targetFramework="net48" />
</packages>