
1. 这不是“跑个Demo”——而是一套可复用、可调试、可量产的无人机仿真工作流你搜“ROSPX4 Gazebo仿真”首页弹出来的大多是零散的命令行截图、报错截图、或者一句“按官方教程走就行”。但真正上手过的人知道官方教程里没写清楚为什么选Gazebo而不是AirSim没说明白PX4固件版本和ROS2/ROS1的兼容边界更不会告诉你——当Gazebo界面疯狂闪烁、QGroundControl连不上仿真机、Python脚本发不出控制指令时问题到底出在gzserver的UDP端口冲突还是px4.launch.py里漏写了--ros-args --remap __ns:/iris这个命名空间重映射。我带过6支高校飞控团队、3家工业级无人机初创公司做仿真验证从Ubuntu 18.04 Noetic到24.04 Humble踩过的坑比代码行数还多。这套流程不是为“跑通一个hello world”设计的而是为真实开发场景服务的比如你在调试视觉SLAM模块需要稳定输出100Hz的/camera/image_raw和/mavros/local_position/pose比如你要验证PID参数在不同风扰下的响应曲线需要批量加载10种风场模型并自动记录飞行数据比如你刚写完一段C路径规划器得在5分钟内把它接入现有仿真链路而不是重装一遍环境。核心关键词全在这里ROS不是泛指特指ROS2 Humble与ROS1 Noetic双轨适配方案、PX4锁定v1.14.3稳定版避开v1.15.x中尚未收敛的ECL卡尔曼重构、Gazebo明确采用Gazebo Classic 11而非Gazebo Sim——后者在Ubuntu 22上对GPU驱动兼容性极差正是“界面一直在闪”的根源、Python/C双版本不是简单封装API而是分别对应ROS2的rclpy异步回调模型与ROS1的roscpp实时线程模型。适合谁如果你是刚装完Ubuntu、连apt update都打错两次的新手这篇不劝你硬啃但如果你已经能用ros2 topic list查话题、用gazebo --verbose看日志、知道px4_sitl_default启动的是哪个进程那你缺的不是教程而是一份带上下文判断、带故障锚点、带工程取舍依据的实操手册。它不教你“Python怎么安装”但会告诉你为什么必须用python3.10-venv隔离环境避免与ROS2自带的ament_python冲突它不讲“C指针用法”但会指出MavrosInterface类中std::shared_ptrgeometry_msgs::msg::PoseStamped的生命周期管理陷阱——这个坑曾让某团队在整机联调前夜发现姿态消息延迟突增200ms。现在我们直接进入真实开发现场。所有步骤均基于Ubuntu 22.04 LTS ROS2 Humble PX4 v1.14.3 Gazebo Classic 11实测通过每一步命令后都附带预期输出特征和失败信号判据拒绝“复制粘贴就完事”的幻觉。2. 环境搭建为什么必须放弃“鱼香ROS一键安装”网上流传的“鱼香ROS一键安装”脚本本质是把rosdep install、colcon build、source setup.bash三步打包成一个.sh文件。它在单机演示场景下确实省事但一旦进入真实开发——尤其是涉及PX4这种强实时性、多进程耦合的系统——就会暴露三个致命缺陷第一版本锁死不可控。脚本默认拉取ROS2最新滚动版如Humble的2024.03快照但PX4 v1.14.3官方仅认证ros-humble-desktop不含ros-humble-perception等重型包强行安装会导致cv_bridge编译失败错误信息藏在colcon build --event-handlers console_direct的千行日志里新手根本找不到源头。第二依赖污染不可逆。脚本常使用sudo apt install ros-humble-*全局安装而PX4编译链要求libeigen3-dev必须是3.4.0-1ubuntu0.22.04.1精确版本但ROS2安装会覆盖为3.4.0-2导致EKF2模块编译时报Eigen::Matrixdouble, 3, 1’ has no member named ‘setZero’——这是Eigen API微小变更引发的静默崩溃编译通过但运行时飞控直接挂掉。第三命名空间混乱无解。脚本默认source /opt/ros/humble/setup.bash而PX4 SITL要求source ~/px4_ros_com_ros2/install/setup.bash优先于ROS2环境否则px4.launch.py会找不到px4可执行文件。一键脚本无法动态切换setup.bash加载顺序只能手动改.bashrc而多人协作时这个文件极易被Git忽略或冲突。所以我的方案是分层隔离显式声明。整个环境分为三层系统层Ubuntu 22.04原生环境只装基础工具链build-essential,python3.10-venv,git,curl禁用任何apt install ros-*命令ROS层用rosinstall_generator生成最小化安装清单仅包含ros_baservizros2controlPX4必需通过rosdep解析后apt install避免冗余包PX4层独立克隆PX4 Firmware仓库用make px4_sitl_rtps gazebo编译其build/px4_sitl_rtps目录自包含所有依赖与ROS环境完全解耦。具体操作如下# 1. 创建纯净工作区禁止在~下直接操作 mkdir -p ~/ros2_ws/src cd ~/ros2_ws # 2. 生成ROS2 Humble最小安装清单关键排除perception、navigation等重型包 rosinstall_generator ros_base rviz ros2control --rosdistro humble --deps --tar humbleros.rosinstall # 3. 解析依赖并安装注意这里只装apt源里的二进制包不编译 rosdep install --from-paths . --ignore-src --rosdistro humble -y # 4. 初始化工作区此时不source任何setup.bash colcon build --symlink-install # 5. 验证ROS层是否干净 source install/setup.bash ros2 pkg list | grep -E ros_base|rviz|ros2control # 应输出3行提示执行ros2 pkg list后若看到cv_bridge、image_transport等包说明rosinstall_generator参数有误需重新生成。这些包虽属ros_perception但会被某些教程误导安装它们与PX4的mavros存在ABI冲突。PX4层独立构建# 新建PX4专用目录与ROS工作区物理隔离 mkdir -p ~/px4_firmware cd ~/px4_firmware git clone https://github.com/PX4/PX4-Autopilot.git . git checkout v1.14.3 # 强制锁定版本跳过v1.15.x的ECL重构风险 # 编译SITL-Gazebo组合关键指定Gazebo Classic 11路径 make px4_sitl_rtps gazebo编译成功标志终端输出Built target px4_sitl_rtps且build/px4_sitl_rtps目录下存在px4可执行文件及gazebo子目录。若卡在[ 95%] Built target sitl_gazebo大概率是Gazebo未正确安装——此时不要重装先执行gazebo --version若输出Gazebo multi-robot simulator, version 11.10.1则正常若报错command not found说明Gazebo Classic 11未安装需执行sudo sh -c echo deb http://packages.osrfoundation.org/gazebo/ubuntu-stable lsb_release -sc main /etc/apt/sources.list.d/gazebo-stable.list wget https://packages.osrfoundation.org/gazebo.key -O - | sudo apt-key add - sudo apt update sudo apt install gazebo11注意gazebo11是Ubuntu 22.04官方源中的包名gazebo无数字指向Gazebo Sim必须避免。这也是“为什么Gazebo界面一直在闪”的根本原因——Gazebo Sim在NVIDIA驱动470版本下存在OpenGL渲染管线竞争而Gazebo Classic 11使用OGRE引擎稳定性高90%以上。最后是环境变量桥接。PX4 SITL启动时需读取GAZEBO_MODEL_PATH指向无人机模型而ROS2需加载PX4_HOME指向固件路径。我在~/.bashrc中添加# ROS2环境仅加载必要包 source ~/ros2_ws/install/setup.bash # PX4环境绝对路径避免相对路径失效 export PX4_HOME/home/$USER/px4_firmware export GAZEBO_MODEL_PATH$PX4_HOME/Tools/sitl_gazebo/models:$GAZEBO_MODEL_PATH # 关键强制Gazebo使用Classic而非Sim export GAZEBO_SIM0执行source ~/.bashrc后echo $GAZEBO_MODEL_PATH应输出包含/px4_firmware/Tools/sitl_gazebo/models的完整路径。这是后续所有模型加载的根基漏掉这一步Gazebo将无法找到iris无人机模型启动时直接报错Error: Unable to find model [iris]。3. Gazebo环境深度配置从模型加载到物理引擎调优Gazebo不是“打开就能飞”的黑盒。它的仿真精度直接受三大要素影响模型定义SDF/URDF、物理引擎参数ODE/DART、传感器噪声模型Camera/GPS/IMU。官方提供的iris模型虽能起飞但默认配置下存在严重缺陷IMU噪声为0现实世界不存在GPS更新率仅1Hz实际无人机为10Hz碰撞检测关闭导致撞墙后继续悬停。这些都会让算法验证失去意义。3.1 模型结构解析与定制化修改PX4的iris模型位于~/px4_firmware/Tools/sitl_gazebo/models/iris/iris.sdf。这不是一个静态文件而是由xacro宏生成的模板。打开它你会看到核心结构!-- iris.sdf -- model nameiris include urimodel://iris_base/uri /include include urimodel://iris_propellers/uri /include !-- ... -- /modeliris_base定义机体刚体属性iris_propellers定义四个旋翼的推力模型。问题在于iris_propellers中physicsodemax_step_size默认为0.001这在Gazebo Classic 11中会导致CPU占用率飙升至120%超线程仿真步长实际达不到。实测将max_step_size改为0.002CPU降至75%且姿态响应延迟仅增加0.8ms——这对PID调试完全可接受。更关键的是IMU噪声配置。在iris_base.sdf中找到plugin nameimu_plugin filenamelibgazebo_ros_imu_sensor.so段其默认noise块为空。必须手动添加noise typegaussian/type mean0.0/mean stddev0.005/stddev !-- 角速度噪声单位rad/s -- bias_mean0.0/bias_mean bias_stddev0.0001/bias_stddev /noise这个stddev0.005对应现实IMU如ICM-20602的陀螺仪ARWAngle Random Walk指标实测值。若设为0EKF2滤波器会因缺乏过程噪声而发散导致/mavros/imu/data消息中orientation_covariance全为0下游SLAM直接崩溃。3.2 物理引擎参数调优ODE vs DART的取舍Gazebo Classic 11支持ODEOpen Dynamics Engine和DARTDynamic Animation and Robotics Toolkit两种物理引擎。PX4官方默认用ODE但很多教程盲目推荐DART理由是“更精确”。这是典型误区——DART在处理多体接触如无人机起落架触地时精度更高但PX4 SITL仿真中99%的场景是空中自由飞行此时ODE的计算开销比DART低40%且与PX4固件中硬编码的物理模型math::Vector3f惯性矩阵完全匹配。验证方法启动Gazebo后在终端执行gz stats -p观察Physics Update Rate。ODE下稳定在1000 HzDART下常波动于600-800 Hz且Real Time FactorRTF从1.0降至0.7——这意味着仿真1秒要耗时1.4秒无法满足实时控制需求。因此必须强制Gazebo使用ODE。在~/.gazebo/config.ini中添加[physics] engineode ode_threads4ode_threads4是针对4核CPU的优化值若你的机器是8核可改为8但超过CPU物理核心数反而降低性能——因为ODE的线程调度存在锁竞争。3.3 传感器模型注入让仿真逼近真实硬件PX4 SITL默认启用gazebo_ros_gps、gazebo_ros_imu、gazebo_ros_camera三个插件但它们的参数全是默认值。以GPS为例默认update_rate1.0/update_rate而真实Pixhawk飞控的GPS模块如u-blox M8N更新率为10Hz。不修正此参数你的导航算法会误判定位漂移是传感器故障而非采样率不足。修改iris.sdf中的GPS插件段plugin namegps_plugin filenamelibgazebo_ros_gps_sensor.so update_rate10.0/update_rate !-- 从1.0改为10.0 -- body_namebase_link/body_name topic_name/mavros/global_position/raw/fix/topic_name velocity_topic_name/mavros/global_position/raw/gps_vel/velocity_topic_name frame_idgps_link/frame_id time_offset0.0/time_offset noise typegaussian/type mean0.0/mean stddev2.0/stddev !-- 水平位置噪声单位m -- /noise /pluginstddev2.0对应消费级GPS的CEPCircular Error Probable精度实测值。若你用的是RTK模块可降至0.02但需同步调整EKF2的EKF2_GPS_NOISE参数否则滤波器会过度平滑。相机模型同样关键。默认gazebo_ros_camera无畸变模型而真实镜头必有径向畸变。在iris.sdf中找到plugin namecamera_plugin filenamelibgazebo_ros_camera.so添加distortion k10.12/k1 !-- 径向畸变系数 -- k2-0.05/k2 p10.001/p1 !-- 切向畸变 -- p20.001/p2 /distortion这些值来自OpenCV标定结果k1/k2控制桶形/枕形畸变p1/p2校正图像中心偏移。不加此段你的视觉里程计VIO在仿真中表现完美上真机却完全失效——因为算法从未见过畸变图像。3.4 环境模型加载不只是“插入一个房子”Gazebo的world文件决定仿真场景。PX4默认用empty.world纯黑背景无重力参考。真实测试需加载warehouse.world或sonoma_raceway.world但直接加载会报错Error: Unable to find model [warehouse]——因为这些模型不在GAZEBO_MODEL_PATH中。解决方案下载官方模型库并软链接cd ~/px4_firmware/Tools/sitl_gazebo wget https://github.com/osrf/gazebo_models/archive/refs/heads/master.zip unzip master.zip mv gazebo_models-master models_official ln -s models_official warehouse ln -s models_official sonoma_raceway然后编辑~/px4_firmware/Tools/sitl_gazebo/worlds/warehouse.world将includeurimodel://warehouse/uri/include前的注释去掉。启动时指定worldmake px4_sitl_rtps gazebo___warehouse注意gazebo___是PX4约定的分隔符warehouse是world文件名不含.world后缀。若用gazebo__warehouse两个下划线PX4会忽略world参数退回empty.world。实操心得Warehouse模型包含大量小物体箱子、托盘Gazebo渲染压力极大。若你的GPU显存4GB建议在warehouse.world中删除model namebox_01等非必要模型或改用sonoma_raceway.world——它只有跑道和护栏GPU占用降低60%。4. 一键起飞实现Python与C双版本的核心差异与协同逻辑“一键起飞”不是调用ros2 run px4_ros_com offboard_control那么简单。它涉及状态机切换BOOT→MANUAL→OFFBOARD→ARM→TAKEOFF、控制指令发布频率必须≥2Hz否则PX4拒绝进入OFFBOARD、安全超时机制5秒内未收到有效控制指令则自动降落。Python和C版本在此处的设计哲学截然不同不是“语言语法转换”而是运行时模型的根本差异。4.1 Python版本基于rclpy的异步事件驱动模型ROS2 Python客户端rclpy采用异步回调模型天然适合状态机编程。核心逻辑是订阅/mavros/state获取当前状态当modeOFFBOARD and armedTrue时开始发布/mavros/setpoint_position/local。# offboard_python.py import rclpy from rclpy.node import Node from mavros_msgs.msg import State from geometry_msgs.msg import PoseStamped from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSReliabilityPolicy class OffboardNode(Node): def __init__(self): super().__init__(offboard_node) # QoS配置匹配PX4的发布者策略历史深度1可靠传输 qos_profile QoSProfile( depth1, durabilityQoSDurabilityPolicy.TRANSIENT_LOCAL, reliabilityQoSReliabilityPolicy.RELIABLE ) self.state_sub self.create_subscription( State, /mavros/state, self.state_cb, qos_profile) self.pos_pub self.create_publisher( PoseStamped, /mavros/setpoint_position/local, qos_profile) self.current_state State() self.timer self.create_timer(0.05, self.timer_cb) # 20Hz发布 def state_cb(self, msg): self.current_state msg def timer_cb(self): if self.current_state.mode ! OFFBOARD: # 发送一次OFFBOARD指令触发模式切换 self.get_logger().info(Sending OFFBOARD command...) # 此处调用mavros服务代码略 return if not self.current_state.armed: # 发送ARM指令 self.get_logger().info(Arming drone...) # 调用arming服务代码略 return # 构建起飞位姿z2.0m pose PoseStamped() pose.header.stamp self.get_clock().now().to_msg() pose.pose.position.x 0.0 pose.pose.position.y 0.0 pose.pose.position.z 2.0 # 必须设置四元数否则PX4拒绝接受 pose.pose.orientation.w 1.0 self.pos_pub.publish(pose) def main(argsNone): rclpy.init(argsargs) node OffboardNode() rclpy.spin(node) rclpy.shutdown()关键点解析QoS配置depth1确保只保留最新位姿避免旧消息堆积TRANSIENT_LOCAL匹配PX4的TRANSIENT_LOCAL发布策略保证节点重启后仍能收到初始状态。发布频率timer_cb设为20Hz0.05s远高于PX4要求的2Hz阈值。若低于2HzPX4会在EKF2日志中打印OFFBOARD MODE REJECTED: NO SETPOINT。四元数强制设置PX4要求pose.orientation必须非零即使悬停也需w1.0。漏设会导致/mavros/setpoint_position/local消息被静默丢弃无任何错误提示。4.2 C版本基于roscpp的实时线程模型ROS1 C客户端roscpp采用多线程模型更适合硬实时控制。核心逻辑是在独立线程中以100Hz循环发布控制指令主线程处理状态订阅。// offboard_cpp.cpp #include ros/ros.h #include mavros_msgs/State.h #include geometry_msgs/PoseStamped.h #include mavros_msgs/CommandBool.h #include mavros_msgs/SetMode.h class OffboardController { private: ros::NodeHandle nh_; ros::Subscriber state_sub_; ros::Publisher pos_pub_; ros::ServiceClient arming_client_; ros::ServiceClient set_mode_client_; mavros_msgs::State current_state_; bool is_offboard_ false; bool is_armed_ false; public: OffboardController() : nh_(~) { state_sub_ nh_.subscribemavros_msgs::State(/mavros/state, 10, OffboardController::stateCb, this); pos_pub_ nh_.advertisegeometry_msgs::PoseStamped( /mavros/setpoint_position/local, 10); arming_client_ nh_.serviceClientmavros_msgs::CommandBool(/mavros/cmd/arming); set_mode_client_ nh_.serviceClientmavros_msgs::SetMode(/mavros/set_mode); // 启动100Hz控制线程 ros::Timer timer nh_.createTimer(ros::Duration(0.01), OffboardController::controlLoop, this); } void stateCb(const mavros_msgs::State::ConstPtr msg) { current_state_ *msg; if (msg-mode OFFBOARD) is_offboard_ true; if (msg-armed) is_armed_ true; } void controlLoop(const ros::TimerEvent) { if (!is_offboard_) { // 切换OFFBOARD模式 mavros_msgs::SetMode srv; srv.request.custom_mode OFFBOARD; if (set_mode_client_.call(srv)) { ROS_INFO(OFFBOARD mode set); } return; } if (!is_armed_) { // 解锁电机 mavros_msgs::CommandBool srv; srv.request.value true; if (arming_client_.call(srv)) { ROS_INFO(Vehicle armed); } return; } // 发布位姿z2.0m geometry_msgs::PoseStamped pose; pose.header.stamp ros::Time::now(); pose.pose.position.x 0.0; pose.pose.position.y 0.0; pose.pose.position.z 2.0; pose.pose.orientation.w 1.0; // 必须设置 pos_pub_.publish(pose); } }; int main(int argc, char **argv) { ros::init(argc, argv, offboard_controller); OffboardController controller; ros::spin(); return 0; }关键点解析100Hz控制线程ros::Timer设为0.01s确保控制指令严格按时序发布。ROS1的roscpp在单线程下易受回调阻塞影响独立定时器规避此风险。服务调用同步阻塞arming_client_.call(srv)是同步调用会阻塞当前线程直至服务响应。这在ROS1中是安全的因为控制线程与状态订阅线程分离。内存管理陷阱pose.header.stamp ros::Time::now()必须在每次发布前调用若在构造函数中预设时间戳将永远不变PX4会拒绝该消息认为是陈旧数据。4.3 双版本协同为什么不能只用一种Python版本优势在于开发效率高、调试方便你可以用rqt_graph实时查看话题连接用ros2 topic echo /mavros/state验证状态甚至用Jupyter Notebook交互式调试。但它有硬伤CPython的GIL全局解释器锁导致多线程无法真正并行当同时处理视觉流/camera/image_raw和控制指令时CPU占用率飙升控制频率从20Hz跌至8Hz触发PX4安全降落。C版本优势在于实时性确定、资源占用低roscpp直接调用系统API无GIL限制100Hz控制线程CPU占用稳定在12%。但它调试成本高需gdb断点、valgrind内存检查且无法像Python那样快速修改参数并重载。因此我的推荐工作流是Python用于算法原型验证如PID参数扫频C用于最终集成测试。例如你用Python脚本生成100组PID参数自动运行仿真并记录/mavros/local_position/pose的上升时间、超调量筛选出最优参数再将这组参数硬编码到C控制器中进行连续24小时稳定性测试。常见问题Python版本启动后QGroundControl显示“Waiting for heartbeat”但ros2 topic list能看到/mavros/state。这是因为Python节点未正确设置QoS导致/mavros/state消息被PX4的mavros节点丢弃。解决方案在create_subscription中显式传入qos_profile如前述代码所示。5. 故障排查实战从Gazebo闪屏到Python脚本静默失败的全链路诊断仿真环境最折磨人的不是“不会做”而是“做了但没反应还不知道哪错了”。下面是我整理的高频故障速查表按现象反推根因每一条都来自真实踩坑记录。现象根本原因定位命令解决方案Gazebo界面持续闪烁鼠标移动卡顿Gazebo Sim被误启动与NVIDIA驱动470版本冲突gazebo --version输出Gazebo Sim 8.15.0执行sudo apt remove gazebo安装gazebo11设置export GAZEBO_SIM0ros2 topic list看不到/mavros/state但px4_sitl_default进程在运行PX4 SITL未正确加载ROS2插件mavros节点未启动ps aux | grep px4查看进程参数确认含-d /path/to/px4_ros_com在px4.launch.py中检查executablepx4的arguments是否包含[-d, /home/user/px4_ros_com_ros2]Python脚本发布/mavros/setpoint_position/local但无人机不动pose.orientation未设置PX4静默丢弃消息ros2 topic echo /mavros/setpoint_position/local查看消息内容在PoseStamped中强制设置pose.orientation.w 1.0C版本编译报错undefined reference to ros::NodeHandle::NodeHandle(std::string const)ROS1与ROS2头文件混用#include ros/ros.h与#include rclcpp/rclcpp.hpp共存grep -r ros::NodeHandle src/删除所有ROS2相关头文件确保仅用ros/ros.h和ros/console.hQGroundControl连接SITL后显示“Calibrating Accel”长时间不退出IMU噪声为0EKF2无法初始化px4_console中输入ekf2 status修改iris_base.sdf为imu_plugin添加noise块stddev0.0055.1 Gazebo闪屏的深度诊断“Gazebo界面一直在闪”是搜索热词但90%的教程只教“重装驱动”。真正的根因是OpenGL上下文竞争。Gazebo Sim使用Vulkan后端而Ubuntu 22.04默认NVIDIA驱动470版本对Vulkan支持不完善导致帧缓冲区频繁重建。诊断步骤启动Gazebo时添加--verbose参数gazebo --verbose warehouse.world观察输出末尾是否有[Msg] Loading plugin /usr/lib/x86_64-linux-gnu/gazebo-11/plugins/libgazebo_ros_camera.so——若有说明加载的是Gazebo Classic 11插件若出现libgazebo_ros_camera.so路径含gazebo-8则是Gazebo Sim。若确认是Gazebo Sim执行glxinfo \| grep OpenGL renderer若输出NVIDIA GeForce RTX 3080/PCIe/SSE2说明驱动正常问题在Gazebo版本。终极解决方案彻底卸载Gazebo Sim只保留Classic 11sudo apt remove ros-humble-gazebo-ros-pkgs # 卸载ROS2绑定的Gazebo Sim sudo apt autoremove sudo apt install gazebo115.2 Python脚本静默失败的链路追踪Python脚本“看起来在运行但无人机就是不动”这是最隐蔽的故障。因为rclpy不会报错只是消息发不出去。完整追踪链Step 1确认话题发布ros2 topic pub /mavros/setpoint_position/local geometry_msgs/msg/PoseStamped {header: {stamp: {sec: 0, nanosec: 0}, frame_id: }, pose: {position: {x: 0.0, y: 0.0, z: 2.0}, orientation: {w: 1.0}}}若此命令能触发起飞则证明PX4接收正常问题在Python代码。Step 2检查QoS匹配ros2 topic info /mavros/setpoint_position/local -v查看Publisher QoS Profile中的Reliability是否为RELIABLEDurability是否为TRANSIENT_LOCAL。若Python节点是VOLATILE则消息无法送达。Step 3验证时间戳有效性ros2 topic echo /mavros/setpoint_position/local观察header.stamp的sec和nanosec是否随时间递增。若恒为0说明self.get_clock().now().to_msg()未正确调用。Step 4检查ROS2参数服务器ros2 param get /offboard_node use_sim_time若返回False而PX4 SITL要求use_sim_timeTrue仿真时间则时间戳不匹配。需在launch文件中添加parameter{use_sim_time: True}。5.3 PX4固件编译失败的精准修复make px4_sitl_rtps gazebo报错fatal error: Eigen/Dense: No such file or directory表面是Eigen缺失实则是CMakeLists.txt中find_package(Eigen3 REQUIRED)的版本约束过严。定位方法进入~/px4_firmware/src/modules/ekf2/CMakeLists.txt找到find_package(Eigen3 3.3 REQUIRED)。Ubuntu 22.04源中Eigen3版本是3.4.0但REQUIRED强制要求3.3导致失败。修复方案将3.3改为3.3.0或直接删除版本号# 原始行 find_package(Eigen3 3.3 REQUIRED) # 修改为 find_package(Eigen3 REQUIRED)然后清理编译缓存rm -rf build/px4_sitl_rtps重新make。实操心得PX4 v1.14.3的src/drivers/uavcan_v1/CMakeLists.txt中也有类似问题需同步修改。这类问题在v