SickTimEnc.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net;
  5. using System.Net.Sockets;
  6. using System.Text;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace Test
  10. {
  11. class SickTimEnc
  12. {
  13. //状态指示变量
  14. /// <summary>
  15. /// 指示初始化是否成功
  16. /// </summary>
  17. public bool initialized { get; set; }
  18. /// <summary>
  19. /// 指示是否工作中
  20. /// </summary>
  21. public bool working { get; set; }
  22. /// <summary>
  23. /// 指示是否检测到障碍物
  24. /// </summary>
  25. public bool obstacleDetected { get; set; }
  26. /// <summary>
  27. /// 发送指令返回值
  28. /// </summary>
  29. private int flag = 0;
  30. //通信参数
  31. private readonly object Lock = new object();
  32. private IPAddress ipa;
  33. private int port;
  34. private TcpClient tcpClient;
  35. private NetworkStream stream;
  36. private int bufferSize = 16384;
  37. private const string startCmd = "02 73 52 4E 20 4C 4D 44 73 63 61 6E 64 61 74 61 03";
  38. private const string endCmd = "02 73 45 4E 20 4C 4D 44 73 63 61 6E 64 61 74 61 20 30 03";
  39. //防撞参数
  40. public int maxDataCount { get; set; }//最大保留历史数据个数
  41. private int left_range = 385;//325;//231;//271;//386;//366;
  42. private int right_range = 425;//485;//581;//541;//426;//446;
  43. public int ground_truth_height { get; set; } = 2200;//2200;//2300;//标准高度
  44. public int min_num_point_of_hinder { get; set; } = 30;//20;//判定为障碍物的最少点的数目
  45. public int min_obstacle_height { get; set; } = 500;//最小障碍物高度
  46. private List<string> originDatalist;//原始数据数组
  47. private List<int> validNum;//811个整型值
  48. private List<List<double>> validNumBuffer;//保存历史点云数据
  49. private List<List<double>> validNumBufferKalman;//保存经过kalman滤波的历史点云数据
  50. private List<double> listy;
  51. public List<double> listx { get; set; }
  52. public List<double> yFiltered { get; set; }
  53. public List<double> yFilteredKalman { get; set; }
  54. //卡尔曼滤波参数
  55. private List<double> prevData, p, q, r, kGain;
  56. public SickTimEnc(string ip, int port)
  57. {
  58. initialized = false;
  59. maxDataCount = 3;
  60. originDatalist = new List<string>();
  61. validNum = new List<int>();
  62. validNumBuffer = new List<List<double>>();
  63. validNumBufferKalman = new List<List<double>>();
  64. prevData = new List<double>();
  65. p = new List<double>();
  66. q = new List<double>();
  67. r = new List<double>();
  68. kGain = new List<double>();
  69. for (int i = 0; i < right_range - left_range; i++)
  70. {
  71. prevData.Add(0);
  72. p.Add(450);
  73. q.Add(0.0001);
  74. r.Add(0.005);
  75. kGain.Add(0);
  76. }
  77. listy = new List<double>();
  78. listx = new List<double>();
  79. tcpClient = new TcpClient();
  80. if(!IPAddress.TryParse(ip, out ipa))
  81. {
  82. ipa = null;
  83. return;
  84. }
  85. try
  86. {
  87. tcpClient.Connect(ipa, port);
  88. stream = tcpClient.GetStream();
  89. }
  90. catch { return; }
  91. initialized = true;
  92. working = true;
  93. work();
  94. }
  95. /// <summary>
  96. /// 暂停工作
  97. /// </summary>
  98. /// <param name="ctrl"></param>
  99. public void Pause()
  100. {
  101. working = !working;
  102. }
  103. /// <summary>
  104. /// 停止工作
  105. /// </summary>
  106. public void Stop()
  107. {
  108. working = false;
  109. flag = sendCmd(endCmd);
  110. if (stream != null)
  111. {
  112. stream.Close();
  113. }
  114. if (tcpClient != null)
  115. {
  116. tcpClient.Close();
  117. }
  118. }
  119. /// <summary>
  120. /// 获取最小角度阈值,范围-45 - 225
  121. /// </summary>
  122. /// <param name="type">类型,点编号index或角度degree</param>
  123. /// <returns></returns>
  124. public int GetLeftRange(string type)
  125. {
  126. if (type == "degree")
  127. return left_range / 3 - 45;
  128. else
  129. return left_range;
  130. }
  131. /// <summary>
  132. /// 获取最大角度阈值,范围-45 - 225
  133. /// </summary>
  134. /// <param name="type">类型,点编号index或角度degree</param>
  135. /// <returns></returns>
  136. public int GetRightRange(string type)
  137. {
  138. if (type == "degree")
  139. return right_range / 3 - 45;
  140. else
  141. return right_range;
  142. }
  143. ///// <summary>
  144. ///// 获取距地面高度
  145. ///// </summary>
  146. ///// <returns></returns>
  147. //public int GetHeight()
  148. //{
  149. // return ground_truth_height;
  150. //}
  151. /// <summary>
  152. /// 设置最小角度阈值
  153. /// </summary>
  154. /// <param name="degree"></param>
  155. public void SetLeftRange(int degree)
  156. {
  157. if (working)
  158. {
  159. working = false;
  160. lock (Lock)
  161. {
  162. left_range = (degree + 45) * 3;
  163. }
  164. working = true;
  165. }
  166. else
  167. {
  168. left_range = (degree + 45) * 3;
  169. }
  170. initKalman();
  171. }
  172. /// <summary>
  173. /// 设置最大角度阈值
  174. /// </summary>
  175. /// <param name="degree"></param>
  176. public void SetRightRange(int degree)
  177. {
  178. if (working)
  179. {
  180. working = false;
  181. lock (Lock)
  182. {
  183. right_range = (degree + 45) * 3;
  184. }
  185. working = true;
  186. }
  187. else
  188. {
  189. right_range = (degree + 45) * 3;
  190. }
  191. initKalman();
  192. }
  193. ///// <summary>
  194. ///// 设置距地面高度
  195. ///// </summary>
  196. ///// <param name="height"></param>
  197. //public void SetHeight(int height)
  198. //{
  199. // ground_truth_height = height;
  200. //}
  201. /// <summary>
  202. /// 初始化kalman滤波参数
  203. /// </summary>
  204. private void initKalman()
  205. {
  206. prevData.Clear();
  207. p.Clear();
  208. q.Clear();
  209. r.Clear();
  210. kGain.Clear();
  211. for (int i = 0; i < right_range - left_range; i++)
  212. {
  213. prevData.Add(0);
  214. p.Add(450);
  215. q.Add(0.0001);
  216. r.Add(0.005);
  217. kGain.Add(0);
  218. }
  219. }
  220. /// <summary>
  221. /// 启动雷达处理数据
  222. /// </summary>
  223. private void work()
  224. {
  225. Task.Factory.StartNew(()=>
  226. {
  227. while (initialized)
  228. {
  229. try
  230. {
  231. if (working)
  232. {
  233. //开辟线程存储雷达原始数据字符串
  234. lock (Lock)
  235. {
  236. originDatalist.Clear();
  237. flag = sendCmd(startCmd);
  238. if (flag == 1)
  239. {
  240. byte[] buffer = new byte[bufferSize];
  241. stream.Read(buffer, 0, buffer.Length);
  242. string str = HexStringToASCII(buffer);
  243. originDatalist.Add(str);
  244. }
  245. if (originDatalist.Count != 0)
  246. {
  247. for (int i = 0; i < originDatalist.Count; i++)
  248. {
  249. string ss = trimDataStr(originDatalist[i], "sRA LMDscandata 1 ", " not defined 0 0 0");
  250. string[] data = ss.Split(' ');
  251. string number = data[22];
  252. int length = Convert.ToInt32(number, 16);
  253. int temp = 0;
  254. //int[] validNum = new int[length];
  255. //string aws = "";
  256. //获取所有距离数值
  257. validNum.Clear();
  258. listy.Clear();
  259. listx.Clear();
  260. for (int j = 0; j < length; j++)
  261. {
  262. try
  263. {
  264. temp = Convert.ToInt32(data[23 + j], 16);
  265. }
  266. catch { temp = ground_truth_height; Console.WriteLine("数据转换异常"); }
  267. validNum.Add(temp);
  268. }
  269. for (int j = 0; j < length; j++)
  270. {
  271. double x = validNum[j] * Math.Cos((-45 + 0.333 * j) * Math.PI / 180);
  272. double y = validNum[j] * Math.Sin((-45 + 0.333 * j) * Math.PI / 180);
  273. listy.Add(y);
  274. listx.Add(x);
  275. }
  276. validNumBuffer.Add(listy);
  277. validNumBufferKalman.Add(kalmanFilter(listy));
  278. while (validNumBufferKalman.Count > maxDataCount)
  279. {
  280. validNumBufferKalman.RemoveAt(0);
  281. validNumBuffer.RemoveAt(0);
  282. }
  283. if(validNumBufferKalman.Count == maxDataCount)
  284. {
  285. bool obstacle = false;
  286. yFiltered = dataFilter(validNumBuffer, out obstacle);
  287. yFilteredKalman = dataFilter(validNumBufferKalman, out obstacle);
  288. obstacleDetected = obstacle;
  289. }
  290. }
  291. }
  292. }
  293. Thread.Sleep(1);
  294. }
  295. else
  296. {
  297. //空闲状态
  298. Thread.Sleep(1000);
  299. }
  300. }
  301. catch (Exception e) { Thread.Sleep(500); Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); }
  302. }
  303. });
  304. }
  305. /// <summary>
  306. /// 向雷达发送指令
  307. /// </summary>
  308. /// <param name="cmd"></param>
  309. /// <returns></returns>
  310. private int sendCmd(string cmd)
  311. {
  312. try
  313. {
  314. if (cmd.Equals(startCmd))
  315. {
  316. byte[] byteStartCmd = HexStringToBinary(startCmd);
  317. string result = HexStringToASCII(byteStartCmd);
  318. byte[] b = Encoding.UTF8.GetBytes(result);
  319. stream.Write(b, 0, b.Length);
  320. return 1;
  321. }
  322. if (cmd.Equals(endCmd))
  323. {
  324. byte[] byteStartCmd = HexStringToBinary(endCmd);
  325. string result = HexStringToASCII(byteStartCmd);
  326. byte[] b = Encoding.UTF8.GetBytes(result);
  327. stream.Write(b, 0, b.Length);
  328. return 2;
  329. }
  330. }
  331. catch { return 0; }
  332. return 0;
  333. }
  334. /// <summary>
  335. /// 将一条十六进制字符串转换为ASCII
  336. /// </summary>
  337. /// <param name="hexstring">一条十六进制字符串</param>
  338. /// <returns>返回一条ASCII码</returns>
  339. private static string HexStringToASCII(byte[] bt)
  340. {
  341. //byte[] bt = HexStringToBinary(hexstring);
  342. string lin = "";
  343. for (int i = 0; i < bt.Length; i++)
  344. {
  345. lin = lin + bt[i] + " ";
  346. }
  347. string[] ss = lin.Trim().Split(new char[] { ' ' });
  348. char[] c = new char[ss.Length];
  349. int a;
  350. for (int i = 0; i < c.Length; i++)
  351. {
  352. a = Convert.ToInt32(ss[i]);
  353. c[i] = Convert.ToChar(a);
  354. }
  355. string b = new string(c);
  356. return b;
  357. }
  358. /// <summary>
  359. /// 16进制字符串转换为二进制数组
  360. /// </summary>
  361. /// <param name="hexstring">用空格切割字符串</param>
  362. /// <returns>返回一个二进制字符串</returns>
  363. private static byte[] HexStringToBinary(string hexstring)
  364. {
  365. string[] tmpary = hexstring.Trim().Split(' ');
  366. byte[] buff = new byte[tmpary.Length];
  367. for (int i = 0; i < buff.Length; i++)
  368. {
  369. buff[i] = Convert.ToByte(tmpary[i], 16);
  370. }
  371. return buff;
  372. }
  373. /// <summary>
  374. /// 截取数据段
  375. /// </summary>
  376. /// <param name="sourse"></param>
  377. /// <param name="startstr"></param>
  378. /// <param name="endstr"></param>
  379. /// <returns></returns>
  380. private static string trimDataStr(string sourse, string startstr, string endstr)
  381. {
  382. string result = string.Empty;
  383. int startindex, endindex;
  384. try
  385. {
  386. startindex = sourse.IndexOf(startstr);
  387. if (startindex == -1)
  388. {
  389. return result;
  390. }
  391. string tmpstr = sourse.Substring(startindex + startstr.Length);
  392. endindex = tmpstr.IndexOf(endstr);
  393. if (endindex == -1)
  394. {
  395. return result;
  396. }
  397. result = tmpstr.Remove(endindex);
  398. }
  399. catch (Exception) { }
  400. return result;
  401. }
  402. /// <summary>
  403. /// 平滑数据,识别障碍物
  404. /// </summary>
  405. /// <param name="y"></param>
  406. /// <param name="isObstacle"></param>
  407. /// <returns></returns>
  408. private List<double> dataFilter(List<List<double>> y, out bool isObstacle)
  409. {
  410. List<double> y_udt = new List<double>();
  411. isObstacle = false;
  412. int hinder_point_count = 0;
  413. for (int i = left_range; i < right_range; i++)
  414. {
  415. int hinder_frame_count = 0;
  416. for (int j = 0; j < y.Count; j++)
  417. {
  418. if (i != left_range && i != right_range - 1)
  419. {
  420. y[j][i] = (y[j][i - 1] + y[j][i] + y[j][i + 1]) / 3;//每个点的y为其和相邻两个点和的平均值
  421. }
  422. double d = Math.Abs(y[j][i] - ground_truth_height);
  423. if (d > min_obstacle_height)//300
  424. hinder_frame_count = hinder_frame_count + 1;
  425. }
  426. if (hinder_frame_count == y.Count)
  427. hinder_point_count = hinder_point_count + 1;
  428. y_udt.Add(y[y.Count - 1][i]);
  429. }
  430. if (hinder_point_count >= min_num_point_of_hinder)
  431. isObstacle = true;
  432. return y_udt;
  433. }
  434. /// <summary>
  435. /// 卡尔曼滤波
  436. /// </summary>
  437. /// <param name="data_in"></param>
  438. /// <returns></returns>
  439. private List<double> kalmanFilter(List<double> data_in)
  440. {
  441. for (int i = left_range; i < right_range; i++)
  442. {
  443. p[i - left_range] = p[i - left_range] + q[i - left_range];
  444. kGain[i - left_range] = p[i - left_range] / (p[i - left_range] + r[i - left_range]);
  445. data_in[i] = prevData[i - left_range] + (kGain[i - left_range] * (data_in[i] - prevData[i - left_range]));
  446. p[i - left_range] = (1 - kGain[i - left_range]) * p[i - left_range];
  447. prevData[i - left_range] = data_in[i];
  448. }
  449. return data_in;
  450. }
  451. }
  452. }