CalibrationStore.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. using MathNet.Numerics.LinearAlgebra;
  2. using Newtonsoft.Json;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Collections.ObjectModel;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text;
  9. using TeamAAS.Robot.Core;
  10. using TeamAAS.Robot.Enums;
  11. using TeamAAS.Vision.Calibration.Enums;
  12. using TeamAAS.Vision.Calibration.Interfaces;
  13. using TeamAAS.Vision.Calibration.Models;
  14. namespace TeamAAS.Vision.Calibration.Services
  15. {
  16. /// <summary>
  17. /// 标定管理服务:负责校准配置的读取/保存(Newtonsoft.Json)与运行时像素→机器人坐标换算。
  18. /// 视觉工具的持久化由各引擎的 <see cref="ICalibrationVisionProvider"/> 负责,本服务只保存其引用 <see cref="CalibrationInfo.VisionToolRef"/>。
  19. /// </summary>
  20. public class CalibrationStore : ICalibrationService
  21. {
  22. private readonly string _calibrationPath;
  23. private readonly string _listFilePath;
  24. private readonly object _sync = new object();
  25. private ObservableCollection<CalibrationInfo> _calibrations = new ObservableCollection<CalibrationInfo>();
  26. public CalibrationStore() : this(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Calibration"))
  27. {
  28. }
  29. public CalibrationStore(string calibrationPath)
  30. {
  31. _calibrationPath = calibrationPath;
  32. _listFilePath = Path.Combine(_calibrationPath, "CalibrationList.json");
  33. }
  34. private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings
  35. {
  36. Formatting = Formatting.Indented,
  37. TypeNameHandling = TypeNameHandling.None,
  38. ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
  39. };
  40. private static void WriteJson<T>(T value, string path)
  41. {
  42. string dir = Path.GetDirectoryName(path);
  43. if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir);
  44. File.WriteAllText(path, JsonConvert.SerializeObject(value, JsonSettings), Encoding.UTF8);
  45. }
  46. private static T ReadJson<T>(string path)
  47. {
  48. if (!File.Exists(path)) return default(T);
  49. return JsonConvert.DeserializeObject<T>(File.ReadAllText(path, Encoding.UTF8), JsonSettings);
  50. }
  51. public IReadOnlyCollection<CalibrationInfo> GetAllCalibrations()
  52. {
  53. lock (_sync)
  54. {
  55. return _calibrations.ToList().AsReadOnly();
  56. }
  57. }
  58. public CalibrationInfo GetCalibration(Guid id)
  59. {
  60. lock (_sync)
  61. {
  62. return _calibrations.FirstOrDefault(c => c.Id == id);
  63. }
  64. }
  65. public CalibrationInfo AddOrUpdateCalibration(CalibrationInfo calib)
  66. {
  67. if (calib == null) return null;
  68. lock (_sync)
  69. {
  70. var exist = _calibrations.FirstOrDefault(c => c.Id == calib.Id);
  71. if (exist != null)
  72. {
  73. var idx = _calibrations.IndexOf(exist);
  74. _calibrations[idx] = calib;
  75. }
  76. else
  77. {
  78. calib.Index = _calibrations.Count + 1;
  79. _calibrations.Add(calib);
  80. }
  81. }
  82. calib.DateTime = DateTime.Now;
  83. calib.IsCreate = false;
  84. string folder = Path.Combine(_calibrationPath, calib.Name);
  85. string calibFile = Path.Combine(folder, calib.Name + ".json");
  86. if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
  87. WriteJson(calib, calibFile);
  88. WriteList();
  89. return calib;
  90. }
  91. public bool RemoveCalibration(Guid id)
  92. {
  93. bool result;
  94. lock (_sync)
  95. {
  96. var removed = _calibrations.FirstOrDefault(c => c.Id == id);
  97. result = removed != null && _calibrations.Remove(removed);
  98. }
  99. WriteList();
  100. // 删除后重新编号并保存。
  101. List<CalibrationInfo> calibs;
  102. lock (_sync) { calibs = _calibrations.OrderBy(c => c.Index).ToList(); }
  103. int index = 1;
  104. foreach (var item in calibs)
  105. {
  106. item.Index = index++;
  107. string folder = Path.Combine(_calibrationPath, item.Name);
  108. WriteJson(item, Path.Combine(folder, item.Name + ".json"));
  109. }
  110. lock (_sync) { _calibrations = new ObservableCollection<CalibrationInfo>(calibs); }
  111. return result;
  112. }
  113. private void WriteList()
  114. {
  115. var list = new Dictionary<Guid, string>();
  116. lock (_sync)
  117. {
  118. foreach (var item in _calibrations) list[item.Id] = item.Name;
  119. }
  120. WriteJson(list, _listFilePath);
  121. }
  122. public void LoadAll()
  123. {
  124. lock (_sync)
  125. {
  126. if (!Directory.Exists(_calibrationPath)) Directory.CreateDirectory(_calibrationPath);
  127. _calibrations.Clear();
  128. if (!File.Exists(_listFilePath))
  129. {
  130. WriteJson(new Dictionary<Guid, string>(), _listFilePath);
  131. return;
  132. }
  133. var list = ReadJson<Dictionary<Guid, string>>(_listFilePath);
  134. if (list == null) return;
  135. foreach (var item in list)
  136. {
  137. try
  138. {
  139. string filePath = Path.Combine(_calibrationPath, item.Value, item.Value + ".json");
  140. if (File.Exists(filePath))
  141. {
  142. var calib = ReadJson<CalibrationInfo>(filePath);
  143. if (calib != null) _calibrations.Add(calib);
  144. }
  145. }
  146. catch
  147. {
  148. // 忽略单个文件错误,继续加载其他配置。
  149. }
  150. }
  151. }
  152. }
  153. public void SaveAll()
  154. {
  155. lock (_sync)
  156. {
  157. var list = new Dictionary<Guid, string>();
  158. foreach (var item in _calibrations)
  159. {
  160. list[item.Id] = item.Name;
  161. string folder = Path.Combine(_calibrationPath, item.Name);
  162. if (!Directory.Exists(folder)) Directory.CreateDirectory(folder);
  163. WriteJson(item, Path.Combine(folder, item.Name + ".json"));
  164. }
  165. WriteJson(list, _listFilePath);
  166. }
  167. }
  168. /// <summary>
  169. /// 校准转换:根据像素坐标与机器人当前坐标换算目标位姿(按相机安装方式分支)。
  170. /// </summary>
  171. 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)
  172. {
  173. double angle = pixelCoord.angle;
  174. Matrix<double> matrix = Matrix<double>.Build.DenseOfArray(calib.AffineTransformationMaterial);
  175. if (calib.CameraMount == CameraMount.FixedDown || calib.CameraMount == CameraMount.MobileJ2 || calib.CameraMount == CameraMount.MobileJ4)
  176. {
  177. angle *= -1;
  178. }
  179. var rpos = matrix * Vector<double>.Build.Dense(new double[] { pixelCoord.X, pixelCoord.Y, 1 });
  180. if (calib.CameraMount == CameraMount.MobileJ4)
  181. {
  182. double Robot_X = rpos[0];
  183. double Robot_Y = rpos[1];
  184. double curpos_x = robotCoord[0];
  185. double curpos_y = robotCoord[1];
  186. double curpos_u = robotCoord[2];
  187. double angle1 = Math.PI * (curpos_u - calib.MarkPoint.U) / 180;
  188. Matrix<double> rotationMatrix = Matrix<double>.Build.DenseOfArray(new double[,]
  189. {
  190. { Math.Cos(angle1), -Math.Sin(angle1) },
  191. { Math.Sin(angle1), Math.Cos(angle1) }
  192. });
  193. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y });
  194. Vector<double> phere = Vector<double>.Build.Dense(new double[] { curpos_x, curpos_y });
  195. var cc = rotationMatrix * p3 + phere;
  196. rpos = Vector<double>.Build.Dense(new double[] { cc[0], cc[1] });
  197. }
  198. else if (calib.CameraMount == CameraMount.MobileDown_XYPlatform)
  199. {
  200. double Robot_X = rpos[0];
  201. double Robot_Y = rpos[1];
  202. double curpos_x = robotCoord[0];
  203. double curpos_y = robotCoord[1];
  204. double angle1 = 0;
  205. Matrix<double> rotationMatrix = Matrix<double>.Build.DenseOfArray(new double[,]
  206. {
  207. { Math.Cos(angle1), -Math.Sin(angle1) },
  208. { Math.Sin(angle1), Math.Cos(angle1) }
  209. });
  210. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Robot_X - calib.CalibPoints[0].Robot.X, Robot_Y - calib.CalibPoints[0].Robot.Y });
  211. Vector<double> phere = Vector<double>.Build.Dense(new double[] { curpos_x, curpos_y });
  212. var cc = rotationMatrix * p3 + phere;
  213. rpos = Vector<double>.Build.Dense(new double[] { cc[0], cc[1] });
  214. }
  215. else
  216. {
  217. if (robotCoord != null)
  218. {
  219. double curposX = robotCoord[0];
  220. double curposY = robotCoord[1];
  221. double curposU = robotCoord[2];
  222. ToolCoord tool = new ToolCoord();
  223. if (robotBrand == RobotBrand.Schneider) curposU *= -1;
  224. tool.ComputeTool(curposX, curposY, curposU, rpos[0], rpos[1]);
  225. rpos = Vector<double>.Build.Dense(new double[] { tool.X, tool.Y });
  226. }
  227. }
  228. return (true, rpos[0], rpos[1], angle);
  229. }
  230. 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)
  231. {
  232. var calib = GetCalibration(calibId);
  233. if (calib == null) return (false, 0, 0, 0);
  234. return ConvertPixelToPosition(pixelCoord, robotCoord, calib, robotBrand);
  235. }
  236. public void Dispose()
  237. {
  238. try { SaveAll(); } catch { }
  239. GC.SuppressFinalize(this);
  240. }
  241. }
  242. }