无人机双遥控器系统:分离式姿态与云台控制的完整实现

前言

在专业航拍和工业级无人机应用中,单遥控器往往难以满足复杂的操作需求。本文将详细介绍如何实现双遥控器系统,一个遥控器专门控制无人机的飞行姿态(俯仰、横滚、偏航、油门),另一个遥控器专门控制云台的三轴运动(俯仰、横滚、偏航)。这种分离式控制架构广泛应用于电影级航拍、工业巡检等专业场景。

系统特点:

  • 双遥控器独立控制,互不干扰
  • 支持多种接收机协议(SBUS/PPM/PWM)
  • 实时数据融合与优先级管理
  • 完整的故障检测与安全机制
  • 可扩展的模块化架构

一、系统架构设计

1.1 整体架构

复制代码
┌─────────────────┐         ┌─────────────────┐
│   姿态遥控器    │         │   云台遥控器    │
│  (主飞手)       │         │  (云台手)       │
└────────┬────────┘         └────────┬────────┘
         │                           │
         │ 2.4GHz                    │ 2.4GHz
         │ SBUS/PPM                  │ SBUS/PPM
         ▼                           ▼
┌─────────────────┐         ┌─────────────────┐
│  接收机1        │         │  接收机2        │
│  (8通道)        │         │  (8通道)        │
└────────┬────────┘         └────────┬────────┘
         │                           │
         │ UART/PWM                  │ UART/PWM
         │                           │
         └───────────┬───────────────┘
                     ▼
         ┌───────────────────────┐
         │   飞控主控制器        │
         │   (STM32F4/F7)        │
         │  ┌─────────────────┐  │
         │  │ 姿态控制模块    │  │
         │  │ - 融合姿态数据  │  │
         │  │ - PID控制       │  │
         │  │ - 电机输出      │  │
         │  └─────────────────┘  │
         │  ┌─────────────────┐  │
         │  │ 云台控制模块    │  │
         │  │ - 云台角度控制  │  │
         │  │ - 平滑滤波      │  │
         │  │ - 跟随模式      │  │
         │  └─────────────────┘  │
         │  ┌─────────────────┐  │
         │  │ 通信管理模块    │  │
         │  │ - 接收机解析    │  │
         │  │ - 数据校验      │  │
         │  │ - 故障检测      │  │
         │  └─────────────────┘  │
         └───────────────────────┘
                     │
         ┌───────────┼───────────┐
         ▼           ▼           ▼
    ┌────────┐  ┌────────┐  ┌────────┐
    │ 电机1  │  │ 电机2  │  │ 云台   │
    └────────┘  └────────┘  └────────┘

1.2 通道映射

姿态遥控器通道(接收机1):

  • CH1: 横滚(Roll) - 左右平移
  • CH2: 俯仰(Pitch) - 前后平移
  • CH3: 油门(Throttle) - 上升下降
  • CH4: 偏航(Yaw) - 左右旋转
  • CH5: 飞行模式切换
  • CH6: 紧急停止开关
  • CH7-CH8: 备用

云台遥控器通道(接收机2):

  • CH1: 云台俯仰(Gimbal Pitch)
  • CH2: 云台横滚(Gimbal Roll)
  • CH3: 云台偏航(Gimbal Yaw)
  • CH4: 云台跟随模式
  • CH5: 云台复位
  • CH6: 焦距控制
  • CH7-CH8: 备用

1.3 数据流处理

复制代码
接收机数据 → 信号解析 → 数据校验 → 死区处理 → 
限幅处理 → 指数曲线 → 控制算法 → 输出限制 → 执行器

二、硬件连接方案

2.1 硬件清单

组件 型号推荐 数量 说明
主控MCU STM32F427VIT6 1 168MHz, 256KB RAM
接收机 FrSky X8R / FlySky FS-iA10B 2 支持SBUS/PPM
无刷电机 2212 920KV 4 含电调
三轴云台 BGC3.1 1 带独立控制板
IMU MPU6050/ICM20602 1 6轴姿态传感器
电源模块 5V/3A + 12V/2A 1 双路输出

2.2 接线图

复制代码
STM32F427 引脚连接:

姿态接收机1 (SBUS):
- RX → PA10 (USART1_RX)
- 5V → 5V
- GND → GND

云台接收机2 (SBUS):
- RX → PA3 (USART2_RX)
- 5V → 5V
- GND → GND

电机输出 (PWM):
- Motor1 → PE9  (TIM1_CH1)
- Motor2 → PE11 (TIM1_CH2)
- Motor3 → PE13 (TIM1_CH3)
- Motor4 → PE14 (TIM1_CH4)

云台控制 (UART):
- TX → PD5 (USART2_TX)
- RX → PD6 (USART2_RX)

IMU (I2C):
- SCL → PB10 (I2C2_SCL)
- SDA → PB11 (I2C2_SDA)

三、核心原理详解

3.1 SBUS协议解析

SBUS是一种串行总线协议,具有以下特点:

  • 波特率:100000 bps
  • 数据格式:8E2(8数据位,偶校验,2停止位)
  • 数据包长度:25字节
  • 刷新率:7-14ms
  • 16个通道,每个通道11位分辨率(0-2047)

数据包格式:

复制代码
Byte 0: 0x0F (起始字节)
Byte 1-22: 16通道数据 (176 bits = 16 × 11 bits)
Byte 23: Flags (数字通道 & 失控标志)
Byte 24: 0x00 (结束字节)

3.2 双遥控器数据融合策略

优先级管理:

  1. 紧急停止优先级最高:任一遥控器触发紧急停止立即生效
  2. 姿态控制优先于云台:飞行安全优先
  3. 失控保护:任一接收机信号丢失触发安全降落

数据融合算法:

c 复制代码
// 伪代码
if (attitude_receiver_valid && gimbal_receiver_valid) {
    // 两个接收机都正常
    attitude_control = parse_attitude_receiver();
    gimbal_control = parse_gimbal_receiver();
} else if (attitude_receiver_valid) {
    // 仅姿态接收机正常,云台保持当前姿态
    attitude_control = parse_attitude_receiver();
    gimbal_control = hold_current_position();
} else if (gimbal_receiver_valid) {
    // 仅云台接收机正常,进入失控保护
    trigger_failsafe_mode();
} else {
    // 两个都失控
    trigger_emergency_landing();
}

3.3 姿态控制算法

采用串级PID控制结构:

外环(角度环):

复制代码
期望角度 → PID控制器 → 期望角速度

内环(角速度环):

复制代码
期望角速度 → PID控制器 → 电机输出

PID计算公式:

复制代码
output = Kp × error + Ki × ∫error·dt + Kd × d(error)/dt

3.4 云台控制算法

增量式控制:

c 复制代码
gimbal_angle += remote_input × sensitivity × dt;
gimbal_angle = constrain(gimbal_angle, min_angle, max_angle);

平滑滤波:

c 复制代码
filtered_angle = alpha × current_angle + (1-alpha) × previous_angle;
// alpha = 0.1-0.3,根据响应速度调整

四、完整源代码实现

4.1 项目结构

复制代码
DualRemoteDrone/
├── Core/
│   ├── Inc/
│   │   ├── main.h
│   │   ├── sbus.h
│   │   ├── receiver_manager.h
│   │   ├── attitude_control.h
│   │   ├── gimbal_control.h
│   │   └── motor_control.h
│   └── Src/
│       ├── main.c
│       ├── sbus.c
│       ├── receiver_manager.c
│       ├── attitude_control.c
│       ├── gimbal_control.c
│       └── motor_control.c
├── Drivers/
└── Makefile

4.2 SBUS接收器驱动 (sbus.h)

c 复制代码
/**
 * @file sbus.h
 * @brief SBUS协议接收器驱动
 * @author Your Name
 * @date 2025-10-27
 */

#ifndef __SBUS_H
#define __SBUS_H

#include "stm32f4xx_hal.h"
#include <stdint.h>
#include <stdbool.h>

/* SBUS协议参数 */
#define SBUS_FRAME_SIZE         25      // 帧长度
#define SBUS_HEADER_BYTE        0x0F    // 起始字节
#define SBUS_FOOTER_BYTE        0x00    // 结束字节
#define SBUS_CHANNEL_COUNT      16      // 通道数量
#define SBUS_MIN_VALUE          172     // 最小值
#define SBUS_MAX_VALUE          1811    // 最大值
#define SBUS_CENTER_VALUE       992     // 中值
#define SBUS_TIMEOUT_MS         100     // 超时时间

/* SBUS标志位 */
#define SBUS_FLAG_CH17          0x01
#define SBUS_FLAG_CH18          0x02
#define SBUS_FLAG_FRAMELOST     0x04
#define SBUS_FLAG_FAILSAFE      0x08

/* SBUS接收器结构体 */
typedef struct {
    uint16_t channels[SBUS_CHANNEL_COUNT];  // 16个通道值
    uint8_t buffer[SBUS_FRAME_SIZE];        // 接收缓冲区
    uint8_t buffer_index;                   // 缓冲区索引
    uint32_t last_update_time;              // 上次更新时间
    bool frame_lost;                        // 丢帧标志
    bool failsafe;                          // 失控标志
    bool is_valid;                          // 数据有效标志
} SBUS_Receiver_t;

/* 函数声明 */
void SBUS_Init(SBUS_Receiver_t *receiver);
void SBUS_ParseByte(SBUS_Receiver_t *receiver, uint8_t byte);
bool SBUS_IsValid(SBUS_Receiver_t *receiver);
uint16_t SBUS_GetChannel(SBUS_Receiver_t *receiver, uint8_t channel);
int16_t SBUS_GetChannelNormalized(SBUS_Receiver_t *receiver, uint8_t channel);
void SBUS_CheckTimeout(SBUS_Receiver_t *receiver);

#endif /* __SBUS_H */

4.3 SBUS接收器驱动实现 (sbus.c)

c 复制代码
/**
 * @file sbus.c
 * @brief SBUS协议接收器驱动实现
 */

#include "sbus.h"
#include <string.h>

/**
 * @brief 初始化SBUS接收器
 * @param receiver: SBUS接收器结构体指针
 */
void SBUS_Init(SBUS_Receiver_t *receiver) {
    memset(receiver, 0, sizeof(SBUS_Receiver_t));
    
    // 初始化通道为中值
    for (int i = 0; i < SBUS_CHANNEL_COUNT; i++) {
        receiver->channels[i] = SBUS_CENTER_VALUE;
    }
    
    receiver->buffer_index = 0;
    receiver->is_valid = false;
    receiver->frame_lost = false;
    receiver->failsafe = false;
    receiver->last_update_time = HAL_GetTick();
}

/**
 * @brief 解析SBUS字节数据
 * @param receiver: SBUS接收器结构体指针
 * @param byte: 接收到的字节
 */
void SBUS_ParseByte(SBUS_Receiver_t *receiver, uint8_t byte) {
    // 检测帧头
    if (receiver->buffer_index == 0 && byte != SBUS_HEADER_BYTE) {
        return;
    }
    
    // 存储字节
    receiver->buffer[receiver->buffer_index++] = byte;
    
    // 检查是否接收完整帧
    if (receiver->buffer_index == SBUS_FRAME_SIZE) {
        receiver->buffer_index = 0;
        
        // 验证帧尾
        if (receiver->buffer[SBUS_FRAME_SIZE - 1] != SBUS_FOOTER_BYTE) {
            return;
        }
        
        // 解析16个通道数据 (11位分辨率)
        receiver->channels[0]  = ((receiver->buffer[1]    | receiver->buffer[2]<<8)                     & 0x07FF);
        receiver->channels[1]  = ((receiver->buffer[2]>>3 | receiver->buffer[3]<<5)                     & 0x07FF);
        receiver->channels[2]  = ((receiver->buffer[3]>>6 | receiver->buffer[4]<<2 | receiver->buffer[5]<<10)  & 0x07FF);
        receiver->channels[3]  = ((receiver->buffer[5]>>1 | receiver->buffer[6]<<7)                     & 0x07FF);
        receiver->channels[4]  = ((receiver->buffer[6]>>4 | receiver->buffer[7]<<4)                     & 0x07FF);
        receiver->channels[5]  = ((receiver->buffer[7]>>7 | receiver->buffer[8]<<1 | receiver->buffer[9]<<9)   & 0x07FF);
        receiver->channels[6]  = ((receiver->buffer[9]>>2 | receiver->buffer[10]<<6)                    & 0x07FF);
        receiver->channels[7]  = ((receiver->buffer[10]>>5| receiver->buffer[11]<<3)                    & 0x07FF);
        receiver->channels[8]  = ((receiver->buffer[12]   | receiver->buffer[13]<<8)                    & 0x07FF);
        receiver->channels[9]  = ((receiver->buffer[13]>>3| receiver->buffer[14]<<5)                    & 0x07FF);
        receiver->channels[10] = ((receiver->buffer[14]>>6| receiver->buffer[15]<<2 | receiver->buffer[16]<<10) & 0x07FF);
        receiver->channels[11] = ((receiver->buffer[16]>>1| receiver->buffer[17]<<7)                    & 0x07FF);
        receiver->channels[12] = ((receiver->buffer[17]>>4| receiver->buffer[18]<<4)                    & 0x07FF);
        receiver->channels[13] = ((receiver->buffer[18]>>7| receiver->buffer[19]<<1 | receiver->buffer[20]<<9)  & 0x07FF);
        receiver->channels[14] = ((receiver->buffer[20]>>2| receiver->buffer[21]<<6)                    & 0x07FF);
        receiver->channels[15] = ((receiver->buffer[21]>>5| receiver->buffer[22]<<3)                    & 0x07FF);
        
        // 解析标志位
        uint8_t flags = receiver->buffer[23];
        receiver->frame_lost = (flags & SBUS_FLAG_FRAMELOST) != 0;
        receiver->failsafe = (flags & SBUS_FLAG_FAILSAFE) != 0;
        
        // 更新状态
        receiver->is_valid = !receiver->failsafe;
        receiver->last_update_time = HAL_GetTick();
    }
}

/**
 * @brief 检查接收器数据是否有效
 * @param receiver: SBUS接收器结构体指针
 * @return true: 有效, false: 无效
 */
bool SBUS_IsValid(SBUS_Receiver_t *receiver) {
    SBUS_CheckTimeout(receiver);
    return receiver->is_valid && !receiver->failsafe;
}

/**
 * @brief 获取指定通道的原始值
 * @param receiver: SBUS接收器结构体指针
 * @param channel: 通道号 (0-15)
 * @return 通道值 (172-1811)
 */
uint16_t SBUS_GetChannel(SBUS_Receiver_t *receiver, uint8_t channel) {
    if (channel >= SBUS_CHANNEL_COUNT) {
        return SBUS_CENTER_VALUE;
    }
    return receiver->channels[channel];
}

/**
 * @brief 获取归一化的通道值
 * @param receiver: SBUS接收器结构体指针
 * @param channel: 通道号 (0-15)
 * @return 归一化值 (-1000 到 1000)
 */
int16_t SBUS_GetChannelNormalized(SBUS_Receiver_t *receiver, uint8_t channel) {
    if (channel >= SBUS_CHANNEL_COUNT) {
        return 0;
    }
    
    uint16_t raw = receiver->channels[channel];
    
    // 映射到 -1000 ~ 1000
    int16_t normalized = (int16_t)((raw - SBUS_CENTER_VALUE) * 1000 / (SBUS_MAX_VALUE - SBUS_CENTER_VALUE));
    
    // 限幅
    if (normalized > 1000) normalized = 1000;
    if (normalized < -1000) normalized = -1000;
    
    return normalized;
}

/**
 * @brief 检查接收器超时
 * @param receiver: SBUS接收器结构体指针
 */
void SBUS_CheckTimeout(SBUS_Receiver_t *receiver) {
    uint32_t current_time = HAL_GetTick();
    if (current_time - receiver->last_update_time > SBUS_TIMEOUT_MS) {
        receiver->is_valid = false;
        receiver->failsafe = true;
    }
}

4.4 双接收器管理器 (receiver_manager.h)

c 复制代码
/**
 * @file receiver_manager.h
 * @brief 双接收器管理模块
 */

#ifndef __RECEIVER_MANAGER_H
#define __RECEIVER_MANAGER_H

#include "sbus.h"
#include <stdbool.h>

/* 接收器类型 */
typedef enum {
    RECEIVER_ATTITUDE = 0,  // 姿态接收器
    RECEIVER_GIMBAL = 1     // 云台接收器
} ReceiverType_e;

/* 飞行模式 */
typedef enum {
    FLIGHT_MODE_MANUAL = 0,     // 手动模式
    FLIGHT_MODE_STABILIZE = 1,  // 自稳模式
    FLIGHT_MODE_ALTITUDE = 2,   // 定高模式
    FLIGHT_MODE_GPS = 3         // GPS模式
} FlightMode_e;

/* 姿态控制数据 */
typedef struct {
    int16_t roll;          // 横滚 (-1000 ~ 1000)
    int16_t pitch;         // 俯仰 (-1000 ~ 1000)
    int16_t yaw;           // 偏航 (-1000 ~ 1000)
    int16_t throttle;      // 油门 (0 ~ 1000)
    FlightMode_e mode;     // 飞行模式
    bool emergency_stop;   // 紧急停止
} AttitudeControl_t;

/* 云台控制数据 */
typedef struct {
    int16_t pitch;         // 云台俯仰 (-1000 ~ 1000)
    int16_t roll;          // 云台横滚 (-1000 ~ 1000)
    int16_t yaw;           // 云台偏航 (-1000 ~ 1000)
    bool follow_mode;      // 跟随模式
    bool reset_position;   // 复位位置
    int16_t zoom;          // 焦距控制 (-1000 ~ 1000)
} GimbalControl_t;

/* 接收器管理器结构体 */
typedef struct {
    SBUS_Receiver_t attitude_receiver;   // 姿态接收器
    SBUS_Receiver_t gimbal_receiver;     // 云台接收器
    AttitudeControl_t attitude_control;  // 姿态控制数据
    GimbalControl_t gimbal_control;      // 云台控制数据
    bool system_armed;                   // 系统解锁状态
    uint32_t last_valid_time;            // 上次有效时间
} ReceiverManager_t;

/* 函数声明 */
void ReceiverManager_Init(ReceiverManager_t *manager);
void ReceiverManager_Update(ReceiverManager_t *manager);
void ReceiverManager_ProcessAttitudeData(ReceiverManager_t *manager);
void ReceiverManager_ProcessGimbalData(ReceiverManager_t *manager);
bool ReceiverManager_CheckSafety(ReceiverManager_t *manager);
AttitudeControl_t* ReceiverManager_GetAttitudeControl(ReceiverManager_t *manager);
GimbalControl_t* ReceiverManager_GetGimbalControl(ReceiverManager_t *manager);

#endif /* __RECEIVER_MANAGER_H */

4.5 双接收器管理器实现 (receiver_manager.c)

c 复制代码
/**
 * @file receiver_manager.c
 * @brief 双接收器管理模块实现
 */

#include "receiver_manager.h"
#include <string.h>

/* 死区阈值 */
#define DEADZONE_THRESHOLD  50

/* 应用死区处理 */
static int16_t apply_deadzone(int16_t value, int16_t threshold) {
    if (value > threshold) {
        return value - threshold;
    } else if (value < -threshold) {
        return value + threshold;
    }
    return 0;
}

/* 限幅函数 */
static int16_t constrain(int16_t value, int16_t min, int16_t max) {
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

/**
 * @brief 初始化接收器管理器
 * @param manager: 接收器管理器指针
 */
void ReceiverManager_Init(ReceiverManager_t *manager) {
    memset(manager, 0, sizeof(ReceiverManager_t));
    
    SBUS_Init(&manager->attitude_receiver);
    SBUS_Init(&manager->gimbal_receiver);
    
    manager->system_armed = false;
    manager->last_valid_time = HAL_GetTick();
}

/**
 * @brief 更新接收器管理器
 * @param manager: 接收器管理器指针
 */
void ReceiverManager_Update(ReceiverManager_t *manager) {
    // 检查超时
    SBUS_CheckTimeout(&manager->attitude_receiver);
    SBUS_CheckTimeout(&manager->gimbal_receiver);
    
    // 处理姿态数据
    if (SBUS_IsValid(&manager->attitude_receiver)) {
        ReceiverManager_ProcessAttitudeData(manager);
        manager->last_valid_time = HAL_GetTick();
    }
    
    // 处理云台数据
    if (SBUS_IsValid(&manager->gimbal_receiver)) {
        ReceiverManager_ProcessGimbalData(manager);
    }
    
    // 安全检查
    if (!ReceiverManager_CheckSafety(manager)) {
        manager->system_armed = false;
        manager->attitude_control.throttle = 0;
        manager->attitude_control.emergency_stop = true;
    }
}

/**
 * @brief 处理姿态接收器数据
 * @param manager: 接收器管理器指针
 */
void ReceiverManager_ProcessAttitudeData(ReceiverManager_t *manager) {
    SBUS_Receiver_t *rx = &manager->attitude_receiver;
    
    // 读取基本控制量
    int16_t roll_raw = SBUS_GetChannelNormalized(rx, 0);
    int16_t pitch_raw = SBUS_GetChannelNormalized(rx, 1);
    int16_t throttle_raw = SBUS_GetChannelNormalized(rx, 2);
    int16_t yaw_raw = SBUS_GetChannelNormalized(rx, 3);
    
    // 应用死区
    manager->attitude_control.roll = apply_deadzone(roll_raw, DEADZONE_THRESHOLD);
    manager->attitude_control.pitch = apply_deadzone(pitch_raw, DEADZONE_THRESHOLD);
    manager->attitude_control.yaw = apply_deadzone(yaw_raw, DEADZONE_THRESHOLD);
    
    // 油门映射到 0-1000
    manager->attitude_control.throttle = constrain((throttle_raw + 1000) / 2, 0, 1000);
    
    // 飞行模式切换 (CH5: 三段开关)
    uint16_t mode_channel = SBUS_GetChannel(rx, 4);
    if (mode_channel < 500) {
        manager->attitude_control.mode = FLIGHT_MODE_MANUAL;
    } else if (mode_channel < 1500) {
        manager->attitude_control.mode = FLIGHT_MODE_STABILIZE;
    } else {
        manager->attitude_control.mode = FLIGHT_MODE_ALTITUDE;
    }
    
    // 紧急停止 (CH6)
    uint16_t emergency_channel = SBUS_GetChannel(rx, 5);
    manager->attitude_control.emergency_stop = (emergency_channel > 1500);
    
    // 解锁逻辑:油门最低 + 偏航最右持续2秒
    if (throttle_raw < -900 && yaw_raw > 900) {
        static uint32_t arm_start_time = 0;
        if (arm_start_time == 0) {
            arm_start_time = HAL_GetTick();
        } else if (HAL_GetTick() - arm_start_time > 2000) {
            manager->system_armed = true;
            arm_start_time = 0;
        }
    } else {
        // 重置解锁计时
        static uint32_t arm_start_time = 0;
        arm_start_time = 0;
    }
}

/**
 * @brief 处理云台接收器数据
 * @param manager: 接收器管理器指针
 */
void ReceiverManager_ProcessGimbalData(ReceiverManager_t *manager) {
    SBUS_Receiver_t *rx = &manager->gimbal_receiver;
    
    // 读取云台控制量
    manager->gimbal_control.pitch = SBUS_GetChannelNormalized(rx, 0);
    manager->gimbal_control.roll = SBUS_GetChannelNormalized(rx, 1);
    manager->gimbal_control.yaw = SBUS_GetChannelNormalized(rx, 2);
    
    // 跟随模式 (CH4)
    uint16_t follow_channel = SBUS_GetChannel(rx, 3);
    manager->gimbal_control.follow_mode = (follow_channel > 1500);
    
    // 复位按钮 (CH5)
    uint16_t reset_channel = SBUS_GetChannel(rx, 4);
    manager->gimbal_control.reset_position = (reset_channel > 1500);
    
    // 焦距控制 (CH6)
    manager->gimbal_control.zoom = SBUS_GetChannelNormalized(rx, 5);
}

/**
 * @brief 安全检查
 * @param manager: 接收器管理器指针
 * @return true: 安全, false: 不安全
 */
bool ReceiverManager_CheckSafety(ReceiverManager_t *manager) {
    // 检查姿态接收器必须有效
    if (!SBUS_IsValid(&manager->attitude_receiver)) {
        return false;
    }
    
    // 检查紧急停止开关
    if (manager->attitude_control.emergency_stop) {
        return false;
    }
    
    // 检查超时
    uint32_t current_time = HAL_GetTick();
    if (current_time - manager->last_valid_time > 500) {
        return false;
    }
    
    return true;
}

/**
 * @brief 获取姿态控制数据
 * @param manager: 接收器管理器指针
 * @return 姿态控制数据指针
 */
AttitudeControl_t* ReceiverManager_GetAttitudeControl(ReceiverManager_t *manager) {
    return &manager->attitude_control;
}

/**
 * @brief 获取云台控制数据
 * @param manager: 接收器管理器指针
 * @return 云台控制数据指针
 */
GimbalControl_t* ReceiverManager_GetGimbalControl(ReceiverManager_t *manager) {
    return &manager->gimbal_control;
}

4.6 姿态控制模块 (attitude_control.h)

c 复制代码
/**
 * @file attitude_control.h
 * @brief 姿态控制模块 - 串级PID控制
 */

#ifndef __ATTITUDE_CONTROL_H
#define __ATTITUDE_CONTROL_H

#include <stdint.h>
#include <stdbool.h>

/* PID控制器结构体 */
typedef struct {
    float kp;              // 比例系数
    float ki;              // 积分系数
    float kd;              // 微分系数
    float integral;        // 积分累积
    float prev_error;      // 上次误差
    float integral_max;    // 积分限幅
    float output_max;      // 输出限幅
} PID_Controller_t;

/* 姿态数据结构体 */
typedef struct {
    float roll;            // 横滚角 (度)
    float pitch;           // 俯仰角 (度)
    float yaw;             // 偏航角 (度)
    float roll_rate;       // 横滚角速度 (度/秒)
    float pitch_rate;      // 俯仰角速度 (度/秒)
    float yaw_rate;        // 偏航角速度 (度/秒)
} Attitude_t;

/* 姿态控制器结构体 */
typedef struct {
    // 角度环PID
    PID_Controller_t roll_angle_pid;
    PID_Controller_t pitch_angle_pid;
    PID_Controller_t yaw_angle_pid;
    
    // 角速度环PID
    PID_Controller_t roll_rate_pid;
    PID_Controller_t pitch_rate_pid;
    PID_Controller_t yaw_rate_pid;
    
    // 当前姿态
    Attitude_t current_attitude;
    
    // 目标姿态
    Attitude_t target_attitude;
    
    // 电机输出
    float motor_output[4];  // 四个电机的输出
} AttitudeController_t;

/* 函数声明 */
void AttitudeController_Init(AttitudeController_t *controller);
void PID_Init(PID_Controller_t *pid, float kp, float ki, float kd, float integral_max, float output_max);
float PID_Update(PID_Controller_t *pid, float setpoint, float measurement, float dt);
void PID_Reset(PID_Controller_t *pid);
void AttitudeController_Update(AttitudeController_t *controller, int16_t roll_cmd, 
                                int16_t pitch_cmd, int16_t yaw_cmd, int16_t throttle_cmd);
void AttitudeController_UpdateIMU(AttitudeController_t *controller, float roll, float pitch, 
                                   float yaw, float roll_rate, float pitch_rate, float yaw_rate);
void AttitudeController_GetMotorOutputs(AttitudeController_t *controller, uint16_t outputs[4]);

#endif /* __ATTITUDE_CONTROL_H */

4.7 姿态控制模块实现 (attitude_control.c)

c 复制代码
/**
 * @file attitude_control.c
 * @brief 姿态控制模块实现
 */

#include "attitude_control.h"
#include <math.h>
#include <string.h>

/* 限幅函数 */
static float constrain_float(float value, float min, float max) {
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

/**
 * @brief 初始化PID控制器
 * @param pid: PID控制器指针
 * @param kp: 比例系数
 * @param ki: 积分系数
 * @param kd: 微分系数
 * @param integral_max: 积分限幅
 * @param output_max: 输出限幅
 */
void PID_Init(PID_Controller_t *pid, float kp, float ki, float kd, float integral_max, float output_max) {
    pid->kp = kp;
    pid->ki = ki;
    pid->kd = kd;
    pid->integral = 0.0f;
    pid->prev_error = 0.0f;
    pid->integral_max = integral_max;
    pid->output_max = output_max;
}

/**
 * @brief 更新PID控制器
 * @param pid: PID控制器指针
 * @param setpoint: 目标值
 * @param measurement: 测量值
 * @param dt: 时间间隔 (秒)
 * @return PID输出
 */
float PID_Update(PID_Controller_t *pid, float setpoint, float measurement, float dt) {
    // 计算误差
    float error = setpoint - measurement;
    
    // 比例项
    float p_term = pid->kp * error;
    
    // 积分项
    pid->integral += error * dt;
    pid->integral = constrain_float(pid->integral, -pid->integral_max, pid->integral_max);
    float i_term = pid->ki * pid->integral;
    
    // 微分项
    float derivative = (error - pid->prev_error) / dt;
    float d_term = pid->kd * derivative;
    pid->prev_error = error;
    
    // 总输出
    float output = p_term + i_term + d_term;
    output = constrain_float(output, -pid->output_max, pid->output_max);
    
    return output;
}

/**
 * @brief 重置PID控制器
 * @param pid: PID控制器指针
 */
void PID_Reset(PID_Controller_t *pid) {
    pid->integral = 0.0f;
    pid->prev_error = 0.0f;
}

/**
 * @brief 初始化姿态控制器
 * @param controller: 姿态控制器指针
 */
void AttitudeController_Init(AttitudeController_t *controller) {
    memset(controller, 0, sizeof(AttitudeController_t));
    
    // 初始化角度环PID (外环)
    // 参数: kp, ki, kd, integral_max, output_max
    PID_Init(&controller->roll_angle_pid, 4.5f, 0.0f, 0.5f, 20.0f, 200.0f);
    PID_Init(&controller->pitch_angle_pid, 4.5f, 0.0f, 0.5f, 20.0f, 200.0f);
    PID_Init(&controller->yaw_angle_pid, 3.0f, 0.0f, 0.0f, 0.0f, 200.0f);
    
    // 初始化角速度环PID (内环)
    PID_Init(&controller->roll_rate_pid, 0.15f, 0.05f, 0.005f, 100.0f, 500.0f);
    PID_Init(&controller->pitch_rate_pid, 0.15f, 0.05f, 0.005f, 100.0f, 500.0f);
    PID_Init(&controller->yaw_rate_pid, 0.20f, 0.02f, 0.0f, 100.0f, 500.0f);
}

/**
 * @brief 更新姿态控制器
 * @param controller: 姿态控制器指针
 * @param roll_cmd: 横滚命令 (-1000~1000)
 * @param pitch_cmd: 俯仰命令 (-1000~1000)
 * @param yaw_cmd: 偏航命令 (-1000~1000)
 * @param throttle_cmd: 油门命令 (0~1000)
 */
void AttitudeController_Update(AttitudeController_t *controller, int16_t roll_cmd, 
                                int16_t pitch_cmd, int16_t yaw_cmd, int16_t throttle_cmd) {
    float dt = 0.01f;  // 100Hz控制频率
    
    // 将命令映射到角度/角速度
    // 角度模式:±30度最大倾角
    controller->target_attitude.roll = roll_cmd * 30.0f / 1000.0f;
    controller->target_attitude.pitch = pitch_cmd * 30.0f / 1000.0f;
    
    // 偏航采用角速度控制:±200度/秒
    float target_yaw_rate = yaw_cmd * 200.0f / 1000.0f;
    
    // 外环:角度PID -> 目标角速度
    float target_roll_rate = PID_Update(&controller->roll_angle_pid, 
                                         controller->target_attitude.roll,
                                         controller->current_attitude.roll, dt);
    
    float target_pitch_rate = PID_Update(&controller->pitch_angle_pid,
                                          controller->target_attitude.pitch,
                                          controller->current_attitude.pitch, dt);
    
    // 内环:角速度PID -> 力矩输出
    float roll_output = PID_Update(&controller->roll_rate_pid,
                                    target_roll_rate,
                                    controller->current_attitude.roll_rate, dt);
    
    float pitch_output = PID_Update(&controller->pitch_rate_pid,
                                     target_pitch_rate,
                                     controller->current_attitude.pitch_rate, dt);
    
    float yaw_output = PID_Update(&controller->yaw_rate_pid,
                                   target_yaw_rate,
                                   controller->current_attitude.yaw_rate, dt);
    
    // 油门基准值
    float throttle_base = throttle_cmd;
    
    // 四旋翼混控矩阵 (X型布局)
    // Motor1: 右前  (+roll, +pitch, -yaw)
    // Motor2: 左后  (-roll, -pitch, -yaw)
    // Motor3: 左前  (-roll, +pitch, +yaw)
    // Motor4: 右后  (+roll, -pitch, +yaw)
    
    controller->motor_output[0] = throttle_base + roll_output + pitch_output - yaw_output;
    controller->motor_output[1] = throttle_base - roll_output - pitch_output - yaw_output;
    controller->motor_output[2] = throttle_base - roll_output + pitch_output + yaw_output;
    controller->motor_output[3] = throttle_base + roll_output - pitch_output + yaw_output;
    
    // 限幅到 0-1000
    for (int i = 0; i < 4; i++) {
        controller->motor_output[i] = constrain_float(controller->motor_output[i], 0.0f, 1000.0f);
    }
}

/**
 * @brief 更新IMU数据
 * @param controller: 姿态控制器指针
 * @param roll: 横滚角 (度)
 * @param pitch: 俯仰角 (度)
 * @param yaw: 偏航角 (度)
 * @param roll_rate: 横滚角速度 (度/秒)
 * @param pitch_rate: 俯仰角速度 (度/秒)
 * @param yaw_rate: 偏航角速度 (度/秒)
 */
void AttitudeController_UpdateIMU(AttitudeController_t *controller, float roll, float pitch, 
                                   float yaw, float roll_rate, float pitch_rate, float yaw_rate) {
    controller->current_attitude.roll = roll;
    controller->current_attitude.pitch = pitch;
    controller->current_attitude.yaw = yaw;
    controller->current_attitude.roll_rate = roll_rate;
    controller->current_attitude.pitch_rate = pitch_rate;
    controller->current_attitude.yaw_rate = yaw_rate;
}

/**
 * @brief 获取电机输出
 * @param controller: 姿态控制器指针
 * @param outputs: 输出数组 (0-1000映射到PWM)
 */
void AttitudeController_GetMotorOutputs(AttitudeController_t *controller, uint16_t outputs[4]) {
    for (int i = 0; i < 4; i++) {
        // 映射到PWM范围 1000-2000us
        outputs[i] = (uint16_t)(1000 + controller->motor_output[i]);
    }
}

4.8 云台控制模块 (gimbal_control.h)

c 复制代码
/**
 * @file gimbal_control.h
 * @brief 三轴云台控制模块
 */

#ifndef __GIMBAL_CONTROL_H
#define __GIMBAL_CONTROL_H

#include <stdint.h>
#include <stdbool.h>

/* 云台轴定义 */
typedef enum {
    GIMBAL_AXIS_PITCH = 0,
    GIMBAL_AXIS_ROLL = 1,
    GIMBAL_AXIS_YAW = 2,
    GIMBAL_AXIS_COUNT = 3
} GimbalAxis_e;

/* 云台模式 */
typedef enum {
    GIMBAL_MODE_LOCK = 0,      // 锁定模式
    GIMBAL_MODE_FOLLOW = 1,    // 跟随模式
    GIMBAL_MODE_FPV = 2        // FPV模式
} GimbalMode_e;

/* 云台控制器结构体 */
typedef struct {
    float target_angle[GIMBAL_AXIS_COUNT];    // 目标角度 (度)
    float current_angle[GIMBAL_AXIS_COUNT];   // 当前角度 (度)
    float filtered_angle[GIMBAL_AXIS_COUNT];  // 滤波后角度
    GimbalMode_e mode;                        // 工作模式
    float sensitivity;                        // 灵敏度
    float smooth_factor;                      // 平滑系数 (0-1)
    bool is_enabled;                          // 使能标志
    
    // 角度限制
    float min_angle[GIMBAL_AXIS_COUNT];
    float max_angle[GIMBAL_AXIS_COUNT];
} GimbalController_t;

/* 函数声明 */
void GimbalController_Init(GimbalController_t *controller);
void GimbalController_Update(GimbalController_t *controller, int16_t pitch_cmd, 
                              int16_t roll_cmd, int16_t yaw_cmd, bool follow_mode);
void GimbalController_SetMode(GimbalController_t *controller, GimbalMode_e mode);
void GimbalController_Reset(GimbalController_t *controller);
void GimbalController_GetAngles(GimbalController_t *controller, float angles[3]);
void GimbalController_ApplySmoothing(GimbalController_t *controller);

#endif /* __GIMBAL_CONTROL_H */

4.9 云台控制模块实现 (gimbal_control.c)

c 复制代码
/**
 * @file gimbal_control.c
 * @brief 三轴云台控制模块实现
 */

#include "gimbal_control.h"
#include <string.h>
#include <math.h>

/* 限幅函数 */
static float constrain_float(float value, float min, float max) {
    if (value < min) return min;
    if (value > max) return max;
    return value;
}

/**
 * @brief 初始化云台控制器
 * @param controller: 云台控制器指针
 */
void GimbalController_Init(GimbalController_t *controller) {
    memset(controller, 0, sizeof(GimbalController_t));
    
    // 设置角度限制 (度)
    controller->min_angle[GIMBAL_AXIS_PITCH] = -90.0f;
    controller->max_angle[GIMBAL_AXIS_PITCH] = 30.0f;
    
    controller->min_angle[GIMBAL_AXIS_ROLL] = -45.0f;
    controller->max_angle[GIMBAL_AXIS_ROLL] = 45.0f;
    
    controller->min_angle[GIMBAL_AXIS_YAW] = -180.0f;
    controller->max_angle[GIMBAL_AXIS_YAW] = 180.0f;
    
    // 初始化参数
    controller->sensitivity = 0.1f;     // 灵敏度
    controller->smooth_factor = 0.2f;   // 平滑系数
    controller->mode = GIMBAL_MODE_LOCK;
    controller->is_enabled = true;
    
    // 初始化角度
    for (int i = 0; i < GIMBAL_AXIS_COUNT; i++) {
        controller->target_angle[i] = 0.0f;
        controller->current_angle[i] = 0.0f;
        controller->filtered_angle[i] = 0.0f;
    }
}

/**
 * @brief 更新云台控制器
 * @param controller: 云台控制器指针
 * @param pitch_cmd: 俯仰命令 (-1000~1000)
 * @param roll_cmd: 横滚命令 (-1000~1000)
 * @param yaw_cmd: 偏航命令 (-1000~1000)
 * @param follow_mode: 跟随模式标志
 */
void GimbalController_Update(GimbalController_t *controller, int16_t pitch_cmd, 
                              int16_t roll_cmd, int16_t yaw_cmd, bool follow_mode) {
    if (!controller->is_enabled) {
        return;
    }
    
    // 设置模式
    if (follow_mode) {
        controller->mode = GIMBAL_MODE_FOLLOW;
    } else {
        controller->mode = GIMBAL_MODE_LOCK;
    }
    
    // 增量式控制
    float dt = 0.01f;  // 100Hz
    
    // 将命令转换为角速度 (度/秒)
    float pitch_rate = pitch_cmd * controller->sensitivity;
    float roll_rate = roll_cmd * controller->sensitivity;
    float yaw_rate = yaw_cmd * controller->sensitivity;
    
    // 更新目标角度
    controller->target_angle[GIMBAL_AXIS_PITCH] += pitch_rate * dt;
    controller->target_angle[GIMBAL_AXIS_ROLL] += roll_rate * dt;
    controller->target_angle[GIMBAL_AXIS_YAW] += yaw_rate * dt;
    
    // 角度限幅
    for (int i = 0; i < GIMBAL_AXIS_COUNT; i++) {
        controller->target_angle[i] = constrain_float(controller->target_angle[i],
                                                      controller->min_angle[i],
                                                      controller->max_angle[i]);
    }
    
    // 应用平滑滤波
    GimbalController_ApplySmoothing(controller);
}

/**
 * @brief 设置云台模式
 * @param controller: 云台控制器指针
 * @param mode: 云台模式
 */
void GimbalController_SetMode(GimbalController_t *controller, GimbalMode_e mode) {
    controller->mode = mode;
}

/**
 * @brief 复位云台位置
 * @param controller: 云台控制器指针
 */
void GimbalController_Reset(GimbalController_t *controller) {
    for (int i = 0; i < GIMBAL_AXIS_COUNT; i++) {
        controller->target_angle[i] = 0.0f;
        controller->filtered_angle[i] = 0.0f;
    }
}

/**
 * @brief 获取云台角度
 * @param controller: 云台控制器指针
 * @param angles: 输出角度数组 [pitch, roll, yaw]
 */
void GimbalController_GetAngles(GimbalController_t *controller, float angles[3]) {
    for (int i = 0; i < GIMBAL_AXIS_COUNT; i++) {
        angles[i] = controller->filtered_angle[i];
    }
}

/**
 * @brief 应用平滑滤波
 * @param controller: 云台控制器指针
 */
void GimbalController_ApplySmoothing(GimbalController_t *controller) {
    float alpha = controller->smooth_factor;
    
    for (int i = 0; i < GIMBAL_AXIS_COUNT; i++) {
        // 一阶低通滤波
        controller->filtered_angle[i] = alpha * controller->target_angle[i] + 
                                        (1.0f - alpha) * controller->filtered_angle[i];
    }
}

4.10 主程序 (main.c)

c 复制代码
/**
 * @file main.c
 * @brief 双遥控器无人机系统主程序
 */

#include "main.h"
#include "sbus.h"
#include "receiver_manager.h"
#include "attitude_control.h"
#include "gimbal_control.h"
#include <stdio.h>

/* 全局变量 */
ReceiverManager_t receiver_manager;
AttitudeController_t attitude_controller;
GimbalController_t gimbal_controller;

/* UART句柄 */
UART_HandleTypeDef huart1;  // 姿态接收机
UART_HandleTypeDef huart2;  // 云台接收机
UART_HandleTypeDef huart3;  // 调试串口
TIM_HandleTypeDef htim1;    // 电机PWM

/* 函数声明 */
void SystemClock_Config(void);
void MX_GPIO_Init(void);
void MX_USART1_UART_Init(void);
void MX_USART2_UART_Init(void);
void MX_USART3_UART_Init(void);
void MX_TIM1_Init(void);
void Motor_SetPWM(uint8_t motor, uint16_t pulse_width);
void Gimbal_SendCommand(float pitch, float roll, float yaw);

int main(void) {
    /* 初始化HAL库 */
    HAL_Init();
    
    /* 配置系统时钟 */
    SystemClock_Config();
    
    /* 初始化外设 */
    MX_GPIO_Init();
    MX_USART1_UART_Init();
    MX_USART2_UART_Init();
    MX_USART3_UART_Init();
    MX_TIM1_Init();
    
    /* 初始化模块 */
    ReceiverManager_Init(&receiver_manager);
    AttitudeController_Init(&attitude_controller);
    GimbalController_Init(&gimbal_controller);
    
    /* 启动PWM */
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3);
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_4);
    
    /* 启动UART接收 */
    uint8_t rx_byte;
    HAL_UART_Receive_IT(&huart1, &rx_byte, 1);
    HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
    
    printf("Dual Remote Control Drone System Started\r\n");
    printf("====================================\r\n");
    
    uint32_t loop_counter = 0;
    uint32_t last_print_time = 0;
    
    /* 主循环 */
    while (1) {
        uint32_t current_time = HAL_GetTick();
        
        /* 100Hz控制频率 */
        static uint32_t last_control_time = 0;
        if (current_time - last_control_time >= 10) {
            last_control_time = current_time;
            
            /* 更新接收器管理器 */
            ReceiverManager_Update(&receiver_manager);
            
            /* 获取控制数据 */
            AttitudeControl_t *att_ctrl = ReceiverManager_GetAttitudeControl(&receiver_manager);
            GimbalControl_t *gim_ctrl = ReceiverManager_GetGimbalControl(&receiver_manager);
            
            /* 姿态控制 */
            if (receiver_manager.system_armed && ReceiverManager_CheckSafety(&receiver_manager)) {
                // TODO: 从IMU读取当前姿态
                // AttitudeController_UpdateIMU(&attitude_controller, roll, pitch, yaw, 
                //                              roll_rate, pitch_rate, yaw_rate);
                
                AttitudeController_Update(&attitude_controller, 
                                         att_ctrl->roll,
                                         att_ctrl->pitch,
                                         att_ctrl->yaw,
                                         att_ctrl->throttle);
                
                /* 输出到电机 */
                uint16_t motor_outputs[4];
                AttitudeController_GetMotorOutputs(&attitude_controller, motor_outputs);
                
                for (int i = 0; i < 4; i++) {
                    Motor_SetPWM(i, motor_outputs[i]);
                }
            } else {
                /* 解锁或失控时,电机输出最小值 */
                for (int i = 0; i < 4; i++) {
                    Motor_SetPWM(i, 1000);
                }
            }
            
            /* 云台控制 */
            if (SBUS_IsValid(&receiver_manager.gimbal_receiver)) {
                if (gim_ctrl->reset_position) {
                    GimbalController_Reset(&gimbal_controller);
                }
                
                GimbalController_Update(&gimbal_controller,
                                       gim_ctrl->pitch,
                                       gim_ctrl->roll,
                                       gim_ctrl->yaw,
                                       gim_ctrl->follow_mode);
                
                /* 发送云台控制命令 */
                float gimbal_angles[3];
                GimbalController_GetAngles(&gimbal_controller, gimbal_angles);
                Gimbal_SendCommand(gimbal_angles[0], gimbal_angles[1], gimbal_angles[2]);
            }
            
            loop_counter++;
        }
        
        /* 1Hz状态打印 */
        if (current_time - last_print_time >= 1000) {
            last_print_time = current_time;
            
            printf("\r\n=== System Status (Loop: %lu Hz) ===\r\n", loop_counter);
            printf("Attitude RX: %s | Gimbal RX: %s\r\n",
                   SBUS_IsValid(&receiver_manager.attitude_receiver) ? "OK" : "LOST",
                   SBUS_IsValid(&receiver_manager.gimbal_receiver) ? "OK" : "LOST");
            printf("Armed: %s | Mode: %d\r\n",
                   receiver_manager.system_armed ? "YES" : "NO",
                   ReceiverManager_GetAttitudeControl(&receiver_manager)->mode);
            printf("Throttle: %d | Roll: %d | Pitch: %d | Yaw: %d\r\n",
                   att_ctrl->throttle, att_ctrl->roll, att_ctrl->pitch, att_ctrl->yaw);
            printf("Gimbal P: %.1f | R: %.1f | Y: %.1f | Follow: %s\r\n",
                   gimbal_controller.filtered_angle[0],
                   gimbal_controller.filtered_angle[1],
                   gimbal_controller.filtered_angle[2],
                   gim_ctrl->follow_mode ? "ON" : "OFF");
            
            loop_counter = 0;
        }
        
        HAL_Delay(1);
    }
}

/**
 * @brief 设置电机PWM
 * @param motor: 电机编号 (0-3)
 * @param pulse_width: 脉宽 (1000-2000us)
 */
void Motor_SetPWM(uint8_t motor, uint16_t pulse_width) {
    /* 限幅 */
    if (pulse_width < 1000) pulse_width = 1000;
    if (pulse_width > 2000) pulse_width = 2000;
    
    /* 设置PWM比较值 */
    switch (motor) {
        case 0: __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse_width); break;
        case 1: __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, pulse_width); break;
        case 2: __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_3, pulse_width); break;
        case 3: __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, pulse_width); break;
    }
}

/**
 * @brief 发送云台控制命令
 * @param pitch: 俯仰角
 * @param roll: 横滚角
 * @param yaw: 偏航角
 */
void Gimbal_SendCommand(float pitch, float roll, float yaw) {
    /* SimpleBGC协议示例 */
    uint8_t cmd_buffer[16];
    cmd_buffer[0] = 0x3E;  // 起始符
    cmd_buffer[1] = 0x43;  // CMD_CONTROL
    cmd_buffer[2] = 12;    // 数据长度
    
    /* 角度转换为整数 (0.02197度分辨率) */
    int16_t pitch_int = (int16_t)(pitch / 0.02197f);
    int16_t roll_int = (int16_t)(roll / 0.02197f);
    int16_t yaw_int = (int16_t)(yaw / 0.02197f);
    
    /* 填充数据 */
    memcpy(&cmd_buffer[3], &roll_int, 2);
    memcpy(&cmd_buffer[5], &pitch_int, 2);
    memcpy(&cmd_buffer[7], &yaw_int, 2);
    
    /* 发送命令 */
    HAL_UART_Transmit(&huart2, cmd_buffer, 15, 100);
}

/**
 * @brief UART接收中断回调
 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    uint8_t rx_byte;
    
    if (huart->Instance == USART1) {
        /* 姿态接收机数据 */
        HAL_UART_Receive(huart, &rx_byte, 1, 0);
        SBUS_ParseByte(&receiver_manager.attitude_receiver, rx_byte);
        HAL_UART_Receive_IT(huart, &rx_byte, 1);
    } else if (huart->Instance == USART2) {
        /* 云台接收机数据 */
        HAL_UART_Receive(huart, &rx_byte, 1, 0);
        SBUS_ParseByte(&receiver_manager.gimbal_receiver, rx_byte);
        HAL_UART_Receive_IT(huart, &rx_byte, 1);
    }
}

/* STM32 HAL初始化函数 (省略部分标准代码) */
void SystemClock_Config(void) {
    /* 配置为168MHz */
}

void MX_USART1_UART_Init(void) {
    /* SBUS配置: 100000bps, 8E2 */
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 100000;
    huart1.Init.WordLength = UART_WORDLENGTH_9B;
    huart1.Init.StopBits = UART_STOPBITS_2;
    huart1.Init.Parity = UART_PARITY_EVEN;
    HAL_UART_Init(&huart1);
}

void MX_TIM1_Init(void) {
    /* PWM频率: 50Hz (20ms周期) */
    htim1.Instance = TIM1;
    htim1.Init.Prescaler = 84 - 1;
    htim1.Init.Period = 20000 - 1;
    HAL_TIM_PWM_Init(&htim1);
}

/* printf重定向 */
int fputc(int ch, FILE *f) {
    HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 100);
    return ch;
}

五、调试与测试

5.1 硬件测试步骤

步骤1:接收机测试

bash 复制代码
# 打开串口监视器,波特率115200
# 观察接收机数据输出
Attitude RX: OK | Gimbal RX: OK
Channel values: [992, 992, 172, 992, ...]

步骤2:电机校准

  1. 断开电池,仅USB供电
  2. 油门杆拉到最高,上电
  3. 听到电调提示音后,油门杆拉到最低
  4. 等待电调校准完成

步骤3:PID调参

c 复制代码
// 从保守参数开始
Roll/Pitch角度环: Kp=3.0, Ki=0.0, Kd=0.3
Roll/Pitch角速度环: Kp=0.10, Ki=0.03, Kd=0.003

// 逐步调整
1. 先调角速度环P,使响应快速但不震荡
2. 加入少量D抑制超调
3. 最后加入少量I消除静差

5.2 安全测试清单

  • 紧急停止开关功能正常
  • 失控保护触发正常
  • 低电压报警功能正常
  • 解锁/上锁逻辑正确
  • 云台限位功能正常
  • 双接收机互不干扰
  • 信号丢失自动降落

5.3 常见问题排查

问题 可能原因 解决方案
接收机无数据 波特率错误 检查UART配置100000bps
电机不转 未解锁 检查解锁逻辑
飞机震荡 PID参数过大 降低P或增大D
云台漂移 积分累积 检查积分限幅
双遥控冲突 数据融合错误 检查优先级逻辑

六、性能优化建议

6.1 控制频率优化

c 复制代码
/* 建议的任务调度 */
1000Hz: IMU数据读取
 500Hz: 姿态解算 (互补滤波/卡尔曼)
 200Hz: 姿态控制器 (角速度环)
 100Hz: 位置控制器 (角度环)
  50Hz: 云台控制器
  10Hz: 数据记录/遥测发送
   1Hz: 状态显示

6.2 通信延迟优化

  • 使用DMA模式接收SBUS数据
  • 优先级中断配置
  • 减少不必要的数据拷贝
  • 使用循环缓冲区

6.3 资源使用统计

复制代码
Flash: ~45KB (含HAL库)
RAM: ~8KB
CPU: ~40% @ 168MHz
控制延迟: <5ms (端到端)

七、扩展功能实现

7.1 数据记录功能

c 复制代码
/* SD卡日志记录 */
typedef struct {
    uint32_t timestamp;
    float attitude[3];
    float gyro[3];
    int16_t control[4];
    uint16_t motor[4];
} FlightLog_t;

void Log_Record(FlightLog_t *log) {
    // 写入SD卡
    f_write(&file, log, sizeof(FlightLog_t), &bytes_written);
}

7.2 地面站通信

c 复制代码
/* MAVLink协议支持 */
void Send_Attitude_MAVLink(float roll, float pitch, float yaw) {
    mavlink_message_t msg;
    mavlink_msg_attitude_pack(1, 200, &msg, HAL_GetTick(), 
                              roll, pitch, yaw, 0, 0, 0);
    uint8_t buf[MAVLINK_MAX_PACKET_LEN];
    uint16_t len = mavlink_msg_to_send_buffer(buf, &msg);
    HAL_UART_Transmit(&huart_telemetry, buf, len, 100);
}

7.3 视觉跟踪联动

c 复制代码
/* 目标跟踪接口 */
typedef struct {
    int16_t target_x;  // 屏幕坐标 (-1000~1000)
    int16_t target_y;
    bool target_locked;
} VisionTarget_t;

void Gimbal_TrackTarget(VisionTarget_t *target) {
    if (target->target_locked) {
        // PID控制云台指向目标
        float pitch_correction = vision_pid_update(target->target_y);
        float yaw_correction = vision_pid_update(target->target_x);
        
        gimbal_controller.target_angle[GIMBAL_AXIS_PITCH] += pitch_correction;
        gimbal_controller.target_angle[GIMBAL_AXIS_YAW] += yaw_correction;
    }
}

八、总结与展望

8.1 系统优势

  1. 专业化控制:飞手和云台手职责明确,提高操作效率
  2. 高可靠性:完善的故障检测和安全机制
  3. 强扩展性:模块化设计便于功能扩展
  4. 低延迟:端到端控制延迟<5ms

8.2 适用场景

  • 电影级航拍
  • 工业巡检
  • 应急救援
  • 大型活动直播
  • 科研教学

8.3 未来改进方向

  1. AI辅助控制:机器学习优化PID参数
  2. 5G图传:超低延迟高清视频传输
  3. 多机协同:编队飞行中的云台协调
  4. 视觉SLAM:自主避障与路径规划
  5. 手势控制:基于计算机视觉的手势识别

附录

A. 完整BOM清单

序号 组件 型号 数量 参考价格
1 主控 STM32F427VIT6 1 ¥45
2 接收机 FrSky X8R 2 ¥180
3 遥控器 FrSky X9D Plus 2 ¥1600
4 电机 2212 920KV 4 ¥160
5 电调 30A ESC 4 ¥120
6 云台 BGC3.1 1 ¥280
7 IMU MPU6050 1 ¥15
8 电池 4S 5000mAh 1 ¥180
总计 ¥2580

B. 参考资料

  1. SBUS Protocol Specification - FrSky
  2. STM32F4 Reference Manual - STMicroelectronics
  3. SimpleBGC Serial API - Basecam Electronics
  4. PX4 Flight Stack Documentation
  5. Betaflight Configurator Source Code

C. 开源项目

互动交流

如果本文对你有帮助,欢迎:

  • ⭐ 点赞收藏
  • 💬 评论交流
  • 🔗 分享转发

有任何问题欢迎在评论区讨论!

#无人机 #嵌入式开发 #STM32 #飞控 #双遥控器 #航拍 #SBUS协议 #PID控制 #云台控制

相关推荐
mit6.8244 小时前
[无人机sdk] CameraModule | GimbalModule
数码相机·无人机
神工坊5 小时前
仿真科普|CAE技术赋能无人机,低空经济蓄势起飞
中间件·云计算·无人机·云平台·hpc·cae·高性能仿真
mit6.8245 小时前
[无人机sdk] AdvancedSensing | 获取实时视频流 | VGA分辨率
数码相机·无人机
恒点虚拟仿真19 小时前
AI+虚拟仿真:无人机飞行专业人才培养的破局之道
无人机·无人机飞行·无人机专业虚拟仿真·无人机飞行虚拟仿真·无人机飞手·无人机实践·无人机飞行实践
无人装备硬件开发爱好者19 小时前
无人机电调芯片替换全解析:从 AM32 架构到 STM32F072、GD32E230 与 AT32F421 的实战对比
stm32·无人机·am32
中达瑞和-高光谱·多光谱1 天前
无人机多光谱遥感在水生植被精细分类中的应用
分类·数据挖掘·无人机
GIS数据转换器4 天前
城市基础设施安全运行监管平台
大数据·运维·人工智能·物联网·安全·无人机·1024程序员节
云卓SKYDROID4 天前
飞控开发难点与技术要点
网络·智能路由器·无人机·高科技·云卓科技
音视频牛哥5 天前
无人机安防体系的音视频超低延迟重构:从“空地融合”到“实时智控”
人工智能·音视频·无人机·大牛直播sdk·rtsp播放器·rtmp播放器·低空经济rtmp rtsp