Laser.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. #include "Laser.h"
  2. #include "laser_message.pb.h"
  3. Laser_base::Laser_base(int laser_id, Laser_proto::laser_parameter laser_param)
  4. :m_laser_id(laser_id)
  5. ,m_laser_param(laser_param)
  6. ,m_laser_scan_flag(false)
  7. ,m_laser_statu(LASER_DISCONNECT)
  8. ,m_points_count(0)
  9. ,m_save_flag(false)
  10. {
  11. mp_thread_receive = NULL;
  12. mp_thread_transform = NULL;
  13. mp_thread_publish = NULL;
  14. init_laser_matrix();
  15. mp_laser_task = NULL;
  16. }
  17. Laser_base::~Laser_base()
  18. {
  19. disconnect_laser();
  20. close_save_path();
  21. }
  22. //雷达链接设备,为3个线程添加线程执行函数。
  23. Error_manager Laser_base::connect_laser()
  24. {
  25. //检查雷达状态
  26. if ( m_laser_statu != LASER_DISCONNECT )
  27. {
  28. return Error_manager(Error_code::LASER_STATUS_ERROR, Error_level::MINOR_ERROR,
  29. " Laser_base::connect_laser m_laser_statu != LASER_DISCONNECT ");
  30. }
  31. else
  32. {
  33. //这里不建议用detach,而是应该在disconnect里面使用join
  34. //创建接受缓存的线程,不允许重复创建
  35. if (mp_thread_receive != NULL)
  36. {
  37. return Error_manager(Error_code::LASER_CONNECT_FAILED, Error_level::MINOR_ERROR,
  38. " mp_thread_receive is alive ");
  39. }
  40. else
  41. {
  42. //接受缓存的线程,线程存活,暂停运行
  43. m_condition_receive.reset(false, false, false);
  44. mp_thread_receive = new std::thread(&Laser_base::thread_receive, this);
  45. }
  46. //转化数据的线程,不允许重复创建
  47. if (mp_thread_transform != NULL)
  48. {
  49. return Error_manager(Error_code::LASER_CONNECT_FAILED, Error_level::MINOR_ERROR,
  50. " mp_thread_transform is alive ");
  51. }
  52. else
  53. {
  54. //转化数据的线程,线程存活,暂停运行
  55. m_condition_transform.reset(false, false, false);
  56. mp_thread_transform = new std::thread(&Laser_base::thread_transform, this);
  57. }
  58. //发布信息的线程,不允许重复创建
  59. if (mp_thread_publish != NULL)
  60. {
  61. return Error_manager(Error_code::LASER_CONNECT_FAILED, Error_level::MINOR_ERROR,
  62. " mp_thread_publish is alive ");
  63. }
  64. else
  65. {
  66. //发布信息的线程,线程存活,循环运行,
  67. //注:mp_thread_publish 需要线程持续不断的往上位机发送信息,不受任务的影响。
  68. m_condition_publish.reset(false, true, false);
  69. mp_thread_publish = new std::thread(&Laser_base::thread_publish, this);
  70. }
  71. m_laser_statu = LASER_READY;
  72. }
  73. return Error_code::SUCCESS;
  74. }
  75. //雷达断开链接,释放3个线程
  76. Error_manager Laser_base::disconnect_laser()
  77. {
  78. //终止队列,防止 wait_and_pop 阻塞线程。
  79. m_queue_laser_data.termination_queue();
  80. //杀死3个线程,强制退出
  81. if (mp_thread_receive)
  82. {
  83. m_condition_receive.kill_all();
  84. }
  85. if (mp_thread_transform)
  86. {
  87. m_condition_transform.kill_all();
  88. }
  89. if (mp_thread_publish)
  90. {
  91. m_condition_publish.kill_all();
  92. }
  93. //回收3个线程的资源
  94. if (mp_thread_receive)
  95. {
  96. mp_thread_receive->join();
  97. delete mp_thread_receive;
  98. mp_thread_receive = NULL;
  99. }
  100. if (mp_thread_transform)
  101. {
  102. mp_thread_transform->join();
  103. delete mp_thread_transform;
  104. mp_thread_transform = 0;
  105. }
  106. if (mp_thread_publish)
  107. {
  108. mp_thread_publish->join();
  109. delete mp_thread_publish;
  110. mp_thread_publish = NULL;
  111. }
  112. //清空队列
  113. m_queue_laser_data.clear_and_delete();
  114. if ( m_laser_statu != LASER_FAULT )
  115. {
  116. m_laser_statu = LASER_DISCONNECT;
  117. }
  118. return Error_code::SUCCESS;
  119. }
  120. //对外的接口函数,负责接受并处理任务单,
  121. //input:p_laser_task 雷达任务单,基类的指针,指向子类的实例,(多态)
  122. //注:这个函数为虚函数,实际的处理任务的代码由子类重载并实现。
  123. Error_manager Laser_base::execute_task(Task_Base* p_laser_task)
  124. {
  125. //检查指针
  126. if(p_laser_task == NULL)
  127. {
  128. return Error_manager(Error_code::POINTER_IS_NULL, Error_level::MINOR_ERROR,
  129. "Laser_base::execute_task failed, POINTER_IS_NULL");
  130. }
  131. else
  132. {
  133. return Error_manager(Error_code::CLASS_BASE_FUNCTION_CANNOT_USE, Error_level::NEGLIGIBLE_ERROR,
  134. "Laser_base::execute_task cannot use");
  135. }
  136. }
  137. //检查雷达状态,是否正常运行
  138. Error_manager Laser_base::check_laser()
  139. {
  140. if ( is_ready() )
  141. {
  142. return Error_code::SUCCESS;
  143. }
  144. return Error_manager(Error_code::LASER_CHECK_FAILED, Error_level::MINOR_ERROR,
  145. " check_laser failed ");
  146. }
  147. //雷达的启动接口函数, 让雷达进行扫描,一般需要子类重载,不同的雷达开始方式不同。
  148. Error_manager Laser_base::start_scan()
  149. {
  150. LOG(INFO) << " Laser_base::start_scan "<< this;
  151. if ( is_ready() )
  152. {
  153. //启动雷达扫描
  154. m_laser_scan_flag=true; //启动雷达扫描
  155. m_laser_statu = LASER_BUSY; //雷达状态 繁忙
  156. m_condition_receive.notify_all(true); //唤醒接受线程
  157. m_condition_transform.notify_all(true); //唤醒转化线程
  158. //重置数据
  159. m_points_count=0;
  160. m_queue_laser_data.clear_and_delete();
  161. m_last_data.clear();
  162. return Error_code::SUCCESS;
  163. }
  164. else
  165. {
  166. return Error_manager(Error_code::LASER_START_FAILED, Error_level::MINOR_ERROR,
  167. "Laser_base::start_scan() is not ready ");
  168. }
  169. }
  170. //雷达的停止接口函数, 让雷达停止扫描,一般需要子类重载,不同的雷达结束方式不同。
  171. Error_manager Laser_base::stop_scan()
  172. {
  173. if(m_laser_scan_flag )
  174. {
  175. LOG(INFO) << " ---Laser_base::stop_scan ---"<< this;
  176. //stop_scan 只是将 m_laser_scan_flag 改为 stop。
  177. //然后多线程内部判断 m_laser_scan_flag,然后自动停止线程
  178. m_laser_scan_flag=false;
  179. }
  180. return Error_code::SUCCESS;
  181. }
  182. //结束任务单,stop之后,要检查线程状态和数据结果,然后才能 end_task
  183. Error_manager Laser_base::end_task()
  184. {
  185. LOG(INFO) << " Laser_base::end_task "<< this;
  186. stop_scan();
  187. m_laser_scan_flag=false;
  188. m_condition_receive.notify_all(false);
  189. m_condition_transform.notify_all(false);
  190. m_points_count=0;
  191. m_queue_laser_data.clear_and_delete();
  192. m_last_data.clear();
  193. close_save_path();
  194. //在结束任务单时,将雷达任务状态改为 TASK_OVER 已结束
  195. if(mp_laser_task !=NULL)
  196. {
  197. //判断任务单的错误等级,
  198. if ( mp_laser_task->get_task_error_manager().get_error_level() <= Error_level::MINOR_ERROR)
  199. {
  200. if ( m_laser_statu == LASER_BUSY )
  201. {
  202. //故障等级为 NORMAL NEGLIGIBLE_ERROR MINOR_ERROR,则恢复待机,可以接受下一次的任务单
  203. m_laser_statu = LASER_READY;
  204. }
  205. }
  206. else
  207. {
  208. //故障等级为 MAJOR_ERROR CRITICAL_ERROR,则雷达故障,不可以再接受任务单。
  209. m_laser_statu = LASER_FAULT;
  210. }
  211. //强制改为TASK_OVER,不管它当前在做什么。
  212. mp_laser_task->set_task_statu(TASK_OVER);
  213. }
  214. return Error_code::SUCCESS;
  215. }
  216. //取消任务单,由发送方提前取消任务单
  217. Error_manager Laser_base::cancel_task()
  218. {
  219. end_task();
  220. //强制改为 TASK_DEAD,不管它当前在做什么。
  221. mp_laser_task->set_task_statu(TASK_DEAD);
  222. return Error_code::SUCCESS;
  223. }
  224. //设置保存文件的路径,并打开文件,
  225. Error_manager Laser_base::set_open_save_path(std::string save_path,bool is_save)
  226. {
  227. m_save_flag = is_save;
  228. m_points_log_tool.close();
  229. m_binary_log_tool.close();
  230. if (is_save == false)
  231. {
  232. return Error_code::SUCCESS;
  233. }
  234. else
  235. {
  236. m_save_path = save_path;
  237. char pts_file[255] = { 0 };
  238. sprintf(pts_file, "%s/pts%d.txt", save_path.c_str(), m_laser_id+1);
  239. m_points_log_tool.open(pts_file);
  240. //std::cout << "huli m_points_log_tool path "<< pts_file << std::endl;
  241. char bin_file[255] = { 0 };
  242. sprintf(bin_file, "%s/laser%d.data", save_path.c_str(), m_laser_id+1);
  243. m_binary_log_tool.open(bin_file,true);
  244. return Error_code::SUCCESS;
  245. }
  246. }
  247. //关闭保存文件,推出前一定要执行
  248. Error_manager Laser_base::close_save_path()
  249. {
  250. m_save_flag = false;
  251. m_points_log_tool.close();
  252. m_binary_log_tool.close();
  253. return Error_code::SUCCESS;
  254. }
  255. //判断雷达状态是否为待机,如果已经准备好,则可以执行任务。
  256. //子类重载 is_ready(),里面增加livox sdk后台线程状态的判断。
  257. bool Laser_base::is_ready()
  258. {
  259. return ( get_laser_statu() == LASER_READY );
  260. }
  261. //获取雷达状态
  262. Laser_statu Laser_base::get_laser_statu()
  263. {
  264. return m_laser_statu;
  265. }
  266. //获取雷达id
  267. int Laser_base::get_laser_id()
  268. {
  269. return m_laser_id;
  270. }
  271. //线程执行函数,将二进制消息存入队列缓存,
  272. void Laser_base::thread_receive()
  273. {
  274. LOG(INFO) << " thread_receive start "<< this;
  275. //接受雷达消息,每次循环只接受一个 Binary_buf
  276. while (m_condition_receive.is_alive())
  277. {
  278. m_condition_receive.wait();
  279. if ( m_condition_receive.is_alive() )
  280. {
  281. //tp_binaty_buf的内存 需要手动分配,然后存入链表。
  282. //m_queue_laser_data的内存由 Laser_base 基类管理。
  283. Binary_buf* tp_binaty_buf = new Binary_buf();
  284. //获取雷达的通信消息缓存
  285. if (this->receive_buf_to_queue(*tp_binaty_buf))
  286. {
  287. //将缓存 存入队列。
  288. //thread_receive存入前要手动分配内存,thread_transform取出后要记得释放。
  289. m_queue_laser_data.push(tp_binaty_buf);
  290. }
  291. else
  292. {
  293. delete tp_binaty_buf;
  294. tp_binaty_buf=NULL;
  295. //接受线程无法接受到数据的同时,也收到了雷达停止扫描的信息,那么线程停止
  296. //如果m_laser_scan_flag停止扫描,但是receive_buf_to_queue仍然能够接受到数据。那么线程继续运行。
  297. //m_laser_scan_flag停止的瞬间,可能还有残留的数据,需要继续执行。
  298. if ( !m_laser_scan_flag )
  299. {
  300. //停止线程,m_condition_receive.wait() 函数将会阻塞。
  301. m_condition_receive.set_pass_ever(false);
  302. }
  303. }
  304. }
  305. }
  306. LOG(INFO) << " thread_receive end :"<<this;
  307. return;
  308. }
  309. //线程执行函数,转化并处理三维点云。
  310. void Laser_base::thread_transform()
  311. {
  312. LOG(INFO) << " thread_transform start "<< this;
  313. //转化雷达数据,每次循环只转化一个 Binary_buf,然后得到 t_point3D_cloud.size() 个三维点,
  314. while (m_condition_transform.is_alive())
  315. {
  316. m_condition_transform.wait();
  317. if ( m_condition_transform.is_alive() )
  318. {
  319. //tp_binaty_buf的内存 从链表取出时,就自带内存了。需要手动释放。
  320. //m_queue_laser_data的内存由 Laser_base 基类管理。
  321. Binary_buf* tp_binaty_buf=NULL;
  322. //第1步,从队列中取出缓存。
  323. int huli_test = m_queue_laser_data.size();
  324. //thread_receive存入前要手动分配内存,thread_transform取出后要记得释放。
  325. bool t_pop_flag = m_queue_laser_data.try_pop(tp_binaty_buf);
  326. /*if ( t_pop_flag )
  327. {
  328. std::cout << "huli m_queue_laser_data.size() last = " << huli_test << std::endl;
  329. std::cout << "huli m_queue_laser_data.size() now = " << m_queue_laser_data.size() << std::endl;
  330. }*/
  331. //第2步,处理数据,缓存转化缓存为三维点。
  332. //如果获取到了 Binary_buf,或者上次有未完成的 m_last_data,那么就执行数据转化。
  333. //注注注注注意了,扫描停止后,m_last_data一定要保证清空,不能有垃圾数据残留,这会阻塞线程的暂停
  334. if (t_pop_flag || m_last_data.get_length() > 0)
  335. {
  336. //std::cout << "huli thread_transform " << m_queue_laser_data.size() << std::endl;
  337. //std::cout << "huli t_pop_flag = "<< t_pop_flag << std::endl;
  338. //缓存类型
  339. Buf_type t_buf_type = BUF_UNKNOW;
  340. //雷达扫描结果,三维点云(雷达自身坐标系)
  341. std::vector<CPoint3D> t_point3D_cloud;
  342. //第2.1步,缓存转化为三维点。
  343. if (t_pop_flag)
  344. {
  345. if (tp_binaty_buf == NULL)
  346. {
  347. //try_pop返回true,但是tp_binaty_buf没有数据,直接跳过。这个一般不可能出现。
  348. continue;
  349. }
  350. else
  351. {
  352. //保存雷达通信 二进制的源文件。
  353. if(m_save_flag)
  354. {
  355. m_binary_log_tool.write(tp_binaty_buf->get_buf(), tp_binaty_buf->get_length());
  356. }
  357. //缓存转化为三维点。传入新的缓存,
  358. t_buf_type= this->transform_buf_to_points(tp_binaty_buf, t_point3D_cloud);
  359. }
  360. }
  361. else if(m_last_data.get_length() > 0)
  362. {
  363. //缓存转化为三维点。没有新消息,就传null,这将处理 m_last_data 上一次遗留的缓存。
  364. t_buf_type = this->transform_buf_to_points(NULL, t_point3D_cloud);
  365. }
  366. //第2.2步,判断t_buf_type,处理雷达数据。并存入任务单
  367. if (t_buf_type == BUF_UNKNOW || t_buf_type == BUF_READY)
  368. {
  369. //跳过这次循环。
  370. continue;
  371. }
  372. else if (t_buf_type == BUF_DATA || t_buf_type == BUF_START || t_buf_type == BUF_STOP)
  373. {
  374. //循环处理雷达数据
  375. for (int i = 0; i < t_point3D_cloud.size(); ++i)
  376. {
  377. //三维点的坐标变换,从雷达自己的坐标系,转化到公共坐标系,(目前以plc为公共坐标系)
  378. CPoint3D t_point = transform_by_matrix(t_point3D_cloud[i]);
  379. //保存雷达扫描 三维点云的最终结果。
  380. if(m_save_flag) {
  381. char buf[64] = {0};
  382. sprintf(buf, "%f %f %f\n", t_point.x, t_point.y, t_point.z);
  383. m_points_log_tool.write(buf, strlen(buf));
  384. //std::cout << "huli m_points_log_tool.write" << std::endl;
  385. }
  386. //此时 t_point 为雷达采集数据的结果,转换后的三维点云。
  387. //将每个点存入任务单里面的点云容器。
  388. if(mp_laser_task!=NULL)
  389. {
  390. Error_manager t_error= mp_laser_task->task_push_point(pcl::PointXYZ(t_point.x,t_point.y,t_point.z));
  391. if ( t_error != Error_code::SUCCESS )
  392. {
  393. mp_laser_task->get_task_error_manager().compare_and_cover_error(t_error);
  394. stop_scan();
  395. }
  396. }
  397. }
  398. //统计扫描点个数。
  399. m_points_count += t_point3D_cloud.size();
  400. //思科雷达停止扫描。
  401. if (t_buf_type == BUF_STOP)
  402. {
  403. //注:这个只是思科的雷达停止处,不同的雷达在不同的地方调用 stop_scan
  404. stop_scan();
  405. }
  406. }
  407. }
  408. else
  409. {
  410. //转化线程 不需要数据的同时,也收到了雷达停止扫描的信息并且接受线程已经停止,那么停止转化线程,
  411. //注:必须先停止接受线程,再停止转化线程。转化线程停止之后即可结束任务。
  412. //如果m_laser_scan_flag停止扫描,接受线程和转化线程还有未完成的数据。那么线程继续运行。
  413. //m_laser_scan_flag停止的瞬间,可能还有残留的数据,需要继续执行。防止数据遗留。
  414. if ( !m_laser_scan_flag && m_condition_receive.get_pass_ever() == false)
  415. {
  416. //停止线程,m_condition_transform.wait() 函数将会阻塞。
  417. m_condition_transform.set_pass_ever(false);
  418. //mp_thread_transform 停止时,雷达扫描任务彻底完成,此时结束任务
  419. end_task();
  420. }
  421. }
  422. //第3步,手动释放缓存
  423. if ( tp_binaty_buf != NULL )
  424. {
  425. delete tp_binaty_buf;
  426. }
  427. }
  428. }
  429. LOG(INFO) << " thread_transform end "<< this;
  430. return;
  431. }
  432. //公开发布雷达信息的功能函数,
  433. #include "../tool/MeasureTopicPublisher.h"
  434. Error_manager Laser_base::publish_laser_to_message()
  435. {
  436. //usleep(5*1000*1000);
  437. laser_message::laserMsg msg;
  438. laser_message::laserStatus status;
  439. if(get_laser_statu()==LASER_READY) status=laser_message::eLaserConnected;
  440. else if(get_laser_statu()==LASER_DISCONNECT) status=laser_message::eLaserDisconnected;
  441. else if(get_laser_statu()==LASER_BUSY) status=laser_message::eLaserBusy;
  442. else if(get_laser_statu()==LASER_FAULT) status=laser_message::eLaserDisconnected;
  443. else status=laser_message::eLaserUnknown;
  444. msg.set_id(m_laser_id);
  445. msg.set_laser_status(status);
  446. msg.set_queue_data_count(m_queue_laser_data.size());
  447. msg.set_cloud_count(m_points_count);
  448. MeasureTopicPublisher::GetInstance()->Publish(msg.SerializeAsString());
  449. return SUCCESS;
  450. }
  451. //线程执行函数,公开发布雷达的相关信息,用作上位机的监视。
  452. void Laser_base::thread_publish(Laser_base *p_laser)
  453. {
  454. LOG(INFO) << " thread_publish start ";
  455. if(p_laser==NULL)
  456. {
  457. return;
  458. }
  459. while (p_laser->m_condition_publish.is_alive())
  460. {
  461. p_laser->m_condition_publish.wait();
  462. if ( p_laser->m_condition_publish.is_alive() )
  463. {
  464. p_laser->publish_laser_to_message();
  465. //每隔300ms,发送一次雷达信息状态。
  466. std::this_thread::sleep_for(std::chrono::milliseconds(300));
  467. }
  468. }
  469. LOG(INFO) << " thread_publish end ";
  470. return;
  471. }
  472. //初始化变换矩阵,设置默认值
  473. Error_manager Laser_base::init_laser_matrix()
  474. {
  475. if ( LASER_MATRIX_ARRAY_SIZE == 12 )
  476. {
  477. //详见转化的算法transfor。保证转化前后坐标一致。
  478. mp_laser_matrix[0] = 1;
  479. mp_laser_matrix[1] = 0;
  480. mp_laser_matrix[2] = 0;
  481. mp_laser_matrix[3] = 0;
  482. mp_laser_matrix[4] = 0;
  483. mp_laser_matrix[5] = 1;
  484. mp_laser_matrix[6] = 0;
  485. mp_laser_matrix[7] = 0;
  486. mp_laser_matrix[8] = 0;
  487. mp_laser_matrix[9] = 0;
  488. mp_laser_matrix[10] = 1;
  489. mp_laser_matrix[11] = 0;
  490. }
  491. else
  492. {
  493. for (int i = 0; i < LASER_MATRIX_ARRAY_SIZE; ++i)
  494. {
  495. //设为0之后,变换之后,新的点坐标全部为0
  496. mp_laser_matrix[i] = 0;
  497. }
  498. }
  499. return Error_code::SUCCESS;
  500. }
  501. //设置变换矩阵,用作三维点的坐标变换,
  502. Error_manager Laser_base::set_laser_matrix(double* p_matrix, int size)
  503. {
  504. if ( p_matrix == NULL )
  505. {
  506. return Error_manager(Error_code::POINTER_IS_NULL, Error_level::MINOR_ERROR,
  507. " set_laser_matrix p_matrix IS_NULL ");
  508. }
  509. else if ( size != LASER_MATRIX_ARRAY_SIZE )
  510. {
  511. return Error_manager(Error_code::PARAMETER_ERROR, Error_level::MINOR_ERROR,
  512. " set_laser_matrix size is not Match");
  513. }
  514. else
  515. {
  516. memcpy(mp_laser_matrix, p_matrix, LASER_MATRIX_ARRAY_SIZE * sizeof(double));
  517. return Error_code::SUCCESS;
  518. }
  519. }
  520. //三维点的坐标变换的功能函数,从雷达自己的坐标系,转化到公共坐标系,(目前以plc为公共坐标系)
  521. CPoint3D Laser_base::transform_by_matrix(CPoint3D point)
  522. {
  523. CPoint3D result;
  524. double x = point.x;
  525. double y = point.y;
  526. double z = point.z;
  527. result.x = x * mp_laser_matrix[0] + y*mp_laser_matrix[1] + z*mp_laser_matrix[2] + mp_laser_matrix[3];
  528. result.y = x * mp_laser_matrix[4] + y*mp_laser_matrix[5] + z*mp_laser_matrix[6] + mp_laser_matrix[7];
  529. result.z = x * mp_laser_matrix[8] + y*mp_laser_matrix[9] + z*mp_laser_matrix[10] + mp_laser_matrix[11];
  530. return result;
  531. }