去年带队完成了一个工业巡检机器人项目,核心控制器选用的正是ESP32-WROOM-32D。当看到这个芯片成功驱动着20公斤重的机器人在厂区自主运行时,我真正体会到了这款低成本MCU的强大。今天就把这些年用ESP32做机器人开发的经验系统梳理一遍,重点讲透那些手册里不会写的实战细节。
ESP32之所以成为机器人开发的热门选择,主要得益于其三合一特性:WiFi/BLE双模无线、双核240MHz主频、超低功耗设计。但实际开发中会遇到各种坑,比如GPIO0在下载模式时的特殊状态、PWM通道的资源冲突、WiFi信号被电机干扰等。下面我就结合智能移动机器人项目,详解开发全流程。
我们的机器人采用模块化设计架构:
关键提示:电机驱动一定要选带光耦隔离的型号,我早期项目用L293D就因电机反电动势烧过两个ESP32。
ESP32的引脚复用情况复杂,推荐这样分配:
c复制// 电机控制
#define MOTOR_L_PWM 12 // 使用LEDC通道0
#define MOTOR_R_PWM 13 // 使用LEDC通道1
#define MOTOR_L_DIR 14
#define MOTOR_R_DIR 15
// 传感器
#define TRIG_PIN 5 // 超声波触发
#define ECHO_PIN 18 // 中断采集
#define IR_PIN 19 // 红外避障
// I2C总线
#define SDA_PIN 21 // 固定引脚
#define SCL_PIN 22 // 固定引脚
特别注意:
推荐使用VSCode+PlatformIO组合,比Arduino IDE更专业:
ini复制lib_deps =
adafruit/Adafruit SSD1306@^2.5.7
madhephaestus/ESP32Servo@^0.11.0
带死区保护的PWM调速实现:
cpp复制void setMotorSpeed(int motor, int speed) {
speed = constrain(speed, -255, 255);
if(motor == LEFT_MOTOR) {
digitalWrite(MOTOR_L_DIR, speed > 0 ? HIGH : LOW);
ledcWrite(0, abs(speed)); // 使用LEDC通道
} else {
digitalWrite(MOTOR_R_DIR, speed > 0 ? HIGH : LOW);
ledcWrite(1, abs(speed));
}
// 死区补偿
if(abs(speed) < 30) {
digitalWrite(MOTOR_L_DIR, LOW);
digitalWrite(MOTOR_R_DIR, LOW);
ledcWrite(0, 0);
ledcWrite(1, 0);
}
}
超声波+红外复合避障算法:
cpp复制bool checkObstacle() {
static uint32_t last_ir = 0;
// 超声波测距
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(15);
digitalWrite(TRIG_PIN, LOW);
float distance = pulseIn(ECHO_PIN, HIGH) / 58.0;
// 红外检测
bool ir_detected = digitalRead(IR_PIN) == LOW;
if(ir_detected) last_ir = millis();
// 综合判断
return (distance < 30) ||
(millis() - last_ir < 500);
}
基于AsyncWebServer的实现:
cpp复制#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
void setupWiFi() {
WiFi.softAP("Robot_CTRL", "12345678");
server.on("/control", HTTP_GET, [](AsyncWebServerRequest *request){
String cmd = request->arg("cmd");
if(cmd == "F") setMotorSpeed(LEFT_MOTOR, 200);
// 其他指令处理...
request->send(200, "text/plain", "OK");
});
server.begin();
}
使用BLE键盘协议避免APP开发:
cpp复制#include <BleKeyboard.h>
BleKeyboard bleKeyboard("Robot Remote");
void handleBLE() {
if(bleKeyboard.isConnected()) {
if(bleKeyboard.isPressed('w'))
setMotorSpeed(LEFT_MOTOR, 150);
// 其他按键处理...
}
}
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 单侧不转 | 电机线接触不良 | 重新压接端子 |
| 转速不稳 | PWM频率过低 | 设置LEDC为5KHz |
| 突然停止 | 过流保护触发 | 检查电机堵转 |
cpp复制esp_task_wdt_init(10, true);
cpp复制void checkWiFi() {
static uint32_t lastCheck = 0;
if(millis() - lastCheck > 5000) {
if(WiFi.status() != WL_CONNECTED) {
WiFi.reconnect();
}
lastCheck = millis();
}
}
cpp复制xTaskCreatePinnedToCore(
motorControlTask, // 电机控制任务
"MotorCtrl",
4096,
NULL,
5,
NULL,
0 // 指定核心0
);
xTaskCreatePinnedToCore(
sensorTask, // 传感器采集任务
"Sensor",
4096,
NULL,
3,
NULL,
1 // 指定核心1
);
夜间自动进入light sleep:
cpp复制void enterSleep() {
gpio_wakeup_enable((gpio_num_t)IR_PIN, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_light_sleep_start();
// 唤醒后重新初始化外设
initPeripherals();
}
经过三个版本迭代,我们最终实现的机器人参数:
最让我自豪的是这个项目后来被本地一所高校采用作为教学案例。其实ESP32机器人开发最难的不是技术实现,而是要在有限资源下做出稳定可靠的系统。建议初学者先从电机基础控制做起,逐步添加传感器功能,最后再考虑无线模块。