using MathNet.Numerics.LinearAlgebra; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Text; using TeamAAS.Robot.Core; using TeamAAS.Robot.Enums; using TeamAAS.Vision.Calibration.Enums; using TeamAAS.Vision.Calibration.Interfaces; using TeamAAS.Vision.Calibration.Models; namespace TeamAAS.Vision.Calibration.Services { /// /// 标定管理服务:负责校准配置的读取/保存(Newtonsoft.Json)与运行时像素→机器人坐标换算。 /// 视觉工具的持久化由各引擎的 负责,本服务只保存其引用 。 /// public class CalibrationStore : ICalibrationService { private readonly string _calibrationPath; private readonly string _listFilePath; private readonly object _sync = new object(); private ObservableCollection _calibrations = new ObservableCollection(); public CalibrationStore() : this(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Calibration")) { } public CalibrationStore(string calibrationPath) { _calibrationPath = calibrationPath; _listFilePath = Path.Combine(_calibrationPath, "CalibrationList.json"); } private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings { Formatting = Formatting.Indented, TypeNameHandling = TypeNameHandling.None, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, }; private static void WriteJson(T value, string path) { string dir = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir); File.WriteAllText(path, JsonConvert.SerializeObject(value, JsonSettings), Encoding.UTF8); } private static T ReadJson(string path) { if (!File.Exists(path)) return default(T); return JsonConvert.DeserializeObject(File.ReadAllText(path, Encoding.UTF8), JsonSettings); } 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; calib.IsCreate = false; string folder = Path.Combine(_calibrationPath, calib.Name); string calibFile = Path.Combine(folder, calib.Name + ".json"); if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); WriteJson(calib, calibFile); WriteList(); return calib; } public bool RemoveCalibration(Guid id) { bool result; lock (_sync) { var removed = _calibrations.FirstOrDefault(c => c.Id == id); result = removed != null && _calibrations.Remove(removed); } WriteList(); // 删除后重新编号并保存。 List calibs; lock (_sync) { calibs = _calibrations.OrderBy(c => c.Index).ToList(); } int index = 1; foreach (var item in calibs) { item.Index = index++; string folder = Path.Combine(_calibrationPath, item.Name); WriteJson(item, Path.Combine(folder, item.Name + ".json")); } lock (_sync) { _calibrations = new ObservableCollection(calibs); } return result; } private void WriteList() { var list = new Dictionary(); lock (_sync) { foreach (var item in _calibrations) list[item.Id] = item.Name; } WriteJson(list, _listFilePath); } public void LoadAll() { lock (_sync) { if (!Directory.Exists(_calibrationPath)) Directory.CreateDirectory(_calibrationPath); _calibrations.Clear(); if (!File.Exists(_listFilePath)) { WriteJson(new Dictionary(), _listFilePath); return; } var list = ReadJson>(_listFilePath); if (list == null) return; foreach (var item in list) { try { string filePath = Path.Combine(_calibrationPath, item.Value, item.Value + ".json"); if (File.Exists(filePath)) { var calib = ReadJson(filePath); if (calib != null) _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(_calibrationPath, item.Name); if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); WriteJson(item, Path.Combine(folder, item.Name + ".json")); } WriteJson(list, _listFilePath); } } /// /// 校准转换:根据像素坐标与机器人当前坐标换算目标位姿(按相机安装方式分支)。 /// 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 == CameraMount.MobileJ4) { double Robot_X = rpos[0]; double Robot_Y = rpos[1]; double curpos_x = robotCoord[0]; double curpos_y = robotCoord[1]; double curpos_u = robotCoord[2]; double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180; 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 == CameraMount.MobileDown_XYPlatform) { double Robot_X = rpos[0]; double Robot_Y = rpos[1]; double curpos_x = robotCoord[0]; double curpos_y = robotCoord[1]; double angle1 = 0; 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 (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); return ConvertPixelToPosition(pixelCoord, robotCoord, calib, robotBrand); } public void Dispose() { try { SaveAll(); } catch { } GC.SuppressFinalize(this); } } }