SickTimEnc.cs 21 KB

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