# 8 如何连接在线雷达 ## 8.1 概述 本文描述如何连接在线雷达,得到点云数据。 这个例子连接单个雷达,完整的代码可以参考示例程序 `ag_sdk/demo/demo_online.cpp`。 如果需要连接多个雷达,请参考示例程序 `demo_online_multi_lidars.cpp`。 这里的点和点云定义,引用了 `ag_driver` 工程的消息文件。 - `ag_sdk/src/ag_driver/msg/point_cloud_msg.hpp` - `ag_sdk/src/ag_driver/msg/pcl_point_cloud_msg.hpp` ## 8.2 步骤 ### 8.2.1 定义点 点是点云的基本组成单元。`ag_driver` 支持点的如下成员。 - x - 坐标 X,类型 float - y - 坐标 Y,类型 float - z - 坐标 Y,类型 float - intensity - 反射率,类型 uint8_t - timestamp - 时间戳,类型 double - ring - 所处行线的编号。以 A0 为例,编号的范围是 0-127 (从上往下),共 128 线。类型 uint8_t。 - range - 点的距离,类型 uint16_t。 如下是几个例子。 - 点的成员包括 **x, y, z, intensity** ```cpp struct PointXYZI { float x; float y; float z; uint8_t intensity; }; ``` - 如果使用 PCL 库,也可以简单使用 PCL 的点定义 **pcl::PointXYZI** ```cpp typedef pcl::PointXYZI PointXYZI; ``` - 点的成员包括 **x, y, z, intensity, timestamp, ring, range** ```cpp struct PointXYZIRT { float x; float y; float z; uint8_t intensity; double timestamp; uint8_t ring; uint16_t range; }; ``` 这些点的定义,使用者可以加入新成员,删除成员,改变成员顺序,但是不可以改变成员的类型。 ### 8.2.2 定义点云 如下是点云的定义。 ```cpp template class PointCloudT { public: typedef T_Point PointT; typedef std::vector VectorT; uint32_t height = 0; ///< Height of point cloud uint32_t width = 0; ///< Width of point cloud bool is_dense = false; ///< If is_dense is true, the point cloud does not contain NAN points double timestamp = 0.0; ///< Timestamp of point cloud uint32_t seq = 0; ///< Sequence number of message VectorT points; }; ``` 这个点云的定义,使用者可以加入新成员,改变成员顺序,但是不可以删除成员,或者改变成员的类型。 这个点云定义是一个模板类,还需要指定一个点类型作为模板参数。 ```cpp typedef PointXYZI PointT; typedef PointCloudT PointCloudMsg; ``` ### 8.2.3 定义 LidarDriver 对象 LidarDriver 类是 `ag_driver` 的接口类。这里定义一个 LidarDriver 的实例。 ```cpp int main() { LidarDriver driver; ///< Declare the driver object ... } ``` ### 8.2.4 配置LidarDriver的参数 AGDriverParam 定义 LidarDriver 的参数。这里定义 AGDriverParam 变量,并配置它。 + `InputType::ONLINE_LIDAR` 意味着从在线雷达得到 MSOP/DIFOP 包。 + `LidarType::A0` 是雷达类型 + 分别设置接收 MSOP/DIFOP Packet 的端口号。 ```cpp int main() { ... AGDriverParam param; ///< Create a parameter object param.input_type = InputType::ONLINE_LIDAR; ///< get packet from online lidar param.input_param.msop_port = 51180; ///< Set the lidar msop port number, the default is 51180 param.input_param.difop_port = 7788; ///< Set the lidar difop port number, the default is 7788 param.lidar_type = LidarType::A0; ///< Set the lidar type. Make sure this type is correct ... } ``` ### 8.2.5 定义和注册点云回调函数 + `ag_driver` 需要调用者通过回调函数,提供空闲的点云实例。这里定义这第一个点云回调函数。 ```cpp SyncQueue> free_cloud_queue; std::shared_ptr driverGetPointCloudFromCallerCallback(void) { std::shared_ptr msg = free_cloud_queue.pop(); if (msg.get() != NULL) { return msg; } return std::make_shared(); } ``` + `ag_driver` 通过回调函数,将填充好的点云返回给调用者。这里定义这第二个点云回调函数。 ```cpp SyncQueue> stuffed_cloud_queue; void driverReturnPointCloudToCallerCallback(std::shared_ptr msg) { stuffed_cloud_queue.push(msg); AG_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << AG_REND; } ``` 注意这两个回调函数都运行在 `ag_driver` 的 MSOP/DIFOP Packet 的处理线程中,所以它们不可以做太耗时的任务,否则会导致 MSOP/DIFOP Packet 不能及时处理。 + 使用者在自己的线程中,处理点云。 ```cpp void processCloud(void) { while (1) { std::shared_ptr msg = stuffed_cloud_queue.popWait(); if (msg.get() == NULL) { continue; } // Well, it is time to process the point cloud msg, even it is time-consuming. AG_MSG << "msg: " << msg->seq << " point cloud size: " << msg->points.size() << AG_REND; free_cloud_queue.push(msg); } } ``` + 注册两个点云回调函数。 ```cpp int main() { ... driver.regPointCloudCallback(driverReturnPointCloudToCallerCallback, driverReturnPointCloudToCallerCallback); ... } ``` ### 8.2.6 定义和注册异常回调函数 + `ag_driver` 检测到异常发生时,通过回调函数通知调用者。这里定义异常回调函数。 ```cpp void exceptionCallback(const Error &code) { AG_WARNING << "Error code : " << code.toString() << AG_REND; } ``` 再一次提醒,这个回调函数运行在 `ag_driver` 的线程中,所以不可以做太耗时的任务,否则可能导致 MSOP/DIFOP 包不能及时接收和处理。 + 在主函数中,注册异常回调函数。 ```cpp int main() { ... driver.regExceptionCallback(exceptionCallback); ///