Docker 容器内 Fast-Planner 下载、编译与测试
前言
本文是《Docker使用教程》的后续,默认已经按照其中的步骤创建了以下环境:
- 容器名:
ros_noetic - 基础镜像:
osrf/ros:noetic-desktop-full - 宿主机
~/docker/ros_root已挂载到容器/root - X11 目录和认证文件已经挂载,容器可以运行 RViz
这里的“全新容器”是指容器已经创建,但还没有下载 Fast-Planner,也没有安装项目依赖。本文不再重复安装 Docker、拉取 ROS 镜像或创建容器,只介绍如何在这个容器中下载、编译和测试 dreamer198/Fast-Planner。
本文默认容器内使用 root 用户,因此 ~ 就是 /root,安装命令不需要 sudo。没有特别说明时,命令都在容器内执行;需要在宿主机执行的命令会单独标出。
Fast-Planner 官方验证过 Ubuntu 20.04 + ROS Noetic。本文只运行项目自带的轻量仿真,不包含 PX4、Gazebo 或真机控制。
一、进入已经创建的容器
如果创建容器后已经停在 root@... 提示符,可以直接进入下一节。
如果容器已经退出,在宿主机执行:
1 | docker start -ai ros_noetic |
如果容器已经在后台运行,则执行:
1 | docker exec -it ros_noetic bash |
进入后确认 ROS 版本:
1 | source /opt/ros/noetic/setup.bash |
输出应为:
1 | noetic |
二、安装编译依赖
ROS Noetic Desktop Full 已经包含 ROS 和 RViz,不需要再次安装。只补充源码编译需要的工具和库:
1 | apt update |
安装 NLopt 2.7.1
Fast-Planner 的 bspline_opt 会从 /usr/local/include 和 /usr/local/lib/libnlopt.so 查找 NLopt。按照项目 README 指定的 v2.7.1 编译安装:
1 | mkdir -p /root/deps |
检查头文件和动态库:
1 | test -f /usr/local/include/nlopt.hpp && echo "nlopt.hpp: OK" |
两行都显示 OK 后再继续。
三、下载 Fast-Planner
新建独立的 catkin 工作空间,并将代码克隆到 src 中:
1 | mkdir -p /root/fast_planner_ws/src |
确认下载地址并记录当前版本:
1 | cd /root/fast_planner_ws/src/Fast-Planner |
第一条命令应输出:
1 | https://github.com/dreamer198/Fast-Planner.git |
记录第二条命令输出的 commit,后续遇到问题时可以确认使用的是不是同一版代码。
四、编译工作空间
1 | source /opt/ros/noetic/setup.bash |
如果容器可用内存较少,编译进程可能被系统终止。此时降低并行数重新编译:
1 | catkin_make -DCMAKE_BUILD_TYPE=Release -j2 |
编译完成后加载工作空间,并确认 ROS 能找到核心包:
1 | source /root/fast_planner_ws/devel/setup.bash |
正常情况下会输出:
1 | /root/fast_planner_ws/src/Fast-Planner/fast_planner/plan_manage |
项目没有提供覆盖这些规划流程的自动化单元测试,因此下面通过仓库自带的仿真 Demo 完成功能测试。
五、运行 Kinodynamic 规划测试
该测试需要两个终端,它们都进入同一个 ros_noetic 容器。第一个终端运行 RViz,第二个终端运行规划器和轻量仿真器。
1. 终端 1:启动 RViz
如果 RViz 还没有图形显示权限,先在宿主机执行:
1 | xhost +SI:localuser:root |
然后在宿主机进入正在运行的容器:
1 | docker exec -it \ |
在容器内启动 RViz:
1 | source /opt/ros/noetic/setup.bash |
2. 终端 2:启动规划器
在宿主机新开一个终端:
1 | docker exec -it ros_noetic bash |
进入容器后执行:
1 | source /opt/ros/noetic/setup.bash |
3. 在 RViz 中发送目标
等待 RViz 中出现随机障碍物和无人机模型,然后:
- 确认 RViz 的
Fixed Frame为world。 - 点击工具栏中的
2D Nav Goal。 - 在地图内没有障碍物的位置点击并拖动,发送一个目标点。
当前仓库的 Kinodynamic Demo 默认使用手动目标模式。目标的 X、Y 坐标来自点击位置,飞行高度默认为 1 m。正常情况下会立即生成 B-spline 轨迹,无人机随后沿轨迹飞向目标。
六、检查测试结果
在宿主机再开一个终端并进入同一容器:
1 | docker exec -it ros_noetic bash |
加载环境:
1 | source /opt/ros/noetic/setup.bash |
先确认节点已经启动:
1 | rosnode list |
列表中应能看到规划器、轨迹服务器、仿真器和点云渲染等节点。再检查里程计是否持续发布:
1 | rostopic hz /state_ukf/odom |
看到稳定频率后按 Ctrl+C 退出,再检查仿真点云:
1 | rostopic hz /pcl_render_node/cloud |
点云同样应持续更新,看到频率后按 Ctrl+C 退出。接着检查规划输出:
1 | rostopic echo -n 1 /planning/bspline |
这条命令会等待下一条轨迹消息。保持命令运行,回到 RViz 再发送一次目标;终端打印出 B-spline 消息并自动退出,说明规划器已经生成轨迹。
在无人机执行轨迹期间,还可以检查控制指令:
1 | rostopic hz /planning/pos_cmd |
如果能看到持续更新的频率,说明“目标输入 -> 路径规划 -> 轨迹下发”链路已经跑通。
七、运行 Topological 规划测试
先在运行 kino_replan.launch 的终端按 Ctrl+C 停止 Kinodynamic Demo,RViz 可以继续运行。不要同时启动两个 Demo,因为它们会创建同名节点并使用相同话题。
然后在规划器终端执行:
1 | source /opt/ros/noetic/setup.bash |
这个仓库的 topo_replan.launch 已经默认使用 flight_type=1,可以直接在 RViz 中通过 2D Nav Goal 发送目标,不需要修改 launch 文件。看到多条拓扑候选路径和最终轨迹,并且无人机开始执行轨迹,即表示 Topological Demo 测试通过。
八、后续重新运行
代码和编译结果都位于 /root。按照《Docker使用教程》的挂载方式,它们会同步保存在宿主机 ~/docker/ros_root 中。apt 安装的软件和 /usr/local 下的 NLopt 属于容器文件系统;停止再启动同一个容器时仍然存在,但删除容器后不会由 /root 挂载保留。
下次启动同一个容器后不需要重新下载和编译,只需在每个新终端加载环境:
1 | source /opt/ros/noetic/setup.bash |
然后分别启动:
1 | roslaunch plan_manage rviz.launch |
以及二选一:
1 | roslaunch plan_manage kino_replan.launch |
1 | roslaunch plan_manage topo_replan.launch |
如果本次测试结束后不再运行容器中的图形程序,可以在宿主机撤销临时的 X11 授权:
1 | xhost -SI:localuser:root |
九、常见问题
1. roslaunch 找不到 plan_manage
说明当前终端没有加载工作空间,或者编译没有成功:
1 | source /opt/ros/noetic/setup.bash |
2. 链接时找不到 libnlopt.so
检查 NLopt 的安装位置并刷新缓存:
1 | ls -l /usr/local/include/nlopt.hpp |
项目明确查找 /usr/local/lib/libnlopt.so。如果动态库被安装到了 /usr/local/lib64,重新执行第二节的 CMake 命令,并保留 -DCMAKE_INSTALL_LIBDIR=lib。
3. RViz 无法打开或提示 X11 权限错误
先退出到宿主机,确认容器仍然按照《Docker使用教程》挂载了 /tmp/.X11-unix 和 .Xauthority,然后执行:
1 | xhost +SI:localuser:root |
再使用第五节中带 DISPLAY 的 docker exec 命令进入容器并启动 RViz。
4. RViz 中有地图,但点击目标后没有轨迹
依次检查输入和输出:
1 | rostopic echo -n 1 /move_base_simple/goal |
每条命令都应先运行,再回到 RViz 发送一次新目标。同时确认目标位于地图范围内、没有落在障碍物中,并且当前只运行了一个规划 Demo。
