123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- /*
- * livox_driver 大疆雷达的底层驱动
- *
- * 本模块作为单例模式,全局使用.
- * 负责通过调用livox sdk,与livox的后台进行进行对接. 直接控制大疆的雷达设备.
- *
- */
- #include "livox_driver.h"
- Livox_driver::Livox_driver()
- {
- m_init_flag = false;
- }
- Livox_driver::~Livox_driver()
- {
- // 因为退出顺序的原因,析构不再自动调用 Livox_driver_uninit ,
- // 而是在推出前手动调用Livox_driver_uninit
- Livox_driver_uninit();
- }
- //livox雷达驱动初始化.
- Error_manager Livox_driver::Livox_driver_init(bool aoto_create_livox_flag)
- {
- LOG(INFO) << " ---Livox_driver_init run --- "<< this;
- if (m_init_flag == false)
- {
- //Init是livox_sdk.h里面的初始化函数, 此时会创建livox后台线程
- if (!Init()) {
- Uninit();
- LOG(ERROR) << "livox sdk init failed...";
- return Error_manager(LIVOX_SKD_INIT_FAILED,MINOR_ERROR,"Livox laser init failed...");
- }
- else
- {
- //显示 livox sdk 版本
- LivoxSdkVersion _sdkversion;
- GetLivoxSdkVersion(&_sdkversion);
- char buf[255] = { 0 };
- sprintf(buf, "livox sdk init success. \n Livox SDK version %d.%d.%d .\n", _sdkversion.major, _sdkversion.minor, _sdkversion.patch);
- LOG(INFO) << buf;
- m_aoto_create_livox_flag = aoto_create_livox_flag;
- //SetBroadcastCallback 设置广播的回调函数,此时会向局域网发送广播消息.
- // 收到广播的所有雷达设备都会调用一次livox_device_broadcast_callback,
- //livox_device_broadcast_callback 函数由livox后台线程来执行.
- SetBroadcastCallback(livox_device_broadcast_callback);
- //SetDeviceStateUpdateCallback 设置状态更新的回调函数.
- //雷达设备建立连接或者断开连接,都会调用 livox_device_change_callback
- //livox_device_change_callback 函数由livox后台线程来执行.
- SetDeviceStateUpdateCallback(livox_device_change_callback);
- //启动设备底层的扫描线程,该线程在单独在后台运行。(该线程由livox_sdk来维护)
- //只有在start之后.各种回调函数才能生效.
- if (!Start())
- {
- m_init_flag = false;
- Uninit();
- LOG(ERROR) << "livox sdk start failed...";
- return Error_manager(LIVOX_SKD_INIT_FAILED,MINOR_ERROR,"Livox laser Start failed...");
- }
- else
- {
- LOG(ERROR) << "livox sdk start success...";
- m_init_flag = true;
- }
- }
- }
- return Error_code::SUCCESS;
- }
- //livox雷达驱动反初始化.
- Error_manager Livox_driver::Livox_driver_uninit()
- {
- if ( m_init_flag )
- {
- LOG(INFO) << " ---Livox_driver::Livox_driver_uninit() run --- "<< this;
- //回收livox_sdk的相关资源.销毁livox_sdk的后台线程
- Uninit();
- m_init_flag = false;
- }
- return Error_code::SUCCESS;
- }
- //插入广播码和雷达实例的映射表
- Error_manager Livox_driver::Livox_insert_sn_laser(std::string sn, CLivoxLaser* p_livox_laser)
- {
- //雷达的sn码一般为15个字符加一个\0
- if ( sn.size() != kBroadcastCodeSize-1 )
- {
- return Error_manager(Error_code::LIVOX_DRIVER_SN_ERROR, Error_level::MINOR_ERROR,
- " Livox_insert_sn_laser error , sn size != 15 ");
- }
- //填充雷达设备广播码和雷达实例的映射表.如果重复,直接报错,决不能重复创建雷达.
- if ( m_sn_laser_map.find(sn) == m_sn_laser_map.end() )
- {
- //没找到就新增一个sn--laser映射
- m_sn_laser_map[sn] = p_livox_laser;
- return Error_code::SUCCESS;
- }
- else
- {
- return Error_manager(Error_code::LIVOX_DRIVER_SN_REPEAT, Error_level::MINOR_ERROR,
- " Livox_insert_sn_laser error , sn repeat ");
- }
- return Error_code::SUCCESS;
- }
- //判断 Livox_driver 模块是否正常, 只有正常之后,才能启动或者停止扫描.
- bool Livox_driver::is_ready()
- {
- return m_init_flag;
- }
- //判断是否自动创建雷达,默认为false
- bool Livox_driver::is_aoto_create_livox()
- {
- return m_aoto_create_livox_flag;
- }
- //雷达驱动层开始取样.
- Error_manager Livox_driver::Livox_driver_start_sample(uint8_t handle)
- {
- if ( is_ready() )
- {
- LidarStartSampling(handle, livox_start_sample_callback, NULL);
- return Error_code::SUCCESS;
- }
- else
- {
- return Error_manager(Error_code::LIVOX_DRIVER_NOT_READY, Error_level::MINOR_ERROR,
- " Livox_driver is_ready error ");
- }
- return Error_code::SUCCESS;
- }
- //雷达驱动层停止取样.
- Error_manager Livox_driver::Livox_driver_stop_sample(uint8_t handle)
- {
- if ( is_ready() )
- {
- LidarStopSampling(handle, livox_stop_sample_callback, NULL);
- return Error_code::SUCCESS;
- }
- else
- {
- return Error_manager(Error_code::LIVOX_DRIVER_NOT_READY, Error_level::MINOR_ERROR,
- " Livox_driver is_ready error ");
- }
- return Error_code::SUCCESS;
- }
- //设备广播的回调函数,在 Livox_driver_init 时使用 SetBroadcastCallback() 进行配置.
- //局域网内的所有livox雷达在收到广播之后,都会调用本函数.
- //本函数由livox后台线程来执行. 每个雷达调用一次.
- void Livox_driver::livox_device_broadcast_callback(const BroadcastDeviceInfo *p_info)
- {
- LOG(INFO) << " ---livox_device_broadcast_callback start--- " ;
- if (p_info == NULL) {
- return;
- }
- //检查设备类型,后续最好确定具体的型号.
- if ( p_info->dev_type != kDeviceTypeHub )
- {
- //自动创建雷达实例,就是不从外部参数创建,在驱动层链接设备的时候,按照能够连接上的设备自动创建雷达实例.默认为false
- if ( Livox_driver::get_instance_references().is_aoto_create_livox() )
- {
- //创建雷达啊实例.
- //为 laser_manager 的 m_laser_vector 分配内存.
- //以后再写.
- ;
- }
- else//默认从外部导入参数来创建雷达. 之前在CLivoxLaser初始化是,就已经填充了 m_sn_laser_map
- {
- //按照输入参数来创建雷达.
- //校验sn码,将雷达与设备链接起来,注意了,要先校验,再连接
- std::string t_sn = p_info->broadcast_code;
- std::map<std::string, CLivoxLaser*> & t_sn_laser_map = Livox_driver::get_instance_references().m_sn_laser_map;
- if ( t_sn_laser_map.find(t_sn) != t_sn_laser_map.end() )
- {
- //找到sn了,就连接laser和设备.
- CLivoxLaser* tp_livox_laser = t_sn_laser_map[t_sn];
- uint8_t t_handle = 0;
- //连接雷达设备,连接成功之后,后台进行会分配一个新的handle,默认0到31
- bool result = AddLidarToConnect(p_info->broadcast_code, &t_handle);
- if (result == kStatusSuccess)
- {
- //SetDataCallback 函数会为每个雷达设置数据回调函数,
- //注意了,livox的后台线程不会立刻调用livox_data_callback,
- //只有在雷达使用 LidarStartSampling 启动扫描后,当有数据返回时,才会调用livox_data_callback 传回雷达数据.
- SetDataCallback(t_handle, livox_data_callback, NULL);
- //此时只是单方面连接,状态仍然是 K_DEVICE_STATE_DISCONNECT
- //需要在 livox_device_change_callback 雷达设备响应连接之后,并确认连接的结果,才会修改雷达连接状态.
- tp_livox_laser->set_handle(t_handle);
- tp_livox_laser->set_device_state( CLivoxLaser::K_DEVICE_STATE_DISCONNECT);
- //填充雷达句柄和雷达实例的映射表. handle是livox后台线程分配的唯一码.一般不会出错.
- Livox_driver::get_instance_references().m_handle_laser_map[t_handle] = tp_livox_laser;
- }
- else
- {
- //连接失败. (一般雷达设备有应答,就会连接成功.)
- tp_livox_laser->set_handle(-1);
- tp_livox_laser->set_device_state( CLivoxLaser::K_DEVICE_STATE_FAULT);
- }
- }
- }
- }
- }
- //雷达设备状态改变的回调函数,在 Livox_driver_init 时使用 SetDeviceStateUpdateCallback() 来进行配置
- //雷达设备建立连接或者断开连接,都会调用 livox_device_change_callback 来通知我们雷达状态的改变.
- //本函数由livox后台线程来执行. 每个雷达可能调用多次.
- void Livox_driver::livox_device_change_callback(const DeviceInfo *p_info, DeviceEvent type)
- {
- LOG(INFO) << " ---livox_device_change_callback start--- " ;
- if (p_info == NULL) {
- return;
- }
- uint8_t t_handle = p_info->handle;
- //检查handle的有效性
- std::map<uint8_t, CLivoxLaser*> & t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
- if ( t_handle_laser_map.find(t_handle) == t_handle_laser_map.end() )
- {
- return;
- }
- else
- {
- CLivoxLaser* tp_livox_laser = t_handle_laser_map[t_handle];
- //正常情况下,每个雷达连接都会调用两次,分别为 kEventConnect 和 kEventStateChange
- switch ( type )
- {
- case kEventConnect:
- //连接成功, 则从断连切换到连接
- if ( tp_livox_laser->get_device_state() == CLivoxLaser::K_DEVICE_STATE_DISCONNECT )
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
- tp_livox_laser->set_device_info(*p_info);
- //LidarGetExtrinsicParameter 函数会为每个雷达设置 获取欧拉角的回调函数,
- //获取欧拉角, 并转化为变换矩阵 mp_laser_matrix
- LidarGetExtrinsicParameter(t_handle, livox_get_extrinsic_parameter_callback,NULL);
- }
- break;
- case kEventDisconnect:
- //断开连接, 如果不是故障,则切换到断连
- if ( tp_livox_laser->get_device_state() != CLivoxLaser::K_DEVICE_STATE_FAULT )
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_DISCONNECT);
- tp_livox_laser->set_device_info(*p_info);
- }
- break;
- case kEventStateChange:
- //单纯的刷新雷达设备信息
- tp_livox_laser->set_device_info(*p_info);
- break;
- default:
- break;
- }
- }
- //注意了:此时并不启动雷达扫描.
- //只有当接受到扫描任务之后,由 CLivoxLaser::start_scan() 来调用 LidarStartSampling
- }
- //获取欧拉角(笛卡尔坐标)的回调函数。在 livox_device_change_callback 连接成功之后使用 LidarGetExtrinsicParameter 来设置回调函数。
- //雷达接受到查询指令后,会调用 livox_get_extrinsic_parameter_callback 来返回 LidarGetExtrinsicParameterResponse 的指针. 里面就有 欧拉角(笛卡尔坐标)
- //本函数由livox后台线程来执行. 每个雷达只需要调用一次.
- void Livox_driver::livox_get_extrinsic_parameter_callback(uint8_t status,
- uint8_t handle,
- LidarGetExtrinsicParameterResponse *response,
- void *client_data)
- {
- LOG(INFO) << " ---livox_get_extrinsic_parameter_callback start--- " ;
- if ( status == kStatusSuccess )
- {
- // 校验handle的有效性
- std::map<uint8_t, CLivoxLaser*> & t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
- if ( t_handle_laser_map.find(handle) == t_handle_laser_map.end() )
- {
- return;
- }
- else
- {
- CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
- double t_pitch = response->pitch;
- double t_roll = response->roll;
- double t_yaw = response->yaw;
- double t_x = response->x;
- double t_y = response->y;
- double t_z = response->z;
- //欧拉角转化为3*4矩阵
- t_roll = t_roll * M_PI / 180.0;
- t_pitch = t_pitch * M_PI / 180.0;
- t_yaw = t_yaw * M_PI / 180.0;
- double rotate[12] = {
- std::cos(t_pitch) * std::cos(t_yaw),
- std::sin(t_roll) * std::sin(t_pitch) * std::cos(t_yaw) - std::cos(t_roll) * std::sin(t_yaw),
- std::cos(t_roll) * std::sin(t_pitch) * std::cos(t_yaw) + std::sin(t_roll) * std::sin(t_yaw),
- t_x,
- std::cos(t_pitch) * std::sin(t_yaw),
- std::sin(t_roll) * std::sin(t_pitch) * std::sin(t_yaw) + std::cos(t_roll) * std::cos(t_yaw),
- std::cos(t_roll) * std::sin(t_pitch) * std::sin(t_yaw) - std::sin(t_roll) * std::cos(t_yaw),
- t_y,
- -std::sin(t_pitch),
- std::sin(t_roll) * std::cos(t_pitch),
- std::cos(t_roll) * std::cos(t_pitch),
- t_z
- };
- //设置变换矩阵
- tp_livox_laser->set_laser_matrix(rotate, 12);
- }
- }
- }
- //雷达设备数据返回 的回调函数. 在 livox_device_broadcast_callback 连接成功之后使用 SetDataCallback 来设置回调函数。
- //雷达在调用 LidarStartSampling 开始扫描之后,就一直调用 livox_data_callback 来返回数据.
- //雷达在调用 LidarStopSampling 停止扫描之后,就停止扫描.本函数也会停止调用.
- //本函数由livox后台线程来执行. 每帧数据都会调用一次. data里面有多个三维点.
- void Livox_driver::livox_data_callback(uint8_t handle, LivoxEthPacket *data, uint32_t data_num, void *laser)
- {
- //livox_data_callback函数会高频率的调用,为了提高2效率,这里就不验证handle的有效性了,这就需要保证前面的正确操作
- std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
- CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
- if (data && tp_livox_laser)
- {
- //判断是否采集完成
- if (tp_livox_laser->is_scan_complete())
- {
- //按照指定的帧数来采集,到点就停止.
- tp_livox_laser->stop_scan();
- //注注注注注意了:stop_scan会调用 LidarStopSampling 通知livox后台线程停止扫描.
- // 但是这个是有延迟的.会导致数据回调函数 livox_data_callback 仍然会调用3~5次.
- // 因此这里会反复调用 stop_scan
- return;
- }
- //data实际就是一个数据指针,里面包含data_num个三维点. data_num默认为100个
- LivoxRawPoint *p_point_data = (LivoxRawPoint *)data->data;
- //把雷达数据填充到livox数据缓存,此时里面是没有解析的原始数据. data_num默认为100个
- Binary_buf* data_bin = new Binary_buf((char*)p_point_data, data_num * sizeof(LivoxRawPoint));
- tp_livox_laser->push_livox_data(data_bin);
- tp_livox_laser->add_livox_frame(1);
- }
- }
- //雷达设备启动扫描的回调函数, 在 CLivoxLaser::start_scan() 需要启动扫描的时候使用 LidarStartSampling 来设置回调函数。
- //调用 LidarStartSampling 之后,livox后台进程就直接开始扫描了. 之后就会一直调用 livox_data_callback 来返回数据
- void Livox_driver::livox_start_sample_callback(uint8_t status, uint8_t handle, uint8_t response, void *data)
- {
- LOG(INFO) << " ---livox_start_sample_callback start--- " ;
- // 校验handle的有效性
- std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
- if (t_handle_laser_map.find(handle) == t_handle_laser_map.end())
- {
- return;
- }
- else
- {
- CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
- if (status == kStatusSuccess)
- {
- if (response != 0)
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
- }
- else
- {
- //返回成功并且应答没有报错,这里将雷达设备状态改为扫描中.
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_SAMPLING);
- }
- }
- else if (status == kStatusTimeout)
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
- }
- else
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_FAULT);
- }
- }
- }
- //雷达设备启动扫描的回调函数, 在 CLivoxLaser::stop_scan() 或者其他位置 需要停止的时候使用 LidarStopSampling 来设置回调函数。
- //调用 LidarStopSampling 之后,livox后台进程就直接停止扫描了. 也会停止调用 livox_data_callback
- void Livox_driver::livox_stop_sample_callback(uint8_t status, uint8_t handle, uint8_t response, void *data)
- {
- LOG(INFO) << " ---livox_stop_sample_callback start--- " ;
- // 校验handle的有效性
- std::map<uint8_t, CLivoxLaser *> &t_handle_laser_map = Livox_driver::get_instance_references().m_handle_laser_map;
- if (t_handle_laser_map.find(handle) == t_handle_laser_map.end())
- {
- return;
- }
- else
- {
- CLivoxLaser *tp_livox_laser = t_handle_laser_map[handle];
- if (status == kStatusSuccess || status == kStatusTimeout)
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_CONNECT);
- }
- else
- {
- tp_livox_laser->set_device_state(CLivoxLaser::K_DEVICE_STATE_FAULT);
- }
- }
- }
|