using Cognex.VisionPro; using Cognex.VisionPro.ToolBlock; using MathNet.Numerics.LinearAlgebra; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Windows.Forms; using TeamAAS_VP.Core; using TeamAAS_VP.Enums; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models.Calibration; using TeamAAS_VP.Resources.Languages; namespace TeamAAS_VP.Services { /// /// Calibration 管理服务,负责校准文件的读取与保存 /// public class CalibrationService : ICalibrationService { private static class Paths { public static readonly string CalibrationPath = "..//Calibration"; public static string CalibrationListFilePath => Path.Combine(CalibrationPath ?? string.Empty, "CalibrationList.cfg"); } private readonly object _sync = new object(); private ObservableCollection _calibrations = new ObservableCollection(); public CalibrationService() { // 不在构造中自动加载,调用者可以选择 LoadAll } public IReadOnlyCollection GetAllCalibrations() { lock (_sync) { return _calibrations.ToList().AsReadOnly(); } } public CalibrationInfo GetCalibration(Guid id) { lock (_sync) { return _calibrations.FirstOrDefault(c => c.Id == id); } } public CalibrationInfo AddOrUpdateCalibration(CalibrationInfo calib) { if (calib == null) return null; lock (_sync) { var exist = _calibrations.FirstOrDefault(c => c.Id == calib.Id); if (exist != null) { var idx = _calibrations.IndexOf(exist); _calibrations[idx] = calib; } else { calib.Index = _calibrations.Count + 1; _calibrations.Add(calib); } } calib.DateTime = DateTime.Now; string folder = Path.Combine(Paths.CalibrationPath, calib.Name); string calibFile = Path.Combine(folder, calib.Name + ".cfg"); if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); // 写入单个校准文件 FileHelper.WriteJsonFile(calib, calibFile); // 更新索引列表 var list = new Dictionary(); lock (_sync) { foreach (var item in _calibrations) { list[item.Id] = item.Name; } } FileHelper.WriteJsonFile(list, Paths.CalibrationListFilePath); // 保存ToolBlock string prcPath = Path.Combine(folder, calib.Name + ".vpp"); if (calib.ToolBlock != null) { CogSerializer.SaveObjectToFile(calib.ToolBlock, prcPath); } else { if (File.Exists(prcPath)) { calib.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } else if (File.Exists("..//Vision Template//Calibration.vpp")) { calib.ToolBlock = CogSerializer.LoadObjectFromFile("..//Vision Template//Calibration.vpp") as CogToolBlock; CogSerializer.SaveObjectToFile(calib.ToolBlock, prcPath); } } return calib; } public bool RemoveCalibration(Guid id) { bool result = false; CalibrationInfo removed = null; lock (_sync) { removed = _calibrations.FirstOrDefault(c => c.Id == id); if (removed != null) result = _calibrations.Remove(removed); } // 更新列表文件 var list = new Dictionary(); lock (_sync) { foreach (var item in _calibrations) { list[item.Id] = item.Name; } } FileHelper.WriteJsonFile(list, Paths.CalibrationListFilePath); //删除后,对所有校准重新排序索引 //先按照Index进行排序 var calibs = _calibrations.OrderBy(c => c.Index).ToList(); int index= 1; foreach (var item in calibs) { item.Index = index; //保存更新后的索引 string folder = Path.Combine(Paths.CalibrationPath, item.Name); string calibFile = Path.Combine(folder, item.Name + ".cfg"); FileHelper.WriteJsonFile(item, calibFile); index++; } _calibrations = new ObservableCollection(calibs); // 可选:删除物理文件和toolblock目录(不自动删除,除非明确需求) return result; } public void LoadAll() { lock (_sync) { if (!Directory.Exists(Paths.CalibrationPath)) Directory.CreateDirectory(Paths.CalibrationPath); _calibrations.Clear(); if (!File.Exists(Paths.CalibrationListFilePath)) { FileHelper.WriteJsonFile(new Dictionary(), Paths.CalibrationListFilePath); return; } var list = FileHelper.ReadJsonFile>(Paths.CalibrationListFilePath); if (list == null) return; foreach (var item in list) { try { string filePath = Path.Combine(FilePath.CalibrationPath, item.Value, item.Value + ".cfg"); if (File.Exists(filePath)) { var calib = FileHelper.ReadJsonFile(filePath); // 尝试加载toolblock string prcPath = Path.Combine(FilePath.CalibrationPath, calib.Name, calib.Name + ".vpp"); if (File.Exists(prcPath)) { calib.ToolBlock = CogSerializer.LoadObjectFromFile(prcPath) as CogToolBlock; } _calibrations.Add(calib); } } catch { // 忽略单个文件错误,继续加载其他文件 } } } } public void SaveAll() { lock (_sync) { var list = new Dictionary(); foreach (var item in _calibrations) { list[item.Id] = item.Name; string folder = Path.Combine(FilePath.CalibrationPath, item.Name); if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); string filePath = Path.Combine(folder, item.Name + ".cfg"); FileHelper.WriteJsonFile(item, filePath); if (item.ToolBlock != null) { string prcPath = Path.Combine(folder, item.Name + ".vpp"); CogSerializer.SaveObjectToFile(item.ToolBlock, prcPath); } } FileHelper.WriteJsonFile(list, Paths.CalibrationListFilePath); } } /// /// 校准转换,将像素坐标转换位置坐标 /// /// /// /// /// public (bool IsSucceed,double X, double Y, double U) ConvertPixelToPosition((double X, double Y, double angle) pixelCoord, double[] robotCoord, CalibrationInfo calib, RobotBrand robotBrand = RobotBrand.Default) { double angle= pixelCoord.angle; //得到校准矩阵 Matrix matrix = Matrix.Build.DenseOfArray(calib.AffineTransformationMaterial); if (calib.CameraMount == CameraMount.FixedDown || calib.CameraMount == CameraMount.MobileJ2 || calib.CameraMount == CameraMount.MobileJ4) { angle *= -1; } //将像素坐标转换成机器人坐标 var rpos = matrix * Vector.Build.Dense(new double[] { pixelCoord.X, pixelCoord.Y, 1 }); if (calib.CameraMount == Enums.CameraMount.MobileJ4) { //像素直接转换成mm时的点位 double Robot_X, Robot_Y; Robot_X = rpos[0]; Robot_Y = rpos[1]; //机器人当前TOOL 0下的坐标 double curpos_x = 0, curpos_y = 0, curpos_u = 0; curpos_x = robotCoord[0]; curpos_y = robotCoord[1]; curpos_u = robotCoord[2]; //得到旋转矩阵 double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180; // 创建T矩阵 Matrix rotationMatrix = Matrix.Build.DenseOfArray(new double[,] { { Math.Cos(angle1), -Math.Sin(angle1) }, { Math.Sin(angle1), Math.Cos(angle1) } }); Vector p3 = Vector.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y }); Vector phere = Vector.Build.Dense(new double[] { curpos_x, curpos_y }); var cc = rotationMatrix * p3 + phere; rpos = Vector.Build.Dense(new double[] { cc[0], cc[1] }); } else if (calib.CameraMount == Enums.CameraMount.MobileDown_XYPlatform) { //像素直接转换成mm时的点位 double Robot_X, Robot_Y; Robot_X = rpos[0]; Robot_Y = rpos[1]; //模组当前TOOL 0下的坐标(相当于模组吸嘴坐标) double curpos_x = 0, curpos_y = 0, curpos_u = 0; curpos_x = robotCoord[0]; curpos_y = robotCoord[1]; curpos_u = robotCoord[2]; //得到旋转矩阵 //double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180; double angle1 = 0; // 创建T矩阵 Matrix rotationMatrix = Matrix.Build.DenseOfArray(new double[,] { { Math.Cos(angle1), -Math.Sin(angle1) }, { Math.Sin(angle1), Math.Cos(angle1) } }); Vector p3 = Vector.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y }); Vector phere = Vector.Build.Dense(new double[] { curpos_x, curpos_y }); var cc = rotationMatrix * p3 + phere; rpos = Vector.Build.Dense(new double[] { cc[0], cc[1] });//Mark点在tool0下的的坐标 } else { //当需要直接转换工具坐标时 if (robotCoord!=null) { double curposX = robotCoord[0]; double curposY = robotCoord[1]; double curposU = robotCoord[2]; //根据机器人当前的点位计算工具坐标 ToolCoord tool = new ToolCoord(); if (robotBrand == RobotBrand.Schneider) { curposU *= -1; } tool.ComputeTool(curposX, curposY, curposU, rpos[0], rpos[1]); rpos = Vector.Build.Dense(new double[] { tool.X, tool.Y }); } } return (true, rpos[0], rpos[1], angle); } /// /// 校准转换,将像素坐标转换位置坐标 /// /// /// /// /// public (bool IsSucceed, double X, double Y, double U) ConvertPixelToPosition((double X, double Y, double angle) pixelCoord, double[] robotCoord, Guid calibId, RobotBrand robotBrand = RobotBrand.Default) { var calib = GetCalibration(calibId); if (calib == null) { return (false, 0, 0, 0); } var result = ConvertPixelToPosition(pixelCoord, robotCoord, calib, robotBrand); return result; } public void Dispose() { SaveAll(); GC.SuppressFinalize(this); } } }