ESP32 + L298N 驱动4个TT马达:PWM调速与编码器测速全解析
在智能小车开发领域,精准控制电机转速是实现复杂运动的基础。本文将深入讲解如何利用ESP32微控制器配合L298N驱动模块,实现对4个TT马达的独立PWM调速控制,并利用双出轴编码器实现轮速闭环反馈。这套方案不仅适用于竞速小车,更是智能巡检、自动导航等高级应用的基石。
1. 硬件系统架构设计
1.1 核心组件选型分析
ESP32-WROOM-32D 作为主控芯片,其优势在于:
- 双核240MHz处理能力
- 16路PWM通道(LEDC模块)
- 超低功耗蓝牙/WiFi双模通信
- 丰富的外设接口(PCNT脉冲计数、MCPWM等)
L298N双H桥驱动模块 关键参数:
| 参数 | 规格 |
|-----------------|-------------------|
| 驱动电压范围 | 5V-35V |
| 单路持续电流 | 2A (峰值3A) |
| 逻辑电平 | 5V TTL兼容 |
| 工作温度 | -25℃ ~ +130℃ |
| PWM频率支持 | 0-100kHz |
**TT马达(1:48减速比)**典型特性:
- 工作电压:3-12VDC
- 空载转速:10000 RPM(电机轴)
- 减速后转速:208 RPM(输出轴)
- 双出轴设计:一侧驱动轮毂,一侧安装编码盘
1.2 系统接线方案
完整接线示意图如下:
ESP32 GPIO L298N接口
-----------------------------
GPIO12 ----> IN1 (电机1方向)
GPIO13 ----> IN2 (电机1方向)
GPIO14 ----> IN3 (电机2方向)
GPIO15 ----> IN4 (电机2方向)
GPIO16 ----> IN5 (电机3方向)
GPIO17 ----> IN6 (电机3方向)
GPIO18 ----> IN7 (电机4方向)
GPIO19 ----> IN8 (电机4方向)
GPIO25 ----> EN1 (电机1使能/PWM)
GPIO26 ----> EN2 (电机2使能/PWM)
GPIO27 ----> EN3 (电机3使能/PWM)
GPIO32 ----> EN4 (电机4使能/PWM)
编码器A相 ESP32 PCNT输入
编码器B相 ESP32 PCNT输入
关键提示:L298N模块的5V输出可为ESP32供电,但建议使用独立电源以避免电机干扰导致系统重启。所有GND必须共地连接。
2. PWM调速实现原理
2.1 ESP32 LEDC PWM配置
ESP32的LED PWM控制器提供16个通道,可配置分辨率(1-16bit)和频率(1-40MHz)。典型电机控制配置如下:
// PWM通道配置
const int freq = 5000; // 5kHz PWM频率
const int resolution = 8; // 8bit分辨率(0-255)
void setupPWM() {
ledcSetup(0, freq, resolution); // 通道0
ledcSetup(1, freq, resolution); // 通道1
ledcSetup(2, freq, resolution); // 通道2
ledcSetup(3, freq, resolution); // 通道3
ledcAttachPin(25, 0); // EN1 -> 通道0
ledcAttachPin(26, 1); // EN2 -> 通道1
ledcAttachPin(27, 2); // EN3 -> 通道2
ledcAttachPin(32, 3); // EN4 -> 通道3
}
2.2 电机方向控制逻辑
L298N的H桥控制真值表:
| IN1 | IN2 | 电机状态 |
|-----|-----|----------|
| 1 | 0 | 正转 |
| 0 | 1 | 反转 |
| 0 | 0 | 刹车 |
| 1 | 1 | 自由停止 |
方向控制代码示例:
void setMotorDirection(int motor, bool forward) {
switch(motor) {
case 1:
digitalWrite(12, forward);
digitalWrite(13, !forward);
break;
case 2:
digitalWrite(14, forward);
digitalWrite(15, !forward);
break;
// 其他电机类似...
}
}
3. 编码器测速与闭环控制
3.1 正交编码器信号处理
TT马达双出轴可安装增量式编码器(建议20脉冲/转),ESP32的PCNT模块可精确计数:
#include <driver/pcnt.h>
void initEncoder(int pulsePin, int channel) {
pcnt_config_t config = {
.pulse_gpio_num = pulsePin,
.ctrl_gpio_num = -1,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.counter_h_lim = 32767,
.counter_l_lim = -32767,
.unit = static_cast<pcnt_unit_t>(channel)
};
pcnt_unit_config(&config);
}
3.2 速度计算算法
采用定时中断计算RPM:
volatile int pulseCount[4] = {0};
hw_timer_t *timer = NULL;
void IRAM_ATTR onTimer() {
for(int i=0; i<4; i++) {
float rpm = (pulseCount[i] * 60.0) / (20 * 0.1); // 20PPR, 100ms间隔
pulseCount[i] = 0;
// 此处可加入PID计算
}
}
void setup() {
timer = timerBegin(0, 80, true); // 1MHz时钟
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 100000, true); // 100ms中断
timerAlarmEnable(timer);
}
3.3 PID速度闭环控制
实现转速稳定控制:
class PIDController {
public:
PIDController(float kp, float ki, float kd)
: Kp(kp), Ki(ki), Kd(kd) {}
float compute(float setpoint, float input) {
float error = setpoint - input;
integral += error;
float derivative = error - prevError;
prevError = error;
return Kp*error + Ki*integral + Kd*derivative;
}
private:
float Kp, Ki, Kd;
float integral = 0;
float prevError = 0;
};
PIDController pid(0.8, 0.05, 0.2);
void controlLoop() {
float targetRPM = 100.0; // 目标转速
float currentRPM = getMotorRPM(1);
float pwm = pid.compute(targetRPM, currentRPM);
pwm = constrain(pwm, 0, 255);
ledcWrite(0, pwm); // 更新PWM输出
}
4. 完整系统调试技巧
4.1 常见问题排查
- 电机不转 :检查使能端跳线帽是否连接,逻辑电压是否正常
- 转速波动大 :尝试增加PID微分项,检查电源电压稳定性
- 编码器计数异常 :添加硬件消抖电路(0.1μF电容并联10kΩ电阻)
4.2 性能优化建议
- 将PWM频率提升至10-20kHz可降低电机啸叫
- 使用FreeRTOS任务分离控制逻辑与通信功能
- 采用Kalman滤波处理编码器数据提升抗干扰性
4.3 进阶功能扩展
- 通过蓝牙/WiFi实现手机遥控
- 添加MPU6050实现姿态控制
- 开发ROS节点支持SLAM导航
这套系统在实际项目中表现出色,我曾用类似方案实现的小车在复杂地形下速度控制误差小于±2%。关键是要确保机械结构牢固,电源供应充足,以及合理的控制周期(建议10-50ms)。
1万+

被折叠的 条评论
为什么被折叠?



