Skip to content

舵机与电机控制

舵机和电机是机器人执行器的两大基石。本章从物理原理讲起,介绍两者的控制方式,并重点说明 GSRL 中 CAN 总线电机的控制架构。

上一篇:定时器与 PWM


1. 舵机(Servo)

1.1 物理结构

舵机是一个闭环角度伺服系统,内部集成了:

┌─────────────────────────────────────────┐
│                舵机内部                   │
│                                         │
│  PWM 信号 ──→ ┌─────────┐               │
│              │ 控制电路 │               │
│              └────┬────┘               │
│                   │                    │
│              ┌────▼────┐               │
│              │ 直流电机 │──→ 齿轮组 ──→ 输出轴
│              └─────────┘               │
│                   │                    │
│              ┌────▼────┐               │
│              │ 电位器   │←── 反馈角度   │
│              │(位置反馈)│               │
│              └─────────┘               │
└─────────────────────────────────────────┘
  • 直流电机:提供动力
  • 齿轮组:减速增扭,同时放大了位置精度
  • 电位器:实时测量输出轴的角度
  • 控制电路:比较目标位置(PWM 信号)和实际位置(电位器),驱动电机消除误差

1.2 控制方式

舵机通过 PWM 信号的脉宽控制角度(见上一章)。重要的是:

  • 舵机内部自带 PID 控制(PID 算法详见 GSRL Algorithm 层),只需要给定目标位置信号
  • 普通舵机角度范围约 180°,旋转角度由内部机械限位决定
  • 总线舵机(如 Dynamixel)通过 UART/RS485 收发数字指令,可反馈位置、速度、电流

1.3 关键参数

参数 典型值 含义
工作电压 4.8V ~ 7.4V 供电电压越高,扭矩和速度越大
堵转扭矩 15 ~ 40 kg·cm 输出轴能承受的最大负载
响应速度 0.1 ~ 0.2 s/60° 转动 60° 所需时间
死区 1~2 μs 脉宽变化小于死区时舵机不响应
控制频率 50Hz (20ms) 大部分舵机的标准控制频率

2. 直流有刷电机

2.1 基本原理

直流有刷电机通过电刷和换向器使转子线圈中的电流方向不断切换,从而在永磁体磁场中持续旋转。

简化模型:
      N          N          N
   ┌─────┐   ┌─────┐   ┌─────┐
   │ →→→ │   │ ↑↑↑ │   │ ←←← │   ← 转子线圈(电流方向随换向器翻转)
   └─────┘   └─────┘   └─────┘
      S          S          S

        换向器 (换角度后电流反向,维持同一方向的电磁力)

2.2 控制方式:H 桥驱动

H 桥通过 4 个开关管(MOSFET)控制电机的转向和转速:

       VCC
        │
   ┌────┴────┐
   │ S1   S3 │      S1+S4 导通 → 正转
   │    M    │      S2+S3 导通 → 反转
   │ S2   S4 │      S1+S2 或 S3+S4 同时导通 → 短路(禁止!)
   └────┬────┘      占空比 0% ~ 100% → 调速
        │
       GND

PWM 信号控制 H 桥开关管的导通时间,实现电压调速


3. 无刷直流电机(BLDC)

3.1 为什么要无刷

有刷电机的电刷是磨损件(接触摩擦、产生火花),高速高功率场景下寿命短、EMI 大。此外,电刷与换向器之间的火花会产生电气噪声(电弧),干扰传感器和无线电通信——在机器人中,这可能导致 IMU 数据抖动或遥控信号丢包。无刷电机效率约 85%~90%,远高于有刷电机的 70%~75%,同等电池容量下续航更长。同时,无刷电机功率密度更高——相同体积下能输出更大的扭矩,这对于空间紧张的机器人关节至关重要。

无刷电机将永磁体放在转子上,线圈放在定子上,通过电子换向代替机械换向。

无刷电机结构:
          ┌──────────┐
          │  定子线圈  │  ← 固定不动
    ┌─────┼──────────┼─────┐
    │     │   ┌──┐   │     │
    │     │   │永│   │     │  ← 转子旋转
    │     │   │磁│   │     │
    │     │   │体│   │     │
    │     │   └──┘   │     │
    └─────┴──────────┴─────┘

3.2 FOC(磁场定向控制)原理简述

比赛机器人中常用的高性能电机(大疆 M3508、达妙 DM4310 等)都使用 FOC 控制:

┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ 目标扭矩 │ → │ PID 控制 │ → │ 坐标变换 │ → │ SVPWM   │ → 三相桥 → 电机
│ (电流)   │    │ (电流环) │    │ Clarke   │    │ (空间矢量)│
└──────────┘    └──────────┘    │ Park     │    └──────────┘
                                └──────────┘
                                     ↑
                               ┌──────────┐
                               │ 角度传感器│  ← 编码器/霍尔
                               └──────────┘

FOC 的核心思想:通过数学变换(Clarke + Park),将三相交流电流转化为等效的直流电流进行控制(转矩电流 Iq 和励磁电流 Id),大幅简化了控制模型。

3.3 为什么用 FOC 而不是简单的六步换相

六步换相(方波驱动)是最简单的 BLDC 控制方式:依次给三相线圈中的两相通电,每 60° 电角度切换一次。它实现简单,但存在固有缺陷——每次换相时刻,电流方向突变导致转矩脉动,电机会产生明显的振动和噪音。

FOC(磁场定向控制)则完全不同:它通过实时测量转子角度,用数学变换(Clarke + Park)将三相交流电流分解为转矩电流 Iq 和励磁电流 Id,然后像控制直流电机一样分别控制它们。FOC 的电流矢量连续平滑旋转,产生的转矩也连续平滑——没有换相突变,没有振动。

在云台(gimbal)应用中,这一差异至关重要:六步换相的振动会导致相机画面抖动,而 FOC 的平滑转矩是实现稳定拍摄的基础。这就是为什么大疆 M3508、达妙 DM4310 等比赛电机全部采用 FOC 的原因。

关键认知:FOC 控制中的电机驱动器(如大疆 C620 电调、达妙电机内置驱动板)已经实现了电流环 FOC,用户只需通过 CAN 总线发送目标电流值。这意味着我们不需要自己写 FOC,只需要做好通信层和控制层。

3.4 为什么用 CAN 总线电机而不是 PWM 电机

传统 PWM 电机控制是单向的——MCU 通过 PWM 占空比告诉电机"转多快",但电机无法向 MCU 返回任何信息。想获得位置反馈?需要额外接编码器线。想知道电流和温度?需要额外接传感器。一个电机至少需要 3 根线(PWM + 编码器 A + 编码器 B),6 个电机就是 18 根线。

CAN 总线电机双向的——MCU 发送目标电流/位置/速度指令,电机同时回报当前角度、转速、转矩电流、温度。这种反馈是实现闭环控制的前提——PID 控制器需要知道当前位置才能计算下一步输出。此外,CAN 总线只需 CAN_H 和 CAN_L 两根线,就能菊花链串联数十个电机,大幅简化布线。在 RoboMaster 机器人中,8 个关节电机通过两根 CAN 线即可全部连接,如果用 PWM + 编码器方案,线缆数量将超过 30 根。


4. GSRL 中的电机控制架构

4.1 电机控制的三层结构

GSRL 中的电机控制是一个经典的闭环控制系统:

┌──────────────────────────────────────────────────┐
│                   控制层 (Control)                │
│                                                  │
│  目标位置/速度 ─→ PID 控制器 ─→ 目标电流/电压     │
│       ↑                              │           │
│       │                              │           │
│  实际位置/速度 (反馈)                  │           │
│       │                              │           │
├───────┼──────────────────────────────┼───────────┤
│       │         通信层 (CAN)          │           │
│       │                              │           │
│  CAN 反馈帧 ←──────────────→ CAN 控制帧          │
│       │                              │           │
├───────┼──────────────────────────────┼───────────┤
│       │        物理层 (电机驱动板)     │           │
│                                                  │
│  编码器 → 驱动板 → 三相桥 → BLDC 电机             │
└──────────────────────────────────────────────────┘

4.2 Motor 基类的设计

GSRL 的 Motor 类(dvc_motor.hpp)封装了所有电机共有的控制逻辑:

class Motor {
protected:
    // 通信
    uint32_t m_motorControlMessageID;    // CAN 控制帧 ID
    uint32_t m_motorFeedbackMessageID;   // CAN 反馈帧 ID
    uint8_t m_motorControlData[8];       // CAN 控制数据(发送)
    uint8_t m_motorFeedbackData[8];      // CAN 反馈数据(接收)

    // 当前状态(从 CAN 反馈中解析)
    fp32 m_currentAngle;                 // 当前角度 (rad)
    fp32 m_currentAngularVelocity;       // 当前角速度 (rad/s)
    int16_t m_currentTorqueCurrent;      // 当前转矩电流
    int8_t m_temperature;               // 温度
    bool m_isMotorConnected;            // 是否在线

    // 目标状态(由上层控制算法设定)
    fp32 m_targetAngle;
    fp32 m_targetAngularVelocity;
    int16_t m_targetTorqueCurrent;

    // 控制器
    Controller *m_controller;            // PID 控制器接口
    fp32 m_controllerOutput;            // 控制器输出

public:
    // 闭环控制(多种模式)
    fp32 angleClosedloopControl();              // 位置闭环
    fp32 angularVelocityClosedloopControl();    // 速度闭环
    int16_t torqueCurrentClosedloopControl();   // 电流闭环
    fp32 revolutionsClosedloopControl();        // 圈数闭环
    void openloopControl(fp32 controlValue);    // 开环控制
};

4.3 支持的电机类型

GSRL 通过继承 Motor 基类,支持多种 CAN 总线电机:

类名 电机型号 通信协议
MotorGM6020 大疆 GM6020 大疆 CAN 协议,一控多(电压控制)
MotorM3508 大疆 M3508 大疆 CAN 协议(电流控制)
MotorM2006 大疆 M2006 与 M3508 协议兼容
MotorDM4310 达妙 DMJ4310 达妙 MIT 模式(力矩控制)
MotorDM2325 达妙 DM-S2325 与 DMJ4310 协议兼容
MotorDMmulti 达妙一控四固件 达妙力位混控模式
MotorLKMG 瓴控 MG 系列 瓴控协议

4.4 完整控制流程示例

tsk_test.cpp 中的达妙电机控制为例:

// 1. 声明 PID 控制器
SimplePID::PIDParam param = {
    10.0f,  // Kp
    0.0f,   // Ki
    0.0f,   // Kd
    100.0f, // outputLimit
    0.0f    // intergralLimit
};
SimplePID myPID(SimplePID::PID_POSITION, param);

// 2. 声明电机对象(ID=1, MasterID=3, PMAX=PI, VMAX=40, TMAX=15)
MotorDM4310 motor(1, 3, 3.1415926f, 40, 15, &myPID);

// 3. 在任务中初始化 CAN
CAN_Init(&hcan1, can1RxCallback);

// 4. 主循环:1ms 周期
while (1) {
    motor.openloopControl(sinf(2 * PI * 0.5f * xTaskGetTickCount() / 1000));
    // 将控制数据通过 CAN 发送给电机
    const uint8_t *data = motor.getMotorControlData();
    HAL_CAN_AddTxMessage(&hcan1, motor.getMotorControlHeader(), data, &send_mail_box);
    vTaskDelayUntil(&taskLastWakeTime, 1);
}

// 5. ISR 中接收反馈
void can1RxCallback(can_rx_message_t *pRxMsg) {
    motor.decodeCanRxMessageFromISR(pRxMsg);
}

数据流

1ms 任务循环:
  motor.openloopControl()              ← 计算控制量
    → motor.getMotorControlHeader()    ← 获取 CAN 帧头
    → motor.getMotorControlData()      ← 获取 CAN 数据
    → HAL_CAN_AddTxMessage()           ← 通过 CAN1 发出

CAN 中断:
  CAN1_RX0_IRQHandler()
    → HAL_CAN_RxFifo0MsgPendingCallback()
      → can1RxCallback()
        → motor.decodeCanRxMessageFromISR()  ← 更新电机当前状态

5. 舵机 vs 电机对比

特性 舵机 直流有刷电机 CAN 总线 BLDC
控制接口 PWM 脉宽 PWM 占空比(H 桥) CAN 总线数字指令
控制量 角度(位置) 电压/转速 转矩电流(FOC)
反馈 无(内部闭环) 无(需编码器) 角度/速度/电流/温度
精度 ±1° 取决于编码器 编码器精度(可达 0.01°)
使用的控制器 舵机内置 需要外部 PID 电机内置电流环 + 外置位置/速度 PID
GSRL 中的角色 较少使用 较少使用 主力执行器

6. 总结

要点 说明
舵机 内置闭环的角位置伺服,PWM 脉宽控制角度
直流有刷电机 H 桥驱动,PWM 占空比调速,电刷是磨损件
BLDC + FOC 电子换向,CAN 总线控制,驱动器内置电流环
GSRL Motor 体系 基类封装闭环控制,继承类适配不同 CAN 协议
控制流程 任务中 PID 计算 → CAN 发送 → ISR 中接收反馈 → 更新状态

与 GSRL 的关系

GSRL 中几乎所有机器人关节都由 CAN 总线电机驱动。Motor 基类定义了位置/速度/电流闭环的接口,SimplePID / CascadePID 作为控制器实现,CAN Driver 负责数据传输。这种分层设计使得同一控制算法可以无缝切换不同品牌的电机——只需替换继承类。

下一篇:UART 通信原理与实践

作者: Qing | 修改日期: 2026-07-18