livox_driver.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. * livox_driver 大疆雷达的底层驱动
  3. *
  4. * 本模块作为单例模式,全局使用.
  5. * 负责通过调用livox sdk,与livox的后台进行进行对接. 直接控制大疆的雷达设备.
  6. *
  7. */
  8. #include "livox_driver.h"
  9. Livox_driver::Livox_driver()
  10. {
  11. m_init_flag = false;
  12. }
  13. Livox_driver::~Livox_driver()
  14. {
  15. // 因为退出顺序的原因,析构不再自动调用 Livox_driver_uninit ,
  16. // 而是在推出前手动调用Livox_driver_uninit
  17. Livox_driver_uninit();
  18. }
  19. //livox雷达驱动初始化.
  20. Error_manager Livox_driver::Livox_driver_init(bool aoto_create_livox_flag)
  21. {
  22. LOG(INFO) << " ---Livox_driver_init run --- "<< this;
  23. if (m_init_flag == false)
  24. {
  25. //Init是livox_sdk.h里面的初始化函数, 此时会创建livox后台线程
  26. if (!Init()) {
  27. Uninit();
  28. LOG(ERROR) << "livox sdk init failed...";
  29. return Error_manager(LIVOX_SKD_INIT_FAILED,MINOR_ERROR,"Livox laser init failed...");
  30. }
  31. else
  32. {
  33. //显示 livox sdk 版本
  34. LivoxSdkVersion _sdkversion;
  35. GetLivoxSdkVersion(&_sdkversion);
  36. char buf[255] = { 0 };
  37. sprintf(buf, "livox sdk init success. \n Livox SDK version %d.%d.%d .\n", _sdkversion.major, _sdkversion.minor, _sdkversion.patch);
  38. LOG(INFO) << buf;
  39. m_aoto_create_livox_flag = aoto_create_livox_flag;
  40. //SetBroadcastCallback 设置广播的回调函数,此时会向局域网发送广播消息.
  41. // 收到广播的所有雷达设备都会调用一次livox_device_broadcast_callback,
  42. //livox_device_broadcast_callback 函数由livox后台线程来执行.
  43. SetBroadcastCallback(livox_device_broadcast_callback);
  44. //SetDeviceStateUpdateCallback 设置状态更新的回调函数.
  45. //雷达设备建立连接或者断开连接,都会调用 livox_device_change_callback
  46. //livox_device_change_callback 函数由livox后台线程来执行.
  47. SetDeviceStateUpdateCallback(livox_device_change_callback);
  48. //启动设备底层的扫描线程,该线程在单独在后台运行。(该线程由livox_sdk来维护)
  49. //只有在start之后.各种回调函数才能生效.
  50. if (!Start())
  51. {
  52. m_init_flag = false;
  53. Uninit();
  54. LOG(ERROR) << "livox sdk start failed...";
  55. return Error_manager(LIVOX_SKD_INIT_FAILED,MINOR_ERROR,"Livox laser Start failed...");
  56. }
  57. else
  58. {
  59. LOG(ERROR) << "livox sdk start success...";
  60. m_init_flag = true;
  61. }
  62. }
  63. }
  64. std::cout << " huli test :::: " << " m_sn_laser_map.size() = " << m_sn_laser_map.size() << std::endl;
  65. std::cout << " huli test :::: " << " m_handle_laser_map.size() = " << m_handle_laser_map.size() << std::endl;
  66. return Error_code::SUCCESS;
  67. }
  68. //livox雷达驱动反初始化.
  69. Error_manager Livox_driver::Livox_driver_uninit()
  70. {
  71. if ( m_init_flag )
  72. {
  73. LOG(INFO) << " ---Livox_driver::Livox_driver_uninit() run --- "<< this;
  74. //回收livox_sdk的相关资源.销毁livox_sdk的后台线程
  75. Uninit();
  76. m_init_flag = false;
  77. }
  78. return Error_code::SUCCESS;
  79. }
  80. //插入广播码和雷达实例的映射表
  81. Error_manager Livox_driver::Livox_insert_sn_laser(std::string sn, CLivoxLaser* p_livox_laser)
  82. {
  83. //雷达的sn码一般为15个字符加一个\0
  84. if ( sn.size() != kBroadcastCodeSize-1 )
  85. {
  86. return Error_manager(Error_code::LIVOX_DRIVER_SN_ERROR, Error_level::MINOR_ERROR,
  87. " Livox_insert_sn_laser error , sn size != 15 ");
  88. }
  89. //填充雷达设备广播码和雷达实例的映射表.如果重复,直接报错,决不能重复创建雷达.
  90. if ( m_sn_laser_map.find(sn) == m_sn_laser_map.end() )
  91. {
  92. //没找到就新增一个sn--laser映射
  93. m_sn_laser_map[sn] = p_livox_laser;
  94. return Error_code::SUCCESS;
  95. }
  96. else
  97. {
  98. return Error_manager(Error_code::LIVOX_DRIVER_SN_REPEAT, Error_level::MINOR_ERROR,
  99. " Livox_insert_sn_laser error , sn repeat ");
  100. }
  101. return Error_code::SUCCESS;
  102. }
  103. //判断 Livox_driver 模块是否正常, 只有正常之后,才能启动或者停止扫描.
  104. bool Livox_driver::is_ready()
  105. {
  106. return m_init_flag;
  107. }
  108. //判断是否自动创建雷达,默认为false
  109. bool Livox_driver::is_aoto_create_livox()
  110. {
  111. return m_aoto_create_livox_flag;
  112. }
  113. //雷达驱动层开始取样.
  114. Error_manager Livox_driver::Livox_driver_start_sample(uint8_t handle)
  115. {
  116. if ( is_ready() )
  117. {
  118. LidarStartSampling(handle, livox_start_sample_callback, NULL);
  119. return Error_code::SUCCESS;
  120. }
  121. else
  122. {
  123. return Error_manager(Error_code::LIVOX_DRIVER_NOT_READY, Error_level::MINOR_ERROR,
  124. " Livox_driver is_ready error ");
  125. }
  126. return Error_code::SUCCESS;
  127. }
  128. //雷达驱动层停止取样.
  129. Error_manager Livox_driver::Livox_driver_stop_sample(uint8_t handle)
  130. {
  131. if ( is_ready() )
  132. {
  133. LidarStopSampling(handle, livox_stop_sample_callback, NULL);
  134. return Error_code::SUCCESS;
  135. }
  136. else
  137. {
  138. return Error_manager(Error_code::LIVOX_DRIVER_NOT_READY, Error_level::MINOR_ERROR,
  139. " Livox_driver is_ready error ");
  140. }
  141. return Error_code::SUCCESS;
  142. }
  143. //设备广播的回调函数,在 Livox_driver_init 时使用 SetBroadcastCallback() 进行配置.
  144. //局域网内的所有livox雷达在收到广播之后,都会调用本函数.
  145. //本函数由livox后台线程来执行. 每个雷达调用一次.
  146. void Livox_driver::livox_device_broadcast_callback(const BroadcastDeviceInfo *p_info)
  147. {
  148. LOG(INFO) << " ---livox_device_broadcast_callback start--- " ;
  149. if (p_info == NULL) {
  150. return;
  151. }
  152. //检查设备类型,后续最好确定具体的型号.
  153. if ( p_info->dev_type != kDeviceTypeHub )
  154. {
  155. //自动创建雷达实例,就是不从外部参数创建,在驱动层链接设备的时候,按照能够连接上的设备自动创建雷达实例.默认为false
  156. if ( Livox_driver::get_instance_references().is_aoto_create_livox() )
  157. {
  158. //创建雷达啊实例.
  159. //为 laser_manager 的 m_laser_vector 分配内存.
  160. //以后再写.
  161. ;
  162. }
  163. else//默认从外部导入参数来创建雷达. 之前在CLivoxLaser初始化是,就已经填充了 m_sn_laser_map
  164. {
  165. //按照输入参数来创建雷达.
  166. //校验sn码,将雷达与设备链接起来,注意了,要先校验,再连接
  167. std::string t_sn = p_info->broadcast_code;
  168. std::map<std::string, CLivoxLaser*> & t_sn_laser_map = Livox_driver::get_instance_references().m_sn_laser_map;
  169. if ( t_sn_laser_map.find(t_sn) != t_sn_laser_map.end() )
  170. {
  171. //找到sn了,就连接laser和设备.
  172. CLivoxLaser* tp_livox_laser = t_sn_laser_map[t_sn];
  173. uint8_t t_handle = 0;
  174. //连接雷达设备,连接成功之后,后台进行会分配一个新的handle,默认0到31
  175. bool result = AddLidarToConnect(p_info->broadcast_code, &t_handle);
  176. if (result == kStatusSuccess)
  177. {
  178. //SetDataCallback 函数会为每个雷达设置数据回调函数,
  179. //注意了,livox的后台线程不会立刻调用livox_data_callback,
  180. //只有在雷达使用 LidarStartSampling 启动扫描后,当有数据返回时,才会调用livox_data_callback 传回雷达数据.
  181. SetDataCallback(t_handle, livox_data_callback, NULL);
  182. //此时只是单方面连接,状态仍然是 K_DEVICE_STATE_DISCONNECT
  183. //需要在 livox_device_change_callback 雷达设备响应连接之后,并确认连接的结果,才会修改雷达连接状态.
  184. tp_livox_laser->set_handle(t_handle);
  185. tp_livox_laser->set_device_state( CLivoxLaser::K_DEVICE_STATE_DISCONNECT);
  186. //填充雷达句柄和雷达实例的映射表. handle是livox后台线程分配的唯一码.一般不会出错.
  187. Livox_driver::get_instance_references().m_handle_laser_map[t_handle] = tp_livox_laser;
  188. }
  189. else
  190. {
  191. //连接失败. (一般雷达设备有应答,就会连接成功.)
  192. tp_livox_laser->set_handle(-1);
  193. tp_livox_laser->set_device_state( CLivoxLaser::K_DEVICE_STATE_FAULT);
  194. }
  195. }
  196. else
  197. {
  198. }
  199. }
  200. }
  201. }
  202. //雷达设备状态改变的回调函数,在 Livox_driver_init 时使用 SetDeviceStateUpdateCallback() 来进行配置
  203. //雷达设备建立连接或者断开连接,都会调用 livox_device_change_callback 来通知我们雷达状态的改变.
  204. //本函数由livox后台线程来执行. 每个雷达可能调用多次.
  205. void Livox_driver::livox_device_change_callback(const DeviceInfo *p_info, DeviceEvent type)
  206. {
  207. LOG(INFO) << " ---livox_device_change_callback start--- " ;
  208. if (p_info == NULL) {
  209. return;
  210. }
  211. uint8_t t_handle = p_info->handle;
  212. //检查handle的有效性
  213. std::map<uint8_t, CLivoxLaser*> & t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  214. if ( t_handle_laser_map.find(t_handle) == t_handle_laser_map.end() )
  215. {
  216. return;
  217. }
  218. else
  219. {
  220. CLivoxLaser* tp_livox_laser = t_handle_laser_map[t_handle];
  221. //正常情况下,每个雷达连接都会调用两次,分别为 kEventConnect 和 kEventStateChange
  222. switch ( type )
  223. {
  224. case kEventConnect:
  225. //连接成功, 则从断连切换到连接
  226. if ( tp_livox_laser->get_device_state() == CLivoxLaser::K_DEVICE_STATE_DISCONNECT )
  227. {
  228. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  229. tp_livox_laser->set_device_info(*p_info);
  230. //LidarGetExtrinsicParameter 函数会为每个雷达设置 获取欧拉角的回调函数,
  231. //获取欧拉角, 并转化为变换矩阵 mp_laser_matrix
  232. LidarGetExtrinsicParameter(t_handle, livox_get_extrinsic_parameter_callback,NULL);
  233. }
  234. break;
  235. case kEventDisconnect:
  236. //断开连接, 如果不是故障,则切换到断连
  237. if ( tp_livox_laser->get_device_state() != CLivoxLaser::K_DEVICE_STATE_FAULT )
  238. {
  239. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_DISCONNECT);
  240. tp_livox_laser->set_device_info(*p_info);
  241. }
  242. break;
  243. case kEventStateChange:
  244. //单纯的刷新雷达设备信息
  245. tp_livox_laser->set_device_info(*p_info);
  246. break;
  247. default:
  248. break;
  249. }
  250. }
  251. //注意了:此时并不启动雷达扫描.
  252. //只有当接受到扫描任务之后,由 CLivoxLaser::start_scan() 来调用 LidarStartSampling
  253. }
  254. //获取欧拉角(笛卡尔坐标)的回调函数。在 livox_device_change_callback 连接成功之后使用 LidarGetExtrinsicParameter 来设置回调函数。
  255. //雷达接受到查询指令后,会调用 livox_get_extrinsic_parameter_callback 来返回 LidarGetExtrinsicParameterResponse 的指针. 里面就有 欧拉角(笛卡尔坐标)
  256. //本函数由livox后台线程来执行. 每个雷达只需要调用一次.
  257. void Livox_driver::livox_get_extrinsic_parameter_callback(livox_status status,
  258. uint8_t handle,
  259. LidarGetExtrinsicParameterResponse *response,
  260. void *client_data)
  261. {
  262. LOG(INFO) << " ---livox_get_extrinsic_parameter_callback start--- " ;
  263. if ( status == kStatusSuccess )
  264. {
  265. // 校验handle的有效性
  266. std::map<uint8_t, CLivoxLaser*> & t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  267. if ( t_handle_laser_map.find(handle) == t_handle_laser_map.end() )
  268. {
  269. return;
  270. }
  271. else
  272. {
  273. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  274. double t_pitch = response->pitch;
  275. double t_roll = response->roll;
  276. double t_yaw = response->yaw;
  277. double t_x = response->x;
  278. double t_y = response->y;
  279. double t_z = response->z;
  280. //欧拉角转化为3*4矩阵
  281. t_roll = t_roll * M_PI / 180.0;
  282. t_pitch = t_pitch * M_PI / 180.0;
  283. t_yaw = t_yaw * M_PI / 180.0;
  284. double rotate[12] = {
  285. std::cos(t_pitch) * std::cos(t_yaw),
  286. std::sin(t_roll) * std::sin(t_pitch) * std::cos(t_yaw) - std::cos(t_roll) * std::sin(t_yaw),
  287. std::cos(t_roll) * std::sin(t_pitch) * std::cos(t_yaw) + std::sin(t_roll) * std::sin(t_yaw),
  288. t_x,
  289. std::cos(t_pitch) * std::sin(t_yaw),
  290. std::sin(t_roll) * std::sin(t_pitch) * std::sin(t_yaw) + std::cos(t_roll) * std::cos(t_yaw),
  291. std::cos(t_roll) * std::sin(t_pitch) * std::sin(t_yaw) - std::sin(t_roll) * std::cos(t_yaw),
  292. t_y,
  293. -std::sin(t_pitch),
  294. std::sin(t_roll) * std::cos(t_pitch),
  295. std::cos(t_roll) * std::cos(t_pitch),
  296. t_z
  297. };
  298. //设置变换矩阵
  299. tp_livox_laser->set_laser_matrix(rotate, 12);
  300. }
  301. }
  302. }
  303. //雷达设备数据返回 的回调函数. 在 livox_device_broadcast_callback 连接成功之后使用 SetDataCallback 来设置回调函数。
  304. //雷达在调用 LidarStartSampling 开始扫描之后,就一直调用 livox_data_callback 来返回数据.
  305. //雷达在调用 LidarStopSampling 停止扫描之后,就停止扫描.本函数也会停止调用.
  306. //本函数由livox后台线程来执行. 每帧数据都会调用一次. data里面有多个三维点.
  307. void Livox_driver::livox_data_callback(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *laser)
  308. {
  309. //livox_data_callback函数会高频率的调用,为了提高2效率,这里就不验证handle的有效性了,这就需要保证前面的正确操作
  310. std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  311. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  312. if (data && tp_livox_laser)
  313. {
  314. // if(data ->data_type == kCartesian) {
  315. // LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
  316. // }else if ( data ->data_type == kSpherical) {
  317. // LivoxSpherPoint *p_point_data = (LivoxSpherPoint *)data->data;
  318. // }else if ( data ->data_type == kExtendCartesian) {
  319. // LivoxExtendRawPoint *p_point_data = (LivoxExtendRawPoint *)data->data;
  320. // }else if ( data ->data_type == kExtendSpherical) {
  321. // LivoxExtendSpherPoint *p_point_data = (LivoxExtendSpherPoint *)data->data;
  322. // }else if ( data ->data_type == kDualExtendCartesian) {
  323. // LivoxDualExtendRawPoint *p_point_data = (LivoxDualExtendRawPoint *)data->data;
  324. // }else if ( data ->data_type == kDualExtendSpherical) {
  325. // LivoxDualExtendSpherPoint *p_point_data = (LivoxDualExtendSpherPoint *)data->data;
  326. // }else if ( data ->data_type == kImu) {
  327. // LivoxImuPoint *p_point_data = (LivoxImuPoint *)data->data;
  328. // }else if ( data ->data_type == kTripleExtendCartesian) {
  329. // LivoxTripleExtendRawPoint *p_point_data = (LivoxTripleExtendRawPoint *)data->data;
  330. // }else if ( data ->data_type == kTripleExtendSpherical) {
  331. // LivoxTripleExtendSpherPoint *p_point_data = (LivoxTripleExtendSpherPoint *)data->data;
  332. // }
  333. if(data ->data_type == kCartesian)
  334. {
  335. //判断是否采集完成
  336. if (tp_livox_laser->is_scan_complete())
  337. {
  338. //按照指定的帧数来采集,到点就停止.
  339. tp_livox_laser->stop_scan();
  340. //注注注注注意了:stop_scan会调用 LidarStopSampling 通知livox后台线程停止扫描.
  341. // 但是这个是有延迟的.会导致数据回调函数 livox_data_callback 仍然会调用3~5次.
  342. // 因此这里会反复调用 stop_scan
  343. return;
  344. }
  345. //data实际就是一个数据指针,里面包含data_num个三维点. data_num默认为100个
  346. LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
  347. //把雷达数据填充到livox数据缓存,此时里面是没有解析的原始数据. data_num默认为100个
  348. Binary_buf* data_bin = new Binary_buf((char*)p_point_data, data_num * sizeof(LivoxRawPoint));
  349. tp_livox_laser->push_livox_data(data_bin);
  350. tp_livox_laser->add_livox_frame(1);
  351. }
  352. else if ( data ->data_type == kExtendCartesian)
  353. {
  354. //判断是否采集完成
  355. if (tp_livox_laser->is_scan_complete())
  356. {
  357. //按照指定的帧数来采集,到点就停止.
  358. tp_livox_laser->stop_scan();
  359. //注注注注注意了:stop_scan会调用 LidarStopSampling 通知livox后台线程停止扫描.
  360. // 但是这个是有延迟的.会导致数据回调函数 livox_data_callback 仍然会调用3~5次.
  361. // 因此这里会反复调用 stop_scan
  362. return;
  363. }
  364. //data实际就是一个数据指针,里面包含data_num个三维点. data_num默认为100个
  365. LivoxExtendRawPoint *p_point_data = (LivoxExtendRawPoint *)data->data;
  366. //把雷达数据填充到livox数据缓存,此时里面是没有解析的原始数据. data_num默认为100个
  367. Binary_buf* data_bin = new Binary_buf((char*)p_point_data, data_num * sizeof(LivoxExtendRawPoint));
  368. tp_livox_laser->push_livox_data(data_bin);
  369. tp_livox_laser->add_livox_frame(1);
  370. }
  371. else
  372. {
  373. return;
  374. }
  375. }
  376. }
  377. //雷达设备启动扫描的回调函数, 在 CLivoxLaser::start_scan() 需要启动扫描的时候使用 LidarStartSampling 来设置回调函数。
  378. //调用 LidarStartSampling 之后,livox后台进程就直接开始扫描了. 之后就会一直调用 livox_data_callback 来返回数据
  379. void Livox_driver::livox_start_sample_callback(livox_status status, uint8_t handle, uint8_t response, void *data)
  380. {
  381. LOG(INFO) << " ---livox_start_sample_callback start--- " ;
  382. // 校验handle的有效性
  383. std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  384. if (t_handle_laser_map.find(handle) == t_handle_laser_map.end())
  385. {
  386. return;
  387. }
  388. else
  389. {
  390. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  391. if (status == kStatusSuccess)
  392. {
  393. if (response != 0)
  394. {
  395. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  396. }
  397. else
  398. {
  399. //返回成功并且应答没有报错,这里将雷达设备状态改为扫描中.
  400. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_SAMPLING);
  401. }
  402. }
  403. else if (status == kStatusTimeout)
  404. {
  405. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  406. }
  407. else
  408. {
  409. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_FAULT);
  410. }
  411. }
  412. }
  413. //雷达设备启动扫描的回调函数, 在 CLivoxLaser::stop_scan() 或者其他位置 需要停止的时候使用 LidarStopSampling 来设置回调函数。
  414. //调用 LidarStopSampling 之后,livox后台进程就直接停止扫描了. 也会停止调用 livox_data_callback
  415. void Livox_driver::livox_stop_sample_callback(livox_status status, uint8_t handle, uint8_t response, void *data)
  416. {
  417. LOG(INFO) << " ---livox_stop_sample_callback start--- " ;
  418. // 校验handle的有效性
  419. std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  420. if (t_handle_laser_map.find(handle) == t_handle_laser_map.end())
  421. {
  422. return;
  423. }
  424. else
  425. {
  426. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  427. if (status == kStatusSuccess || status == kStatusTimeout)
  428. {
  429. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  430. }
  431. else
  432. {
  433. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_FAULT);
  434. }
  435. }
  436. }