有刷直流电机作为最经典的电机类型之一,在机器人、智能小车和各种自动化设备中广泛应用。虽然结构简单,但要想精准控制其转速和转向,需要掌握几个核心概念。
脉宽调制(PWM)是通过快速开关电源来控制平均电压的技术。对于Arduino UNO这类开发板:
实际测试发现,当占空比低于约15%时,很多电机可能无法启动(存在死区)。这是因为:
提示:对于小型130电机,建议初始占空比设为20%(约51)以上
直接使用Arduino引脚驱动电机存在三大问题:
L298N驱动模块的典型参数:
| 参数 | 数值 |
|---|---|
| 驱动电压 | 5-35V |
| 逻辑电压 | 5-7V |
| 单路持续电流 | 2A |
| 峰值电流 | 3A |
| 启用方式 | PWM或使能 |
电路连接时需注意:
对于不同功率的电机,驱动方案选择差异很大:
小型电机(<5W)
中型电机(5-50W)
大型电机(>50W)
使用L298N时的经典接线错误:
正确接线步骤:
重要:上电顺序应为先逻辑电源后电机电源,断电时相反
直接跳变PWM值会导致机械冲击,推荐使用渐变算法:
cpp复制void smoothAccel(int targetPWM, int duration) {
int current = analogRead(enA);
int step = (targetPWM - current) / (duration / 10);
while(abs(current - targetPWM) > 5) {
current += step;
analogWrite(enA, constrain(current, 0, 255));
delay(10);
}
}
实测对比数据:
| 控制方式 | 启动时间 | 机械振动 |
|---|---|---|
| 直接跳变 | <1ms | 剧烈 |
| 50ms渐变 | 50ms | 轻微 |
| 100ms渐变 | 100ms | 几乎无 |
更完善的保护电路应包含:
改进后的保护代码:
cpp复制#define OVER_CURRENT 2000 // 2A
float readCurrent() {
const int samples = 10;
float sum = 0;
for(int i=0; i<samples; i++) {
sum += analogRead(currentSensor) * 0.0049 / 0.1; // 假设采样电阻0.1Ω
delay(1);
}
return sum / samples;
}
void safetyCheck() {
static unsigned long lastCheck = 0;
if(millis() - lastCheck > 100) {
float current = readCurrent();
if(current > OVER_CURRENT) {
emergencyStop();
}
lastCheck = millis();
}
}
检查电源指示灯
测量控制信号
测试电机直接供电
温度异常的可能原因:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 芯片烫手但电机正常 | 散热不足 | 加装散热风扇 |
| 特定方向发热严重 | H桥半边损坏 | 更换驱动芯片 |
| 伴随电机抖动 | PWM频率不匹配 | 调整至1-20kHz |
| 空载也发热 | 内部短路 | 立即断电检查 |
开环控制的转速会随负载变化,可通过编码器实现闭环:
cpp复制#include <Encoder.h>
Encoder myEnc(2, 3);
long oldPosition = 0;
float rpm = 0;
void updateSpeed() {
long newPosition = myEnc.read();
rpm = (newPosition - oldPosition) * 30.0 / (PPR * sampleTime);
oldPosition = newPosition;
// PID计算
float error = targetRpm - rpm;
integral += error * dt;
derivative = (error - lastError) / dt;
output = Kp*error + Ki*integral + Kd*derivative;
analogWrite(enA, constrain(output, 0, 255));
}
通过蓝牙模块实现手机控制:
cpp复制#include <SoftwareSerial.h>
SoftwareSerial BT(11, 12); // RX, TX
void setup() {
BT.begin(9600);
}
void loop() {
if(BT.available()) {
char cmd = BT.read();
switch(cmd) {
case 'F': // 前进
setMotor(128, FORWARD);
break;
case 'S': // 停止
setMotor(0, BRAKE);
break;
// 其他命令...
}
}
}
我在实际项目中发现,电机控制最关键的三个经验是: