前言
本文记录在 Docker 容器中搭建 PX4 SITL + ROS1 + Diff-Planner 的流程,并分别跑通深度相机和 Mid360 两条仿真链路。
如果还没有创建 ROS Noetic 容器,可以先参考《Docker使用教程》。
本文默认在容器内 root 用户下操作,~ 即 /root。
一、环境说明
Docker 容器:ros_noetic
ROS:Noetic
PX4:v1.14.3
仿真:Gazebo classic
PX4 目录:~/PX4-Autopilot
Catkin 工作空间:~/catkin_ws
整体顺序:
安装并编译 PX4 SITL
配置 ROS、Gazebo 和 MAVROS
编译 Diff-Planner-PX4
分别启动深度相机和 Mid360 仿真
二、PX4 SITL 基础环境
1. 下载 PX4
1 2 3 4 5 6 7 8 apt update apt install -y git cd ~git clone https://github.com/PX4/PX4-Autopilot.git cd PX4-Autopilotgit checkout -b dev v1.14.3 git submodule update --init --recursive
2. 安装 PX4 依赖
1 2 3 cd ~/PX4-Autopilot/Tools/setupchmod +x ubuntu.sh./ubuntu.sh --no-nuttx --no-sim-tools
这个脚本会补齐 PX4 基础依赖,首次执行会比较久。
如果出现 pandas 和 numpy 版本冲突,例如:
1 ERROR: pandas 2.0.3 has requirement numpy>=1.20.3; python_version < "3.10", but you'll have numpy 1.17.4 which is incompatible.
可以先清理 pip 中的版本,再使用系统源安装兼容版本:
1 2 3 4 5 6 7 cd ~/PX4-Autopilot/Tools/setuppython3 -m pip uninstall -y pandas numpy apt update apt install -y python3-numpy python3-pandas ./ubuntu.sh --no-nuttx --no-sim-tools
3. 安装 Gazebo 相关依赖
下载 Gazebo 模型:
1 2 3 4 mkdir -p ~/.gazebo/modelscd ~git clone https://gitee.com/tyx6/gazebo_models.git mv ./gazebo_models/* ~/.gazebo/models/
因为前面使用了 --no-sim-tools,后续编译 Gazebo classic 前需要补充 GStreamer 依赖:
1 2 3 4 5 6 7 apt update apt install -y \ pkg-config \ libgstreamer1.0-dev \ libgstreamer-plugins-base1.0-dev \ gstreamer1.0-plugins-base \ gstreamer1.0-plugins-good
4. 编译 PX4
1 2 cd ~/PX4-Autopilotmake px4_sitl_default gazebo
如果编译时报 GSTREAMER_APP_LIBRARIES 或 GSTREAMER_LIBRARIES 为 NOTFOUND,安装上面的 GStreamer 依赖后清理 Gazebo classic 缓存,再重新编译:
1 2 3 4 5 6 cd ~/PX4-Autopilotrm -rf build/px4_sitl_default/build_gazebo-classicrm -rf build/px4_sitl_default/external/Stamp/sitl_gazebo-classicmake px4_sitl_default gazebo
三、ROS 环境和 MAVROS
1. 写入环境变量
建议把环境变量写进 ~/.bashrc。如果容器的 /root 挂载到了宿主机,也可以直接在宿主机编辑:
1 nano ~/docker/ros_root/.bashrc
在文件末尾添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [ -f /opt/ros/noetic/setup.bash ] && source /opt/ros/noetic/setup.bash [ -f ~/catkin_ws/devel/setup.bash ] && source ~/catkin_ws/devel/setup.bash if [ -f ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ]; then source ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ~/PX4-Autopilot/ ~/PX4-Autopilot/build/px4_sitl_default > /dev/null fi export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH :~/PX4-Autopilot/export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH :~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic
当前终端手动加载一次:
2. 安装 MAVROS
PX4 通过 MAVROS 和 ROS 通信。容器内默认是 root 用户,不需要加 sudo:
1 2 3 4 5 apt update apt install -y \ ros-noetic-mavros \ ros-noetic-mavros-extras \ wget
安装 GeographicLib datasets:
1 2 3 4 cd ~wget https://gitee.com/tyx6/mytools/raw/main/mavros/install_geographiclib_datasets.sh chmod a+x ./install_geographiclib_datasets.sh./install_geographiclib_datasets.sh
这个脚本会下载地理坐标数据集,可能需要等待一段时间。
3. 验证 PX4 和 MAVROS
1 roslaunch px4 mavros_posix_sitl.launch
另开一个终端检查连接状态:
1 rostopic echo /mavros/state | grep connected
看到 connected: True 后,再继续后面的步骤。
四、Diff-Planner-PX4 集成
1. 安装依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 apt update apt install -y \ python3-catkin-tools \ python3-rosinstall-generator \ python3-osrf-pycommon \ libgoogle-glog-dev \ libgflags-dev \ libeigen3-dev \ libarmadillo-dev \ ros-noetic-pcl-ros \ ros-noetic-tf2-geometry-msgs \ ros-noetic-laser-geometry \ ros-noetic-tf2-sensor-msgs
2. 下载并编译
1 2 3 4 5 6 7 8 mkdir -p ~/catkin_ws/srccd ~/catkin_ws/srcgit clone https://github.com/dreamer198/Diff-Planner-PX4.git cd ~/catkin_wssource /opt/ros/noetic/setup.bashcatkin init catkin build
如果工作空间已经初始化过,直接执行 catkin build 即可。
3. 复制仿真配置
把 Diff-Planner-PX4 中的模型、世界文件和 launch 文件复制到 PX4 工程:
1 2 3 4 5 6 7 8 cp -r ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/models/* ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/cp ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/worlds/* ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/worlds/cp ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/outdoor_depth_camera.launch ~/PX4-Autopilot/launch/cp ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/outdoor_mid360.launch ~/PX4-Autopilot/launch/cp ~/catkin_ws/src/Diff-Planner-PX4/sitl_config/px4_config.yaml ~/PX4-Autopilot/launch/
如果 PX4 目录中已有同名文件,复制前可以先备份。
五、深度相机仿真
这一条链路需要三个终端:Gazebo、se3_controller、diff_planner。
终端一:启动 Gazebo
1 roslaunch px4 outdoor_depth_camera.launch
终端二:启动控制器
1 2 3 cd ~/catkin_wssource devel/setup.bashroslaunch se3_controller sitl_se3_controller.launch
控制器启动后,通常会自动进入 offboard 模式、解锁,并起飞到 2 m。
终端三:启动 Diff-Planner
1 2 3 cd ~/catkin_wssource devel/setup.bashroslaunch diff_planner run_px4_sitl_gazebo.launch
六、Mid360 仿真
Mid360 需要额外编译 Livox Gazebo 插件,并把插件和模型复制到 PX4。
1. 下载并编译插件
1 2 3 4 5 6 cd ~/catkin_ws/srcgit clone https://github.com/Tfly6/Mid360_px4_sim_plugin.git cd ~/catkin_wssource /opt/ros/noetic/setup.bashcatkin build
2. 测试插件
1 2 3 source ~/catkin_ws/devel/setup.bashsource ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bashroslaunch livox_laser_simulation test_pattern.launch
如果能正常看到点云,说明插件加载成功。
3. 复制 Mid360 配置
1 2 3 4 5 cd ~/catkin_wscp ./src/Mid360_px4_sim_plugin/livox_laser_simulation/launch/mavros_posix_sitl_mid360.launch ~/PX4-Autopilot/launch/cp ./devel/lib/liblivox_laser_simulation.so ~/PX4-Autopilot/build/px4_sitl_default/build_gazebo-classic/cp -r ./src/Mid360_px4_sim_plugin/livox_laser_simulation/models/Mid360 ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/cp -r ./src/Mid360_px4_sim_plugin/livox_laser_simulation/models/iris_mid360 ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/
终端一:启动 Gazebo
1 roslaunch px4 outdoor_mid360.launch
终端二:启动控制器
1 2 3 cd ~/catkin_wssource devel/setup.bashroslaunch se3_controller sitl_se3_controller.launch
终端三:启动 Diff-Planner
1 2 3 cd ~/catkin_wssource devel/setup.bashroslaunch diff_planner run_px4_sitl_gazebo_mid360.launch
七、宿主机一键启动仿真
上面的三条命令都在容器内执行。如果想从宿主机一键拉起仿真,可以用 tmux + docker exec 管理三个终端。
先在宿主机安装 tmux:
1 2 sudo apt updatesudo apt install -y tmux
在宿主机创建脚本:
1 2 mkdir -p ~/codenano ~/code/start_sim_diff_planner_px4.sh
写入下面内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 #!/usr/bin/env bash set -euo pipefailCONTAINER="${CONTAINER:-ros_noetic} " SESSION="${SESSION:-diff_px4_sitl} " WAIT_GAZEBO="${WAIT_GAZEBO:-12} " WAIT_CONTROLLER="${WAIT_CONTROLLER:-5} " ACTION="${1:-start} " MODE="${2:-${MODE:-mid360} } " usage () { cat <<EOF Usage: $0 start [mid360|depth] $0 restart [mid360|depth] $0 stop $0 status $0 attach Environment: CONTAINER=ros_noetic SESSION=diff_px4_sitl WAIT_GAZEBO=12 WAIT_CONTROLLER=5 EOF } need_tmux () { if ! command -v tmux >/dev/null 2>&1; then echo "tmux not found. Install it first: sudo apt install -y tmux" exit 1 fi } require_container () { if ! docker inspect "$CONTAINER " >/dev/null 2>&1; then echo "Docker container not found: $CONTAINER " exit 1 fi } ensure_container_running () { local running running="$(docker inspect -f '{{.State.Running}}' "$CONTAINER " ) " if [ "$running " != "true" ]; then docker start "$CONTAINER " >/dev/null fi } allow_x11 () { if [ -n "${DISPLAY:-} " ] && command -v xhost >/dev/null 2>&1; then xhost +SI:localuser:root >/dev/null 2>&1 || true fi } quote () { printf '%q' "$1 " } docker_exec_cmd () { local cmd="$1 " printf 'docker exec -it %q bash -lc %s' "$CONTAINER " "$(quote "$cmd " ) " } build_commands () { local gazebo_launch planner_launch extra_setup case "$MODE " in mid360) gazebo_launch="outdoor_mid360.launch" planner_launch="run_px4_sitl_gazebo_mid360.launch" extra_setup='[ -f ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash ] && source ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash' ;; depth|depth_camera) MODE="depth" gazebo_launch="outdoor_depth_camera.launch" planner_launch="run_px4_sitl_gazebo.launch" extra_setup='true' ;; *) usage exit 1 ;; esac BASE_SETUP='source /opt/ros/noetic/setup.bash; [ -f ~/catkin_ws/devel/setup.bash ] && source ~/catkin_ws/devel/setup.bash; if [ -f ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ]; then source ~/PX4-Autopilot/Tools/simulation/gazebo-classic/setup_gazebo.bash ~/PX4-Autopilot ~/PX4-Autopilot/build/px4_sitl_default >/dev/null; fi; export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:~/PX4-Autopilot:~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic' GAZEBO_CMD="$BASE_SETUP ; $extra_setup ; roslaunch px4 $gazebo_launch ; exec bash" CONTROLLER_CMD="$BASE_SETUP ; cd ~/catkin_ws; roslaunch se3_controller sitl_se3_controller.launch; exec bash" PLANNER_CMD="$BASE_SETUP ; cd ~/catkin_ws; roslaunch diff_planner $planner_launch ; exec bash" } start_stack () { need_tmux require_container ensure_container_running allow_x11 build_commands if tmux has-session -t "$SESSION " 2>/dev/null; then echo "tmux session already exists: $SESSION " echo "Use '$0 attach' to view it, or '$0 restart $MODE ' to restart." exit 1 fi tmux new-session -d -s "$SESSION " -n gazebo "$(docker_exec_cmd "$GAZEBO_CMD " ) " sleep "$WAIT_GAZEBO " tmux new-window -t "$SESSION :" -n controller "$(docker_exec_cmd "$CONTROLLER_CMD " ) " sleep "$WAIT_CONTROLLER " tmux new-window -t "$SESSION :" -n diff_planner "$(docker_exec_cmd "$PLANNER_CMD " ) " tmux select-window -t "$SESSION :gazebo" echo "Started $MODE simulation in tmux session: $SESSION " echo "Attach: $0 attach" } stop_stack () { if command -v tmux >/dev/null 2>&1 && tmux has-session -t "$SESSION " 2>/dev/null; then tmux kill-session -t "$SESSION " fi if docker inspect "$CONTAINER " >/dev/null 2>&1 \ && [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER " 2>/dev/null) " = "true" ]; then docker exec "$CONTAINER " bash -lc ' pkill -INT -f "[r]oslaunch px4 outdoor_.*launch" || true pkill -INT -f "[r]oslaunch se3_controller sitl_se3_controller.launch" || true pkill -INT -f "[r]oslaunch diff_planner run_px4_sitl_gazebo" || true sleep 2 pkill -TERM -f "[g]zserver|[g]zclient|[p]x4|[r]osmaster|[r]osout" || true ' >/dev/null 2>&1 || true fi echo "Stopped tmux session and simulation processes." } status_stack () { if command -v tmux >/dev/null 2>&1 && tmux has-session -t "$SESSION " 2>/dev/null; then tmux list-windows -t "$SESSION " else echo "tmux session not running: $SESSION " fi docker ps --filter "name=$CONTAINER " --format "container={{.Names}} status={{.Status}}" } attach_stack () { need_tmux tmux attach -t "$SESSION " } case "$ACTION " in start) start_stack ;; restart) stop_stack start_stack ;; stop) stop_stack ;; status) status_stack ;; attach) attach_stack ;; -h|--help |help ) usage ;; *) usage exit 1 ;; esac
赋予执行权限:
1 chmod +x ~/code/start_sim_diff_planner_px4.sh
启动 Mid360 仿真:
1 ~/code/start_sim_diff_planner_px4.sh start mid360
启动深度相机仿真:
1 ~/code/start_sim_diff_planner_px4.sh start depth
常用命令:
1 2 3 4 ~/code/start_sim_diff_planner_px4.sh status ~/code/start_sim_diff_planner_px4.sh attach ~/code/start_sim_diff_planner_px4.sh stop ~/code/start_sim_diff_planner_px4.sh restart mid360
attach 后会进入 tmux 会话,三个窗口分别是:
1 2 3 gazebo controller diff_planner
tmux 常用快捷键:
1 2 3 Ctrl-b n 切到下一个窗口 Ctrl-b p 切到上一个窗口 Ctrl-b d 退出 tmux 会话但不停止仿真
如果 Gazebo 窗口没有显示,先确认创建容器时已经挂载 X11,并且宿主机执行过:
1 xhost +SI:localuser:root
八、常见问题
1. roslaunch 找不到 package
确认当前终端已经加载环境:
1 2 source /opt/ros/noetic/setup.bashsource ~/catkin_ws/devel/setup.bash
如果已经写入 ~/.bashrc,重新打开终端或执行:
2. Gazebo 找不到模型
检查模型是否已经复制到:
1 2 ~/.gazebo/models/ ~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/
3. Mid360 插件没加载出来
重点检查:
是否执行过 source ~/catkin_ws/src/Mid360_px4_sim_plugin/gazebo_setup.bash
liblivox_laser_simulation.so 是否已经复制到 ~/PX4-Autopilot/build/px4_sitl_default/build_gazebo-classic/
Mid360 和 iris_mid360 模型是否已经复制到 PX4 的 Gazebo models 目录
4. 出现 obs Land enabled
这通常是地理围栏触发,不一定是程序错误。先确认仿真流程是否还能继续,再决定是否调整参数。
总结
这套流程的关键是先跑通 PX4 SITL + MAVROS,再接入 Diff-Planner。深度相机和 Mid360 的差异主要在 Gazebo 模型、插件和对应的 launch 文件。