# 18 点云格式与点布局 ## 18.1 概述 这里先回顾点及点云的定义,看看有哪些属性,再说明这些属性的确切含义。 ## 18.2 点云格式 ### 18.2.1 点 点的属性如下。这里列出的是 `XYZIRT` 类型,它包括了 `XYZI` 类型的所有属性。 ```cpp struct PointXYZIRT { float x; float y; float z; uint8_t intensity; uint8_t ring; double timestamp; uint16_t range; }; ``` ### 18.2.2 定义点云类型 点云的属性如下。它的成员 `points` 是一个点的 `vector`。 ```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 point cloud VectorT points; }; ``` ## 18.3 点的 ring ### 18.3.1 MEMS 雷达 MEMS 雷达每轮扫描同时扫 5 个区域,每个区域得到上下两个点,总共得到 10 个点,这 10 个点有一样的时间戳。如图中的 t1 时刻。下一时刻同理继续扫描,如 t2 时刻。 ![](./img/18_02_mems_lasers.png) ## 18.4 点的布局 ### 18.4.1 MEMS 雷达 MEMS 雷达每轮扫描同时扫 5 个区域,每个区域得到上下两个点,总共得到 10 个点,打包到一个 Block,写入 MSOP Packet。 `ag_driver` 解析 MSOP Packet 时,按照 Block 的顺序解析,所以在点云的 points 数组中,每个 Block 的 10 个点连续保存为一组。 ![](./img/18_05_mems_lasers_and_points.png) 这里以 A0 为例,说明每个区域的点布局。 A0 的每个区域 (128行 * 256列),上下两个为一组从上往下呈 Z 字型扫描。也因此,128 行只需左右扫描 64 次,即 32 个左右来回。 ![](./img/18_06_mems_points.png) ### 18.4.2 点云中点的序号 在点云的 `points` 数组中,点的序号并非按照上述顺序。而是将其按从左到右的顺序,修改点云的序号。如下图。原本一区 1x1 位置的点序号为 1,其下方点为序号 2,而序号 3 在二区的 1x1。 ![](./img/18_07_points_sn.png) 即点的序号如下图箭头顺序 ![](./img/18_08_points_sn_direction.png) ## 18.5 点的坐标系 `ag_driver` 输出的点遵循右手坐标系。 如果想改变这一点,可以改变雷达解码器 `Decoder` 的实现代码,改变坐标 (x,y,z) 的映射关系。如下是 A0 Decoder 的例子。 ```cpp //.../decoder/decoder_A0.hpp int elevation = ntohs(channel.elevation) - ANGLE_OFFSET; int azimuth = ntohs(channel.azimuth) - ANGLE_OFFSET; float x = distance * COS (elevation) * COS (azimuth); float y = distance * COS (elevation) * SIN (azimuth); float z = distance * SIN (elevation); ``` ## 18.6 点的 timestamp MEMS 雷达扫描一帧的时间是 100 毫秒。点云中的点散布在这 100 毫秒的范围内。 如何想得到比点云时间戳更高精度的时间戳,请考虑使用 `XYZIRT` 点格式,它的 `T` 是点的时间戳。 ## 18.7 点云的 timestamp 点云的时间戳可以来自第一个点,也可以来自最后一点。 ### 18.7.1 雷达的时间,还是主机的时间? `ag_driver` 有两种方式得到点云的时间戳。 + 从 MSOP Packet 解析。这个时间是雷达根据自身的时间设置。一般需要对雷达进行 PTP 时间同步,以保证雷达的时间与 PTP Master 保持一致。 + 调用操作系统的函数,得到 `ag_driver` 运行的这台主机的时间。这是 `ag_driver` 的默认配置。 `ag_driver` 的选项 `use_lidar_clock` 可以改变这个配置。 ```cpp struct AGDecoderParam // LiDAR decoder parameter { ... bool use_lidar_clock = false; // true: use LiDAR clock as timestamp; // false: use system clock as timestamp ... } ``` ### 18.7.2 取第一个点的时间,还是最后一个点的? 默认情况下,取最后一个点的时间。 `ag_driver` 的选项 `ts_first_point` 可以改变这个配置。 ```cpp struct AGDecoderParam // LiDAR decoder parameter { ... bool ts_first_point = false; // true: time-stamp point cloud with the first point; // false: with the last point; ... } ``` ### 18.7.3 UTC 时间还是本地时间? 雷达写入 MSOP Packet 的时间是 UTC 时间。 `ag_driver` 工程的 `CMakeLists.txt` 中的 `ENABLE_STAMP_WITH_LOCAL` 宏,可以根据本地的时区,将这个时间转换成本地时间。 ```cmake option(ENABLE_STAMP_WITH_LOCAL "Enable stamp point cloud with local time" OFF) ```