保姆级教程:用PX4和ROS在Gazebo仿真中实现无人机自动画圆(附完整代码与脚本)
从零实现PX4无人机Gazebo仿真中的圆形轨迹控制
第一次接触无人机仿真时,看着屏幕上那个悬停的虚拟四旋翼,我脑海中浮现的第一个念头就是:"能不能让它按照我设计的路径飞行?"这个问题困扰了我整整一个周末。作为ROS和PX4的初学者,当时最需要的不是复杂的理论推导,而是一份能直接上手的实践指南。本文将带你一步步实现无人机在Gazebo中的圆形轨迹飞行,从环境配置到代码调试,每个环节都配有可运行的代码片段和常见问题解决方案。
1. 环境准备与基础概念
在开始编写控制代码前,我们需要确保开发环境正确配置。这套方案基于Ubuntu 20.04+ROS Noetic+PX4 v1.13的版本组合验证通过,其他版本可能需要适当调整。
1.1 必备软件安装
首先通过apt安装核心组件:
sudo apt install ros-noetic-mavros ros-noetic-mavros-extras wget https://raw.githubusercontent.com/mavlink/mavros/master/mavros/scripts/install_geographiclib_datasets.sh chmod +x install_geographiclib_datasets.sh ./install_geographiclib_datasets.sh接着配置PX4开发环境:
git clone https://github.com/PX4/PX4-Autopilot.git --recursive cd PX4-Autopilot make px4_sitl_default gazebo1.2 关键通信接口解析
PX4与ROS通过MAVROS桥接通信,几个核心话题需要特别关注:
| 话题名称 | 消息类型 | 功能描述 |
|---|---|---|
| /mavros/state | mavros_msgs/State | 获取飞控当前状态 |
| /mavros/setpoint_raw/local | mavros_msgs/PositionTarget | 发送位置/速度控制指令 |
| /mavros/cmd/arming | mavros_msgs/CommandBool | 无人机解锁服务 |
| /mavros/set_mode | mavros_msgs/SetMode | 飞行模式设置服务 |
提示:OFFBOARD模式下必须保持2Hz以上的控制指令发送频率,否则飞控会自动切换回之前的安全模式。
2. 圆形轨迹的数学建模
实现圆形飞行的核心在于将连续的圆形路径离散化为时间序列上的目标点。我们采用参数方程来描述这个圆形轨迹。
2.1 参数方程推导
考虑在XY平面内半径为r的圆,其参数方程为:
x = r * cos(θ) y = r * sin(θ)其中θ∈[0,2π]。但实际实现时需要处理几个关键细节:
- 起始点调整:为了让无人机从原点平滑过渡到圆周,我们将初始角度设为-π/2
- 高度控制:保持固定高度z=2米
- 速度计算:通过参数θ对时间t的导数得到切向速度
对应的代码实现结构:
#define RATE 20 // 控制频率20Hz #define RADIUS 2.5 // 圆半径2.5米 #define CYCLE_TIME 15 // 完成一圈所需时间(秒) // 计算每步角度增量 float theta = -M_PI/2; float delta_theta = 2*M_PI/(RATE*CYCLE_TIME);2.2 坐标系转换
PX4使用NED(北东地)坐标系,而Gazebo默认使用ENU(东北天)坐标系。MAVROS提供了自动转换功能,但需要明确指定坐标系类型:
Target_P.coordinate_frame = mavros_msgs::PositionTarget::FRAME_LOCAL_NED;3. 控制代码实现
完整的控制逻辑分为初始化、状态切换和轨迹跟踪三个阶段。下面我们拆解关键代码模块。
3.1 节点初始化配置
创建ROS节点并设置发布/订阅关系:
ros::NodeHandle nh; ros::Publisher target_pub = nh.advertise<mavros_msgs::PositionTarget> ("mavros/setpoint_raw/local", 10); ros::Subscriber state_sub = nh.subscribe<mavros_msgs::State> ("mavros/state", 10, state_cb);配置PositionTarget消息的type_mask字段非常重要,它决定了飞控将处理哪些控制量:
Target_P.type_mask = mavros_msgs::PositionTarget::IGNORE_VX | mavros_msgs::PositionTarget::IGNORE_VY | mavros_msgs::PositionTarget::IGNORE_VZ | mavros_msgs::PositionTarget::IGNORE_AFX | mavros_msgs::PositionTarget::IGNORE_AFY | mavros_msgs::PositionTarget::IGNORE_AFZ | mavros_msgs::PositionTarget::IGNORE_YAW_RATE;3.2 状态机实现
使用有限状态机管理飞行流程:
- 初始化状态:发送若干初始指令激活OFFBOARD模式
- 起飞阶段:控制无人机上升到目标高度
- 轨迹跟踪:按照圆形方程更新目标位置
- 降落阶段:切换至AUTO.LAND模式
enum FlightState { INIT, TAKEOFF, CIRCLE, LAND }; FlightState current_state = INIT;3.3 轨迹生成核心算法
在20Hz的控制频率下,每个周期更新目标位置:
theta += delta_theta; Target_P.position.x = RADIUS * cos(theta); Target_P.position.y = RADIUS * sin(theta); Target_P.position.z = 2.0; // 保持固定高度 // 计算并设置偏航角(机头指向运动方向) Target_P.yaw = atan2(Target_P.velocity.y, Target_P.velocity.x);4. 系统集成与调试
完成代码编写后,需要将各个模块整合成可运行的完整系统。
4.1 构建配置
CMakeLists.txt关键配置:
find_package(catkin REQUIRED COMPONENTS geometry_msgs mavros_msgs roscpp std_msgs ) add_executable(offb_node src/offb_node.cpp) target_link_libraries(offb_node ${catkin_LIBRARIES})4.2 一键启动脚本
创建启动脚本launch_demo.sh简化测试流程:
#!/bin/bash gnome-terminal --tab --title="ROS Core" -- bash -c "roscore; exec bash" sleep 3 gnome-terminal --tab --title="PX4 SITL" -- bash -c "roslaunch px4 mavros_posix_sitl.launch; exec bash" sleep 5 gnome-terminal --tab --title="Control Node" -- bash -c "rosrun offb_pkg offb_node; exec bash"4.3 常见问题排查
- OFFBOARD模式无法激活:检查控制指令发送频率是否≥2Hz
- 无人机剧烈抖动:调整控制频率或减小圆半径
- 轨迹偏离圆形:确认坐标系设置和参数方程正确性
- 高度不稳定:检查type_mask是否忽略了垂直速度控制
注意:首次运行前务必在Gazebo中测试手动飞行,确认基础功能正常。
实现过程中最让我意外的是type_mask参数的重要性——最初因为没有正确设置它,无人机对速度指令毫无反应。后来通过仔细阅读MAVROS文档才发现,这个位掩码实际上决定了飞控会处理消息中的哪些字段。这种开环控制虽然简单,但为后续开发更复杂的闭环控制算法奠定了基础。
