livox_driver.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  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. return Error_code::SUCCESS;
  65. }
  66. //livox雷达驱动反初始化.
  67. Error_manager Livox_driver::Livox_driver_uninit()
  68. {
  69. if ( m_init_flag )
  70. {
  71. LOG(INFO) << " ---Livox_driver::Livox_driver_uninit() run --- "<< this;
  72. //回收livox_sdk的相关资源.销毁livox_sdk的后台线程
  73. Uninit();
  74. m_init_flag = false;
  75. }
  76. return Error_code::SUCCESS;
  77. }
  78. //插入广播码和雷达实例的映射表
  79. Error_manager Livox_driver::Livox_insert_sn_laser(std::string sn, CLivoxLaser* p_livox_laser)
  80. {
  81. //雷达的sn码一般为15个字符加一个\0
  82. if ( sn.size() != kBroadcastCodeSize-1 )
  83. {
  84. return Error_manager(Error_code::LIVOX_DRIVER_SN_ERROR, Error_level::MINOR_ERROR,
  85. " Livox_insert_sn_laser error , sn size != 15 ");
  86. }
  87. //填充雷达设备广播码和雷达实例的映射表.如果重复,直接报错,决不能重复创建雷达.
  88. if ( m_sn_laser_map.find(sn) == m_sn_laser_map.end() )
  89. {
  90. //没找到就新增一个sn--laser映射
  91. m_sn_laser_map[sn] = p_livox_laser;
  92. return Error_code::SUCCESS;
  93. }
  94. else
  95. {
  96. return Error_manager(Error_code::LIVOX_DRIVER_SN_REPEAT, Error_level::MINOR_ERROR,
  97. " Livox_insert_sn_laser error , sn repeat ");
  98. }
  99. return Error_code::SUCCESS;
  100. }
  101. //判断 Livox_driver 模块是否正常, 只有正常之后,才能启动或者停止扫描.
  102. bool Livox_driver::is_ready()
  103. {
  104. return m_init_flag;
  105. }
  106. //判断是否自动创建雷达,默认为false
  107. bool Livox_driver::is_aoto_create_livox()
  108. {
  109. return m_aoto_create_livox_flag;
  110. }
  111. //雷达驱动层开始取样.
  112. Error_manager Livox_driver::Livox_driver_start_sample(uint8_t handle)
  113. {
  114. if ( is_ready() )
  115. {
  116. LidarStartSampling(handle, livox_start_sample_callback, NULL);
  117. return Error_code::SUCCESS;
  118. }
  119. else
  120. {
  121. return Error_manager(Error_code::LIVOX_DRIVER_NOT_READY, Error_level::MINOR_ERROR,
  122. " Livox_driver is_ready error ");
  123. }
  124. return Error_code::SUCCESS;
  125. }
  126. //雷达驱动层停止取样.
  127. Error_manager Livox_driver::Livox_driver_stop_sample(uint8_t handle)
  128. {
  129. if ( is_ready() )
  130. {
  131. LidarStopSampling(handle, livox_stop_sample_callback, NULL);
  132. return Error_code::SUCCESS;
  133. }
  134. else
  135. {
  136. return Error_manager(Error_code::LIVOX_DRIVER_NOT_READY, Error_level::MINOR_ERROR,
  137. " Livox_driver is_ready error ");
  138. }
  139. return Error_code::SUCCESS;
  140. }
  141. //设备广播的回调函数,在 Livox_driver_init 时使用 SetBroadcastCallback() 进行配置.
  142. //局域网内的所有livox雷达在收到广播之后,都会调用本函数.
  143. //本函数由livox后台线程来执行. 每个雷达调用一次.
  144. void Livox_driver::livox_device_broadcast_callback(const BroadcastDeviceInfo *p_info)
  145. {
  146. LOG(INFO) << " ---livox_device_broadcast_callback start--- " ;
  147. if (p_info == NULL) {
  148. return;
  149. }
  150. //检查设备类型,后续最好确定具体的型号.
  151. if ( p_info->dev_type != kDeviceTypeHub )
  152. {
  153. //自动创建雷达实例,就是不从外部参数创建,在驱动层链接设备的时候,按照能够连接上的设备自动创建雷达实例.默认为false
  154. if ( Livox_driver::get_instance_references().is_aoto_create_livox() )
  155. {
  156. //创建雷达啊实例.
  157. //为 laser_manager 的 m_laser_vector 分配内存.
  158. //以后再写.
  159. ;
  160. }
  161. else//默认从外部导入参数来创建雷达. 之前在CLivoxLaser初始化是,就已经填充了 m_sn_laser_map
  162. {
  163. //按照输入参数来创建雷达.
  164. //校验sn码,将雷达与设备链接起来,注意了,要先校验,再连接
  165. std::string t_sn = p_info->broadcast_code;
  166. std::map<std::string, CLivoxLaser*> & t_sn_laser_map = Livox_driver::get_instance_references().m_sn_laser_map;
  167. if ( t_sn_laser_map.find(t_sn) != t_sn_laser_map.end() )
  168. {
  169. //找到sn了,就连接laser和设备.
  170. CLivoxLaser* tp_livox_laser = t_sn_laser_map[t_sn];
  171. uint8_t t_handle = 0;
  172. //连接雷达设备,连接成功之后,后台进行会分配一个新的handle,默认0到31
  173. bool result = AddLidarToConnect(p_info->broadcast_code, &t_handle);
  174. if (result == kStatusSuccess)
  175. {
  176. //SetDataCallback 函数会为每个雷达设置数据回调函数,
  177. //注意了,livox的后台线程不会立刻调用livox_data_callback,
  178. //只有在雷达使用 LidarStartSampling 启动扫描后,当有数据返回时,才会调用livox_data_callback 传回雷达数据.
  179. SetDataCallback(t_handle, livox_data_callback, NULL);
  180. //此时只是单方面连接,状态仍然是 K_DEVICE_STATE_DISCONNECT
  181. //需要在 livox_device_change_callback 雷达设备响应连接之后,并确认连接的结果,才会修改雷达连接状态.
  182. tp_livox_laser->set_handle(t_handle);
  183. tp_livox_laser->set_device_state( CLivoxLaser::K_DEVICE_STATE_DISCONNECT);
  184. //填充雷达句柄和雷达实例的映射表. handle是livox后台线程分配的唯一码.一般不会出错.
  185. Livox_driver::get_instance_references().m_handle_laser_map[t_handle] = tp_livox_laser;
  186. }
  187. else
  188. {
  189. //连接失败. (一般雷达设备有应答,就会连接成功.)
  190. tp_livox_laser->set_handle(-1);
  191. tp_livox_laser->set_device_state( CLivoxLaser::K_DEVICE_STATE_FAULT);
  192. }
  193. }
  194. }
  195. }
  196. }
  197. //雷达设备状态改变的回调函数,在 Livox_driver_init 时使用 SetDeviceStateUpdateCallback() 来进行配置
  198. //雷达设备建立连接或者断开连接,都会调用 livox_device_change_callback 来通知我们雷达状态的改变.
  199. //本函数由livox后台线程来执行. 每个雷达可能调用多次.
  200. void Livox_driver::livox_device_change_callback(const DeviceInfo *p_info, DeviceEvent type)
  201. {
  202. LOG(INFO) << " ---livox_device_change_callback start--- " ;
  203. if (p_info == NULL) {
  204. return;
  205. }
  206. uint8_t t_handle = p_info->handle;
  207. //检查handle的有效性
  208. std::map<uint8_t, CLivoxLaser*> & t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  209. if ( t_handle_laser_map.find(t_handle) == t_handle_laser_map.end() )
  210. {
  211. return;
  212. }
  213. else
  214. {
  215. CLivoxLaser* tp_livox_laser = t_handle_laser_map[t_handle];
  216. //正常情况下,每个雷达连接都会调用两次,分别为 kEventConnect 和 kEventStateChange
  217. switch ( type )
  218. {
  219. case kEventConnect:
  220. //连接成功, 则从断连切换到连接
  221. if ( tp_livox_laser->get_device_state() == CLivoxLaser::K_DEVICE_STATE_DISCONNECT )
  222. {
  223. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  224. tp_livox_laser->set_device_info(*p_info);
  225. //LidarGetExtrinsicParameter 函数会为每个雷达设置 获取欧拉角的回调函数,
  226. //获取欧拉角, 并转化为变换矩阵 mp_laser_matrix
  227. LidarGetExtrinsicParameter(t_handle, livox_get_extrinsic_parameter_callback,NULL);
  228. }
  229. break;
  230. case kEventDisconnect:
  231. //断开连接, 如果不是故障,则切换到断连
  232. if ( tp_livox_laser->get_device_state() != CLivoxLaser::K_DEVICE_STATE_FAULT )
  233. {
  234. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_DISCONNECT);
  235. tp_livox_laser->set_device_info(*p_info);
  236. }
  237. break;
  238. case kEventStateChange:
  239. //单纯的刷新雷达设备信息
  240. tp_livox_laser->set_device_info(*p_info);
  241. break;
  242. default:
  243. break;
  244. }
  245. }
  246. //注意了:此时并不启动雷达扫描.
  247. //只有当接受到扫描任务之后,由 CLivoxLaser::start_scan() 来调用 LidarStartSampling
  248. }
  249. //获取欧拉角(笛卡尔坐标)的回调函数。在 livox_device_change_callback 连接成功之后使用 LidarGetExtrinsicParameter 来设置回调函数。
  250. //雷达接受到查询指令后,会调用 livox_get_extrinsic_parameter_callback 来返回 LidarGetExtrinsicParameterResponse 的指针. 里面就有 欧拉角(笛卡尔坐标)
  251. //本函数由livox后台线程来执行. 每个雷达只需要调用一次.
  252. void Livox_driver::livox_get_extrinsic_parameter_callback(uint8_t status,
  253. uint8_t handle,
  254. LidarGetExtrinsicParameterResponse *response,
  255. void *client_data)
  256. {
  257. LOG(INFO) << " ---livox_get_extrinsic_parameter_callback start--- " ;
  258. if ( status == kStatusSuccess )
  259. {
  260. // 校验handle的有效性
  261. std::map<uint8_t, CLivoxLaser*> & t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  262. if ( t_handle_laser_map.find(handle) == t_handle_laser_map.end() )
  263. {
  264. return;
  265. }
  266. else
  267. {
  268. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  269. double t_pitch = response->pitch;
  270. double t_roll = response->roll;
  271. double t_yaw = response->yaw;
  272. double t_x = response->x;
  273. double t_y = response->y;
  274. double t_z = response->z;
  275. //欧拉角转化为3*4矩阵
  276. t_roll = t_roll * M_PI / 180.0;
  277. t_pitch = t_pitch * M_PI / 180.0;
  278. t_yaw = t_yaw * M_PI / 180.0;
  279. double rotate[12] = {
  280. std::cos(t_pitch) * std::cos(t_yaw),
  281. std::sin(t_roll) * std::sin(t_pitch) * std::cos(t_yaw) - std::cos(t_roll) * std::sin(t_yaw),
  282. std::cos(t_roll) * std::sin(t_pitch) * std::cos(t_yaw) + std::sin(t_roll) * std::sin(t_yaw),
  283. t_x,
  284. std::cos(t_pitch) * std::sin(t_yaw),
  285. std::sin(t_roll) * std::sin(t_pitch) * std::sin(t_yaw) + std::cos(t_roll) * std::cos(t_yaw),
  286. std::cos(t_roll) * std::sin(t_pitch) * std::sin(t_yaw) - std::sin(t_roll) * std::cos(t_yaw),
  287. t_y,
  288. -std::sin(t_pitch),
  289. std::sin(t_roll) * std::cos(t_pitch),
  290. std::cos(t_roll) * std::cos(t_pitch),
  291. t_z
  292. };
  293. //设置变换矩阵
  294. tp_livox_laser->set_laser_matrix(rotate, 12);
  295. }
  296. }
  297. }
  298. //雷达设备数据返回 的回调函数. 在 livox_device_broadcast_callback 连接成功之后使用 SetDataCallback 来设置回调函数。
  299. //雷达在调用 LidarStartSampling 开始扫描之后,就一直调用 livox_data_callback 来返回数据.
  300. //雷达在调用 LidarStopSampling 停止扫描之后,就停止扫描.本函数也会停止调用.
  301. //本函数由livox后台线程来执行. 每帧数据都会调用一次. data里面有多个三维点.
  302. void Livox_driver::livox_data_callback(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *laser)
  303. {
  304. //livox_data_callback函数会高频率的调用,为了提高2效率,这里就不验证handle的有效性了,这就需要保证前面的正确操作
  305. std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  306. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  307. if (data && tp_livox_laser)
  308. {
  309. //判断是否采集完成
  310. if (tp_livox_laser->is_scan_complete())
  311. {
  312. //按照指定的帧数来采集,到点就停止.
  313. tp_livox_laser->stop_scan();
  314. //注注注注注意了:stop_scan会调用 LidarStopSampling 通知livox后台线程停止扫描.
  315. // 但是这个是有延迟的.会导致数据回调函数 livox_data_callback 仍然会调用3~5次.
  316. // 因此这里会反复调用 stop_scan
  317. return;
  318. }
  319. //data实际就是一个数据指针,里面包含data_num个三维点. data_num默认为100个
  320. LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
  321. //把雷达数据填充到livox数据缓存,此时里面是没有解析的原始数据. data_num默认为100个
  322. Binary_buf* data_bin = new Binary_buf((char*)p_point_data, data_num * sizeof(LivoxRawPoint));
  323. tp_livox_laser->push_livox_data(data_bin);
  324. tp_livox_laser->add_livox_frame(1);
  325. }
  326. }
  327. //雷达设备启动扫描的回调函数, 在 CLivoxLaser::start_scan() 需要启动扫描的时候使用 LidarStartSampling 来设置回调函数。
  328. //调用 LidarStartSampling 之后,livox后台进程就直接开始扫描了. 之后就会一直调用 livox_data_callback 来返回数据
  329. void Livox_driver::livox_start_sample_callback(uint8_t status, uint8_t handle, uint8_t response, void *data)
  330. {
  331. LOG(INFO) << " ---livox_start_sample_callback start--- " ;
  332. // 校验handle的有效性
  333. std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  334. if (t_handle_laser_map.find(handle) == t_handle_laser_map.end())
  335. {
  336. return;
  337. }
  338. else
  339. {
  340. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  341. if (status == kStatusSuccess)
  342. {
  343. if (response != 0)
  344. {
  345. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  346. }
  347. else
  348. {
  349. //返回成功并且应答没有报错,这里将雷达设备状态改为扫描中.
  350. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_SAMPLING);
  351. }
  352. }
  353. else if (status == kStatusTimeout)
  354. {
  355. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  356. }
  357. else
  358. {
  359. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_FAULT);
  360. }
  361. }
  362. }
  363. //雷达设备启动扫描的回调函数, 在 CLivoxLaser::stop_scan() 或者其他位置 需要停止的时候使用 LidarStopSampling 来设置回调函数。
  364. //调用 LidarStopSampling 之后,livox后台进程就直接停止扫描了. 也会停止调用 livox_data_callback
  365. void Livox_driver::livox_stop_sample_callback(uint8_t status, uint8_t handle, uint8_t response, void *data)
  366. {
  367. LOG(INFO) << " ---livox_stop_sample_callback start--- " ;
  368. // 校验handle的有效性
  369. std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
  370. if (t_handle_laser_map.find(handle) == t_handle_laser_map.end())
  371. {
  372. return;
  373. }
  374. else
  375. {
  376. CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
  377. if (status == kStatusSuccess || status == kStatusTimeout)
  378. {
  379. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
  380. }
  381. else
  382. {
  383. tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_FAULT);
  384. }
  385. }
  386. }