ARTICLE DETAIL

资讯详情

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

PX4与ROS协同仿真:从键盘控制到自主飞行航点任务全指南

PX4与ROS协同仿真:从键盘控制到自主飞行航点任务全指南 我见过太多人刚燃起做无人机的心就兴冲冲买了套件回来捣鼓真机结果往往是一天改飞控参数一台飞机摔了再改第二天电机烧了GPS没校准好飞起来像个醉汉。我自己早年也走过这条路直到切换到PX4 与 ROS 协同仿真这条开发管线才真正把“改代码、试飞、调参”这个循环从按天计算压缩到按分钟计算。这篇博文就围绕这条主线展开从最基础的键盘控制一台虚拟无人机开始一步步搭建仿真环境最后实现自主飞行航点任务。适合刚入门飞控开发、准备做无人机方向毕设或者已经玩过 ROS 小车想横向拓展到无人机的朋友。很多人在这一步最大的误区是想直接跳过基础一上来就跑 SLAM、跑避障、跑视觉跟随。真出了问题根本分不清是 PX4 飞控的问题、MAVROS 转发的问题还是上层算法的问题。我建议老老实实先把键盘控制这条链路跑通再谈自主飞行。1. 协同仿真为什么是一笔划算的“零成本试错”投资1.1 三个软件到底各管什么很多初学者容易把 PX4、ROS、Gazebo 三者的边界搞混先把这个搞清楚后面才不会两眼一抹黑。PX4是开源飞控固件它把 IMU、GPS、气压计、磁力计这些传感器数据读进来跑姿态估计、位置估计、姿态控制和位置控制最终输出电机转速指令。在仿真环境里它编译成普通 Linux 进程运行叫 SITLSoftware In The Loop。对 PX4 来说它感知到的世界就是从 Gazebo 模拟出来的传感器数据它输出的控制指令也真实作用在虚拟飞机上。ROS是连接飞控和上层算法的“神经网络”。飞控本身并不理解什么叫“去三号航点”它只理解速度指令、位置指令、姿态指令这些底层控制目标。ROS 负责把人的意图或算法的决策翻译成飞控能看懂的指令再通过 MAVROS 这个桥接节点以 MAVLink 协议发给 PX4。同时PX4 的状态估计结果、传感器数据也会通过 MAVROS 回传到 ROS供上层算法使用。Gazebo是一个物理仿真环境它负责提供一个虚拟世界重力、空气阻力、碰撞、地面、光照、传感器噪声。没有 GazeboPX4 就成了没有身体的“大脑”没有物理反馈控制算法根本无法闭环。1.2 键盘控制是整个开发链路的“最小闭环”很多人觉得用键盘控制无人机太小儿科实际上这恰恰是理解协同仿真数据流的最佳方式。你按下一个按键数据要从键盘事件一路流到 Gazebo 里的虚拟电机再流回你的终端显示这个闭环里覆盖了 ROS 话题通信、MAVROS 协议转换、PX4 控制模式切换、Gazebo 物理反馈几乎是整个开发框架的微缩版。把这条链路走通之后再去做自主飞行本质上只是把“人按键盘”替换成“代码发指令”整体架构没有任何变化。所以我在带人入门的时候一定会要求先跑通键盘控制否则后面遇到问题会非常难排查。1.3 这套方案适合哪些人我总结了一下下面几类人最适合从这套仿真流程入手无人机相关专业学生毕设或课程项目需要验证控制算法、路径规划算法。ROS 开发者想从地面机器人扩展到空中机器人但不想一开始就冒着炸机风险。硬件爱好者想在买新飞控和机架之前比较不同参数对飞行的影响仿真先行。行业项目预研人员比如做电力巡检、农业植保航线规划的方案验证。硬件方面要求不高一台 8GB 内存以上的普通电脑就能跑得很舒服。如果你有 NVIDIA 显卡Gazebo 渲染会更流畅但并不是必须。2. 从零搭环境PX4 ROS Gazebo 的版本选型和安装实战2.1 版本搭配为什么我推荐 Ubuntu 20.04 Noetic PX4 1.14版本选型是整个环境搭建里最容易被轻视的一步。很多人图新鲜直接用 Ubuntu 22.04 ROS 2 Humble然后发现很多老教程是基于 ROS 1 写的CtrlC 复制下来的命令跑都跑不通心态直接爆炸。我的建议非常明确新手老老实实用 Ubuntu 20.04 ROS Noetic PX4 v1.14.x Gazebo 11 MAVROS。组件推荐版本说明操作系统Ubuntu 20.04生态最成熟ROS Noetic 官方支持ROSNoeticROS 1 最后一个长期支持版本教程数量碾压 ROS 2PX4v1.14.3当前最稳定的版本官方文档丰富GazeboGazebo 11Ubuntu 20.04 默认版本PX4 支持度高MAVROSNoetic 对应版本稳定可靠社区排错经验多有朋友会问现在 ROS 2 不是大势所趋吗确实但从学习成本和资源丰富度来看ROS 1 Noetic 在无人机生态里依然是主流。PX4 官方大量示例、MAVROS 的文档、各种开源项目绝大多数都是 ROS 1 环境的。先用 Noetic 把整个机制跑通后面想迁移到 ROS 2 时概念都是相通的。2.2 安装步骤与常用命令这里我把完整过程写出来你跟着敲就行。先装 ROS Noeticsudo sh -c echo deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main /etc/apt/sources.list.d/ros-latest.list sudo apt install curl curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.key | sudo apt-key add - sudo apt update sudo apt install ros-noetic-desktop-full如果国外源速度不理想可以换成清华或中科大镜像把packages.ros.org替换成镜像地址即可这是常规操作。安装完成后初始化 rosdepecho source /opt/ros/noetic/setup.bash ~/.bashrc source ~/.bashrc sudo apt install python3-rosdep python3-rosinstall python3-rosinstall-generator python3-wstool build-essential sudo rosdep init rosdep update提示rosdep update偶尔会因为网络问题失败多试几次一般能过。如果你所在网络环境访问 GitHub 和 ROS 官方源不稳定可以考虑配置一下rosdep的国内镜像源具体方法在 ROS 社区里有很多人分享过。然后编译 PX4 源码cd ~ git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot git checkout v1.14.3 git submodule update --init --recursive这里有一个需要特别注意的点PX4 仓库包含大量的子模块git clone --recursive如果因为网络原因中断后面一定要手动补拉子模块否则编译到一半会报各种头文件找不到的错误。接下来运行依赖安装脚本bash ./Tools/setup/ubuntu.sh这个脚本会自动安装 GCC、CMake、Python 依赖和 Gazebo 等一整套工具链中间会询问几次是否继续一路回车即可。等它跑完之后就可以尝试编译并启动仿真了make px4_sitl gazebo第一次编译需要下载大量依赖并编译整个固件慢的话二三十分钟很正常。看到终端里出现[INFO] [px4] Startup script returned并且弹出 Gazebo 界面说明 PX4 SITL 已经跑起来了。最后安装 MAVROSsudo apt install ros-noetic-mavros ros-noetic-mavros-extras sudo /usr/lib/ros/noetic/lib/mavros/install_geographiclib_datasets.sh第二个命令是下载地理数据集的脚本MAVROS 在启动时需要加载这些数据来做 UTM 坐标转换。这个脚本在有些网络环境下会卡住。如果你只是做仿真暂时卡住也可以强行跳过很多基础功能不受影响但后续如果要跑 GPS 相关的真实定位还是建议找时间把它装好。2.3 编译启动时最容易卡住的三个点第一个卡点是git submodule update --init --recursive反复失败。这时候不要重复make而是先检查子模块是否完整。在PX4-Autopilot目录下执行git submodule status看到前面有-的说明对应子模块没有拉下来多试几次或者用git config --global submodule.recurse true后再 update 一次。第二个卡点是 Gazebo 启动后黑屏或卡在加载界面。Gazebo 首次运行会尝试下载一些标准模型文件如果下载不了就会一直卡住。一个稳妥的做法是提前把模型库整理好放到~/.gazebo/models目录下然后重启 Gazebo。第三个卡点是编译到一半内存不足导致进程被杀。X86 平台上同时跑编译和后续的 Gazebo 仿真内存占用很容易超过 4GB。建议 8GB 内存起步如果物理内存确实小可以加 4GB 的 Swapsudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile3. 键盘控制一台虚拟无人机按下按键背后发生了什么3.1 数据链路从按键事件到电机模型按下键盘上的i键到无人机动起来中间走了一条非常清晰的链路我先把这条链路讲清楚后面每一步操作你都知道自己在干什么。键盘按键事件被teleop_twist_keyboard节点捕获它根据你按的键生成一个geometry_msgs/Twist消息包含线速度x/y/z和角速度yaw。在 ROS 里这种“速度指令”通常发布在/cmd_vel话题上。我们把/cmd_vel重映射到 MAVROS 的速度指令话题MAVROS 把这个 Twist 消息打包成 MAVLink 的SET_POSITION_TARGET_LOCAL_NED消息通过 UDP 发送给本机运行的 PX4 SITL 进程。PX4 在收到指令后基于当前的状态估计和外部目标计算出电机转速再把这个结果通过仿真接口发给 Gazebo 里的电机模型Gazebo 根据物理引擎更新飞机的姿态和位置同时把新的传感器读数反馈给 PX4。PX4 的状态变化会通过 MAVLink 回传给 MAVROSMAVROS 再发布成 ROS 话题比如/mavros/local_position/pose、/mavros/state。也就是说你在终端里看到的飞机状态和实际 Gazebo 虚拟世界中的飞机状态是一条完整的闭环数据流。3.2 启动仿真和 MAVROS需要开两个终端。第一个终端启动 PX4 和 Gazebocd ~/PX4-Autopilot make px4_sitl gazebo第二个终端启动 MAVROS 与 PX4 的连接roslaunch mavros px4.launch fcu_url:udp://:14540127.0.0.1:14557这个fcu_url指定了 PX4 SITL 的通信端口。PX4 在 SITL 模式下默认通过 UDP 14557 端口接收 GCS 或外部设备的数据MAVROS 则监听 14540 端口。启动完成后可以在另一个新终端验证连接状态rostopic echo /mavros/state看到connected: True同时mode是MANUAL或AUTO.LOITER说明链路已经通了。这一步非常重要如果你发现connected: False后面的操作全部无法进行。3.3 先把无人机稳住持续发布位置指令的悬停脚本这里要说一个最关键的概念PX4 的Offboard 模式下要求外部持续不断地发送设定值setpoint如果它一段时间内没有收到新的指令会认为外部控制失效自动退出 Offboard 模式甚至触发失控保护。所以不能一启动就切 Offboard必须先有一个节点在持续发布指令。我通常先跑一个最简单的悬停脚本让无人机先稳定在 2 米高度#!/usr/bin/env python3 import rospy from geometry_msgs.msg import PoseStamped rospy.init_node(hold_position) pub rospy.Publisher(/mavros/setpoint_position/local, PoseStamped, queue_size10) rate rospy.Rate(20) pose PoseStamped() pose.pose.position.x 0.0 pose.pose.position.y 0.0 pose.pose.position.z 2.0 while not rospy.is_shutdown(): pub.publish(pose) rate.sleep()把这段代码保存为hold_position.py加上执行权限chmod x hold_position.py在第三个终端运行它。这里解释一下为什么发布频率要 20HzPX4 的 Offboard 指令过期时间通常是 0.5 秒左右如果发布频率太低超过这个时间窗口飞控就会认为指令超时所以 10~20Hz 比较稳妥。3.4 解锁并切换到 Offboard 模式等待悬停脚本跑起来几秒钟之后在第四终端执行服务调用rosservice call /mavros/set_mode custom_mode: OFFBOARD rosservice call /mavros/cmd/arming value: true执行成功后你会看到 Gazebo 里的无人机四个旋翼开始转动然后缓缓升起到两米高度。这里有一个先后顺序的细节必须先切 Offboard后解锁。如果先解锁再切 OffboardPX4 有可能会因为没有收到“足够多”的外部指令而拒绝切换。实际上 PX4 要求进入 Offboard 模式前外部已经以一定频率持续发送了至少 1 秒的 setpoint所以悬停脚本先跑起来是最稳妥的。3.5 用键盘接管方向控制现在无人机已经稳稳悬停在空中了接下来让键盘接管方向控制。新开一个终端rosrun teleop_twist_keyboard teleop_twist_keyboard.py cmd_vel:/mavros/setpoint_velocity/cmd_vel_unstamped注意后面的cmd_vel:是 ROS 话题重映射语法把 teleop 节点默认发布的/cmd_vel重定向到 MAVROS 的速度指令话题。MAVROS 同时提供了cmd_velTwistStamped和cmd_vel_unstampedTwist两个速度话题而 teleop_twist_keyboard 发布的是不带时间戳的 Twist所以必须用cmd_vel_unstamped。启动之后按一下i无人机应该会向前移动按j/l控制左右偏航按k停住按u/o/m/,/.控制各种斜向和旋转组合。在终端窗口里会打印出每个按键对应的速度值默认线速度是 0.5 m/s角速度是 1.0 rad/s在仿真里这个速度比较合适不用去改。注意如果你按了键但无人机没反应优先检查 MAVROS 是否还在运行、悬停脚本是否停止了发布以及当前模式是否还是 Offboard。很多时候是因为悬停脚本被 CtrlC 关掉了PX4 因为收不到新的设定值而退出了 Offboard 模式。键盘控制之所以用速度指令而不是位置指令是因为键盘按键本身是“二元”的按下代表正向速度、松开代表零速度。如果改用位置控制每按一次键都得算一个增量目标点不仅啰嗦而且很难实现平滑的手动操作。4. 从手动转向自主用航点任务让无人机自己飞4.1 自主飞行的本质把“人按键盘”替换成“代码发指令”键盘控制跑通之后自主飞行其实只是换了个“发指令的人”。在 Offboard 模式下PX4 并不关心指令是来自键盘还是来自一个 Python 脚本它只关心能不能持续收到外部控制目标。所以做自主飞行的核心就是写一个上层节点让它按照预定的逻辑依次发布位置目标并且判断飞机是否到达目标点到了就发下一个目标。这个逻辑在工程上叫“航点任务状态机”实现起来不复杂但它是后面所有智能飞行功能的地基。不管是巡检、搜救还是表演编队本质上都是航点任务的不同变体。4.2 航点飞行完整代码下面这段代码实现了一个矩形航线起飞到 2 米高度依次飞过 (0,0)、(4,0)、(4,4)、(0,4)最后回到原点。我加了不少注释方便你对照着理解。#!/usr/bin/env python3 import rospy import math from geometry_msgs.msg import PoseStamped from mavros_msgs.msg import State from mavros_msgs.srv import CommandBool, SetMode current_pose None current_state None def pose_cb(msg): global current_pose current_pose msg.pose def state_cb(msg): global current_state current_state msg rospy.init_node(waypoint_flight) rospy.Subscriber(/mavros/local_position/pose, PoseStamped, pose_cb) rospy.Subscriber(/mavros/state, State, state_cb) setpoint_pub rospy.Publisher(/mavros/setpoint_position/local, PoseStamped, queue_size10) arming_srv rospy.ServiceProxy(/mavros/cmd/arming, CommandBool) set_mode_srv rospy.ServiceProxy(/mavros/set_mode, SetMode) rate rospy.Rate(20) # 等待 MAVROS 连接 for _ in range(100): if current_state is not None and current_state.connected: break rate.sleep() # 先发布几秒原地悬停指令让 PX4 有足够时间接收 setpoint hold PoseStamped() hold.pose.position.z 2.0 for _ in range(100): setpoint_pub.publish(hold) rate.sleep() # 切换到 Offboard 并解锁 set_mode_srv(custom_modeOFFBOARD) arming_srv(valueTrue) # 矩形航点 waypoints [ (0.0, 0.0, 2.0), (4.0, 0.0, 2.0), (4.0, 4.0, 2.0), (0.0, 4.0, 2.0), (0.0, 0.0, 2.0), ] for wp in waypoints: target PoseStamped() target.pose.position.x wp[0] target.pose.position.y wp[1] target.pose.position.z wp[2] rospy.loginfo(f前往航点 {wp}) while not rospy.is_shutdown(): # 每次循环都把当前目标发出去 setpoint_pub.publish(target) if current_pose is None: rate.sleep() continue # 计算当前位置与目标点的距离 dist math.sqrt( (current_pose.position.x - wp[0]) ** 2 (current_pose.position.y - wp[1]) ** 2 (current_pose.position.z - wp[2]) ** 2 ) if dist 0.5: rospy.loginfo(f到达航点 {wp}) break rate.sleep() rospy.loginfo(全部航点完成切换为悬停)把脚本保存为waypoint_flight.py运行前先确保当前没有在跑键盘控制或悬停脚本否则会产生指令冲突chmod x waypoint_flight.py python3 waypoint_flight.py本机如果没有配置 conda 或者虚拟环境建议直接用python3运行。这个脚本里的距离判断阈值 0.5 米是经验值太小了可能在目标点附近反复震荡导致判断超时太大了误差积累明显。在不同的 PX4 参数配置下你可以适当调大或调小这个阈值。4.3 关于调参的思考为什么飞机会在航点之间画弧线很多朋友第一次跑航点飞行会发现飞机并不是直线飞向下一个航点而是在航点之间画了一条弧线。这是正常的原因在于 PX4 的默认位置控制器规划的是加加速度受限的平滑轨迹不是简单的“点到点直线”。它在计算轨迹时会考虑到最大速度、最大加速度和加加速度限制所以飞机会转一个小弯再切入目标点。如果这种弧线飞行不符合你的任务要求可以调整 PX4 的MPC_XY_VEL_MAX最大水平速度、MPC_ACC_HOR最大水平加速度和MPC_JERK_AUTO自动模式下的加加速度限制等参数。在 SITL 仿真里调参非常安全随便试错这也是协同仿真相比真机最大的优势。真机上你根本不敢把加速度参数调到离谱仿真里可以随意折腾。4.4 从航点飞行到避障导航的进阶路线航点飞行只是自主飞行最基础的一层。如果你后面想做类似 ROS 小车自主导航那样的事情在无人机上是这样展开的给 Gazebo 里的无人机模型挂载一个激光雷达或深度相机插件一般会模拟出/scan或/camera/depth/points话题。运行 SLAM 算法例如hector_slam、gmapping、cartographer或使用 PX4 自带的 EKF 定位结果构建环境的代价地图。引入全局路径规划器A*、RRT*和局部规划器TEB、DWA输出速度指令再通过 MAVROS 发送给 PX4跟在键盘控制里重映射/cmd_vel的套路一模一样。在做避障时要注意PX4 的 Offboard 模式接收速度指令时飞行高度控制要单独处理通常建议把 z 轴速度或高度保持在一个固定值避免避障过程中忽上忽下。5. 仿真中的常见坑与排查思路5.1 无人机起飞后乱飘或位置漂移这个现象在 SITL 仿真里不算罕见尤其是 Gazebo 物理引擎负载较高或者传感器模拟出现异常时。排查思路是先看/mavros/local_position/pose的话题输出是否正常确认 PX4 是否拿到了稳定的位置估计。如果话题输出跳变得很厉害最直接的办法就是重启 Gazebo 和 PX4 进程让模型重新加载一般能解决一大部分“玄学”问题。另外要注意如果你之前跑过真机参数、改过 EKF 相关配置那仿真里可能会出现定位异常建议用默认参数启动 PX4 SITL。5.2 无法切换到 Offboard 模式这是整个流程里最常见的问题原因归纳起来基本是这几个悬停节点没有持续发布 setpoint或者发布频率太低。用rostopic hz /mavros/setpoint_position/local查看频率是否达到 10Hz 以上。切模式和解锁的命令写错了模式名。PX4 的 Offboard 模式在 MAVROS 里拼写是OFFBOARD注意全大写。当前模式和指令类型不匹配。如果你之前跑的是速度控制切模式前又切换了位置指令PX4 内部可能会因为指令类型变化而拒绝切换保持一个稳定的话题类型持续发送几次再切模式。5.3 Gazebo 运行卡顿Gazebo 是比较吃资源的大型软件卡顿会直接影响飞行体验。如果你的电脑配置一般有几个降负载的办法启动 PX4 时使用不带复杂场景的模型例如make px4_sitl gazebo_iris或make px4_sitl gazebo_iris_empty可以省去加载地形和建筑物的开销。关闭 Gazebo 的 GUI 界面在启动参数里加GUIfalse虽然看不到飞机样子但能明显减少渲染占用。配合rqt_image_view或rviz看状态数据调试体验不会差太多。增加 Swap 空间避免内存不足导致的卡顿甚至进程被杀。在终端里执行export GAZEBO_MODEL_DATABASE_URI可以在部分情况下减少模型下载相关的阻塞不过慎重使用可能导致某些模型无法加载。5.4 编译和启动的其他细节编译时如果报fatal error: *.h: No such file or directory九成是子模块没拉全先git submodule update --init --recursive。启动 MAVROS 报端口冲突检查你是否同时开了多个 MAVROS 进程。终端环境变量丢了重新source ~/.bashrc。这些看起来是小问题但每一件都可能浪费半小时以上的时间。网上也有人把 ROS 安装封装成了一键脚本确实能省去手动配源的麻烦但我个人建议用之前扫一眼脚本内容确认它执行了哪些操作对后续排错有帮助。环境这东西自己亲手搭过一遍后面遇到问题才知道去哪看。从键盘控制到自主飞行中间跨越的其实不只是代码量更是对“谁在什么时刻给飞控发什么指令”这件事的掌控感。我在带新手的过程中发现愿意老老实实从键盘控制开始、把链路一步步验证清楚的人后面做路径规划、目标跟踪这些复杂任务时基本不会遇到难以定位的玄学问题。反而是那些急着跳级的人经常在三个系统之间来回折腾。如果你正在学 PX4建议按下键盘那一步多停留一会儿把发布的话题、转发的协议、飞控的模式变化都亲手观察一遍这会比看十篇教程都管用。
返回列表