PX4飞控系统中的vehicle_command消息是一个强大的底层控制接口,它允许开发者在不修改飞控核心代码的情况下,通过发送特定的UORB消息来实现各种高级控制功能。这个机制为无人机二次开发提供了极大的灵活性。
vehicle_command消息的核心结构包含以下关键字段:
cpp复制struct vehicle_command_s {
uint32_t timestamp; // 消息时间戳
uint8_t target_system; // 目标系统ID
uint8_t target_component; // 目标组件ID
uint16_t command; // 命令类型
uint8_t confirmation; // 确认标志
float param1; // 参数1
float param2; // 参数2
float param3; // 参数3
float param4; // 参数4
float param5; // 参数5
float param6; // 参数6
float param7; // 参数7
};
关键特性:
注意:使用
vehicle_command需要确保发送频率适中,过高频率可能导致飞控系统过载。
PX4支持多种飞行模式,通过VEHICLE_CMD_DO_SET_MODE命令可以实现模式切换。以下是一个典型的模式切换代码示例:
cpp复制// 准备命令消息
vehicle_command_s cmd = {};
cmd.target_system = 1; // 通常设为1表示主飞控
cmd.command = vehicle_command_s::VEHICLE_CMD_DO_SET_MODE;
// 参数设置
cmd.param1 = 1.0f; // 主模式标志
cmd.param2 = PX4_CUSTOM_MAIN_MODE_OFFBOARD; // 主模式:OFFBOARD
cmd.param3 = 0; // 子模式(某些模式需要)
cmd.timestamp = hrt_absolute_time();
// 发布命令
uORB::Publication<vehicle_command_s> cmd_pub{ORB_ID(vehicle_command)};
cmd_pub.publish(cmd);
常用主模式常量:
| 常量 | 值 | 描述 |
|---|---|---|
| PX4_CUSTOM_MAIN_MODE_MANUAL | 1 | 手动模式 |
| PX4_CUSTOM_MAIN_MODE_ALTCTL | 2 | 高度控制模式 |
| PX4_CUSTOM_MAIN_MODE_POSCTL | 3 | 位置控制模式 |
| PX4_CUSTOM_MAIN_MODE_AUTO | 4 | 自动模式 |
| PX4_CUSTOM_MAIN_MODE_ACRO | 5 | 特技模式 |
| PX4_CUSTOM_MAIN_MODE_OFFBOARD | 6 | Offboard模式 |
对于需要同时指定主模式和子模式的复杂切换,如自动返航(RTL)或自动降落(LAND),需要正确设置param3参数:
cpp复制// 切换到自动降落模式
cmd.command = vehicle_command_s::VEHICLE_CMD_DO_SET_MODE;
cmd.param1 = 1.0f;
cmd.param2 = PX4_CUSTOM_MAIN_MODE_AUTO; // 主模式:自动
cmd.param3 = PX4_CUSTOM_SUB_MODE_AUTO_LAND; // 子模式:降落
常见子模式组合:
PX4_CUSTOM_SUB_MODE_AUTO_MISSIONPX4_CUSTOM_SUB_MODE_AUTO_RTLPX4_CUSTOM_SUB_MODE_AUTO_LANDPX4_CUSTOM_SUB_MODE_AUTO_READYPX4_CUSTOM_SUB_MODE_AUTO_TAKEOFF对于垂直起降(VTOL)无人机,模式切换更为复杂,需要使用专门的VTOL过渡命令。
VTOL过渡的核心命令是VEHICLE_CMD_DO_VTOL_TRANSITION,其参数设置如下:
cpp复制vehicle_command_s cmd = {};
cmd.command = vehicle_command_s::VEHICLE_CMD_DO_VTOL_TRANSITION;
// 旋翼模式切换到固定翼模式
cmd.param1 = vtol_vehicle_status_s::VEHICLE_VTOL_STATE_FW; // 值为4
// 固定翼模式切换到旋翼模式
// cmd.param1 = vtol_vehicle_status_s::VEHICLE_VTOL_STATE_MC; // 值为3
VTOL状态常量:
| 常量 | 值 | 描述 |
|---|---|---|
| VEHICLE_VTOL_STATE_MC | 3 | 多旋翼模式 |
| VEHICLE_VTOL_STATE_FW | 4 | 固定翼模式 |
| VEHICLE_VTOL_STATE_TRANSITION_TO_FW | 5 | 向固定翼过渡中 |
| VEHICLE_VTOL_STATE_TRANSITION_TO_MC | 6 | 向多旋翼过渡中 |
以下是一个完整的VTOL模式切换函数实现:
cpp复制bool transitionToFixedWing()
{
vehicle_command_s cmd = {};
cmd.target_system = 1;
cmd.target_component = 1;
cmd.command = vehicle_command_s::VEHICLE_CMD_DO_VTOL_TRANSITION;
cmd.param1 = vtol_vehicle_status_s::VEHICLE_VTOL_STATE_FW; // 切换到固定翼
cmd.timestamp = hrt_absolute_time();
uORB::Publication<vehicle_command_s> cmd_pub{ORB_ID(vehicle_command)};
return cmd_pub.publish(cmd) == PX4_OK;
}
重要提示:VTOL切换前应确保无人机处于适当的高度和速度,避免在低空或低速时尝试切换。
在较旧版本的PX4中,可以使用VEHICLE_CMD_DO_SET_SERVO命令直接控制舵机:
cpp复制vehicle_command_s cmd = {};
cmd.command = vehicle_command_s::VEHICLE_CMD_DO_SET_SERVO;
cmd.param1 = 5; // 舵机通道号(1-8)
cmd.param2 = 1500; // PWM脉宽(1000-2000μs)
cmd.timestamp = hrt_absolute_time();
uORB::Publication<vehicle_command_s> cmd_pub{ORB_ID(vehicle_command)};
cmd_pub.publish(cmd);
参数说明:
param1: 舵机通道号(1-8对应主输出,9-16对应辅助输出)param2: PWM脉宽值,单位微秒(μs),典型范围1100-1900新版本PX4推荐使用actuator_outputs消息或actuator_controls消息来控制舵机输出,这提供了更灵活和安全的控制方式。
首先需要配置mixer文件定义舵机输出映射,然后可以通过以下方式控制:
cpp复制// 发布执行器控制消息
actuator_controls_s ctrls = {};
ctrls.control[0] = 0.5f; // 通道1输出(值范围-1到1)
ctrls.timestamp = hrt_absolute_time();
uORB::Publication<actuator_controls_s> ctrls_pub{ORB_ID(actuator_controls_0)};
ctrls_pub.publish(ctrls);
新旧版本控制方式对比:
| 特性 | 旧版(VEHICLE_CMD_DO_SET_SERVO) | 新版(actuator_controls) |
|---|---|---|
| 兼容性 | 仅旧版支持 | 所有版本支持 |
| 安全性 | 较低,直接控制输出 | 较高,经过混控器处理 |
| 灵活性 | 有限 | 高,支持复杂混控 |
| 推荐度 | 不推荐 | 推荐 |
发送vehicle_command消息时需要注意频率控制,以下是一些指导原则:
cpp复制// 示例:安全的重复命令发送
static hrt_abstime last_send_time = 0;
if (hrt_elapsed_time(&last_send_time) > 50_ms) {
sendCommand();
last_send_time = hrt_absolute_time();
}
发送命令后,应该检查命令是否被正确执行:
cpp复制// 订阅命令确认消息
uORB::Subscription cmd_ack_sub{ORB_ID(vehicle_command_ack)};
// 检查命令确认
vehicle_command_ack_s ack;
if (cmd_ack_sub.update(&ack)) {
if (ack.command == my_command &&
ack.result == vehicle_command_ack_s::VEHICLE_CMD_RESULT_ACCEPTED) {
// 命令被接受
}
}
命令结果常量:
| 常量 | 值 | 描述 |
|---|---|---|
| VEHICLE_CMD_RESULT_ACCEPTED | 0 | 命令已接受 |
| VEHICLE_CMD_RESULT_TEMPORARILY_REJECTED | 1 | 临时拒绝 |
| VEHICLE_CMD_RESULT_DENIED | 2 | 拒绝 |
| VEHICLE_CMD_RESULT_UNSUPPORTED | 3 | 不支持 |
| VEHICLE_CMD_RESULT_FAILED | 4 | 执行失败 |
| VEHICLE_CMD_RESULT_IN_PROGRESS | 5 | 执行中 |
在实际应用中,应该实现完善的错误处理机制:
cpp复制bool sendCommandWithRetry(uint16_t command, int max_retries = 3)
{
for (int i = 0; i < max_retries; ++i) {
if (sendCommand(command)) {
// 检查确认
if (checkCommandAck(command)) {
return true;
}
}
usleep(100000); // 等待100ms
}
return false;
}
结合模式切换和舵机控制,可以实现自动任务中的相机控制:
cpp复制// 进入自动任务模式
setMode(PX4_CUSTOM_MAIN_MODE_AUTO, PX4_CUSTOM_SUB_MODE_AUTO_MISSION);
// 到达拍照点时触发相机
setServo(CAMERA_SHUTTER_CHANNEL, 2000); // 触发快门
usleep(50000); // 保持50ms
setServo(CAMERA_SHUTTER_CHANNEL, 1000); // 释放快门
实现一个安全的应急处理流程:
cpp复制void emergencyProcedure()
{
// 1. 尝试切换到位置保持模式
if (!setMode(PX4_CUSTOM_MAIN_MODE_POSCTL)) {
// 2. 如果失败,尝试切换到高度保持模式
if (!setMode(PX4_CUSTOM_MAIN_MODE_ALTCTL)) {
// 3. 最后尝试切换到手动模式
setMode(PX4_CUSTOM_MAIN_MODE_MANUAL);
}
}
// 释放所有负载确保安全
for (int ch = 1; ch <= 8; ++ch) {
setServo(ch, 1000); // 最小PWM值
}
}
优化消息发布性能:
cpp复制// 使用Queued发布者减少延迟
uORB::PublicationQueued<vehicle_command_s> cmd_pub{ORB_ID(vehicle_command)};
// 预分配消息内存
vehicle_command_s cmd = {};
cmd.target_system = 1;
cmd.target_component = 1;
// 重用消息结构体减少内存分配
for (/*...*/) {
cmd.command = /*...*/;
cmd.timestamp = hrt_absolute_time();
cmd_pub.publish(cmd);
}
对于需要发送多个命令的场景,可以批量处理:
cpp复制void sendMultipleCommands(const std::vector<CommandRequest>& requests)
{
uORB::PublicationQueued<vehicle_command_s> cmd_pub{ORB_ID(vehicle_command)};
vehicle_command_s cmd = {};
cmd.target_system = 1;
cmd.target_component = 1;
for (const auto& req : requests) {
cmd.command = req.command;
cmd.param1 = req.param1;
// ...设置其他参数
cmd.timestamp = hrt_absolute_time();
cmd_pub.publish(cmd);
usleep(10000); // 小延迟防止队列溢出
}
}
实现版本自适应控制:
cpp复制#include <px4_platform_common/version.h>
bool isVersionNewerThan(const char* compare_version)
{
return strcmp(px4_firmware_version_string(), compare_version) >= 0;
}
void setup()
{
if (isVersionNewerThan("v1.13.0")) {
// 使用新版API
} else {
// 使用旧版兼容代码
}
}
创建一个兼容性封装层来屏蔽版本差异:
cpp复制class PX4CommandInterface {
public:
virtual bool setServo(uint8_t channel, uint16_t pwm) = 0;
// ...其他接口
};
// 旧版实现
class LegacyCommandInterface : public PX4CommandInterface {
bool setServo(uint8_t channel, uint16_t pwm) override {
// 使用VEHICLE_CMD_DO_SET_SERVO实现
}
};
// 新版实现
class ModernCommandInterface : public PX4CommandInterface {
bool setServo(uint8_t channel, uint16_t pwm) override {
// 使用actuator_controls实现
}
};
// 工厂方法
std::unique_ptr<PX4CommandInterface> createCommandInterface()
{
if (isVersionNewerThan("v1.13.0")) {
return std::make_unique<ModernCommandInterface>();
} else {
return std::make_unique<LegacyCommandInterface>();
}
}