前言
- 最近刚好有一个项目在使用无人机,在经历了无数次炸机和仿真环境配置踩坑之后,决定把整个过程整理出来。
- 前几期我们刚好讲到
- 刚好本期我们就来研究一下如何从零搭建一套完整的
ROS2+PX4+Gazebo无人机仿真环境,包括键盘 Offboard 控制、3D LiDAR 集成、网笼测试场地等。 - 核心链路 :
ROS2 Humble+PX4 SITL+Gazebo Classic+Velodyne VLP-16+IMU+双路 RGB 相机+9m×4.6m 网笼测试场+键盘 Offboard 控制

### 文章目录
- [前言](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [@TOC](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [1 编译 PX4 SITL 固件](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [1-1 介绍 PX4](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [1-2 依赖安装](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [1-3 编译](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [2 安装 Micro XRCE-DDS Agent](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [2-1 介绍](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [2-2 安装](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [3 工作空间创建](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [3-1 介绍 px4_msgs](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [3-2 启动飞机脚本](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [4 首次起飞与话题说明](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [4-1 话题含义](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [4-2 机型](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [5 键盘控制节点](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [5-1 PX4 飞行模式](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [5-2 键盘控制脚本](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [5-3 当前项目结构](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [6 配置 Gazebo 无人机模型](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [6-1 Velodyne VLP-16 3D LiDAR](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [6-2 传感器嵌套与 PX4 模型注册](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [6-3 IMU](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [6-4 双路 RGB 相机](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [6-5 启动全新的飞机](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [7 RViz2 可视化](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [8 网笼测试场地](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [8-1 当前完整项目结构](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [9 完整演示流程](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
- [总结](#文章目录 前言 @[TOC] 1 编译 PX4 SITL 固件 1-1 介绍 PX4 1-2 依赖安装 1-3 编译 2 安装 Micro XRCE-DDS Agent 2-1 介绍 2-2 安装 3 工作空间创建 3-1 介绍 px4_msgs 3-2 启动飞机脚本 4 首次起飞与话题说明 4-1 话题含义 4-2 机型 5 键盘控制节点 5-1 PX4 飞行模式 5-2 键盘控制脚本 5-3 当前项目结构 6 配置 Gazebo 无人机模型 6-1 Velodyne VLP-16 3D LiDAR 6-2 传感器嵌套与 PX4 模型注册 6-3 IMU 6-4 双路 RGB 相机 6-5 启动全新的飞机 7 RViz2 可视化 8 网笼测试场地 8-1 当前完整项目结构 9 完整演示流程 总结)
1 编译 PX4 SITL 固件
1-1 介绍 PX4

PX4是一套开源的无人机飞行控制软件,支持从多旋翼到固定翼的各类机型SITL(Software In The Loop,软件在环)模式允许飞控固件直接在电脑上编译成可执行程序,和 Gazebo 仿真器联动------行为几乎和真飞控一模一样,但不用担心炸机- PX4 与 ROS2 之间的通信通过
Micro XRCE-DDS(uORB → DDS 桥梁)实现,从 PX4 v1.14 起这是官方标准方案 - 说人话就是:你在 Gazebo 里飞仿真飞机,PX4 负责飞控运算,ROS2 负责算法处理,三者之间通过 UDP 端口互相通信
1-2 依赖安装
- 克隆 PX4 v1.14.3 稳定版:
bash
cd ~
git clone https://github.com/PX4/PX4-Autopilot.git --branch v1.14.3 --depth 1 --recurse-submodules
- 运行官方依赖安装脚本(约 5-10 分钟,需要 sudo):
bash
cd ~/PX4-Autopilot
bash ./Tools/setup/ubuntu.sh
- 如果系统已有
libgazebo11-dev等基础包,脚本会跳过大部分依赖。但实测可能缺少libgstreamer导致 CMake 报GSTREAMER_APP_LIBRARIES NOTFOUND,补充安装:
bash
sudo apt install -y libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev \
libgstreamer-plugins-good1.0-dev libgstreamer-plugins-bad1.0-dev
1-3 编译
- 首次编译 SITL 固件(约 5-10 分钟):
bash
cd ~/PX4-Autopilot
make px4_sitl gazebo-classic
- 编译成功后会自动弹出 Gazebo 窗口,可以看到一架四旋翼无人机,终端出现
Ready for takeoff!说明安装成功


2 安装 Micro XRCE-DDS Agent
2-1 介绍
Micro XRCE-DDS Agent是 PX4(uORB 消息总线)与 ROS2(DDS 中间件)之间的通信桥梁- PX4 v1.14 起用这个替代了旧版的 MAVROS------它直接通过 UDP 将 PX4 的 uORB 话题映射为 ROS2 的 DDS 话题,延时更低、配置更简单
- 说人话就是:PX4 内部的传感器数据(IMU、GPS、里程计等)通过 Agent "翻译"成 ROS2 标准话题,ROS2 的控制指令也通过 Agent 发回 PX4
2-2 安装
bash
cd ~
git clone https://github.com/eProsima/Micro-XRCE-DDS-Agent.git --depth 1
cd Micro-XRCE-DDS-Agent
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=~/microros_agent ..
make -j$(nproc)
sudo make install
sudo ldconfig
- 添加到环境变量:
bash
echo 'export PATH="$HOME/microros_agent/bin:$PATH"' >> ~/.bashrc
echo 'export LD_LIBRARY_PATH="$HOME/microros_agent/lib:$LD_LIBRARY_PATH"' >> ~/.bashrc
source ~/.bashrc
- 验证安装(看到
running... port: 8888即可 Ctrl+C):
bash
MicroXRCEAgent udp4 -p 8888

3 工作空间创建
3-1 介绍 px4_msgs
-
px4_msgs是 PX4 官方定义的 ROS2 消息包,将 PX4 内部的 uORB 消息一对一映射为 ROS2 的msg定义 -
所有消息都带有
uint64 timestamp(微秒级时间戳,从系统启动开始计数),这是 PX4 内部同步机制的基础 -
包中共有 20+ 个消息类型,但无人机 Offboard 控制只需关注三个核心消息
-
这是 ROS2 ↔ PX4 通信的"语言词典"------你的 ROS2 节点要控制飞机,必须依赖这个包
-
说人话就是:你要给 PX4 发"飞到 (x,y,z)"的指令,得用 PX4 认识的格式,
px4_msgs定义了这个格式 -
核心消息一:
TrajectorySetpoint--- 轨迹设定点
bash
# NED 局部世界坐标系
uint64 timestamp # 系统启动以来的微秒时间
float32[3] position # [m] 目标位置 (x=前, y=右, z=下)
float32[3] velocity # [m/s] 目标速度
float32[3] acceleration # [m/s²] 目标加速度
float32[3] jerk # [m/s³] 仅用于日志
float32 yaw # [rad] 目标偏航角 -PI..+PI
float32 yawspeed # [rad/s] 目标偏航角速度
-
这就是你告诉飞机"去(x,y,z)这个位置、面朝 yaw 方向"的指令
-
关键细节 :任何分量设为
NaN表示该维度不受控。比如只设position不设velocity,飞控自己会用 PID 位置控制器去跟踪 -
坐标系是 NED(North-East-Down):x=前、y=右、z=下------所以上升时 z 是负值
-
核心消息二:
OffboardControlMode--- 外部控制模式声明
bash
uint64 timestamp
bool position # 位置控制
bool velocity # 速度控制
bool acceleration # 加速度控制
bool attitude # 姿态控制
bool body_rate # 角速度控制
bool thrust_and_torque # 推力/扭矩控制
bool direct_actuator # 直接执行器控制
-
这是一张"控制权限表",告诉 PX4"我要控制哪些自由度"
-
致命要求 :必须以 2Hz 以上的频率持续发布,否则 PX4 会触发 Offboard 失效保护(failsafe),自动切回上一模式
-
我们脚本用 20Hz(
timer = 0.05s)发送,与TrajectorySetpoint在同一回调中发布,保证同步 -
核心消息三:
VehicleCommand--- 车辆命令
bash
uint64 timestamp
uint32 command # MAVLink 标准命令 ID
float32 param1 ~ param7 # 命令参数(含义取决于具体命令)
uint8 target_system # 目标系统 ID(通常为 1)
uint8 target_component # 目标组件 ID(通常为 1)
uint8 source_system # 源系统 ID
uint16 source_component # 源组件 ID
bool from_external # 是否来自外部(必须为 True)
- 这个消息遵循 MAVLink
COMMAND_INT协议,所有命令通过command字段区分,param1 ~ param7的含义完全不同
| 命令 ID | 枚举名 | 用途 | 关键参数 |
|---|---|---|---|
400 |
COMPONENT_ARM_DISARM |
解锁/上锁 | param1:1=解锁,0=上锁 |
176 |
DO_SET_MODE |
切换飞行模式 | param1=1(自定义模式), param2=6(OFFBOARD) |
22 |
NAV_TAKEOFF |
起飞 | param7:目标高度(m),NED 坐标下为负值 |
21 |
NAV_LAND |
降落 | 无需参数 |
180 |
DO_SET_PARAMETER |
设置飞控参数 | param1:参数 ID,param2:参数值 |
-
踩坑警示 :
param2=6才是 OFFBOARD,写成4是 AUTO 模式------飞机悬停不动、所有 setpoint 被忽略,这是最容易踩的坑 -
三个消息的关系:
OffboardControlMode= 声明要"接管"什么TrajectorySetpoint= 具体设的"目标值"VehicleCommand= 一次性指令(解锁、切换模式等)- 三者缺一不可:有 Setpoint 但没有 ControlMode → PX4 忽略。有 ControlMode 但没有持续发送 → 2 秒后 Failsafe 触发
bash
mkdir -p ~/postgraduate0/px4_ws/src
cd ~/postgraduate0/px4_ws
git clone https://github.com/PX4/px4_msgs.git src/px4_msgs --depth 1
source /opt/ros/humble/setup.bash
colcon build
echo "source ~/postgraduate0/px4_ws/install/setup.bash" >> ~/.bashrc
3-2 启动飞机脚本
- 仿真需要三个组件同时运行:
MicroXRCEAgent(UDP 8888 端口)、PX4 SITL + Gazebo、以及后续的 ROS2 控制节点 - 把前两个写进
1_bringup.sh,一键启动:
bash
#!/bin/bash
# ============================================================
# PX4 + Gazebo + ROS2 联合启动脚本
# 启动顺序: MicroXRCEAgent → PX4 SITL → ROS2 就绪
# Ctrl+C 退出全部
# ============================================================
set -e
cleanup() {
echo ""
echo ">>> 正在关闭所有进程..."
kill $AGENT_PID 2>/dev/null
pkill -f "gzserver" 2>/dev/null
pkill -f "gzclient" 2>/dev/null
pkill -f "px4" 2>/dev/null
echo ">>> 已全部关闭"
exit 0
}
trap cleanup SIGINT SIGTERM
# ==================== 配置 ====================
WORKSPACE_DIR="/home/lzh/postgraduate0/px4_ws"
PX4_DIR="$HOME/PX4-Autopilot"
AGENT_BIN="$HOME/microros_agent/bin/MicroXRCEAgent"
AGENT_LIB="$HOME/microros_agent/lib"
# ==================== 1. 启动 Micro XRCE-DDS Agent ====================
echo ">>> [1/3] 启动 Micro XRCE-DDS Agent (UDP port 8888)..."
export LD_LIBRARY_PATH="$AGENT_LIB:$LD_LIBRARY_PATH"
$AGENT_BIN udp4 -p 8888 &
AGENT_PID=$!
sleep 2
if kill -0 $AGENT_PID 2>/dev/null; then
echo ">>> [1/3] Agent 启动成功 (PID: $AGENT_PID)"
else
echo "!!! Agent 启动失败,请检查"
exit 1
fi
# ==================== 2. 启动 PX4 SITL + Gazebo ====================
echo ">>> [2/3] 启动 PX4 SITL + Gazebo Classic..."
cd $PX4_DIR
make px4_sitl gazebo-classic &
PX4_PID=$!
sleep 5
echo ""
echo "============================================"
echo " 仿真环境已启动!"
echo "============================================"
echo " ROS2 话题测试:"
echo " ros2 topic list | grep fmu"
echo " 按 Ctrl+C 退出全部"
echo "============================================"
wait
- 运行
./1_bringup.sh:

4 首次起飞与话题说明
4-1 话题含义
- 仿真启动后,另开终端验证:
bash
source /opt/ros/humble/setup.bash
source ~/postgraduate0/px4_ws/install/setup.bash
ros2 topic list

-
完整话题分类说明:
-
/fmu/in/*--- ROS2 发给 PX4 的控制指令
| 话题 | 含义 |
|---|---|
/fmu/in/trajectory_setpoint |
最核心:位置/速度/加速度设定点,Offboard 模式下持续发送目标轨迹 |
/fmu/in/offboard_control_mode |
声明控制模式(位置/速度/姿态等),必须 2Hz 以上持续发送 |
/fmu/in/vehicle_command |
一次性命令:解锁/上锁、模式切换、起飞/降落 |
/fmu/in/vehicle_attitude_setpoint |
姿态设定点(四元数 + 推力) |
/fmu/in/vehicle_rates_setpoint |
角速度设定点 |
/fmu/in/vehicle_mocap_odometry |
外部动捕里程计输入 |
/fmu/in/vehicle_visual_odometry |
外部视觉/激光里程计输入,可替代 GPS |
/fmu/in/vehicle_trajectory_bezier |
贝塞尔曲线轨迹(平滑飞行) |
/fmu/in/vehicle_trajectory_waypoint |
航点轨迹 |
/fmu/in/obstacle_distance |
障碍物距离数据(避障用) |
/fmu/in/onboard_computer_status |
机载计算机心跳信号 |
/fmu/in/telemetry_status |
数传链路状态 |
/fmu/in/sensor_optical_flow |
光流传感器数据 |
/fmu/out/*--- PX4 发给 ROS2 的飞控状态
| 话题 | 含义 |
|---|---|
/fmu/out/vehicle_odometry |
飞控 EKF 里程计(位置 + 速度 + 姿态) |
/fmu/out/vehicle_local_position |
本地 NED 坐标系下的位置和速度 |
/fmu/out/vehicle_global_position |
WGS-84 全球坐标(经纬度 + 高度) |
/fmu/out/vehicle_gps_position |
原始 GPS 数据 |
/fmu/out/vehicle_attitude |
当前姿态(四元数) |
/fmu/out/vehicle_status |
飞控状态:解锁状态、飞行模式、电池等 |
/fmu/out/vehicle_control_mode |
当前控制模式标志位 |
/fmu/out/sensor_combined |
融合后的 IMU 数据(加速度 + 角速度) |
/fmu/out/position_setpoint_triplet |
当前位置设定点三元组(当前/前一个/下一个) |
/fmu/out/failsafe_flags |
失效保护标志位 |
/fmu/out/timesync_status |
时间同步状态 |
- 说人话:
/fmu/in/*是"你想让飞机干什么",/fmu/out/*是"飞机现在什么状态"
4-2 机型
- PX4 Gazebo Classic 支持多种无人机和载具模型:
| 模型 | 类型 | 说明 |
|---|---|---|
iris |
四旋翼 | 默认机型,通用测试平台 |
iris_foggy_lidar |
四旋翼 + 2D 激光 | 带雾环境模拟的 2D 激光雷达 |
iris_rplidar |
四旋翼 + RPLidar | 带 2D 激光扫描仪 |
iris_vision |
四旋翼 + 视觉 | 带视觉传感器 |
standard_vtol |
垂直起降固定翼 | VTOL 机型 |
plane |
固定翼 | 常规固定翼 |
rover |
地面小车 | 差分驱动地面机器人 |
typhoon_h480 |
六旋翼 | 大疆台风 H480 六旋翼 |
- 通过
make px4_sitl gazebo-classic_<模型名>即可切换到对应机型
5 键盘控制节点
5-1 PX4 飞行模式
- PX4 有多种飞行模式,理解模式切换是 Offboard 控制的前提:
| 模式 | custom_main_mode |
说明 |
|---|---|---|
| MANUAL | 1 | 手动/RC 遥控器模式,直接控制电机 |
| ALTCTL | 2 | 高度保持模式,自动维持高度 |
| POSCTL | 3 | 位置保持模式,自动维持位置和高度 |
| AUTO | 4 | 自动模式(任务/降落/返航等) |
| OFFBOARD | 6 | 外部控制模式,完全由 ROS2 控制 |
| STABILIZED | 7 | 自稳模式,姿态保持 |
-
注意 :
OFFBOARD的 custom_main_mode 是6,不是4(4是 AUTO)。踩坑重灾区------写成 4 飞机会进入 AUTO_LOITER 悬停不动,所有 setpoint 被忽略 -
进入 Offboard 模式前必须:
- 持续以 2Hz 以上频率发布
OffboardControlMode - 设置
COM_RCL_EXCEPT = 4(bit 2),允许无 RC 遥控器进入 Offboard(SITL 没有物理遥控器)
- 持续以 2Hz 以上频率发布
5-2 键盘控制脚本
- 注意务必看到
Ready for takeoff!再运行控制脚本:

- 启动脚本
2_keyboard.sh:
bash
#!/bin/bash
source /opt/ros/humble/setup.bash
source /home/lzh/postgraduate0/px4_ws/install/setup.bash
exec python3 /home/lzh/postgraduate0/px4_ws/2_keyboard_control.py
- 核心设计:速度模式 + 松手超时悬停
- 按住 W →
vx = 2.0 m/s,timer 20Hz 持续积分到位置设定点,飞机持续前进 - 松手 → 0.2 秒内没有新按键触发超时,速度归零,飞机悬停
- 因为终端
cbreak模式无法检测"松键"事件,用last_key_time时间戳模拟
- 按住 W →
python
#!/usr/bin/env python3
"""
PX4 键盘控制 (Offboard 速度模式)
按住飞、松开悬停: WASD=水平 R/F=升降 Q/E=偏航
空格=解锁/上锁 O=Offboard L=降落 ESC=退出
"""
import rclpy, sys, tty, select, termios, time
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy, HistoryPolicy
from px4_msgs.msg import OffboardControlMode, TrajectorySetpoint, VehicleCommand
VEL_XY = 2.0; VEL_Z = 1.5; VEL_YAW = 1.0; TIMEOUT = 0.2
class KeyboardControl(Node):
def __init__(self):
super().__init__("keyboard_control")
qos = QoSProfile(reliability=ReliabilityPolicy.BEST_EFFORT,
history=HistoryPolicy.KEEP_LAST, depth=10)
self.offboard_pub = self.create_publisher(
OffboardControlMode, "/fmu/in/offboard_control_mode", qos)
self.traj_pub = self.create_publisher(
TrajectorySetpoint, "/fmu/in/trajectory_setpoint", qos)
self.cmd_pub = self.create_publisher(
VehicleCommand, "/fmu/in/vehicle_command", qos)
self.x = 0.0; self.y = 0.0; self.z = -1.0; self.yaw = 0.0
self.vx = self.vy = self.vz = self.vyaw = 0.0
self.last_key_time = 0.0; self.last_hud_time = 0.0
self.armed = False
self.timer = self.create_timer(0.05, self.timer_cb)
sys.stdout.write("""
╔══════════════════════════════════════════╗
║ PX4 键盘控制 (速度模式) ║
║ W/S 前后 A/D 左右 R/F 升降 ║
║ Q/E 偏航 空格解锁 O=Offboard ║
║ L 降落 ESC 退出 ║
║ 按住飞, 松开悬停 ║
╚══════════════════════════════════════════╝
就绪 --- 按 空格 解锁\r\n""")
sys.stdout.flush()
def send_cmd(self, cmd, p1=0., p2=0., p3=0., p4=0., p5=0., p6=0., p7=0.):
msg = VehicleCommand()
msg.timestamp = int(self.get_clock().now().nanoseconds / 1000)
msg.command = cmd
msg.param1 = float(p1); msg.param2 = float(p2); msg.param3 = float(p3)
msg.param4 = float(p4); msg.param5 = float(p5); msg.param6 = float(p6)
msg.param7 = float(p7)
msg.target_system = 1; msg.target_component = 1
msg.source_system = 1; msg.source_component = 1
msg.from_external = True
self.cmd_pub.publish(msg)
def timer_cb(self):
ts = int(self.get_clock().now().nanoseconds / 1000)
now = time.time()
if now - self.last_key_time > TIMEOUT:
self.vx = self.vy = self.vz = self.vyaw = 0.0
else:
dt = 1.0 / 20.0
self.x += self.vx * dt; self.y += self.vy * dt
self.z += self.vz * dt; self.yaw += self.vyaw * dt
off = OffboardControlMode(); off.timestamp = ts; off.position = True
self.offboard_pub.publish(off)
traj = TrajectorySetpoint(); traj.timestamp = ts
traj.position = [float(self.x), float(self.y), float(self.z)]
traj.yaw = float(self.yaw)
self.traj_pub.publish(traj)
if now - self.last_hud_time > 0.5:
self.last_hud_time = now
spd = (self.vx**2 + self.vy**2 + self.vz**2)**0.5
sys.stdout.write(
f"\r {'ARMED' if self.armed else 'DISARMED'} "
f"X={self.x:+.1f} Y={self.y:+.1f} H={-self.z:.1f}m V={spd:.1f}m/s ")
sys.stdout.flush()
def handle_key(self, ch):
if ch == " ":
if self.armed:
self.send_cmd(400, 0.0); self.armed = False
else:
self.send_cmd(180, 8.0, 4.0) # COM_RCL_EXCEPT=4
self.send_cmd(400, 1.0) # ARM
self.armed = True
elif ch.lower() == "o":
self.send_cmd(176, 1.0, 6.0) # OFFBOARD (main_mode=6, 不是4!)
elif ch.lower() == "l":
self.vx=self.vy=self.vz=self.vyaw = 0.0
self.send_cmd(176, 1.0, 4.0, 6.0) # AUTO.LAND
self.armed = False
elif ch.lower() == "w": self.vx = VEL_XY; self.last_key_time = time.time()
elif ch.lower() == "s": self.vx = -VEL_XY; self.last_key_time = time.time()
elif ch.lower() == "d": self.vy = VEL_XY; self.last_key_time = time.time()
elif ch.lower() == "a": self.vy = -VEL_XY; self.last_key_time = time.time()
elif ch.lower() == "r": self.vz = -VEL_Z; self.last_key_time = time.time()
elif ch.lower() == "f": self.vz = VEL_Z; self.last_key_time = time.time()
elif ch.lower() == "q": self.vyaw = VEL_YAW; self.last_key_time = time.time()
elif ch.lower() == "e": self.vyaw = -VEL_YAW; self.last_key_time = time.time()
elif ch == "x": self.vx=self.vy=self.vz=self.vyaw = 0.0
def run(self):
fd = sys.stdin.fileno(); old = termios.tcgetattr(fd)
tty.setcbreak(fd)
try:
while rclpy.ok():
dr, _, _ = select.select([sys.stdin], [], [], 0.01)
if dr:
ch = sys.stdin.read(1)
if ch == "\x1b": sys.stdout.write("\r\n退出\r\n"); break
self.handle_key(ch)
rclpy.spin_once(self, timeout_sec=0.005)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old)
def main():
rclpy.init(); node = KeyboardControl()
try: node.run()
except KeyboardInterrupt: pass
finally: node.destroy_node(); rclpy.shutdown()
if __name__ == "__main__": main()
- 操作流程:
| 步骤 | 按键 | 说明 |
|---|---|---|
| 1 | 空格 | 解锁飞控(ARM),自动设置 COM_RCL_EXCEPT=4 |
| 2 | O | 切换到 Offboard 模式,飞控控制权交给 ROS2 |
| 3 | R(按住) | 上升,松手悬停 |
| 4 | WASD(按住) | 水平移动,松手悬停 |
| 5 | Q/E | 偏航旋转 |
| 6 | L | 降落(切回 AUTO.LAND 模式) |
| 7 | X | 紧急悬停(速度瞬间归零) |

5-3 当前项目结构
- 经过前五节的搭建,
px4_ws目录下的文件结构如下:
bash
~/postgraduate0/px4_ws/
├── src/px4_msgs/ # PX4 ROS2 消息定义包
├── install/ # colcon build 编译产物
├── 1_bringup.sh # 一键启动仿真 (Agent + PX4 + Gazebo)
├── 2_keyboard.sh # 键盘控制启动脚本
└── 2_keyboard_control.py # 键盘控制 Python 节点
- 两个脚本的启动顺序:
| 脚本 | 终端 | 职责 |
|---|---|---|
./1_bringup.sh |
终端 1 | 启动 Micro XRCE-DDS Agent + PX4 SITL + Gazebo 仿真 |
./2_keyboard.sh |
终端 2 | 启动键盘控制节点,接管飞机(等 Ready for takeoff! 出现后) |
- 典型工作流 :
- 终端 1:
./1_bringup.sh→ 等待 Gazebo 窗口弹出、终端显示Ready for takeoff! - 终端 2:
./2_keyboard.sh→ 空格解锁 → O 进 Offboard → WASD 飞
- 终端 1:
- 注意 :
1_bringup.sh的前台进程会阻塞终端,Ctrl+C 一键关闭所有后台进程(Agent + Gazebo + PX4)
6 配置 Gazebo 无人机模型
6-1 Velodyne VLP-16 3D LiDAR
-
PX4 内置模型只支持 2D 激光扫描(
foggy_lidar、rplidar)和单点测距(lidar),没有一个能输出sensor_msgs/PointCloud2------这是FastLIO2唯一需要的输入格式 -
Velodyne VLP-16是经典的 16 线旋转式 3D 激光雷达,Gazebo 有现成的 ROS2 插件libgazebo_ros_velodyne_laser.so,直接输出PointCloud2 -
安装插件:
bash
sudo apt install -y ros-humble-velodyne-gazebo-plugins ros-humble-velodyne-description
- 在
~/.gazebo/models/velodyne_vlp16/model.sdf创建雷达模型:
xml
<sensor name="vlp16" type="gpu_ray"> <!-- GPU 加速,不用 CPU -->
<update_rate>10</update_rate>
<ray>
<scan>
<horizontal>
<samples>720</samples> <!-- 每圈 720 采样点 -->
<min_angle>-3.14159</min_angle>
<max_angle>3.14159</max_angle>
</horizontal>
<vertical>
<samples>16</samples> <!-- 16 条垂直线 -->
<min_angle>-0.785</min_angle> <!-- -45° -->
<max_angle>0.785</max_angle> <!-- +45° -->
</vertical>
</scan>
</ray>
<plugin name="vlp16" filename="libgazebo_ros_velodyne_laser.so">
<ros><namespace>/velodyne</namespace></ros>
<frame_name>velodyne_link</frame_name>
<min_range>0.5</min_range>
<max_range>30.0</max_range>
<gaussian_noise>0.01</gaussian_noise>
</plugin>
</sensor>
- 关键参数演变:
| 参数 | 初始值 | 最终值 | 为什么改 |
|---|---|---|---|
type |
ray (CPU) |
gpu_ray |
CPU 模式 30 万射线/秒直接卡死 Gazebo,飞机桨叶都不转 |
samples |
1875 | 720 | 降低 CPU 负担,GPU 下够用 |
lasers |
16→8 | 16 | 先降后恢复,确认 GPU 不卡 |
update_rate |
10→5 | 10 | 同上 |
mass |
0.83 kg | 0.05 kg | Iris 自重 ~1.5kg,真机 VLP-16 830g 压在上面飞不起来 |
- 降低传感器重量:
xml
<mass>0.05</mass> <!-- 真机 830g,仿真压到 50g -->
-
GPU 加速下的最终设置:
gpu_ray+ 720 水平采样 + 16 线 + 10Hz = 115,200 pts/s,Gazebo 流畅 -
垂直视场从 ±15° 扩大到 ±45°,在网笼中能打到更多结构:
xml
<min_angle>-0.785</min_angle> <!-- -45° -->
<max_angle>0.785</max_angle> <!-- +45° -->
6-2 传感器嵌套与 PX4 模型注册
- 创建
iris_vlp16模型,在 iris 无人机上嵌套 Velodyne + IMU + 双路 RGB 相机:
bash
iris_vlp16
├── iris (PX4 飞控)
├── [fixed joint] velodyne_vlp16 → velodyne_link (顶置 12cm)
├── [fixed joint] gazebo_ros_imu → imu_link (中部 5cm)
├── [fixed joint] camera_front → camera_front_link (机头前 15cm)
└── [fixed joint] camera_down → camera_down_link (底部,pitch 90° 朝下)
-
为什么嵌套? PX4 已有的
iris_foggy_lidar、iris_rplidar等模型都采用<include>嵌套结构------SDF 的<include>机制 +<joint>固定关节是 Gazebo Classic 的标准做法,PX4 源码中已经验证了这条路 -
注册三步走:
-
第一步:注册 cmake 模型白名单
-
PX4 通过
sitl_targets_gazebo-classic.cmake维护一个模型白名单,不在名单里的模型无法通过make启动 -
编辑
~/PX4-Autopilot/src/modules/simulation/simulator_mavlink/sitl_targets_gazebo-classic.cmake,找到set(models ...)(约第 73 行),在列表中插入iris_vlp16:
cmake
set(models
advanced_plane
believer
...
iris_vision
iris_vlp16 # ← 新增这一行
omnicopter
...
)
-
这个列表决定了哪些模型能通过
make px4_sitl gazebo-classic_<模型名>启动。当运行make px4_sitl gazebo-classic_iris_vlp16时,cmake 会调用sitl_run.sh把iris_vlp16作为模型名传入,Gazebo 从模型路径中查找同名 SDF 文件 -
第二步:模型文件 + airframe 启动脚本
-
把写好的模型复制到 PX4 模型目录,并创建同名 SDF(PX4 cmake 第 140 行显式检查
${model_only}/${model_only}.sdf,只认iris_vlp16.sdf,不认model.sdf):
bash
cp -r ~/.gazebo/models/iris_vlp16 \
~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/
ln -sf model.sdf \
~/PX4-Autopilot/Tools/.../models/iris_vlp16/iris_vlp16.sdf
- 子模型(velodyne_vlp16、gazebo_ros_imu、rgb_camera)同样需要链接到 PX4 的模型目录,否则
<include><uri>model://velodyne_vlp16</uri></include>会找不到:
bash
for m in velodyne_vlp16 gazebo_ros_imu rgb_camera; do
ln -sf ~/.gazebo/models/$m \
~/PX4-Autopilot/Tools/simulation/gazebo-classic/sitl_gazebo-classic/models/$m
done
- 创建 airframe 启动文件
11015_gazebo-classic_iris_vlp16,文件名格式为{SYS_AUTOSTART}_gazebo-classic_{模型名}。PX4 启动时rcS脚本通过PX4_SIM_MODEL环境变量自动匹配 airframe 文件,提取SYS_AUTOSTART来初始化飞控参数:
bash
cp ~/PX4-Autopilot/ROMFS/.../airframes/10015_gazebo-classic_iris \
~/PX4-Autopilot/ROMFS/.../airframes/11015_gazebo-classic_iris_vlp16
- 在文件末尾追加仿真专用参数(见下方 Airframe 参数),然后拷贝到 build rootfs 使改动立即生效:
bash
cp ~/PX4-Autopilot/ROMFS/.../airframes/11015_gazebo-classic_iris_vlp16 \
~/PX4-Autopilot/build/px4_sitl_default/rootfs/etc/init.d-posix/airframes/
-
每次
make会重新生成 rootfs,所以 ROMFS 和 build rootfs 两处都要放 -
第三步:更新 bringup 脚本
-
修改
1_bringup.sh中的 make target,从默认 iris 切换到 iris_vlp16:
bash
# 旧: 默认 iris, 空白世界
make px4_sitl gazebo-classic
# 新: iris_vlp16 + 网笼世界
export PX4_SITL_WORLD="$WORKSPACE_DIR/cage.world"
make px4_sitl gazebo-classic_iris_vlp16
-
PX4_SITL_WORLD环境变量会被sitl_run.sh读取,用绝对路径加载 world 文件,绕过 cmake 的 worlds 白名单 -
Airframe 文件中禁用电池模拟和 RC 遥控器检查(SITL 没有物理电池和遥控器):
bash
param set-default PWM_MAIN_FUNC1 101
param set-default PWM_MAIN_FUNC2 102
param set-default PWM_MAIN_FUNC3 103
param set-default PWM_MAIN_FUNC4 104
# 仿真环境: 禁用电池故障保护 + 允许无RC进入Offboard
param set-default COM_BAT_ACT_T 0
param set-default COM_RCL_EXCEPT 4
6-3 IMU
- 飞机(iris)自带的 IMU 通过 PX4 EKF 处理,经 uXRCE-DDS 发布到
/fmu/out/sensor_combined - 但 FastLIO2 需要 原始 IMU 数据 (未经 EKF 滤波的
sensor_msgs/Imu),因此在gazebo_ros_imu模型中额外挂载了一个libgazebo_ros_imu_sensor.so,200Hz,直接发布/imu/data到 ROS2
6-4 双路 RGB 相机
- 挂载两个
libgazebo_ros_camera.so实例:- 前视相机
camera_front:机头前 15cm,高 8cm,640×480 30Hz,话题/camera_front/cam/image_raw - 下视相机
camera_down:机身底部 2cm,pitch 90° 朝下,话题/camera_down/cam/image_raw
- 前视相机
6-5 启动全新的飞机
- 新的 bringup 脚本加入网笼世界指定:
bash
#!/bin/bash
# ============================================================
# PX4 + Gazebo + ROS2 联合启动脚本
# 启动顺序: MicroXRCEAgent → PX4 SITL → ROS2 就绪
# Ctrl+C 退出全部
# ============================================================
set -e
cleanup() {
echo ""
echo ">>> 正在关闭所有进程..."
kill $AGENT_PID 2>/dev/null
pkill -f "gzserver" 2>/dev/null
pkill -f "gzclient" 2>/dev/null
pkill -f "px4" 2>/dev/null
echo ">>> 已全部关闭"
exit 0
}
trap cleanup SIGINT SIGTERM
# ==================== 配置 ====================
WORKSPACE_DIR="/home/lzh/postgraduate0/px4_ws"
PX4_DIR="$HOME/PX4-Autopilot"
AGENT_BIN="$HOME/microros_agent/bin/MicroXRCEAgent"
AGENT_LIB="$HOME/microros_agent/lib"
# ==================== 1. 启动 Micro XRCE-DDS Agent ====================
echo ">>> [1/3] 启动 Micro XRCE-DDS Agent (UDP port 8888)..."
export LD_LIBRARY_PATH="$AGENT_LIB:$LD_LIBRARY_PATH"
$AGENT_BIN udp4 -p 8888 &
AGENT_PID=$!
sleep 2
# 检查 Agent 是否成功启动
if kill -0 $AGENT_PID 2>/dev/null; then
echo ">>> [1/3] Agent 启动成功 (PID: $AGENT_PID)"
else
echo "!!! Agent 启动失败,请检查"
exit 1
fi
# ==================== 2. 启动 PX4 SITL + Gazebo ====================
echo ">>> [2/3] 启动 PX4 SITL + Gazebo Classic (iris_vlp16 + 网笼)..."
export PX4_SITL_WORLD="$WORKSPACE_DIR/cage.world"
cd $PX4_DIR
make px4_sitl gazebo-classic_iris_vlp16 &
PX4_PID=$!
sleep 8
# ==================== 3. 最终提示 ====================
echo ""
echo "============================================"
echo " 仿真环境已启动!"
echo " Agent PID: $AGENT_PID"
echo " PX4 PID: $PX4_PID"
echo "============================================"
echo ""
echo " ROS2 话题测试:"
echo " source /opt/ros/humble/setup.bash"
echo " source $WORKSPACE_DIR/install/setup.bash"
echo " ros2 topic list | grep fmu"
echo ""
echo " 按 Ctrl+C 退出全部"
echo "============================================"
echo ""
# 等待子进程
wait

7 RViz2 可视化
- PX4 不发布传统 TF 变换,需要手动补充传感器 frame 的静态变换,然后把
world改成base_link作为固定参考帧 3_rviz.sh启动脚本:先拉起 4 个静态 TF 发布器(传感器外参),再启动 RViz2 加载配置文件:
bash
#!/bin/bash
# RViz2 可视化: Velodyne点云 + PX4里程计 + TF
source /opt/ros/humble/setup.bash
source /home/lzh/postgraduate0/px4_ws/install/setup.bash
# 传感器静态 TF: base_link → velodyne_link / imu_link / camera
ros2 run tf2_ros static_transform_publisher \
0 0 0.12 0 0 0 base_link velodyne_link &
TF1=$!
ros2 run tf2_ros static_transform_publisher \
0 0 0.05 0 0 0 base_link imu_link &
TF2=$!
ros2 run tf2_ros static_transform_publisher \
0.15 0 0.08 0 0 0 base_link camera_front_link &
TF3=$!
ros2 run tf2_ros static_transform_publisher \
0 0 -0.02 0 1.5708 0 base_link camera_down_link &
TF4=$!
# 启动 RViz2
rviz2 -d /home/lzh/postgraduate0/px4_ws/px4_sim.rviz
# 清理
kill $TF1 $TF2 $TF3 $TF4 2>/dev/null
- RViz 配置文件核心显示项:
| 显示 | 话题 | 类型 |
|---|---|---|
| Grid | --- | 参考网格 |
| TF | --- | 坐标系树 |
| PointCloud2 | /velodyne/points |
彩虹着色 |
| Odometry | /fmu/out/vehicle_odometry |
绿色轨迹箭头 |
| AccelStamped | /imu/data |
IMU 加速度矢量 |
| Image ×2 | /camera_front/cam/image_raw、/camera_down/cam/image_raw |
双路 RGB |
-
注意 Humble 没有
rviz_default_plugins/Imu,改用AccelStamped -
完整
px4_sim.rviz配置文件:
yaml
Panels:
- Class: rviz_common/Displays
Name: Displays
- Class: rviz_common/Views
Name: Views
Visualization Manager:
Class: ""
Displays:
- Class: rviz_default_plugins/Grid
Name: Grid
Plane Cell Count: 20
Cell Size: 1
Color: 160; 160; 164
Enabled: true
Plane: XY
Reference Frame: base_link
- Class: rviz_default_plugins/TF
Name: TF
Enabled: true
Frame Timeout: 15
Marker Scale: 0.3
Show Arrows: true
Show Axes: true
Show Names: true
- Class: rviz_default_plugins/PointCloud2
Name: Velodyne
Enabled: true
Topic:
Depth: 5
Reliability Policy: Best Effort
Value: /velodyne/points
Style: Points
Size (m): 0.03
Size (Pixels): 2
Color Transformer: Intensity
Decay Time: 0.1
Use Rainbow: true
- Class: rviz_default_plugins/Odometry
Name: PX4 Odometry
Enabled: true
Topic:
Depth: 5
Reliability Policy: Best Effort
Value: /fmu/out/vehicle_odometry
Keep: 100
Shape: Arrow
Color: 0; 200; 100
Alpha: 0.7
Shaft Length: 0.2
Head Length: 0.1
Head Radius: 0.05
- Class: rviz_default_plugins/AccelStamped
Name: IMU Accel
Enabled: true
Topic:
Depth: 5
Reliability Policy: Best Effort
Value: /imu/data
Scale: 0.5
Color: 255; 100; 100
Alpha: 0.7
- Class: rviz_default_plugins/Image
Name: Camera Front
Enabled: true
Topic:
Depth: 5
Reliability Policy: Best Effort
Value: /camera_front/cam/image_raw
Transport Hint: raw
- Class: rviz_default_plugins/Image
Name: Camera Down
Enabled: true
Topic:
Depth: 5
Reliability Policy: Best Effort
Value: /camera_down/cam/image_raw
Transport Hint: raw
- Class: rviz_default_plugins/Axes
Name: Axes
Enabled: true
Length: 0.3
Radius: 0.02
Reference Frame: base_link
Enabled: true
Global Options:
Background Color: 48; 48; 48
Fixed Frame: base_link
Frame Rate: 30
Name: root
Tools:
- Class: rviz_default_plugins/MoveCamera
- Class: rviz_default_plugins/Select
- Class: rviz_default_plugins/Measure
Value: true
Views:
Current:
Class: rviz_default_plugins/Orbit
Distance: 10
Focal Point: {X: 0, Y: 0, Z: 3}
Name: Current View
Pitch: 0.5
Target Frame: base_link
Yaw: 0.8
Window Geometry:
Height: 800
Width: 1200
8 网笼测试场地
- 需要一个封闭空间验证 SLAM 和避障:9m × 4.6m × 4m,四面 + 天花板共五面网栏
- 没有现成模型,使用 Python 生成 SDF World 文件------竖杆 + 横杆间距 0.4m,细杆直径 2cm,共 139 根杆
python
W,H = 9, 4.6; Hgt, S = 4, 0.4; T = 0.02; parts = []; cnt = 0
def bar(x,y,z,sx,sy,sz):
global cnt; parts.append(f'<model name="b{cnt}"><static>true</static>'
f'<pose>{x} {y} {z} 0 0 0</pose><link name="l">'
f'<collision name="c"><geometry><box><size>{sx} {sy} {sz}</size></box></geometry></collision>'
f'<visual name="v"><geometry><box><size>{sx} {sy} {sz}</size></box></geometry>'
f'<material><ambient>0.4 0.4 0.4 1</ambient></material></visual></link></model>')
cnt += 1
# 四面墙: 竖杆 + 横杆
for y in [-H/2, H/2]:
for i in range(int(W/S)+1): bar(-W/2+i*S, y, Hgt/2, T, T, Hgt)
for j in range(1, int(Hgt/S)): bar(0, y, j*S, W, T, T)
for x in [-W/2, W/2]:
for i in range(1, int(H/S)): bar(x, -H/2+i*S, Hgt/2, T, T, Hgt)
for j in range(1, int(Hgt/S)): bar(x, 0, j*S, T, H, T)
# 四角柱 + 天花板
for x in [-W/2, W/2]:
for y in [-H/2, H/2]: bar(x, y, Hgt/2, T, T, Hgt)
for i in range(int(W/S)+1): bar(-W/2+i*S, 0, Hgt, T, H, T)
for j in range(1, int(H/S)): bar(0, -H/2+j*S, Hgt, W, T, T)
with open('cage.world','w') as f:
f.write(f'<?xml version="1.0"?><sdf version="1.6"><world name="cage">'
f'<include><uri>model://sun</uri></include>'
f'<include><uri>model://ground_plane</uri></include>'
f'{"".join(parts)}</world></sdf>')
- 通过
PX4_SITL_WORLD环境变量指定 world 文件,飞机自动生成在场地中央(0, 0, 0)

8-1 当前完整项目结构
- 经过六、七、八三章的传感器配置和场地搭建,
px4_ws目录结构更新为:
bash
~/postgraduate0/px4_ws/
├── src/px4_msgs/ # PX4 ROS2 消息定义包
├── install/ # colcon build 编译产物
├── 1_bringup.sh # 一键启动仿真 (+网笼 +iris_vlp16)
├── 2_keyboard.sh # 键盘控制启动脚本
├── 2_keyboard_control.py # 键盘控制 Python 节点
├── 3_rviz.sh # RViz2 可视化启动脚本
├── px4_sim.rviz # RViz2 配置文件
└── cage.world # 网笼测试场地 SDF
~/.gazebo/models/
├── iris_vlp16/ # 嵌套 iris + 全部传感器
├── velodyne_vlp16/ # VLP-16 3D LiDAR 模型
├── gazebo_ros_imu/ # ROS2 IMU 传感器模型
└── rgb_camera/ # RGB 相机模型
~/PX4-Autopilot/
├── src/modules/.../sitl_targets_gazebo-classic.cmake # 已注册 iris_vlp16
├── ROMFS/.../airframes/11015_gazebo-classic_iris_vlp16 # airframe 启动脚本
└── Tools/simulation/gazebo-classic/.../models/iris_vlp16/ # 模型+传感器 SDF
- 三个脚本的启动顺序与职责:
| 脚本 | 终端 | 职责 |
|---|---|---|
./1_bringup.sh |
终端 1 | 启动 Agent + PX4 + Gazebo(带 iris_vlp16 + 网笼) |
./2_keyboard.sh |
终端 2 | 启动键盘控制节点,接管飞机(等 Ready for takeoff! 出现后) |
./3_rviz.sh |
终端 3 | 启动 RViz2 可视化(点云 + 里程计 + IMU + 双路相机) |
9 完整演示流程


总结
- 本文从零搭建了一套完整的 ROS2 无人机仿真闭环:
- PX4 SITL + Gazebo Classic + Micro XRCE-DDS 基础通信链路
- 键盘 Offboard 速度模式控制(松手悬停,点按微调)
- 四传感器集成(3D LiDAR + IMU + 双路 RGB),全部输出标准 ROS2 话题
- 9m×4.6m 网笼封闭测试场地
- RViz2 全数据可视化
- 核心踩坑回顾:
- PX4 cmake 模型白名单------不注册就没法用
make启动 - 雷达 CPU 模式直接卡死 Gazebo------必须用
gpu_ray+ 降参数 - 传感器重量仿真------真机 830g 的 VLP-16 在仿真里压得飞机飞不起来,必须减到 50g
COM_RCL_EXCEPT=4+COM_BAT_ACT_T=0------SITL 没有 RC 遥控器和真实电池,不设这两个参数 Offboard 进不去- Offboard 模式
custom_main_mode=6不是4(4是AUTO,6才是OFFBOARD)
- PX4 cmake 模型白名单------不注册就没法用
- 后续计划:接上
FAST-LIO2(LiDAR+IMU 融合里程计)→OctoMap(3D 占据地图)→EGO-Planner(轨迹规划) - 如有错误,欢迎指出!
- 感谢观看!