1 aglidar_sdk 源代码解析
1 简介
aglidar_sdk 是基于 ROS/ROS2 的雷达驱动。aglidar_sdk 依赖 ag_driver 接收和解析 MSOP/DIFOP Packet。
aglidar_sdk 的基本功能如下:
从在线雷达或 PCAP 文件得到点云,通过 ROS 主题
/aglidar_points发布。使用者可以订阅这个主题,在 rviz 中看到点云。从在线雷达得到原始的 MSOP/DIFOP Packet,通过 ROS 主题
/aglidar_packets发布。使用者可以订阅这个主题,将 Packet 记录到 rosbag 文件。从 ROS 主题
/aglidar_packets得到 MSOP/DIFOP Packet,解析得到点云,再发布到主题/aglidar_points。这里的主题
/aglidar_packets,由使用者通过回放 Packet rosbag 文件发布。
2 Source 与 Destination
如前面所说,aglidar_sdk 从在线雷达、PCAP 文件、ROS 主题这三种源得到 MSOP/DIFOP Packet,将 Packet 发布到 ROS 主题 /aglidar_packets,将点云发布到目标 - ROS 主题 /aglidar_points。
Source 定义源接口;
DestinationPointCloud 定义发送点云的目标接口;
DestinationPacket 定义发送 MSOP/DIFOP Packet 的目标接口。

2.1 DestinationPointCloud
DestinationPointCloud 定义发送点云的接口。
虚拟成员函数 init() 对 DestinationPointCloud 实例初始化;
虚拟成员函数 start() 启动实例;
虚拟成员函数 sendPointCloud() 发送 PointCloud 消息。
2.2 DestinationPacket
DestinationPacket 定义发送 MSOP/DIFOP Packet 的接口。
虚拟成员函数 init() 对 DestinationPacket 实例初始化;
虚拟成员函数 start() 启动实例;
虚拟成员函数 sendPacket() 启动发送 Packet 消息。
2.3 Source
Source 是定义源的接口。
成员
src_type_是源的类型enum SourceType { MSG_FROM_LIDAR = 1, MSG_FROM_ROS_PACKET = 2, MSG_FROM_PCAP = 3, };
成员
pc_cb_vec_[]中是一组 DestinationPointCloud 的实例。成员函数 sendPointCloud() 调用point_cb_vec_[]中的实例,发送点云消息。成员
pkt_cb_vec_[]中是一组 DestinationPacket 实例。成员函数 sendPacket() 将 Packet 消息发送到pkt_cb_vec_[]中的实例中。虚拟成员函数 init() 初始化 Source 实例。
虚拟成员函数 start() 启动实例。
虚拟成员函数 regPointCloudCallback() 将 PointCloudDestination 实例注册到
point_cb_vec_[]。虚拟成员函数 regPacketCallback() 将 PacketDestination 实例注册到
packet_cb_vec_[]。
2.4 DestinationPointCloudRos
DestinationPointCloudRos 在 ROS 主题 /aglidar_points 发布点云。
成员
pkt_pub_是 ROS 主题发布器。成员
frame_id_保存frame_id。frame_id是坐标系名字。

2.4.1 DestinationPointCloudRos::init()
init() 初始化 DestinationPointCloudRos 实例。
从 YAML 文件读入用户配置参数。
读入
frame_id,保存在成员frame_id_,默认值是aglidar。读入 ROS 主题,保存在本地变量
ros_send_topic_,默认值是/aglidar_points。
创建 ROS 主题发布器,保存在成员
pkt_sub_.
2.4.2 DestinationPointCloudRos::sendPointCloud()
sendPacket() 在 ROS 主题 /aglidar_points 发布点云。
调用
Publisher::publish()发布 ROS 格式的点云消息。
2.5 DestinationPacketRos
DestinationPacketRos 在 ROS 主题 /aglidar_packets 发布 MSOP/DIFOP Packet。
成员
pkt_sub_是 ROS 主题发布器。成员
frame_id_保存frame_id。frame_id是坐标系名字。

2.5.1 DestinationPacketRos::init()
init() 初始化 DestinationPacketRos 实例。
从 YAML 文件读入用户配置参数。
读入
frame_id,保存在成员frame_id_,默认值是aglidar读入 ROS 主题,保存在本地变量
ros_send_topic_,默认值是/aglidar_packets。
创建 ROS 主题发布器,保存在成员
pkt_sub_。
2.5.2 DestinationPacketRos::sendPacket()
sendPacket() 在 ROS 主题 /aglidar_packets 发布 MOSP/DIFOP packet。
调用
Publisher::publish()发布 ROS 格式的 Packet 消息。
2.6 SourceDriver
SourceDriver 从在线雷达和 PCAP 文件得到 MSOP/DIFOP Packet,并解析得到点云。
成员
driver_ptr_是 ag_driver 驱动的实例,也就是 LidarDriver。成员
free_point_cloud_queue_和point_cloud_queue_,分别是空闲点云的队列和待处理点云的队列。成员
point_cloud_handle_thread_是点云的处理线程。

2.6.1 SourceDriver::init()
init() 初始化 SourceDriver 实例。
读取 YAML 配置文件,得到雷达的用户配置参数。
根据源类型,也就是成员
src_type_,创建相应类型的 LidarDriver 实例,也就是成员driver_ptr_。src_type_是在 SourceDriver 中的构造函数中指定的。
调用 LidarDriver::regPointCloudCallback(),注册回调函数。这里是 getPointCloud() 和 putPointCloud()。前者给
driver_ptr_提供空闲点云,后者从driver_ptr_得到填充好的点云。注意,这里没有注册 MSOP/DIFOP Packet 的回调函数,因为 Packet 是按需获取的。这时为了避免不必要地消耗 CPU 资源。
调用 LidarDriver::init(),初始化
driver_ptr_。创建、启动点云处理线程
point_cloud_handle_thread_, 线程函数是 processPointCloud()。
2.6.2 SourceDriver::getPointCloud()
getPointCloud() 给成员 driver_ptr_ 提供空闲的点云。
优先从成员
free_point_cloud_queue_得到点云。如果得不到,分配新的点云。
2.6.3 SourceDriver::putPointCloud()
putPointCloud() 给从成员 driver_ptr_ 得到填充好的点云。
将得到的点云推送到成员
point_cloud_queue_,等待处理。
2.6.4 SourceDriver::processPointCloud()
processPointCloud() 处理点云。在 while 循环中,
从待处理点云的队列
point_cloud_queue_,得到点云,调用 sendPointCloud(),其中调用成员
pc_cb_vec_[]中的 DestinationPointCloud 实例,发送点云。回收点云,放入空闲点云的队列
free_cloud_queue_,待下次使用。
2.6.5 SourceDriver::regPacketCallback()
regPacketCallback() 用来注册 DestinationPacket。
调用 Source::regPacketCallback(),将 DestinationPacket 实例,加入成员
pkt_cb_vec_[]。如果这是首次要求 Packet(
pkt_cb_vec_[]的第1个实例),调用 LidarDriver::regPacketCallback(),向driver_ptr_注册 Packet 回调函数,开始接收 Packet。回调函数是 putPacket()。
2.6.6 SourceDriver::putPacket()
putPacket() 调用 sendPacket(),其中调用成员 pkt_cb_vec_[] 中的所有实例,发送 MSOP/DIFOP Packet。
2.7 SourcePacketRos
SourcePacketRos 在 ROS 主题 /aglidar_packets 得到 MSOP/DIFOP Packet,解析后得到点云。
SourcePacketRos 从 SourceDriver 派生,而不是直接从 Source 派生,是因为它用 SourceDriver 解析 Packet 得到点云。
成员
pkt_sub_,是 ROS 主题/aglidar_packets的订阅器。

2.7.1 SourcePacketRos::init()
init() 初始化 SourcePacketRos 实例。
调用 SourceDriver::init() 初始化成员
driver_ptr_。在 SourcePacketRos 的构造函数中,SourceType 设置为
SourceType::MSG_FROM_ROS_PACKET。这样,在 SourceDriver::init() 中,driver_ptr_的input_type就是InputType::RAW_PACKET,它通过 LidarDriver::feedPacket 接收 Packet 作为源。
解析 YAML 文件得到雷达的用户配置参数
得到接收 Packet 的主题,默认值为
/aglidar_packets。
创建 Packet 主题的订阅器,也就是成员
pkt_sub_,接收函数是 putPacket()。
2.7.2 SourcePacketRos::putPacket()
putPacket() 接收 Packet,送到 driver_ptr_ 解析。
调用 LidarDriver::decodePacket(),将 Packet 喂给
driver_ptr_。点云的接收,使用 SourceDriver 的已有实现。
3 NodeManager
NodeManager 管理 Source 实例,包括创建、初始化、启动、停止 Source。它支持多个源,但是这些源的类型必须相同。
成员
sources_[]是一个 Source 实例的数组。

3.1 NodeManager::init()
init() 初始化 NodeManger 实例。
从 config.yaml 文件得到用户配置参数
本地变量
msg_source,数据源类型本地变量
send_point_cloud_ros, 是否在 ROS 主题发送点云。本地变量
send_packet_ros,是否在 ROS 主题发送 MSOP/DIFOP packet,
在 .yaml 文件中遍历数据源。在循环中,
根据
msg_source创建 Source 实例。如果是在线雷达(
SourceType::MSG_FROM_LIDAR),创建 SourceDriver 实例并初始化, 源类型为MSG_FROM_LIDAR。如果是 PCAP 文件(
SourceType::MSG_FROM_PCAP),创建 SourceDriver 实例并初始化,源类型为MSG_FROM_PCAP。如果是 ROS 主题(
SourceType::MSG_FROM_ROS_PACKET),创建 SourcePacketRos 并初始化。SourcePacketRos 构造函数已将源类型设置为MSG_FROM_ROS_PACKET
如果在 ROS 主题发送点云(
send_point_cloud_ros = true),则创建 DestinationPointCloudRos 实例、初始化,调用 Source::regPointCloudCallback(),将它加入 Source 的pc_cb_vec_[]。如果在 ROS 主题发送 Packet(
send_packet_ros = true),则创建 DestinationPacketRos 实例、初始化,调用 Source::regPacketCallback() 将它加入 Source 的pkt_cb_vec_[]。将 Source 实例,加入成员
sources_[]。
3.2 NodeManager::start()
start() 启动成员 sources_[] 中的所有实例。