第 5.8 节

pointcloud_to_laserscan(三维地形点云转二维扫描)

作者:董佳辉

pointcloud_to_laserscan(三维地形点云转二维扫描)

实车先看:哪些三维点会被画进二维地图

当前建图链为:

terrain_map_ext(odom系地形点)
    → pointcloud_to_laserscan(转到底盘系,筛选并投影)
    → obstacle_scan(base_footprint系二维扫描)
    → slam_toolbox(二维建图)

这个节点在整套 slam:=True 启动中使用。已有地图导航 slam:=False 不走这个入口;当前局部/全局代价地图直接使用地形点云,并非依靠这份二维扫描更新障碍。

扫描以 base_footprint 原点为中心,方向按底盘系表达。 输入虽来自MID360,却不是原始雷达点云,而是 terrain_map_ext;其中 intensity 已被改成相对估计地面的高差,单位m,不是雷达反射强度。

实车上看到的现象先看哪些参数或条件怎样理解
正常坡面被画成障碍地形估计、min_intensity当前10 cm高差门槛可能接纳被误估成凸起/凹陷的坡点;降低门槛也会接纳更多小障碍和噪声
下坡较远处的点消失min_height、target_frame方向这里重新要求底盘系z不低于-0.5 m;上游按距离放宽高度范围,并不保证能通过这里
高处横梁、悬空结构被画成墙max_height、max_intensity、上游地形筛选通过筛选的点会压到二维平面;目前z上限6 m很宽,是否妨碍车辆通行需按实车包络判断
车旁小障碍没进入地图range_min、min_intensity 和上游输出0.3 m内的点或高差不足10 cm的点会被删;本节点也不能恢复上游丢掉的点
小杆或障碍边缘漏掉angle_increment、点云密度当前约0.5°一个角度格,每格只留最近合格点;分格更细不能补出原本没有的回波
以为扫描固定20 Hz,实际不是scan_time、输入点云周期0.05 s只写入消息,发布由点云回调驱动,没有20 Hz发布定时器
有点云却没有扫描下游订阅、坐标TF、字段没人订阅扫描时不接点云;当前需要有效TF以及XYZ、intensity字段

参数

ros__parameters(ROS节点参数)

参数表

前13项对应当前实物 YAML;仿真仅 use_sim_time=True。另外列出源码默认的3项,明确TF等待与无回波编码的条件。

pointcloud_to_laserscan:
  ros__parameters:
    use_sim_time: False
    target_frame: base_footprint
    min_height: -0.5
    max_height: 6.0
    min_intensity: 0.1
    max_intensity: 2.0
    angle_min: -3.1415926                   # 约 -π,-180°
    angle_max: 3.1415926                    # 约 +π,+180°
    angle_increment: 0.00872665             # 约 π/360,0.5°
    scan_time: 0.05
    range_min: 0.3
    range_max: 10.0
    use_inf: true

原配置把 angle_min/max 的注释写成 ±π/2,与数值不符;当前数值对应约±π,扫描角域约360°,不是180°。

参数名当前值单位实车处理建议作用调大效果调小效果生效条件/备注
use_sim_timeFalse;仿真 True不适用,布尔上实物前必须核对并正确配置控制节点与TF缓存的ROS时间不适用不适用输出沿用输入点云时间戳,不重新按当前时间打点;应与上游和机械TF使用相同时间域
target_framebase_footprint不适用,坐标系名称上实物前必须核对并正确配置先把点坐标转到此系,再按它的原点和轴投影不适用不适用当前是底盘系虚拟扫描;不是只改frame_id;TF不可用时无法正常转换;空值保留输入系
min_height-0.5m,目标系z下界上实物前必须核对并正确配置删除低于底盘原点下方0.5 m的点下界上移,更多低处/下坡点被删下界下移,保留更多低处点,也可能引入无关低层固定高度窗口,不随距离扩大;不是相对局部地面的高差
max_height6.0m,目标系z上界上实物前必须核对并正确配置删除高于底盘原点上方6 m的点保留更多高处点,悬空结构也可能进入二维图更多高处点被删,也可能丢掉障碍的可见部分不是整车高度;点还必须通过高差门槛与上游规则,调大不保证出现更多点
min_intensity0.1m,当前地形点的高差下界实物运行时重点观察,有问题再针对性调整只接纳相对估计地面高差至少10 cm的点忽略更多小高差,可能减少坡面误判,也会漏低障碍接纳更多低障碍,也更容易把地面噪声/坡面当障碍当前输入不是反射强度;地形估计不准时不能只靠这项纠正
max_intensity2.0m,当前地形点的高差上界通常保持当前值,不作为优先调试项删除高差超过2 m的点允许更多大高差点更多大高差点被删当前近场正常高差低于0.5 m、远场低于1 m,2 m通常不再额外裁剪;条件仍被执行
angle_min-3.1415926rad,目标系平面方位角通常保持当前值,不作为优先调试项指定角域下界,当前约-180°下界提高,角域缩小下界降低,角域扩大与上界共同定义方位覆盖;不是雷达垂直视场或云台机械角
angle_max3.1415926rad,目标系平面方位角通常保持当前值,不作为优先调试项指定角域上界,当前约+180°上界提高,角域扩大上界降低,角域缩小当前近似整圈;需与下界、增量及数组边界共同核对
angle_increment0.00872665rad/角度格,约0.5°实物运行时重点观察,有问题再针对性调整把方位角划分为离散格格子更粗、项数减少,近邻方向更易合并格子更细、项数增加,稀疏输入时空格更多当前生成720项;不改变MID360采样密度或点云降采样;应为正数
scan_time0.05s,消息中的扫描间隔上实物前必须核对并正确配置给LaserScan.scan_time字段赋值消息声明更长间隔,发布周期不变消息声明更短间隔,发布周期不变应与实际输入/输出扫描周期对应;不是固定20 Hz开关;time_increment另写死为0
range_min0.3m,目标系水平距离上实物前必须核对并正确配置删除虚拟中心0.3 m内的点近处点删得更多,可能漏车旁障碍保留更多近点,也可能带入自车回波距离为sqrt(x²+y²),以base_footprint为中心;不是雷达三维斜距或完整自车掩膜
range_max10.0m,目标系水平距离实物运行时重点观察,有问题再针对性调整删除虚拟中心10 m外的点接纳更远的已有点,也增加远处稀疏/噪声影响截掉更多远处轮廓不提高硬件量程;还受地形输出覆盖和下游扫描范围限制
use_inftrue不适用,布尔根据实际功能需求开启或设置为没有合格点的角度格选择无回波表示不适用不适用True写+∞;False写range_max+inf_epsilon;没有合格回波不等于确定空地
transform_tolerance0.01,源码默认;YAML未配置s,变换查询等待时间通常保持当前值,不作为优先调试项点云转换时等待所需TF可等待更久,也可能延后处理更快放弃缺失变换当前传给TF查询的timeout;不是时间戳偏移或点云同步误差容差;空target_frame时不用
queue_size硬件并发线程数,源码默认;YAML未配置消息数,计数通常保持当前值,不作为优先调试项设置点云订阅及TF消息过滤队列深度可暂存更多消息,可能增加积压缓存更少,TF未就绪时更易丢消息默认随机器而变;不是CPU工作线程数、发布频率或扫描点数
inf_epsilon1.0,源码默认;YAML未配置m,超出量程的附加值当前运行不使用这个参数,暂不调整use_inf=False时生成无回波哨兵值启用该分支后:哨兵值离range_max更远启用该分支后:哨兵值更接近range_max当前use_inf=True,不参与结果;False时默认无回波为10+1=11 m;启用时宜保持正附加值,避免落入有效量程
参数详解
target_frame:扫描的中心和方向是底盘,不是云台或雷达

当前输入 terrain_map_ext 的点坐标在odom中;转换使用输入消息时间戳对应的 base_footprint←odom,随后以转换后的x、y、z筛选。输出 obstacle_scan.header.frame_id 为base_footprint,时间戳沿用输入。

平面角度以该系+x为0,绕+z逆时针为正;距离以该系原点计算。它与 fake_vel_transform 最终按云台系表达的速度指令是两条不同接口:这里处理测距轮廓,没有给底盘或云台下运动指令。

因此这是底盘原点上的虚拟二维扫描,不是MID360原点上的真实激光束。MID360与底盘有安装偏移,不能把range_min理解成“离雷达外壳0.3 m”。输入是累积地形结果,含不同时间、不同高度的点;同一角度格只留最近的合格点,也不重新验证从底盘虚拟中心到该点是否存在真实光路或遮挡。

正确的上游配准和机械TF应让独立云台yaw转动后的静态环境仍转换到底盘方向;不能对这份点再手工重复加一个云台角。MID360挂在gimbal_yaw、随yaw不随pitch转动,两条odom关节固定的机械约定继续适用。

base_footprint 这个名字不会自动把TF中的z、roll、pitch清零。当前sensor_scan_generation保留完整三维位姿,所以投影是该系的x-y平面;上坡时它是否真正与地面投影/预期水平面对应,需要核对参考系定义及上游姿态,不能仅凭名称判断。

若target_frame设空,源码不转换点,直接在输入系中计算。当前输入为odom,这就变成以odom原点和方向作虚拟中心;机器人移动后,它不等价于仍以底盘为中心投影。

min_height、max_height 与两项intensity:两把不同的尺

高度窗口看 z_base_footprint,即点相对底盘原点、沿底盘z轴的坐标;高差窗口看 intensity,即上游地形分析给出的“离估计地面多远”。它们单位都是m,但基准不同。

假设上游已保留这些点,其他条件也满足:

点在底盘系中的z上游给出的地形高差本节点结果
+0.2 m0.15 m两个窗口都通过,参与二维投影
+0.2 m0.06 m高差不足0.1 m,被删
-0.7 m0.2 m低于-0.5 m高度下界,被删,即使高差足够

所以远处下坡点即使通过扩展地形节点随距离放宽的窗口,仍可能在这里被固定下界删掉。上坡、下坡是否连续可见,要同时看target_frame的方向与高度窗口;disRatioZ 不会替本节点放宽范围。

min_intensity=0.1 把10 cm作为进入二维障碍轮廓的高差下限,不是说车辆能跨10 cm台阶。降低它可能看见更低障碍,也可能把正常地面或坡面误差画进地图;提高它会减少小高差轮廓,却可能漏掉对实车危险的低障碍。远场地形高差取绝对值,坡面较低一侧也可能过门槛;近场目前忽略负高差,4 m交接处的差异仍会传入扫描。

max_height=6.0 的窗口很宽。若高处横梁等点通过上游和高差筛选,就会被压到二维地图中,即使车本来能从下面通过也可能形成障碍轮廓。应该根据底盘、云台活动包络与悬空结构核对要保留的高度,不能把它当模型尺寸;同时也不能只调小上限而漏掉障碍唯一可见的部位。

当前正常地形输出近场高差低于0.5 m、远场低于1 m,因此max_intensity=2 m通常不额外删点;这不表示参数没被读取。若换输入、调整上游或出现其他标记点,效果会改变。高度与高差门槛使用“小于下界/大于上界则删除”,边界值可通过。

本地实现要求float类型的XYZ与intensity字段;没有intensity字段的点云不能直接套用。若接原始雷达点,intensity可能恢复为反射强度,这时0.1/2.0不再代表10 cm/2 m;不应仅换话题继续沿用当前解释。

angle_min、angle_max 与 angle_increment:整圈分成720格

当前角域约为-180°~+180°,增量约0.5°,源码生成720个距离项。角度是目标系中的 atan2(y,x),不是雷达垂直视场、安装倾角或云台机械yaw范围。

同一角度格内有多个合格点时,只保留水平距离最近的一个。例如,同方向2 m和5 m处各有点,扫描只留2 m;二维扫描不会同时表达它们的高度或多层结构。

0.5°在10 m处对应横向约8.7 cm,在1 m处约8.7 mm;这里只说明角度分格的几何尺度,不是定位精度,也不保证这么细的障碍都能看见。输入点云经过上游筛选与降采样,调小角增量不会增加实际回波。

源码先以 ceil((angle_max-angle_min)/angle_increment) 建数组,再计算下标。当前实现没有检查下标是否越界,而角度上界允许通过;当角域与增量恰好形成整数分格时,上界点存在越界路径。当前近似整圈设置得到720项,不能据此证明其他角度组合的边界安全;该缺口尚无运行触发记录。

range_min 与 range_max:底盘周围的水平圆环

当前保留0.3~10.0 m水平距离的点,按 sqrt(x²+y²) 计算,不含z。相同x、y而高度不同的点,只要过高度/高差窗口,测距数值就一样。

增大range_min可排掉更多近处回波,但也会漏掉车旁障碍;减小则可能带入车体、云台或其他自车点。0.3 m圆形门槛不是完整车辆轮廓,距底盘原点更远的自车回波仍可能留下,需要结合安装位置与真实车体观察。

增大range_max只允许更多已经存在的远点,不能让雷达看到更远,也不能恢复上游删点。当前slam_toolbox另配置min_laser_range=0.3、max_laser_range=10.0,两级范围应一起核对;下游参数用途另属建图参数块。

scan_time:时间字段,不是发布频率开关

LaserScan定义中scan_time表示扫描之间的时间间隔。这里没有测量实际间隔,只把参数0.05 s写入消息;输出由输入点云驱动,实际周期取决于地形点云、TF与计算过程。

time_increment 写死为0,表示没有逐束时间差;header.stamp 沿用输入点云时间。地形输入含历史点,不能把这个结果当成所有射线确实在同一时刻由物理二维雷达采集,也不能用scan_time代替运动补偿或修复上游点时间单位。

实际发布周期应按对应启动模式测量后核对。修改scan_time只会改字段,不会使10 Hz输入变成20 Hz扫描,也不会给各个角度格补出真实采样时间。

use_inf 与 inf_epsilon:无回波不等于确定空地

True先把每格距离设为+∞,再用最近合格点覆盖;某格没有合格点就保留∞。False则先设为range_max+inf_epsilon,当前默认组合是11 m,仍超出10 m有效量程。

“没有合格点”可以来自真实无回波,也可以来自缺数据、遮挡、上游删除、高度/高差过滤或角度格里没有样本;不能仅凭∞就判断这条方向安全。下游怎样处理无效/超量程值,应按对应扫描使用者的实现核对。

输出只填ranges,不填LaserScan.intensities。这里的两项intensity参数用于挑选输入点,并不把地形高差继续作为扫描强度发布。

默认TF参数与订阅条件

transform_tolerance=0.01 s 在当前代码中作为转换查询的等待超时;它不是点云和TF可以错开0.01秒的补偿量,也没有把消息时间戳前移/后移。TF消息过滤器按输入时间等待目标变换,queue_size控制其队列以及点云订阅深度。TF转换失败的回调直接返回,不用单位变换继续生成扫描。

节点只有在scan输出存在订阅者时才订阅cloud_in;最后一个扫描订阅者消失后,会取消点云订阅。输入和输出均使用SensorDataQoS;兼容的订阅QoS与有效TF是收到扫描的前提。全是被过滤的点时,仍可发布一帧全为无回波值的扫描,不能把“有扫描消息”视为障碍轮廓正确。

默认queue_size取硬件并发线程数,适合先沿用;若消息排队或TF等待导致丢点,再按实际数据率和负载核对,不把队列长度当成工作线程数。

启动来源与验证状态
启动方式配置与输入输出来源
整套reality,slam=True实物nav2_params.yaml经slam_launch传入;cloud_in重映射为terrain_map_ext,scan为obstacle_scan;target_frame=base_footprint
整套simulation,slam=True仿真nav2_params.yaml,13项中仅use_sim_time=True不同;同样的输入输出与目标系
整套slam=False进入已有地图定位流程,不启动这个转换节点
包内sample_pointcloud_to_laserscan_launch.py示例点云发布器与静态TF;目标系cloud、高度0~1 m、角域约±90°、scan_time=0.3333、距离0.45~4 m,未读取整套YAML

15项自定义参数在构造时读取,没有动态参数更新回调;同名参数运行时改变不自动更新这些成员或重建TF过滤器。ROS标准use_sim_time由节点时钟处理。当前use_inf=True使inf_epsilon不参与输出,其他门槛仍按各自条件执行。

  • 源码依据: pointcloud_to_laserscan/src/pointcloud_to_laserscan_node.cpp;启动接线见 pb2025_nav_bringup/launch/slam_launch.py,LaserScan字段定义见ROS2 sensor_msgs。
  • 已有验证范围: 独立Point-LIO静止与短距离往返已有输出记录;该转换节点的二维扫描、建图轮廓、云台连续yaw和坡道投影尚未验证。
  • 实物待核实: 底盘原点与投影平面、IMU/雷达/底盘转换、上下坡点是否被固定高度窗口截掉、低障碍与悬空结构的保留范围、自车回波、角度覆盖、实际扫描周期及无回波区域的下游处理。