欢迎光临
我们一直在努力

Nav2 跑不起来?别急着改参数,先把 map→odom→base_footprint 这条 TF 链查明白

可以参考这个开源项目的架构

GitHub – Ikunio/Lidar_nav2_ws: 基于 Livox MID-360 3D LiDAR 的 ROS 2 自主导航工作空间,集成 LIO 里程计、重定位、Nav2 导航,支持仿真与实机部署。 · GitHub基于 Livox MID-360 3D LiDAR 的 ROS 2 自主导航工作空间,集成 LIO 里程计、重定位、Nav2 导航,支持仿真与实机部署。 – Ikunio/Lidar_nav2_wshttps://github.com/Ikunio/Lidar_nav2_ws

很多人在第一次部署 ROS2 Nav2 的时候,都会遇到一个很迷惑的问题:

RViz 里机器人能显示,地图也能显示,目标点也能点,但是机器人就是不动。

然后第一反应通常是:

  • 是不是 DWB 参数没调好?

  • 是不是速度限制太小?

  • 是不是 costmap 参数错了?

  • 是不是 planner 没起来?

  • 是不是 controller 不工作?

这些都有可能,但在我自己做 ROS2 3D LiDAR 导航系统的时候,发现很多 Nav2 问题最后都绕不开一个基础问题:

TF 树有没有接对?

尤其是这一条链:

map → odom → base_footprint → chassis → livox_frame

如果这条链不完整、不连续、时间戳不对,或者被多个节点重复发布,那么 Nav2 很可能会出现各种奇怪问题。

本文不先讲复杂参数,而是从 Nav2 最基础的数据链路讲起: 为什么 Nav2 这么依赖 TF?map、odom、base_footprint 分别负责什么?以及实机部署时应该怎么排查。


1. 先给结论

Nav2 不是只要有 /scan 和 /map 就能跑。

它真正需要的是一套完整的机器人定位链路:

map

odom

base_footprint / base_link

laser_frame / lidar_frame

换句话说,Nav2 必须能回答几个问题:

  • 机器人在地图里的位置在哪里?

  • 机器人相对于自己的 odom 坐标系移动了多少?

  • 雷达装在机器人身体的哪个位置?

  • 激光数据能不能转换到机器人本体坐标系?

  • costmap 能不能把障碍物投影到正确的位置?

  • controller 算出来的速度能不能作用到正确的机器人坐标系?

  • 如果 TF 树断了,Nav2 就会不知道机器人在哪。 如果 TF 树错了,Nav2 就会以为机器人在错误的位置。 如果 TF 树重复发布,Nav2 可能会在两个位姿之间跳。 如果 TF 时间戳不对,Nav2 会直接报 transform timeout。

    所以一句话:

    Nav2 大部分“看起来像参数问题”的问题,本质上可能是 TF 链路问题。


    2. Nav2 为什么依赖 TF?

    Nav2 是一个导航框架,它本身不是传感器驱动,也不是 SLAM,也不是里程计。

    它负责的事情大概是:

    知道机器人在哪里

    知道目标点在哪里

    根据地图规划路径

    根据局部障碍物避障

    输出 /cmd_vel 控制机器人运动

    但是这里面有一个前提:

    Nav2 必须知道机器人当前在地图坐标系下的位姿。

    这个位姿不是凭空来的,而是通过 TF 查出来的。

    例如 Nav2 想知道机器人在 map 下的位置,它通常会查询:

    map -> base_link

    或者:

    map -> base_footprint

    但是在 ROS2 里,这个变换通常不是一个节点直接发布的,而是由多段 TF 拼出来的:

    map -> odom
    odom -> base_footprint
    base_footprint -> chassis
    chassis -> livox_frame

    只要这条链路能连通,Nav2 就能知道机器人在哪里。

    如果中间断了一段,例如没有:

    odom -> base_footprint

    那 Nav2 就没法从 map 查到机器人本体。

    如果没有:

    base_footprint -> livox_frame

    那 costmap 就不知道雷达数据相对于机器人身体的位置。

    所以 Nav2 对 TF 的依赖非常强。


    3. map、odom、base_footprint 到底分别是什么?

    很多初学者最容易混淆的是:

    map、odom、base_link、base_footprint

    这几个坐标系名字看着简单,但职责完全不同。


    4. map:全局地图坐标系

    map 是全局坐标系。

    它通常代表机器人运行环境中的固定地图坐标系。

    例如:

    • 2D 栅格地图的原点;

    • 3D 点云地图的原点;

    • SLAM 建图时的全局坐标系;

    • 重定位成功后的全局参考系。

    map 的特点是:

    全局一致,但不一定连续。

    什么意思?

    假设机器人刚开机,里程计认为自己在原点附近:

    odom 下机器人位置:x = 0.0, y = 0.0

    但是重定位系统发现机器人其实在地图里的:

    map 下机器人位置:x = 5.2, y = -1.3

    那么 map -> odom 可能会发生一次修正。

    也就是说,map 坐标系允许发生全局修正。

    在 2D 导航里,AMCL 会维护:

    map -> odom

    在 3D LiDAR 导航里,如果你做了点云重定位,也可以通过 ICP、small_gicp、KISS-Matcher 这类方法维护:

    map -> odom

    所以 map 的核心职责是:

    提供全局位置参考。


    5. odom:局部连续里程计坐标系

    odom 是局部连续坐标系。

    它通常来自:

    • 轮速计;

    • 视觉里程计;

    • 激光里程计;

    • LiDAR-Inertial Odometry,比如 FAST-LIO、Point-LIO;

    • 多传感器融合后的 EKF 输出。

    odom 的特点是:

    局部连续,但会漂移。

    也就是说,odom 不应该突然跳变。

    机器人从 A 点移动到 B 点,odom -> base_footprint 应该是平滑变化的。

    如果你的 odom 一会儿在原点,一会儿突然跳到 10 米外,Nav2 controller 很容易炸。

    对于 Nav2 来说,odom 的意义是:

    告诉机器人短时间内自己怎么连续运动。

    所以 odom 不追求全局绝对准确,它追求连续、平滑、实时。

    在我的 3D LiDAR 导航系统里,LIO 可以作为 odom 来源。 比如 FAST-LIO 或 Point-LIO 输出雷达/IMU的运动估计,然后经过桥接,转换成 Nav2 需要的:

    odom -> base_footprint


    6. base_link 和 base_footprint:机器人本体坐标系

    base_link 一般表示机器人本体中心坐标系。

    base_footprint 一般表示机器人在地面上的投影坐标系。

    两者区别在于:

    base_link 可以包含 roll、pitch、z 高度
    base_footprint 通常只保留平面运动,也就是 x、y、yaw

    对于地面移动机器人,Nav2 更常用:

    base_footprint

    因为 Nav2 主要处理 2D 平面导航。

    如果你的机器人有轻微俯仰,例如雷达装在车体上,机器人过坡时 base_link 可能有 roll/pitch。 但是对于 2D costmap 和路径规划来说,通常只关心机器人在地面上的投影。

    所以常见结构是:

    odom -> base_footprint -> base_link

    或者:

    odom -> base_link

    具体用哪个,要和你的 Nav2 参数保持一致。

    例如:

    robot_base_frame: base_footprint

    那么 TF 里必须能查到:

    map -> base_footprint

    如果你的 TF 只有 base_link,但参数里写的是 base_footprint,Nav2 就会查不到。


    7. livox_frame / laser_frame:雷达坐标系

    雷达坐标系表示雷达安装在机器人上的位置和姿态。

    例如 MID-360 的坐标系可以叫:

    livox_frame

    或者:

    laser_frame

    这个坐标系一般通过 URDF 或 static transform 发布到机器人本体上:

    base_link -> livox_frame

    或者:

    chassis -> livox_frame

    它的作用是:

    告诉系统雷达数据是从机器人身体的哪个位置采集到的。

    如果这段 TF 错了,会导致很多问题:

    • RViz 中点云位置不对;

    • LaserScan 投影位置不对;

    • costmap 障碍物偏移;

    • 机器人避障方向异常;

    • 明明前面有障碍,costmap 却显示在侧面;

    • 雷达扫到自己的车体,被当成障碍物。

    所以雷达 TF 不是可有可无,它直接影响 costmap。


    8. 一棵比较合理的 TF 树

    以一个 3D LiDAR 地面机器人为例,可以设计成这样:

    map

    odom

    base_footprint

    chassis

    livox_frame

    每一段的职责如下:

    TF谁发布作用
    map -> odom AMCL / SLAM / 3D 重定位节点 全局定位修正
    odom -> base_footprint 轮速计 / LIO / EKF / odom bridge 局部连续里程计
    base_footprint -> chassis URDF / static tf 机器人底盘结构
    chassis -> livox_frame URDF / static tf 雷达安装外参

    这里面最重要的是:

    map -> odom
    odom -> base_footprint

    前者负责全局定位,后者负责局部连续运动。

    二者职责不能混。


    9. 谁应该发布 map -> odom?

    这是实机部署里很容易混乱的地方。

    一般情况下,map -> odom 只能由一个模块维护。

    常见情况:

    9.1 使用 AMCL

    如果你用 2D 栅格地图 + AMCL,那么通常是 AMCL 发布:

    map -> odom

    这时候你的轮速计或 LIO 发布:

    odom -> base_footprint

    系统结构是:

    map
    ↓ AMCL
    odom
    ↓ wheel odom / LIO
    base_footprint


    9.2 使用 SLAM Toolbox

    如果你用 slam_toolbox 建图和定位,它也可能发布:

    map -> odom

    这时也不要再让别的节点重复发这段 TF。


    9.3 使用 3D 点云重定位

    如果你做的是 3D LiDAR 重定位,比如:

    KISS-Matcher + small_gicp

    那么可以由重定位节点发布:

    map -> odom

    LIO 负责:

    odom -> base_footprint

    整体结构就是:

    3D PCD map

    KISS-Matcher / small_gicp

    map -> odom

    FAST-LIO / Point-LIO

    odom -> base_footprint

    这样 Nav2 最终就可以查询:

    map -> base_footprint


    10. 谁应该发布 odom -> base_footprint?

    odom -> base_footprint 表示机器人局部连续运动。

    它可以来自:

    • 轮速计;

    • LIO;

    • VIO;

    • robot_localization;

    • 自己写的 odom bridge。

    如果你使用 FAST-LIO 或 Point-LIO,原始输出的 frame 可能不是 Nav2 想要的格式。

    比如 LIO 可能发布的是:

    camera_init -> body

    或者:

    map -> body

    但是 Nav2 想要的是:

    odom -> base_footprint

    这时候就需要桥接。

    桥接做的事情不是“凭空造一个 odom”,而是把 LIO 输出的位姿转成 Nav2 约定的坐标关系。

    例如:

    LIO 输出当前雷达/IMU 位姿

    根据雷达到车体外参转换到 base_footprint

    发布 nav_msgs/Odometry

    发布 odom -> base_footprint

    这一步非常关键。

    因为 FAST-LIO 很准,不代表它的输出可以直接喂给 Nav2。 Nav2 关心的是 frame 是否正确、TF 是否连续、时间戳是否稳定。


    11. 常见错误一:TF 树断了

    最典型的错误是:

    map -> odom

    有,但是没有:

    odom -> base_footprint

    或者:

    odom -> base_footprint

    有,但是没有:

    base_footprint -> livox_frame

    这种情况下 Nav2 可能会报:

    Timed out waiting for transform

    或者:

    Invalid frame ID

    或者:

    Could not transform from base_link to map

    排查方法:

    ros2 run tf2_ros tf2_echo map base_footprint

    如果查不到,说明从 map 到 base_footprint 中间断了。

    再逐段查:

    ros2 run tf2_ros tf2_echo map odom
    ros2 run tf2_ros tf2_echo odom base_footprint
    ros2 run tf2_ros tf2_echo base_footprint chassis
    ros2 run tf2_ros tf2_echo chassis livox_frame

    哪一段查不到,就先修哪一段。


    12. 常见错误二:frame 名字不一致

    这是非常低级但非常常见的问题。

    例如 Nav2 参数里写的是:

    robot_base_frame: base_link

    但是你的 TF 里只有:

    base_footprint

    或者你的 LaserScan 里写的是:

    frame_id: laser

    但是 TF 树里叫:

    livox_frame

    这都会导致 Nav2 查 TF 失败。

    检查 /scan 的 frame:

    ros2 topic echo /scan –once

    重点看:

    header:
    frame_id: livox_frame

    然后确认 TF 树里是否存在:

    base_footprint -> livox_frame

    检查 /odom:

    ros2 topic echo /odom –once

    重点看:

    header:
    frame_id: odom
    child_frame_id: base_footprint

    如果这里写的是:

    header:
    frame_id: map
    child_frame_id: base_link

    那就要确认它是否符合你的系统设计。


    13. 常见错误三:多个节点重复发布同一段 TF

    比如同时有两个节点都在发布:

    map -> odom

    一个是 AMCL,另一个是你的重定位节点。

    这会导致什么?

    机器人在 RViz 里可能会出现:

    • 抖动;

    • 跳变;

    • 原地旋转;

    • costmap 漂移;

    • 路径规划忽左忽右;

    • Nav2 controller 输出异常。

    原则很简单:

    同一段父子 TF,最好只允许一个权威节点发布。

    也就是说:

    map -> odom

    只能由 AMCL、SLAM、重定位节点中的一个发布。

    odom -> base_footprint

    只能由轮速计、LIO、EKF、odom bridge 中的一个发布。

    如果你用了 robot_localization,那一般应该让 EKF 发布融合后的 odom,而不是让每个传感器都发布同一段 TF。


    14. 常见错误四:TF 时间戳不对

    Nav2 很依赖时间同步。

    如果 TF 时间戳太旧,或者传感器数据时间戳和 TF 时间戳对不上,会出现:

    Lookup would require extrapolation into the future

    或者:

    Transform data too old

    常见原因:

    • 使用仿真时没有设置 use_sim_time;

    • Gazebo 时间和系统时间混用;

    • 传感器驱动时间戳异常;

    • LIO 输出时间戳和 ROS 时间不一致;

    • TF 发布频率太低;

    • 多机通信没有时间同步。

    检查是否使用仿真时间:

    ros2 param get /controller_server use_sim_time
    ros2 param get /planner_server use_sim_time
    ros2 param get /bt_navigator use_sim_time

    如果是 Gazebo 仿真,相关节点通常都应该使用:

    use_sim_time: true

    如果是真机,一般是:

    use_sim_time: false

    检查 TF 频率:

    ros2 topic hz /tf

    检查某段 TF 是否稳定:

    ros2 run tf2_ros tf2_echo odom base_footprint

    如果输出断断续续,说明 TF 发布不稳定。


    15. 常见错误五:LaserScan 的 frame 没有接到机器人本体

    Nav2 的 costmap 通常会订阅 /scan。

    但是 /scan 不是孤立数据。

    它的 header 里有一个:

    frame_id

    例如:

    frame_id: livox_frame

    Nav2 会通过 TF 把这个 LaserScan 转换到机器人本体或地图坐标系下。

    所以必须存在:

    base_footprint -> livox_frame

    或者通过其他链路能连到。

    如果没有这段 TF,costmap 就无法正确使用 scan。

    排查:

    ros2 topic echo /scan –once

    看:

    header:
    frame_id: livox_frame

    然后查:

    ros2 run tf2_ros tf2_echo base_footprint livox_frame

    如果查不到,说明雷达外参没有发布。


    16. 常见错误六:RViz 里看着对,但 Nav2 还是不走

    RViz 只是可视化工具。

    RViz 里能看到机器人,不代表 Nav2 所需链路全部正常。

    Nav2 真正需要的是:

    /map
    /scan
    /odom
    /tf
    /tf_static
    /cmd_vel
    costmap
    lifecycle active

    所以遇到“RViz 看着对,但机器人不走”,不要只盯着 RViz。

    可以按下面顺序排查。


    17. Nav2 排查 checklist

    17.1 查节点是否启动

    ros2 node list

    重点看:

    /controller_server
    /planner_server
    /bt_navigator
    /behavior_server
    /waypoint_follower
    /map_server
    /lifecycle_manager_navigation


    17.2 查 lifecycle 状态

    ros2 lifecycle nodes

    然后查具体节点:

    ros2 lifecycle get /controller_server
    ros2 lifecycle get /planner_server
    ros2 lifecycle get /bt_navigator

    正常应该是:

    active

    如果不是 active,Nav2 不会正常工作。


    17.3 查 TF 主链路

    ros2 run tf2_ros tf2_echo map base_footprint

    如果这里查不到,Nav2 基本不可能正常跑。

    继续逐段查:

    ros2 run tf2_ros tf2_echo map odom
    ros2 run tf2_ros tf2_echo odom base_footprint
    ros2 run tf2_ros tf2_echo base_footprint livox_frame


    17.4 生成 TF 树 PDF

    ros2 run tf2_tools view_frames

    它会生成一个 TF 树文件,可以直观看到:

    • 哪些 frame 存在;

    • 哪些 frame 断开;

    • 哪些 TF 发布频率异常;

    • 哪些 TF 时间戳有问题。


    17.5 查 /scan

    ros2 topic echo /scan –once
    ros2 topic hz /scan

    重点看:

    header:
    frame_id: livox_frame

    以及频率是否稳定。


    17.6 查 /odom

    ros2 topic echo /odom –once
    ros2 topic hz /odom

    重点看:

    header:
    frame_id: odom
    child_frame_id: base_footprint

    如果 frame_id 和 child_frame_id 不符合你的系统设计,Nav2 就可能查错。


    17.7 查 /cmd_vel

    ros2 topic echo /cmd_vel

    如果点了目标点之后没有 /cmd_vel 输出,说明 Nav2 没有成功进入控制阶段。

    如果有 /cmd_vel,但是底盘不动,那问题可能在底盘驱动或串口通信。


    18. 一个推荐的排查顺序

    我建议不要一上来就改 Nav2 参数。

    推荐顺序是:

    1. 先查节点是否 active
    2. 再查 map -> base_footprint 是否能查到
    3. 再查 /scan 的 frame_id 是否能接到 base_footprint
    4. 再查 /odom 的 frame_id 和 child_frame_id
    5. 再查 costmap 是否正常显示障碍物
    6. 再查 /cmd_vel 是否输出
    7. 最后再调 planner/controller 参数

    也就是:

    先查系统链路,再查算法参数。

    很多人一上来就调 DWB,其实 TF 都没通。 这种情况下,不管你怎么调 controller,机器人都不会正常跑。


    19. LIO 接 Nav2 时要特别注意什么?

    如果你的系统使用 FAST-LIO、Point-LIO 这类 LIO 作为里程计来源,要额外注意以下几点。


    19.1 FAST-LIO 输出的 frame 不一定等于 Nav2 需要的 frame

    LIO 可能输出:

    camera_init -> body

    或者:

    map -> body

    但是 Nav2 通常希望:

    odom -> base_footprint

    所以中间需要转换。

    不要简单地认为:

    FAST-LIO 有 odometry 输出 = Nav2 可以直接用

    真正要看:

    • frame 名是否对;

    • child frame 是否对;

    • TF 是否连续;

    • 时间戳是否对;

    • 是否会和其他节点重复发布 TF;

    • 雷达到车体外参是否处理正确。


    19.2 LIO 更适合作为 odom,不一定适合直接作为 map

    LIO 是局部里程计,长期会漂移。 它很适合发布:

    odom -> base_footprint

    但如果没有全局约束,直接把 LIO 当成 map 可能会导致全局地图慢慢漂。

    比较合理的结构是:

    3D 重定位 / SLAM / AMCL

    map -> odom

    FAST-LIO / Point-LIO

    odom -> base_footprint

    也就是:

    全局定位负责 map -> odom
    局部里程计负责 odom -> base_footprint

    这样职责更清楚。


    19.3 不要让 LIO 和重定位节点同时发布 map -> odom

    如果你已经有一个 3D 重定位节点在发布:

    map -> odom

    那 LIO 就不要再发这段 TF。

    否则系统会出现两个全局定位来源。

    正确做法通常是:

    LIO:发布 odom -> base_footprint
    重定位:发布 map -> odom


    20. 一个推荐的 Nav2 参数片段

    下面是一个示意配置,不是通用答案,但可以作为检查思路。

    amcl:
    ros__parameters:
    use_sim_time: false
    base_frame_id: "base_footprint"
    odom_frame_id: "odom"
    global_frame_id: "map"

    bt_navigator:
    ros__parameters:
    use_sim_time: false
    global_frame: "map"
    robot_base_frame: "base_footprint"
    odom_topic: "/odom"

    controller_server:
    ros__parameters:
    use_sim_time: false
    odom_topic: "/odom"

    local_costmap:
    local_costmap:
    ros__parameters:
    global_frame: "odom"
    robot_base_frame: "base_footprint"
    observation_sources: scan
    scan:
    topic: /scan
    data_type: "LaserScan"
    marking: true
    clearing: true

    global_costmap:
    global_costmap:
    ros__parameters:
    global_frame: "map"
    robot_base_frame: "base_footprint"
    observation_sources: scan
    scan:
    topic: /scan
    data_type: "LaserScan"
    marking: true
    clearing: true

    重点不是照抄参数,而是理解这几个 frame 必须和 TF 树一致:

    global_frame: map
    local_costmap.global_frame: odom
    robot_base_frame: base_footprint
    odom_topic: /odom
    scan.topic: /scan


    21. 一个正确系统应该是什么样?

    假设我的系统是:

    MID-360 + IMU

    FAST-LIO / Point-LIO

    odom -> base_footprint

    PointCloud2 -> LaserScan

    Nav2 costmap

    Planner / Controller

    /cmd_vel

    同时如果需要全局定位:

    PCD map

    KISS-Matcher / small_gicp

    map -> odom

    最终 TF 树就是:

    map

    odom

    base_footprint

    chassis

    livox_frame

    这时 Nav2 就能做三件事:

  • 通过 map -> base_footprint 知道机器人在地图里的位置;

  • 通过 base_footprint -> livox_frame 正确使用雷达数据;

  • 通过 /cmd_vel 控制底盘运动。


  • 22. 什么时候才应该开始调 Nav2 参数?

    只有当下面这些都正常之后,再去调 planner/controller 参数:

    1. map -> base_footprint 能查到;
    2. odom -> base_footprint 连续稳定;
    3. base_footprint -> livox_frame 正确;
    4. /scan 有数据,frame_id 正确;
    5. costmap 能正确显示障碍物;
    6. Nav2 lifecycle 节点都是 active;
    7. 点目标后 /cmd_vel 有输出。

    如果这些还没满足,就去调 DWB、NavFn、inflation、footprint,很容易越调越乱。


    23. 总结

    最后总结一下:

  • Nav2 不是只要有 /scan 和 /map 就能跑,它依赖完整 TF 链。

  • map -> odom -> base_footprint 是 ROS2 地面机器人导航的核心链路。

  • map 负责全局定位,odom 负责局部连续运动,base_footprint 负责机器人本体平面坐标。

  • 同一段 TF 不要让多个节点重复发布,尤其是 map -> odom 和 odom -> base_footprint。

  • LIO 可以作为 Nav2 的 odom 来源,但需要桥接成 Nav2 需要的 frame。

  • 遇到 Nav2 跑不起来,不要第一时间调 controller 参数,先查 TF、frame_id、时间戳和 lifecycle。

  • 我的建议是:

    先让 TF 树正确,再让 costmap 正确,最后再调 Nav2 参数。

    因为在 ROS2 导航系统里,很多问题不是“算法不会跑”,而是系统链路一开始就没接对。

    如果 TF 树是错的,后面的 planner、controller、costmap 都会跟着错。

    所以 Nav2 排坑第一步,不是改参数,而是先问自己一句:

    map 到 base_footprint 真的能查到吗?

    赞(0)
    未经允许不得转载:171主机测评 » Nav2 跑不起来?别急着改参数,先把 map→odom→base_footprint 这条 TF 链查明白
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址