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

    struct PointXYZI
    {
      float x;
      float y;
      float z;
      uint8_t intensity;
    };
    
  • 如果使用 PCL 库,也可以简单使用 PCL 的点定义 pcl::PointXYZI

    typedef pcl::PointXYZI PointXYZI; 
    
  • 点的成员包括 x, y, z, intensity, timestamp, ring, range

    struct PointXYZIRT
    {
      float x;
      float y;
      float z;
      uint8_t intensity;
      double timestamp;
      uint8_t ring;
      uint16_t range;
    };
    

这些点的定义,使用者可以加入新成员,删除成员,改变成员顺序,但是不可以改变成员的类型。

8.2.2 定义点云

如下是点云的定义。

template <typename T_Point>
class PointCloudT
{
public:
  typedef T_Point PointT;
  typedef std::vector<PointT> 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;
};

这个点云的定义,使用者可以加入新成员,改变成员顺序,但是不可以删除成员,或者改变成员的类型。

这个点云定义是一个模板类,还需要指定一个点类型作为模板参数。

typedef PointXYZI PointT;
typedef PointCloudT<PointT> PointCloudMsg;

8.2.3 定义 LidarDriver 对象

LidarDriver 类是 ag_driver 的接口类。这里定义一个 LidarDriver 的实例。

int main()
{
  LidarDriver<PointCloudMsg> driver;          ///< Declare the driver object
  ...
}

8.2.4 配置LidarDriver的参数

AGDriverParam 定义 LidarDriver 的参数。这里定义 AGDriverParam 变量,并配置它。

  • InputType::ONLINE_LIDAR 意味着从在线雷达得到 MSOP/DIFOP 包。

  • LidarType::A0 是雷达类型

  • 分别设置接收 MSOP/DIFOP Packet 的端口号。

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 需要调用者通过回调函数,提供空闲的点云实例。这里定义这第一个点云回调函数。

SyncQueue<std::shared_ptr<PointCloudMsg>> free_cloud_queue;

std::shared_ptr<PointCloudMsg> driverGetPointCloudFromCallerCallback(void)
{
  std::shared_ptr<PointCloudMsg> msg = free_cloud_queue.pop();
  if (msg.get() != NULL)
  {
    return msg;
  }

  return std::make_shared<PointCloudMsg>();
}
  • ag_driver 通过回调函数,将填充好的点云返回给调用者。这里定义这第二个点云回调函数。

SyncQueue<std::shared_ptr<PointCloudMsg>> stuffed_cloud_queue;

void driverReturnPointCloudToCallerCallback(std::shared_ptr<PointCloudMsg> 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 不能及时处理。

  • 使用者在自己的线程中,处理点云。

void processCloud(void)
{
  while (1)
  {
    std::shared_ptr<PointCloudMsg> 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);
  }
}
  • 注册两个点云回调函数。

int main()
{
  ...
  driver.regPointCloudCallback(driverReturnPointCloudToCallerCallback, 
                               driverReturnPointCloudToCallerCallback);
  ...
}

8.2.6 定义和注册异常回调函数

  • ag_driver 检测到异常发生时,通过回调函数通知调用者。这里定义异常回调函数。

void exceptionCallback(const Error &code)
{
  AG_WARNING << "Error code : " << code.toString() << AG_REND;
}

再一次提醒,这个回调函数运行在 ag_driver 的线程中,所以不可以做太耗时的任务,否则可能导致 MSOP/DIFOP 包不能及时接收和处理。

  • 在主函数中,注册异常回调函数。

int main()
{
  ...
  driver.regExceptionCallback(exceptionCallback);  ///<Register the exception callback function
  ...
}

8.2.7 初始化 LidarDriver 对象

按照 AGDriverParam 指定的配置,初始化 LidarDriver 对象。

int main()
{
  ...
  if (!driver.init(param))  ///< Call the init function with the parameter
  {
    AG_ERROR << "Driver Initialize Error..." << AG_REND;
    return -1;
  }
  ...
}

8.2.8 启动 LidarDriver

启动 LidarDriver 对象。

int main()
{
  ...
  driver.start();  ///< Call the start function. The driver thread will start
  ...
}

8.2.9 运行

编译 demo_online 并运行。您应该可以看到类似如下点云打印了。

Asensing Lidar-Driver Linux online demo start......
msg: 0 point cloud size: 96
msg: 1 point cloud size: 28800
msg: 2 point cloud size: 28800
msg: 3 point cloud size: 28800
msg: 4 point cloud size: 28800
msg: 5 point cloud size: 28800
msg: 6 point cloud size: 28800
msg: 7 point cloud size: 28832
msg: 8 point cloud size: 28800
msg: 9 point cloud size: 28800

如果您没有看到,可能是因为网络选项的配置不正确,请参考在线雷达-高级主题,获得正确的配置。