跳转至

直流电机与编码器

概述

有刷直流电机(DC Brushed Motor)是最基本的电机类型,配合编码器可以实现精确的速度和位置控制。在教育机器人、小型轮式平台中广泛使用。

有刷直流电机内部结构

核心部件

部件 功能
永磁体(定子) 提供恒定磁场
电枢绕组(转子) 载流导体,产生力矩
换向器 铜片环,机械切换电流方向
电刷 碳刷或金属刷,将电流导入换向器
轴承 支撑转子旋转

工作原理

  1. 电流通过电刷流入换向器,进入电枢绕组
  2. 载流导体在永磁场中受安培力:\(F = BIL\)
  3. 力矩驱动转子旋转
  4. 换向器在适当位置切换电流方向,维持同向旋转

常见型号

型号 电压 空载转速 堵转扭矩 典型应用
130微型电机 3-6V 8000rpm 0.02N·m 玩具、小实验
370电机 6-12V 6000rpm 0.1N·m 小型机器人
550电机 12-24V 5000rpm 0.5N·m 中型平台
775电机 12-36V 4000rpm 1.5N·m 大型底盘

减速直流电机

为什么需要减速

裸电机转速高(数千rpm)、扭矩低,不适合直接驱动机器人轮子或关节。加装减速箱可以:

\[ \tau_{out} = N \cdot \tau_{motor} \cdot \eta_{gear} \]
\[ \omega_{out} = \frac{\omega_{motor}}{N} \]

其中 \(N\) 为减速比,\(\eta_{gear}\) 为齿轮效率(通常0.7-0.9)。

常用减速电机

型号 电压 减速比 输出转速 输出扭矩 带编码器
JGA25-370 6-12V 1:21.3 ~280rpm 1.5kg·cm
JGB37-520 6-12V 1:30 ~200rpm 3kg·cm
GM25-370 12V 1:34 ~180rpm 2.5kg·cm

减速箱类型

  • 正齿轮组:成本低,噪音较大,效率约80%
  • 行星齿轮:紧凑同轴,效率85-95%,常见于高端减速电机
  • 蜗轮蜗杆:自锁特性,减速比大,效率较低(40-70%)

编码器

编码器(Encoder)是测量电机转轴角位置和速度的传感器,是闭环控制的核心。

光电增量编码器

原理

  • 码盘上有均匀分布的透光缝隙
  • 光源(LED)和光敏元件分居码盘两侧
  • 转动时产生脉冲信号

参数

  • PPR(Pulses Per Revolution):每圈脉冲数,如100/200/400/600
  • CPR(Counts Per Revolution):四倍频后每圈计数,\(CPR = 4 \times PPR\)
  • 分辨率\(\Delta\theta = \frac{360°}{CPR}\)

磁编码器

工作原理

  • 转轴末端安装径向磁化的小磁铁
  • 霍尔/磁阻传感器IC检测磁场角度

AS5600磁编码器

参数
分辨率 12位(4096位置/圈)
接口 I2C / 模拟输出
精度 ±1°
工作电压 3.3V / 5V
尺寸 非常小巧
# AS5600 通过I2C读取角度(MicroPython示例)
from machine import I2C, Pin

i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq=400000)
AS5600_ADDR = 0x36
RAW_ANGLE_REG = 0x0C

def read_angle():
    data = i2c.readfrom_mem(AS5600_ADDR, RAW_ANGLE_REG, 2)
    raw = (data[0] << 8) | data[1]
    angle = raw * 360.0 / 4096.0
    return angle

绝对值编码器

  • 每个位置有唯一编码(格雷码或二进制)
  • 上电即知位置,无需回零
  • 单圈绝对值:一圈内位置唯一
  • 多圈绝对值:可记录多圈旋转

编码器类型对比

特性 光电增量 磁编码器 绝对值
成本
分辨率
抗干扰
上电归零 需要 不需要 不需要
典型应用 减速电机测速 舵机/关节 工业伺服

正交解码(Quadrature Decoding)

双通道信号

增量编码器通常输出A、B两路信号,相位差90°:

        ┌──┐  ┌──┐  ┌──┐
A通道:  │  │  │  │  │  │
     ───┘  └──┘  └──┘  └──

           ┌──┐  ┌──┐  ┌──┐
B通道:     │  │  │  │  │  │
        ───┘  └──┘  └──┘  └──

方向判断

  • 正转:A上升沿时B为低电平
  • 反转:A上升沿时B为高电平

四倍频

利用A和B通道的所有边沿(上升+下降),计数分辨率提高4倍:

\[ CPR = 4 \times PPR \]

速度计算

在固定时间间隔 \(\Delta t\) 内统计脉冲数 \(\Delta n\)

\[ \omega = \frac{2\pi \cdot \Delta n}{CPR \cdot \Delta t} \quad (\text{rad/s}) \]
\[ RPM = \frac{60 \cdot \Delta n}{CPR \cdot \Delta t} \]

PID速度控制

PID控制器

PID(比例-积分-微分)是电机速度控制最常用的算法:

\[ u(t) = K_p e(t) + K_i \int_0^t e(\tau)d\tau + K_d \frac{de(t)}{dt} \]

其中:

  • \(e(t) = \omega_{target} - \omega_{actual}\) — 速度误差
  • \(K_p\) — 比例增益:误差越大,输出越大
  • \(K_i\) — 积分增益:消除稳态误差
  • \(K_d\) — 微分增益:抑制超调和振荡

离散化实现

实际控制器以固定周期 \(T_s\) 运行:

\[ u[k] = K_p \cdot e[k] + K_i \cdot T_s \sum_{i=0}^{k} e[i] + K_d \cdot \frac{e[k] - e[k-1]}{T_s} \]

Arduino/ESP32代码示例

// 编码器引脚定义
#define ENCODER_A 2   // 中断引脚
#define ENCODER_B 3
#define MOTOR_PWM 5
#define MOTOR_DIR 4

// 编码器计数(volatile用于中断安全)
volatile long encoderCount = 0;

// PID参数
float Kp = 2.0, Ki = 0.5, Kd = 0.1;
float targetRPM = 100.0;
float integral = 0, prevError = 0;

// 编码器中断服务程序
void encoderISR() {
    if (digitalRead(ENCODER_B) == LOW) {
        encoderCount++;
    } else {
        encoderCount--;
    }
}

void setup() {
    pinMode(ENCODER_A, INPUT_PULLUP);
    pinMode(ENCODER_B, INPUT_PULLUP);
    pinMode(MOTOR_PWM, OUTPUT);
    pinMode(MOTOR_DIR, OUTPUT);

    attachInterrupt(digitalPinToInterrupt(ENCODER_A), encoderISR, RISING);
    Serial.begin(115200);
}

// PID控制循环(每20ms执行一次)
unsigned long lastTime = 0;
long lastCount = 0;
const float CPR = 4 * 11 * 21.3;  // 4倍频 × PPR × 减速比
const float dt = 0.02;             // 20ms

void loop() {
    unsigned long now = millis();
    if (now - lastTime >= 20) {
        // 计算当前RPM
        noInterrupts();
        long count = encoderCount;
        interrupts();

        float deltaCount = count - lastCount;
        float currentRPM = (deltaCount / CPR) * (60.0 / dt);

        // PID计算
        float error = targetRPM - currentRPM;
        integral += error * dt;
        integral = constrain(integral, -100, 100);  // 积分限幅
        float derivative = (error - prevError) / dt;

        float output = Kp * error + Ki * integral + Kd * derivative;
        output = constrain(output, -255, 255);

        // 输出到电机
        if (output >= 0) {
            digitalWrite(MOTOR_DIR, HIGH);
            analogWrite(MOTOR_PWM, (int)output);
        } else {
            digitalWrite(MOTOR_DIR, LOW);
            analogWrite(MOTOR_PWM, (int)(-output));
        }

        prevError = error;
        lastCount = count;
        lastTime = now;

        // 调试输出
        Serial.print("Target:");  Serial.print(targetRPM);
        Serial.print(" Current:"); Serial.print(currentRPM);
        Serial.print(" Output:");  Serial.println(output);
    }
}

PID调参技巧

步骤 操作 预期效果
1 \(K_i=0, K_d=0\),逐渐增大 \(K_p\) 系统开始响应,可能振荡
2 \(K_p\) 略降,逐渐增大 \(K_i\) 消除稳态误差
3 增大 \(K_d\) 减小超调,加快收敛
4 微调三个参数 达到满意的响应

Ziegler-Nichols法

  1. 仅用P控制,增大 \(K_p\) 直到系统等幅振荡,记为 \(K_u\)(临界增益)
  2. 测量振荡周期 \(T_u\)
  3. 按公式设置:\(K_p = 0.6K_u\)\(K_i = 2K_p/T_u\)\(K_d = K_p T_u/8\)

ESP32特殊考虑

硬件编码器接口

ESP32具有PCNT(Pulse Counter)硬件模块,可高效处理编码器信号:

// 使用ESP32 PCNT硬件解码
#include "driver/pcnt.h"

void setupPCNT() {
    pcnt_config_t pcnt_config;
    pcnt_config.pulse_gpio_num = ENCODER_A;
    pcnt_config.ctrl_gpio_num = ENCODER_B;
    pcnt_config.channel = PCNT_CHANNEL_0;
    pcnt_config.unit = PCNT_UNIT_0;
    pcnt_config.pos_mode = PCNT_COUNT_INC;
    pcnt_config.neg_mode = PCNT_COUNT_DEC;
    pcnt_config.lctrl_mode = PCNT_MODE_REVERSE;
    pcnt_config.hctrl_mode = PCNT_MODE_KEEP;
    pcnt_config.counter_h_lim = 32767;
    pcnt_config.counter_l_lim = -32768;

    pcnt_unit_config(&pcnt_config);
    pcnt_counter_pause(PCNT_UNIT_0);
    pcnt_counter_clear(PCNT_UNIT_0);
    pcnt_counter_resume(PCNT_UNIT_0);
}

PWM分辨率

ESP32的LEDC PWM支持高达16位分辨率:

// ESP32高分辨率PWM
ledcSetup(0, 20000, 10);  // 通道0, 20kHz, 10位分辨率(0-1023)
ledcAttachPin(MOTOR_PWM, 0);
ledcWrite(0, dutyCycle);   // dutyCycle: 0-1023

小结

  • 有刷直流电机结构简单,适合入门和小型机器人项目
  • 减速电机通过齿轮箱平衡转速和扭矩
  • 编码器是实现闭环控制的关键传感器
  • 正交解码利用A/B通道实现方向判断和四倍频
  • PID控制是电机速度/位置控制的基础算法
  • ESP32的PCNT硬件和高分辨率PWM使其成为电机控制的理想平台

评论 #