← 返回文章库

两代激光雷达 SDK 对着读:从 unilidar_sdk 到 unilidar_sdk2 的接口演进

最后更新 2026-08-23
⏱ 约 15 分钟 🟡 涉接线/强电
你将学到
  • 学会用「两代 SDK 对读」的方法,从目录差异里反推作者修正了哪些设计
  • 搞懂点云里每个点带什么信息,以及为什么逐点时间戳决定了点云正不正
  • 掌握拿到点云之后的标准处理链路:滤波降采样 → 配准 → 里程计 → 建图

给机器人装一颗激光雷达,难的从来不是把线插上。

线其实很好插——网线或者串口线,接上、配好 IP,跑一下官方示例,终端里唰唰唰开始滚一堆浮点数。到这一步大部分人都能在半小时内做到。真正让人卡住的是下一秒的问题:这堆点我该拿它干什么?

它不是图像,你没法直接「看」;它每帧几千个点,一秒钟来好几帧,你也没法一个个看;它还是在动的载体上采的,你把两帧叠一起会发现墙是斜的、桌子是重影的。示例程序打印完就退出了,README 到此为止,剩下的路没人告诉你怎么走。

这篇我们用一个有点特别的角度切进去:把同一家公司同一类产品的两代 SDK 摆在一起读

这是我认为性价比最高的学习材料之一。因为一份孤立的 SDK 只能告诉你「作者是这么设计的」,而两代放一起,它会额外告诉你一件更值钱的事——作者后来觉得哪里设计得不好。第二代里被删掉的东西、被重写的东西、被从文档挪进代码的东西,每一处都是一次真实的返工记录。这种信息你在任何教程里都读不到。

先把边界说清楚:这篇文章基于两个公开仓库的文档与代码结构,没有真机验证。所有结论都来自仓库里的 README、目录树和文件名。凡是需要接上硬件才能知道的事——测距表现、点云密度、实际延迟——我一概不写。

第一代:unilidar_sdk 的样子

unilidar_sdk 对应 L1 雷达,BSD-3-Clause 许可。仓库顶层是很标准的三段式:

unitree_lidar_sdk/     —— 原生 C++ SDK
unitree_lidar_ros/     —— ROS 包
unitree_lidar_ros2/    —— ROS2 包

README 里也就是这么说的:想直接用原生 SDK 看第一个,用 ROS 看第二个,用 ROS2 看第三个。这个三选一的结构本身很值得肯定——它承认了一个现实:雷达的使用者分成两拨人,一拨在写自己的裸程序,一拨活在 ROS 生态里,硬把后者按到前者的接口上是自找麻烦。

顶层 README 的内容不多,只有两节:介绍,和坐标系定义。坐标系那节写得相当细致:右手系;点云坐标系原点在雷达底部安装面的中心;+X 轴与底部出线方向相反;+Y 轴由 +X 轴逆时针旋转 90 度得到;+Z 轴垂直向上。IMU 坐标系三轴与点云坐标系平行,只有原点平移,平移量在 README 里给了具体数值。

一份传感器 SDK 的 README 第一件事就讲坐标系,这是内行做法。 因为坐标系是所有后续算法的前提:你要把雷达装到机器人身上,就得知道「雷达坐标系原点到底在这个铁疙瘩的哪个位置」;你要做激光和 IMU 的融合,就得知道这两个坐标系之间差了多少。这两个数写错,后面全盘皆错,而且错得非常隐蔽——点云看着挺正常,就是建出来的图慢慢地歪。

真正有意思的在 unitree_lidar_sdk/include/ 里。除了几个常规头文件,这里塞进了一整套 MAVLink:

include/mavlink/
    checksum.h
    mavlink_conversions.h
    mavlink_helpers.h
    mavlink_types.h
    protocol.h
    SysMavlink/
        SysMavlink.h
        mavlink_msg_config_lidar_working_mode.h
        mavlink_msg_config_led_ring_table_packet.h
        mavlink_msg_device_command.h
        mavlink_msg_device_request_data.h
        mavlink_msg_ret_imu_attitude_data_packet.h
        mavlink_msg_ret_lidar_auxiliary_data_packet.h
        mavlink_msg_ret_lidar_distance_data_packet.h
        mavlink_msg_ret_lidar_time_sync_data.h
        mavlink_msg_ret_lidar_version.h

MAVLink 是无人机圈子里用得非常广的一套轻量通信协议。第一代雷达选它做上下位机的报文格式,不难理解——协议现成、代码生成器现成、跨平台解析器现成,省掉自己定义一套报文的所有麻烦。仓库里还专门有一份 HowToParsePointCloudAndIMUDataFromMavLinkMessages.md,教你怎么从 MAVLink 报文里把点云和 IMU 数据抠出来。

顺带一提,仓库索引里 unilidar_sdk 的语言字段是 C,而 unilidar_sdk2C++。看到这里我原本以为是 SDK 换了语言,但 L1 的 README 明明白白写着自己提供的是「原生 C++ SDK」。我猜——这只是猜测,仓库里没有任何说明——语言字段是按代码行数统计的,而 include/mavlink/ 下那一大堆 C 头文件体量太大,把整个仓库的统计结果带偏了。这个小插曲本身也是个提醒:仓库元数据上的语言标签只反映行数分布,不反映作者的设计意图,别拿它当结论。

再看示例目录:

unitree_lidar_sdk/examples/
    example_lidar.cpp
    example_lidar_udp.cpp
    unilidar_publisher_udp.cpp
    unilidar_subscriber_udp.cpp
    unilidar_subcriber_udp.py

前两个是常规的「连上雷达、取数据、打印」。后三个是一组:一个 UDP 发布端,一个 C++ 订阅端,一个 Python 订阅端。这组示例透露的信息量不小——它演示的是把雷达数据转发出去、让别的进程(甚至别的语言)来消费的模式。这在实际项目里非常常见:雷达驱动跑在贴着硬件的那台小板子上,算法跑在另一台机器上,中间用 UDP 打通。官方直接给了范例,省事。

(那个 Python 文件名里 subcriber 少了个 s,是个笔误。这种细节没什么技术含量,但它能帮你确认一件事:你读的是真实的工程仓库,不是被打磨过的展示品。)

最后,lib/ 下面躺着 aarch64x86_64 两个预编译静态库,另外还有一个 win64/ 目录,里面是三个 MinGW 运行时 DLL 加一个 Windows 可执行程序。核心实现是闭源的,你拿到的是静态库加头文件。

第二代:unilidar_sdk2 改了什么

unilidar_sdk2 对应 L2 雷达,同样是 BSD-3-Clause,顶层结构一模一样

unitree_lidar_sdk/     —— 原生 C++ SDK
unitree_lidar_ros/     —— ROS 包
unitree_lidar_ros2/    —— ROS2 包

三选一的框架保留了,ROS / ROS2 两个包的内部文件构成也几乎逐一对应(同样的 CMakeLists.txtconfig/launch/rviz/src/xxx_node.cpp)。坐标系定义也原样继承:还是右手系,原点还是在底部安装面中心,+X 还是与出线方向相反,IMU 相对点云坐标系的平移量数值与第一代 README 里给的完全相同,第二代还额外把它写成了一个 4×4 齐次变换矩阵 T_LI

框架不动、坐标系不动——这本身就是一条有价值的信息:作者认为第一代的顶层拆分是对的,不需要推倒重来。接下来看它动了哪儿。

第二代 include/ 目录长这样:

unitree_lidar_sdk/include/
    example.h
    udp_handler.h
    unitree_lidar_protocol.h
    unitree_lidar_sdk.h
    unitree_lidar_sdk_config.h
    unitree_lidar_sdk_pcl.h
    unitree_lidar_utilities.h

mavlink/ 整个不见了,取而代之的是 unitree_lidar_protocol.hunitree_lidar_utilities.h。README 第 6 节说得很直接:如果你想自己解析原始的网口或串口数据,参考用户手册以及这两个头文件里的自定义通信协议

这是两代之间最大的一处结构性改动。我不去猜作者内部的权衡,但从工程角度看,从通用协议换成自定义协议通常是这么个取舍:通用协议前期省事,代价是报文格式受制于人、字段塞得别扭、还得背一整套你其实只用到十分之一的代码;自研协议前期贵,但报文可以按自家数据的真实形状来切,而且整个仓库瘦了一大圈。第一代 include/ 里 MAVLink 占了二十来个文件,第二代整个 include 只剩七个。

改动二:工作模式收敛成一个整数

第二代的 README 里多了一节,讲怎么配置工作模式,并且给出了 SDK 头文件里的接口原型:

virtual void setLidarWorkMode(uint32_t mode) = 0;

这一行信息量很大。virtual ... = 0 是纯虚函数,说明 unitree_lidar_sdk.h 里定义的是一个抽象接口类,真正的实现藏在那个静态库里,通过工厂函数拿实例。这是闭源 SDK 的标准做法:头文件只暴露契约,实现随时可以改而不破坏调用方。

而模式本身,被设计成一个 uint32_t位域——每一位控制一项开关。README 给了完整的位表:第 0 位在标准视场与广角视场之间切换,第 1 位在三维测量与二维测量之间切换,第 2 位控制 IMU 的启用与关闭,第 3 位在网口模式与串口模式之间切换,第 4 位决定上电后是自动开始旋转还是等待启动指令,第 5 位往后保留。

位域这个设计有它的道理:这些开关彼此独立、组合起来是笛卡尔积,如果给每个组合定义一个枚举值会爆炸。而且预留了大量位,以后加功能不用改接口签名。

README 还贴心地把最常见的情况直接告诉你:常规用法是标准视场 + 三维测量 + 启用 IMU + 上电自启,也就是这几位全为 0,你唯一要决定的是走网口还是走串口——网口填 0,串口把第 3 位置 1(也就是整数 8)。出厂默认是 0,即网口模式。

把「你只需要关心哪一位」写进文档,比把位表列全更重要。 位域接口最劝退的地方就是新手不知道该填几,一句「常规情况填 0 或 8」直接把门槛铲平了。

改动三:配置动作从文档变成了可执行程序

对比两代的示例目录,这处变化很明显:

第一代 examples/                    第二代 examples/
    example_lidar.cpp                   example_lidar_udp.cpp
    example_lidar_udp.cpp               example_lidar_serial.cpp
    unilidar_publisher_udp.cpp          set_ip_address.cpp
    unilidar_subscriber_udp.cpp         set_to_serial_mode.cpp
    unilidar_subcriber_udp.py           set_to_udp_mode.cpp

第二代砍掉了那组 UDP 转发示例,加进来三个一次性配置工具:改 IP、切串口模式、切网口模式。

这个变化背后的场景其实挺具体的。README 第 3.4 节描述了一个真实的鸡生蛋问题:雷达出厂是网口模式,你想用串口,就得先用网口连上去把它改成串口模式,然后断电重启。如果这个操作只能靠上位机软件完成,那么一台没装上位机软件的 Linux 开发机就被卡住了。给一个 set_to_serial_mode 可执行文件,这个死结就解开了。

同理,set_ip_address 解的是另一个死结:多台雷达接在同一个网段上会撞 IP,而改 IP 这件事按第一代 README 的说法要「参考用户手册使用上位机」。

把只在文档里描述的操作,变成仓库里能编译出来的一个可执行文件——这是 SDK 成熟度的一个可靠指标。 你可以拿这条去衡量任何硬件 SDK:凡是文档里写「请使用我们的上位机软件完成 XX」而代码里没有对应接口的地方,都是将来你自动化流程时会撞的墙。

改动四:README 从索引变成了手册

第一代顶层 README 只有介绍和坐标系两节,具体步骤指向三个子目录里各自的 README。第二代把主干流程全部提到了顶层,一路写到底:

  • C++ SDK 怎么编译(mkdir build && cd build && cmake .. && make
  • 网口模式怎么跑:先把网卡配到雷达的默认目标地址 192.168.1.2,再跑 example_lidar_udp
  • 串口模式怎么跑:默认串口名 /dev/ttyACM0,跑 example_lidar_serial
  • ROS 包怎么用:依赖 PCL 与 ROS,配置文件在 unitree_lidar_ros/src/unitree_lidar_ros/config/config.yamlcatkin_make 编译,roslaunch unitree_lidar_ros run.launch 运行
  • ROS2 包怎么用:colcon build 编译,ros2 launch unitree_lidar_ros2 launch.py 运行,配置项直接写在 launch.py
  • 默认话题名与坐标系名:点云走 unilidar/cloud(坐标系 unilidar_lidar),IMU 走 unilidar/imu(坐标系 unilidar_imu
  • 想自己解原始报文,去看那两个协议头文件

README 里给的验证环境是 Ubuntu 20.04、ROS noetic、PCL-1.10 这一套(以官方仓库当前文档为准)。

注意 ROS 和 ROS2 两个包在「配置写在哪」上的分歧:ROS 版有独立的 config.yaml,ROS2 版则把参数直接放进 launch.py。这不是随意的——ROS2 的 launch 文件本来就是 Python 脚本,参数天然可以写在里面;这是两代 ROS 各自习惯的产物,不是设计失误。

最后一个细微差别:第二代仓库里多了个 VERSION.md,静态库的名字也从 libunitree_lidar_sdk.a 改成了 libunilidar_sdk2.a。另外,第二代仓库快照里没有 win64/ 目录——这只说明这个仓库没有放 Windows 预编译产物,不能反推成「第二代用不了 Windows」,仓库里没写的事我们不猜。

两代对读,能学到的通用规律

把上面几条抽象一层,其实就是一套硬件 SDK 该怎么设计的清单:

  1. 按使用者分群拆包,别搞大一统。 原生 SDK / ROS / ROS2 三条线并行,各自能独立编译。裸程序用户不用装 ROS,ROS 用户不用自己封节点。
  2. 坐标系写在最前面,写到能照着装机的程度。 原点在哪个物理面上、轴指向哪、和 IMU 差多少——这是传感器 SDK 的第一性文档。
  3. 协议层要能被绕过。 两代都提供了原始报文解析的入口。这意味着当官方库在你的平台上编不过时,你还有自己撸一个解析器的退路。给用户留后路的 SDK 才敢用。
  4. 配置项要有代码接口,不能只有上位机。 上位机是给现场调试用的,代码接口才是给产线和自动化用的。
  5. 常见组合要在文档里给出默认答案。 位域很灵活,但用户第一次用需要的是「填 0」这三个字。
  6. 示例要覆盖真实的部署形态。 第一代给了 UDP 转发的发布/订阅示例,第二代给了配置工具——它们都不是「演示 API 怎么调」,而是「演示这东西在项目里怎么用」。

拿到点云之后:先补三块通用知识

回到开头那个问题。示例跑通了,终端里滚出来的到底是什么?

L2 的 README 里贴了示例程序的输出格式,一帧点云带着时间戳和帧号,每个点是这样一组字段:

(x, y, z, intensity, time, ring)

这六个字段几乎就是点云的标准配置,值得逐个说一下。

激光雷达是怎么测出距离的

主流机械式激光雷达用的是飞行时间法(Time of Flight,ToF):发射一个极短的激光脉冲,打到物体上反射回来,测量往返耗时,乘以光速再除以二,就是距离。原理简单到一句话说完,工程上难在光速太快——要分辨厘米级的距离差,计时精度得做到皮秒量级,这也是激光雷达成本的主要去处之一。

有了距离还不够,还得知道方向。机械旋转式的做法是让激光头绕轴转起来,同时记录当前的角度;固态方案则不靠机械转动,而是用其他方式偏转光束或者用面阵接收。两条路线的差别在使用者这一侧很直接:机械式有运动部件,寿命和振动是要考虑的;固态没有旋转机构,但视场的组织方式往往和机械式不一样,扫描图案也可能不是均匀的一圈一圈。

距离加角度,做个球坐标到直角坐标的转换,就得到了 (x, y, z)注意这个转换是在雷达自己的坐标系里做的——这就是为什么前面那节坐标系定义那么重要。

每个点带的其他三个字段

intensity(强度)是回波的能量强弱。同样距离上,白墙的回波比黑色吸光材料强得多。强度信息在很多算法里被用来做材质区分、标线识别、或者剔除低置信度的点。

ring(线号)标记这个点属于第几条扫描线。多线雷达在垂直方向上有多个激光发射接收单元,同一线上的点在空间里是连续的,这个连续性对特征提取非常有用——很多经典的激光里程计算法就是靠「同一 ring 上相邻点的曲率」来判断这里是平面还是边角。

time 是这个点相对于本帧起始时刻的时间偏移。这是整个点云结构里最容易被忽略、也最要命的一个字段。

为什么逐点时间戳决定了点云正不正

想象一下机器人一边往前走一边扫描。雷达转一圈需要时间,在这段时间里,载体已经往前移动了一段距离。

结果是:这一「帧」点云里的点,根本不是在同一个位置采集的。开始扫描时雷达在 A 点,扫完时它已经到了 B 点,中间每一个点对应的观测原点都不一样。你却把它们一股脑塞进同一个坐标系里当成一帧——这就是运动畸变(motion distortion)。

表现出来是什么样?直的墙变成弯的,柱子变成斜的,走廊两侧不平行。载体运动越快、旋转越剧烈,畸变越严重。旋转比平移更致命,因为一点点角速度在远处会被距离放大成很大的位置偏差。

修正的办法叫运动补偿(也叫去畸变):用其他信息源估计出在这一帧扫描期间载体的运动轨迹,然后把每个点按它自己的 time 反推回该帧起始时刻的位置。

这里的「其他信息源」通常就是 IMU——它的输出频率远高于点云帧率,可以在两帧之间把姿态变化积分出来。这也解释了为什么这颗雷达要把 IMU 集成进去,并且在 README 里花那么大篇幅讲两个坐标系之间的平移量。 没有内置 IMU、没有精确外参,运动补偿这一步就得你自己想办法。

顺便说,工作模式位表里那个「IMU 启用 / 禁用」的开关,现在你就知道它意味着什么了:关掉它,你就自己去解决畸变问题。

点云的典型处理链路

理清了单帧点云,再来看完整的处理流水线。这条链路在绝大多数机器人项目里长得都差不多:

原始点云
   ↓  滤波与降采样
   ↓  (去离群点、体素栅格降采样、按距离/强度裁剪)
去噪后的点云
   ↓  运动补偿(用 IMU 把每个点拉回同一时刻)
   ↓
配准(Registration)
   ↓  (把当前帧对齐到上一帧或已有地图,ICP / NDT / 特征匹配)
帧间位姿变换
   ↓
里程计(Odometry)
   ↓  (把一连串帧间变换累积成轨迹)
自身轨迹
   ↓
建图(Mapping)
   ↓  (把所有帧按估计出的位姿拼进同一个坐标系)
点云地图

几点值得强调:

滤波降采样不是可选项。 原始点云的数据量对后续算法是很重的负担,而且相邻点之间信息高度冗余。体素栅格降采样——把空间切成小立方格,每格只保留一个代表点——是最常用的手段,它同时解决了数据量和密度不均两个问题。

配准是整条链路的核心。 它回答的问题是:这两坨点云之间差了一个什么样的旋转加平移?ICP(迭代最近点)是最经典的做法,思路是反复地「给每个点找最近邻、算出最优变换、应用、再找」。NDT 则把空间划成格子、用格子内点的正态分布来做匹配,对初值不那么敏感。

里程计的本质是累积,而累积必然漂移。 每一次帧间配准都有微小误差,一路加下去就会越走越偏。这就是为什么完整的 SLAM 系统还需要回环检测和后端优化——发现「我回到走过的地方了」,然后把整条轨迹拉回来。

建图和里程计是一体两面。 你需要位姿才能把点云拼成地图,又需要地图来给下一帧配准提供参考。所以实际系统里这两步是交织进行的。

如果你之前做过基于超声波或红外的避障小车,可以对比一下感受这里的量级差异:那类传感器给你的是「前方 X 厘米有东西」,而激光雷达给你的是整个空间的三维结构。信息量的跃升带来的是处理复杂度的跃升。想系统了解各类传感器的取舍,可以翻传感器图鉴

官方给的下一步:point_lio_unilidar

链路讲完了,那实际项目里这些步骤自己写吗?大部分情况下不用。

point_lio_unilidar 这个仓库,做的就是把上面「运动补偿 → 配准 → 里程计 → 建图」整条链路打包好。README 里说得很明确:它把 Point-LIO 这个激光惯性里程计算法适配到了自家的 L1 和 L2 雷达上。

LIO 是 Lidar-Inertial Odometry 的缩写,激光惯性里程计。 名字里的两个词就是它的两条输入:激光负责提供环境结构,惯性(IMU)负责提供高频的运动估计。两者融合的逻辑很自然——IMU 频率高、短时间内准,但积分会飘;激光频率低,但能提供绝对的环境约束把飘拉回来。取长补短。

看这个仓库的目录,能读出它的组织方式:

config/
    unilidar_l1.yaml      —— L1 的配置
    unilidar_l2.yaml      —— L2 的配置
    avia.yaml  horizon.yaml  ouster64.yaml  velody16.yaml
launch/
    mapping_unilidar_l1.launch
    mapping_unilidar_l2.launch
    mapping_avia.launch  mapping_horizon.launch  ...
src/
    preprocess.cpp        —— 点云预处理
    IMU_Processing.hpp    —— IMU 处理
    Estimator.cpp         —— 状态估计
    laserMapping.cpp      —— 建图主流程
include/
    ikd-Tree/             —— 增量式 kd 树
    IKFoM/                —— 流形上的迭代卡尔曼滤波工具箱
    FOV_Checker/

config/launch/ 里同时躺着好几个其他厂牌雷达的配置——这说明它是在上游 Point-LIO 的基础上加了两组自家雷达的适配配置,而不是从零重写。这种做法很实在:算法主干跟着上游走,自己只维护「我这颗雷达的点云长什么样」这部分差异。差异集中在 preprocess.cpp(怎么把不同雷达的点云格式解析成算法内部结构)和那两个 yaml 里。

include/ 里的两样东西值得记住名字。ikd-Tree 是增量式 kd 树:kd 树用来做最近邻查询,而配准每一步都要疯狂查最近邻;「增量式」意味着新点进来时不用整棵树重建,这对实时性是决定性的。IKFoM 是在流形上做迭代卡尔曼滤波的工具箱——机器人的姿态属于旋转群,不是普通的向量空间,直接用欧氏空间的卡尔曼滤波会出问题,这类工具箱专门处理这件事。

在运行方式上,两个仓库是通过 ROS 话题连起来的。README 里给的步骤是:先起 unilidar_sdk 那边的 ROS 节点(用不带 rviz 的那个 launch),让它把点云和 IMU 发到话题上;再起 Point-LIO 的节点去订阅。跑完之后,缓存的点云地图会存成一个 .pcd 文件,可以用 pcl_viewer 打开看。

这就是前面「三选一」结构的价值所在:算法侧只认 ROS 话题,完全不需要知道雷达是网口还是串口、协议是 MAVLink 还是自定义的。驱动包把硬件差异吃掉了,上层算法才能在两代雷达之间无痛切换——这也是 mapping_unilidar_l1.launchmapping_unilidar_l2.launch 能长得几乎一样的原因。

README 里还有两条实操提示。一是算法启动的最初几秒要让雷达保持静止,以便 IMU 完成初始化——几乎所有惯性融合算法都有这个要求,因为它需要在静止状态下估计零偏和重力方向。二是官方提供了录好的 rosbag 数据集,没有雷达也能先把算法跑一遍。

(读文档时的一个小发现:这个 README 里 L2 那节的准备步骤,克隆的是 unilidar_sdk2,但下一行 cd 进的却是 unilidar_sdk 目录,是个复制粘贴留下的笔误。照着敲会失败,改一下路径即可。这类小毛病在开源仓库里很常见,遇到别慌,也别怀疑自己。)

关于 Point-LIO 的算法细节、状态量怎么定义、为什么它对剧烈运动更鲁棒,我们在足式机器人上的 Point-LIO 实践那篇里单独展开。

换个角度看选型:开发者真正会踩的坑

选雷达的文章满大街都是,但它们几乎都在比参数。参数当然重要,我在这篇里一个都不写,不是因为它们不重要,而是因为参数你迟早会去官网查,而下面这几条没人提醒你

第一,SDK 的接口是不是清晰。 具体地看:有没有抽象接口类、配置项是不是有代码接口、错误怎么返回、能不能同时开多台。一个只给你 getPointCloud() 的 SDK 和一个把工作模式、IP、通信方式全暴露出来的 SDK,在项目后期完全是两种命运。

第二,有没有官方维护的 ROS / ROS2 包。 这条几乎是硬指标。如果没有,你要么自己写节点封装(还得处理时间戳对齐、坐标系发布、PCL 类型转换这些琐事),要么在社区找一个第三方的、维护状态未知的包。这两代 SDK 都自带 ROS 和 ROS2 两套包,省掉的是实打实的一周工作量。

第三,示例是不是覆盖了真实部署形态。 只有一个「打印点云」的示例,说明作者只考虑了「你验证它能通」;有配置工具、有转发示例、有 rosbag 数据集,说明作者考虑过你要把它装到机器人上、量产、和别人的模块对接。

第四,社区算法的适配情况。 这一条决定你能不能站在别人肩膀上。一颗雷达如果主流的开源 SLAM / LIO 算法都有现成配置,你的起点就是「跑通并调参」;如果没有,你的起点是「先写点云解析和适配层」。官方主动去适配一个成熟算法并开源出来,是很有含金量的一件事——它等于把最难的那一段路铺好了。

第五,原始协议是不是可解析。 这是保险条款。哪怕预编译库在你的架构上用不了、哪怕你要跑在没有操作系统的裸机上,只要协议文档和解析头文件在,你就还有路。

收束

这篇从头到尾没接过硬件,只是把两个仓库的目录树摊开对着看,就读出了不少东西。留三个可以带走的点:

一,两代对读是逆向工程设计意图的捷径。 第一代到第二代之间,MAVLink 换成了自研协议、UDP 转发示例换成了配置工具、README 从索引变成了手册。每一处改动都是一次真实的返工,比任何设计文档都诚实。以后你接手任何有历史版本的库,第一件事就该是把新旧目录摆一起。

二,点云里最不起眼的 time 字段,决定了你的点云正不正。 机器人在动,一帧扫描期间载体已经位移,不做运动补偿建出来的图会慢慢歪掉。这也是雷达要内置 IMU、SDK 要把外参写进 README 的根本原因。

三,拿到点云的下一步不是自己写算法,是接一条成熟的链路。 滤波降采样、配准、里程计、建图这四步每一步都有几十年的积累,官方适配好的 LIO 方案是最省力的起点。你的价值在链路之上——用建出来的图去做什么,而不是重写一遍 ICP。

想接着往下看这套体系怎么和整机的控制、通信打通,可以回到这一卷的目录;如果你手上还没有真实硬件,先在机器人卷里用便宜的传感器把感知这条链路的直觉建立起来,回头再读这些工业级 SDK,会顺很多。

📄 来源 / 自校链接

本文为公开资料整理,非亲测。关键参数与代码请结合实物与下列官方来源验证。

内容有错、看不懂、或想看下一期?告诉我们 →

本文为公开资料的学习整理,非亲测。涉接线/花钱/合规的步骤请结合实物与官方最新资料验证,风险自负。见免责声明