ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Docker的PX4-ROS2开发环境搭建:从仿真到真机全链路实践

基于Docker的PX4-ROS2开发环境搭建:从仿真到真机全链路实践 搞过PX4和ROS2联调的人都知道最耗时间的往往不是业务代码本身而是搭环境。PX4固件版本和ROS2发行版只要一不匹配编译报错能查一晚上Ubuntu系统被各种依赖装到面目全非后哪天崩了只能重装。我后来把整套PX4-ROS2开发环境塞进Docker这个问题才算根治。这篇就是我在Docker里搭建、使用PX4-ROS2联合开发环境的完整记录包含基础镜像选型、容器启动参数、PX4编译、MicroROS Agent配置、Gazebo仿真以及从仿真到连接QGroundControl的完整链路适合每一位用Ubuntu做无人机二次开发、需要把ROS2跑通的同学参考。1. Docker、PX4、ROS2三者怎么配合才能不白踩坑1.1 无人机开发为什么需要环境隔离先讲个不算笑话的真实经历。我最早在物理机上装PX4稳定版一切正常结果为了跑一个behavio树库升级了ROS2的某个核心包把PX4依赖的Fast DDS版本顶掉仿真直接起不来。那两天我把能搜到的教程翻了个遍最后发现是第三方库把系统级依赖改了。这就是没有环境隔离的代价你以为只动了一个包实际牵一发动全身。Docker解决的核心问题就一句话把构建环境、运行时依赖、工具链彻底锁死在一个可复现的容器里。PX4用的是自己的交叉编译器、CMake版本、Python依赖ROS2又有自己的一套ament生态和消息生成器。两边都放在宿主机上早晚会因为某个implicit依赖打架。容器化之后宿主机只负责提供CPU、GPU、USB设备和显示转发其余全在隔离层里重装了宿主机也不影响开发环境。1.2 这套环境能覆盖哪些开发环节先说清楚边界。我目前的日常开发流程已经基本全部在容器内完成包括PX4固件的获取、子模块更新、编译和直接生成SITL仿真固件ROS2功能包的创建、构建和运行比如无人机状态订阅、offboard控制脚本通过MicroROS Agent建立PX4与ROS2之间的消息桥梁启动Gazebo头进行PX4 SITL仿真并接收无人机状态数据宿主机或容器内启动QGroundControl查看飞行状态和日志真机开发时通过USB直通把Pixhawk接入容器用px4.py导出参数或刷写固件但有一类工作我不建议放进容器需要极高实时性的控制算法原型验证或者需要绑定专业无人机仿真软件并占用大量GPU的渲染任务。虽然Docker也能透传GPU但复杂场景下性能和调试体验都不如直接宿主除非你用的是nvidia-container-toolkit且显卡驱动很干净。另外如果你要做的是PX4底层驱动开发比如新增一个传感器驱动并需要反复插拔硬件、抓取时序波形容器化反而会给你操作设备节点增加一层麻烦这类工作还是放到物理机更顺手。1.3 方案对比Docker、虚拟机、双系统、物理机我整理了一张表是我自己给团队新人做环境方案时的对比结论方案环境隔离编译性能图形界面/GPUUSB设备可复现性我的推荐场景Docker容器好接近原生支持需配置支持需映射极好日常PX4-ROS2联合开发传统虚拟机好损耗明显弱一般较好快速尝鲜不建议编译双系统无隔离原生原生原生差不介意系统脏了重装物理机直接装无原生原生原生差硬件驱动/RT调试看完应该很清楚了除非你有特殊理由否则Docker是最优解。编译性能损耗主要在磁盘I/O写代码完全没感觉频繁编译时建议把源码目录挂载到宿主机避开Docker桌面版在挂载层上的I/O瓶颈。2. 镜像选型与宿主机准备从零拉起一个可用环境2.1 宿主机要装的东西容器环境再方便宿主机也有几件事必须做。我用的宿主机是Ubuntu 22.04你可以根据顺手程度换其他发行版但下面这几点是通用的。安装Docker Engine不要用老旧系统自带的老版本。装完把当前用户加入docker组省得每条命令都sudo。安装nvidia-container-toolkit如果你用的是NVIDIA显卡。这是让容器里能调用GPU的官方方案没有它即使你加了--gpus all也会报错。装好X11相关包。容器内要弹Gazebo、RViz2窗口必须让容器访问宿主的X server。我用的是X11转发方式后面会细说。关闭或调整防火墙/代理工具避免干扰UDP端口映射。PX4 SITL默认会用14540、14550、18570等端口代理工具可能会拦截多播或UDP广播。装Docker的命令我就不重复贴官网的长篇了记住一条Docker的版本直接影响后续镜像兼容性尤其是Docker Desktop与Linux版在挂载性能上差别较大。我自己的建议是能上Linux原生Docker Engine就别用Docker Desktop虽然桌面版界面好看但开发体验和稳定性差距肉眼可见。2.2 基础镜像怎么选PX4官方其实提供了现成的Docker镜像名字叫px4io/px4-dev下面带不同tag区分环境。项目非常早期我直接用过px4io/px4-dev-ros2-humble这个tag里面已经预装了ROS2 Humble、Gazebo、依赖工具、Qt库等拿到手就能编译PX4固件。如果你用Ubuntu 24.04 ROS2 Jazzy也有对应的tag。对照关系大致如下Ubuntu版本ROS2发行版推荐PX4官方镜像tagPX4常用版本20.04Foxypx4io/px4-dev-ros2-foxy1.13.x22.04Humblepx4io/px4-dev-ros2-humble1.14.x、1.15.x24.04Jazzy需要自测或使用px4-dev-base1.16.x后续我自己主力环境是Ubuntu 22.04 ROS2 Humble PX4 1.14.3这个组合非常稳。注意的是镜像tag只在首次pull时存在选型问题后续完全可以通过Dockerfile再叠一层不必纠结镜像是否包含所有工具。比如我在官方镜像基础上额外装了python3-pip、vim、git-lfs、nano、还有一组调试工具费不了几行但日常用起来顺手很多。2.3 自搭镜像 vs 官方镜像不少教程会建议从一个干净的Ubuntu镜像开始从零安装ROS2、PX4依赖理由是“知道每一条依赖装了什么”。我建议你第一次就别这么折腾直接用官方镜像。原因很实际PX4的编译依赖会随版本变化官方镜像已经针对特定版本验证过你要从零装至少多踩十几个坑而且这些坑大概率不是你技术问题纯是版本文档滞后。等你跑通一遍、对依赖有了感知再考虑精简自搭镜像也不迟。把官方镜像拉下来后建议立刻做一个本地tag留着比如px4-humble:dev后续容器就基于这个tag避免每次改动都从远端重新拉。3. 构建PX4-ROS2开发容器一条踩过无数遍的运行命令3.1 核心启动参数逐个拆解很多人第一次在Docker里跑PX4-ROS2容器起来了但Gazebo弹不出来或者QGroundControl连不上仿真多半是启动参数没给全。下面这条命令是我实际使用的基础版每一个参数背后都有对应原因docker run -it \ --name px4_ros2_dev \ --network host \ --privileged \ -e DISPLAY$DISPLAY \ -e QT_X11_NO_MITSHM1 \ -v /tmp/.X11-unix:/tmp/.X11-unix:rw \ -v /dev/dri:/dev/dri \ -v ~/px4_ws:/workspace \ -v /dev/ttyUSB0:/dev/ttyUSB0 \ px4io/px4-dev-ros2-humble:latest \ bash逐项解释一下--network host这是最容易忽略但影响最大的一项。PX4 SITL会向局域网发UDP数据包QGroundControl用UDP 14550端口接收MicroROS Agent用UDP 18570端口桥接。如果不设host网络容器内端口和宿主机之间要做大量映射而且多播包很可能传不出来。用host网络后容器直接用宿主机网络栈仿真数据天然可达省了无数烦恼。--privileged给容器足够权限访问设备节点尤其是后面要接Pixhawk时。如果你只在纯仿真环境可以不加但一旦插了USB转串口没反应第一个就要怀疑这行。加上--privileged后权限类问题的概率基本归零。-e DISPLAY$DISPLAY和-v /tmp/.X11-unix:/tmp/.X11-unix:rw这两个是X11图形转发核心。宿主机上要确认有GUI会话如果通过SSH远程登录还得用ssh -X或ssh -Y并把xauth装好。-e QT_X11_NO_MITSHM1这是给Qt程序打的下火针。Qt界面在X11转发模式下经常因为共享内存报段错误设置这个环境变量可以绕过Gazebo、QGroundControl都能受益。-v ~/px4_ws:/workspace源码挂载。容器内建的目录会被挂载目录覆盖所以PX4源码、ROS2功能包都放在这里保证宿主机也能直接编辑容器内编译两不耽误。-v /dev/dri:/dev/dri允许容器直接使用GPU的DRM渲染节点。如果没有这个目录可以忽略有的话建议挂上Gazebo渲染会更顺畅。-v /dev/ttyUSB0:/dev/ttyUSB0真机USB设备映射。注意这个设备路径在每次拔插后可能变化我后来改用了udev规则固定设备名不然每次都要检查是不是变成了ttyUSB1。3.2 用docker compose管理复杂参数命令行的容器跑起来后参数一长串容易漏也不好分享给同事。我大概用了不到一周就换成了docker compose把启动配置写进YAML文件团队里任何一个人docker compose up -d就能拉同一套环境。services: px4-ros2-dev: image: px4io/px4-dev-ros2-humble:latest container_name: px4_ros2_dev network_mode: host privileged: true environment: - DISPLAY${DISPLAY} - QT_X11_NO_MITSHM1 - NVIDIA_VISIBLE_DEVICESall - NVIDIA_DRIVER_CAPABILITIESall volumes: - /tmp/.X11-unix:/tmp/.X11-unix:rw - /dev/dri:/dev/dri - ~/px4_ws:/workspace - /dev/ttyUSB0:/dev/ttyUSB0 working_dir: /workspace stdin_open: true tty: true如果用到NVIDIA GPU记得加上NVIDIA_VISIBLE_DEVICES和NVIDIA_DRIVER_CAPABILITIES这是nvidia-container-toolkit读的环境变量不然--gpus all在compose里不会自动生效。我个人建议在compose里也写上working_dir一进容器就在工作区根目录省得每次cd。3.3 关于“用户身份”的一点经验PX4容器里默认是root用户这有好处也有麻烦。好处是编译、写系统目录都不愁权限麻烦是挂载目录里生成的文件属于root宿主机上你自己要修改就得sudo甚至删不掉。我见过同事把整个workspace的owner全改成root最后只能sudo chown回来的情况。更稳妥的做法是运行时指定-u $(id -u):$(id -g)让容器内进程以宿主用户的身份运行。但这样会带来一个新麻烦容器内很多工具比如catkin、colcon需要写~/.ros等目录而这些目录的owner是镜像里预建的用户未必匹配宿主uid。我目前的做法是保留root运行但每次编译完在宿主机上做一次sudo chown -R $USER:$USER ~/px4_ws基本无痛。正式做一个项目镜像时可以写entrypoint脚本自动处理目录权限但现在这个方案最省事。4. 容器内编译PX4固件与ROS2工作空间4.1 PX4源码获取与编译前的准备进到容器后先在/workspace下把PX4源码clone下来。官方仓库比较大建议使用--recurse-submodules保证子模块一起拉取cd /workspace git clone --recursive https://github.com/PX4/PX4-Autopilot.git cd PX4-Autopilot git checkout v1.14.3 git submodule update --init --recursivePX4源码里包含很多子模块比如Firmware、uORB生成工具、NuttX、mavlink不先更新子模块直接编译大概率会在某个阶段卡在“could not find package”。即使clone时带了--recursive我建议还是显式执行一次git submodule update --init --recursive确保切分支后子模块版本同步。第一次编译前还要确认px4board工具链可用。进入容器后先跑px4_usage如果输出帮助信息说明官方镜像里的PX4环境变量已经加载好了。如果是自己搭的镜像可能得手动source一下~/catkin_ws/devel/setup.bash之类的文件。这一步很多人漏掉结果直接make报一堆找不到cmake、ninja的错误。4.2 编译PX4 SITL仿真固件编译仿真固件用下面这条cd /workspace/PX4-Autopilot make px4_sitl gazebo这里有个很深刻的体验第一次编译会把大量依赖重新编译时间可能长达20到40分钟取决于核数和磁盘速度。编译过程中如果网络不稳定某些依赖可能下载失败建议用make px4_sitl gazebo -j4先降并行数确保成功后再用-j$(nproc)抢速度。别问我怎么知道的家里宽带抖动那晚我连续编译失败四次。如果你只需要仿真带不带图形界面的区别也很重要。我平时调试ROS2节点逻辑就用make px4_sitl不带Gazebo这样没有图形窗口资源占用少需要看无人机模型运动轨迹时再用make px4_sitl gazebo。这个选择直接影响调试效率建议都试一遍。4.3 初始化ROS2 workspace并编译功能包PX4编译好之后接着在容器内建一个ROS2工作空间。惯例上我会单独建ros2_ws不和PX4源码混在同一个目录避免colcon build和PX4的CMake缓存互相干扰mkdir -p /workspace/ros2_ws/src cd /workspace/ros2_ws colcon init接着把你的功能包放进src/。如果是第一次跑我建议先跑官方示例中的px4_ros_com包里面包含px4_msgs、px4_ros_com等与PX4通信的核心库。克隆方式很简单cd /workspace/ros2_ws/src git clone https://github.com/PX4/px4_msgs.git git clone https://github.com/PX4/px4_ros_com.git然后编译cd /workspace/ros2_ws source /opt/ros/humble/setup.bash colcon build --symlink-install如果你不熟悉--symlink-install我解释一下它把Python源码以符号链接方式安装改完代码后不需要重新build就能生效写节点的时候体验提升巨大。C库如果改了仍然需要重新build对应的包。4.4 打通PX4与ROS2的桥梁MicroROS AgentPX4本身不直接发ROS2话题它是通过uORB内部通信再经MicroROS Agent转成DDS话题ROS2这边才能收到。容器内跑一个Agent即可桥接cd /workspace/ros2_ws source install/setup.bash ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888这里端口8888不是随便选的。PX4 SITL默认的MicroROS端口是8888如果你改别的端口需要在PX4启动参数里也对应改。启动Agent之后需要重新启动PX4仿真固件让它通过eProsima Fast DDS发现Agent。我发现一个规律先启动Agent再启动Gazebo里的PX4连接成功率非常高反过来则经常等到超时。如果运行micro_ros_agent提示包不存在确认你是否编译过px4_ros_com例子中的microros依赖该包在px4_ros_com/scripts下有个build_micro_ros.sh脚本跑一遍即可。官方镜像一般不预装这个第一次用务必先执行。5. 跑通仿真Gazebo、QGroundControl与RViz2的完整链路5.1 启动PX4 SITL仿真并验证话题启动仿真我一般分两步走。先确保PX4进程正常cd /workspace/PX4-Autopilot make px4_sitl gazebo等Gazebo窗口弹出来无人机会出现在停机坪场景里。随后另开一个终端进入容器docker exec -it px4_ros2_dev bash source /opt/ros/humble/setup.bash source /workspace/ros2_ws/install/setup.bash现在可以看PX4的uORB话题是否传到ROS2ros2 topic list正常情况下能看到/fmu/out/vehicle_attitude、/fmu/out/vehicle_global_position、/fmu/out/vehicle_odometry等话题。跑到这一步PX4和ROS2链路已经通了。如果话题列表里没有这些大概率是MicroROS Agent没连上回到上一小节检查顺序和端口。5.2 QGroundControl如何连接容器里的仿真QGroundControl跑在宿主机上是最省心的方案。容器使用host网络后QGC会自动发现本地的PX4 SITL实例端口是14550不需要特殊设置。如果没自动发现手动添加通信方式为UDP监听端口14550即可。如果你非要把QGC也跑在容器里也可以但需要额外把视频渲染库装全、把/dev/dri挂进去我更建议QGC放宿主机。原因很明确QGC只是一个地面站工具放在容器里只会增加占用而宿主机原生跑它窗口切换、USB连接、录像都更舒畅。5.3 RViz2查看三维状态ROS2和PX4联通后想可视化无人机的姿态和轨迹用RViz2最直接。既然要把话题数据可视化得在容器里装rviz2apt install -y ros-humble-rviz2 source /opt/ros/humble/setup.bash rviz2等界面出来后Add一个RobotModel并选择对应的topic或者直接Add一个Path订阅/fmu/out/trajectory就能看到无人机位置。如果打开rviz2出现控件卡死或黑屏还是X11转发底子没打好回到第3章的启动参数检查QT_X11_NO_MITSHM1是否设置。从经验来看RViz2适合调试ROS2侧的坐标变换、话题频率如果你只关心PX4控制效果QGroundControl看飞行姿态信息就够了两者不用同时开。6. 真机接入、版本锁定与最现实的坑6.1 USB设备映射与Pixhawk连接从仿真切到真机开发容器需要的改动其实很小。最核心的是把Pixhawk的USB设备映射进容器。我先说最直接的排查链路宿主机执行lsusb确认是否存在“Hex Cubana”或“3D Robotics”这类设备ID。执行ls /dev/ttyUSB*看设备是ttyACM0还是ttyUSB0。Pixhawk V5X通常映射为ttyACM0而部分USB转串口是ttyUSB0。用--privileged且-v /dev/ttyACM0:/dev/ttyACM0启动容器或者直接挂载-v /dev/bus/usb:/dev/bus/usb。很多人的问题是设备节点在容器里存在但PX4还是找不到。这时候多半是宿主机上的ModemManager或brltty抢占了USB串口。解决方式sudo systemctl disable brltty sudo systemctl stop brltty另外docker compose里的-v /dev/ttyACM0:/dev/ttyACM0在设备拔插后会失效最好使用--device-cgroup-rule或者绑定整个USB目录我用的-v /dev/bus/usb:/dev/bus/usb是整体透传拔插后重新进容器就能识别不用改配置。6.2 固件版本与文件挂载权限PX4和ROS2都极其看重版本一致性。我给你列几个我亲手踩过的版本组合组合结果PX4 v1.14.3 ROS2 Humble px4_msgs main分支编译正常但运行时消息版本不匹配PX4 v1.13 ROS2 Foxy px4_msgs v1.13.0稳定PX4 v1.14.3 ROS2 Humble px4_msgs v1.14.0非常稳定我用了半年PX4 v1.15 ROS2 Humble px4_msgs早期main可跑但偶尔话题延迟所以每次在新环境拉px4_msgs别直接clone默认分支一定要切到对应PX4大版本的tag或branch。这个坑普遍到我想在所有教程开头加粗。6.3 编译慢、磁盘大与容器清理PX4 Gazebo完整编译完镜像体积轻松超过10GB如果历史版本多Docker磁盘占用非常夸张。我一般每季度做一次docker system prune -a前提是做好镜像之外的数据备份因为源码在挂载目录里不受影响。另外如果你用Docker Desktop虚拟磁盘膨胀后性能下降明显记得定期清理。6.4 从我的流程里提取一份常规检查清单每次新建一个开发环境或者隔了很久再回到PX4-ROS2项目我会按下面的顺序快速验证环境可用性避免一上来就花半小时编译启动容器确认host网络和X11转发正常。跑一个最小PX4 SITL让Gazebo起来。另开终端跑ros2 topic list确认PX4话题出现。启动QGC确认无人机姿态刷新。执行一次offboard小脚本比如官方offboard例程看螺旋桨是否按预期动起来。这五步全部通过说明Docker内开发PX4-ROS2的整套链路是通的可以安心写业务代码了。我这套环境从半年前的使用频率来看让我最庆幸的不是省了多少次重装系统的功夫而是每次换电脑、换工位只要装着Docker拉一下镜像十分钟就能回到完全一致的开发环境。最后补一个我自己养成的小习惯所有的启动配置和版本记录都写进项目仓库里的README或docker-compose.yml旁边这样即使半年后回来或者同事需要复现都不会因为“我记得当时用了某个版本”而卡壳。做机器人开发环境可复现这事比多写一百行业务代码更重要。
返回列表