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

前言

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

系统特点:

  • 双遥控器独立控制,互不干扰
  • 支持多种接收机协议(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 硬件清单

组件型号推荐数量说明
主控MCUSTM32F427VIT61168MHz, 256KB RAM
接收机FrSky X8R / FlySky FS-iA10B2支持SBUS/PPM
无刷电机2212 920KV4含电调
三轴云台BGC3.11带独立控制板
IMUMPU6050/ICM2060216轴姿态传感器
电源模块5V/3A + 12V/2A1双路输出

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. 失控保护:任一接收机信号丢失触发安全降落

数据融合算法:

// 伪代码
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 云台控制算法

增量式控制:

gimbal_angle += remote_input × sensitivity × dt;
gimbal_angle = constrain(gimbal_angle, min_angle, max_angle);

平滑滤波:

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)

/**
* @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)

/**
* @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)

/**
* @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)

/**
* @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)

/**
* @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)

/**
* @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)

/**
* @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)

/**
* @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)

/**
* @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:接收机测试

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

步骤2:电机校准

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

步骤3:PID调参

// 从保守参数开始
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 控制频率优化

/* 建议的任务调度 */
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 数据记录功能

/* 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 地面站通信

/* 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 视觉跟踪联动

/* 目标跟踪接口 */
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主控STM32F427VIT61¥45
2接收机FrSky X8R2¥180
3遥控器FrSky X9D Plus2¥1600
4电机2212 920KV4¥160
5电调30A ESC4¥120
6云台BGC3.11¥280
7IMUMPU60501¥15
8电池4S 5000mAh1¥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. 开源项目

  • GitHub: https://github.com/your-repo/dual-remote-drone
  • License: MIT
  • 贡献指南: CONTRIBUTING.md

互动交流

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

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

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

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

posted @ 2025-11-23 08:17  gccbuaa  阅读(30)  评论(0)    收藏  举报