直流电机与编码器
概述
有刷直流电机(DC Brushed Motor)是最基本的电机类型,配合编码器可以实现精确的速度和位置控制。在教育机器人、小型轮式平台中广泛使用。
有刷直流电机内部结构
核心部件
| 部件 | 功能 |
|---|---|
| 永磁体(定子) | 提供恒定磁场 |
| 电枢绕组(转子) | 载流导体,产生力矩 |
| 换向器 | 铜片环,机械切换电流方向 |
| 电刷 | 碳刷或金属刷,将电流导入换向器 |
| 轴承 | 支撑转子旋转 |
工作原理
- 电流通过电刷流入换向器,进入电枢绕组
- 载流导体在永磁场中受安培力:\(F = BIL\)
- 力矩驱动转子旋转
- 换向器在适当位置切换电流方向,维持同向旋转
常见型号
| 型号 | 电压 | 空载转速 | 堵转扭矩 | 典型应用 |
|---|---|---|---|---|
| 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法
- 仅用P控制,增大 \(K_p\) 直到系统等幅振荡,记为 \(K_u\)(临界增益)
- 测量振荡周期 \(T_u\)
- 按公式设置:\(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使其成为电机控制的理想平台