如果你最近刷短视频,一定见过那种十几厘米高、会摇头晃脑、能听懂简单指令的小机器人。它们看起来像是工业机器人的缩小版,但背后真正驱动它们的核心,往往不是昂贵的树莓派加激光雷达,而是一块几十块钱的 ESP32 开发板。
很多人第一反应是:ESP32 不是一块 WiFi 蓝牙单片机吗?它也能做 AI 机器人?
这正是这篇教程想解决的问题。我会从一个真实场景出发:你想打造一台属于自己的桌面积木机器人,它有电机能移动,有超声波能避障,有屏幕能显示表情,还能通过手机或网页控制,甚至接入 ROS 2 做更复杂的导航。传统方案可能需要树莓派、Arduino、各种驱动板拼在一起,而 ESP32 一块芯片就能承担主控、通信、传感器采样和电机控制。
文章会讲清楚三件事:ESP32 凭什么能当机器人主控、如何用 Arduino 或 Micro-ROS 完成底层开发、以及从硬件选型到代码调试的完整落地流程。
无论你是机甲爱好者、刚入门嵌入式的大学生,还是想给孩子的科技作品加点智能,这篇教程都值得收藏。
1. 这篇文章真正要解决的问题
先聊聊痛点。
很多新手做机器人,开场就掉进选型迷宫。想用树莓派,发现要等供电、等系统启动、等摄像头驱动,一套下来成本五六百;想用 STM32,发现 HAL 库和寄存器文档对新手不友好,WiFi 还得外挂模块;最后听人说 Arduino 简单,结果 Uno 只有 2KB 内存,连个摄像头图传都跑不动,更别提 AI 推理和 ROS 2 通信。
ESP32 恰好卡在中间位置:它既不像 STM32 那样硬核,也不像树莓派那样臃肿。更重要的是,ESP32 原生集成 WiFi 和蓝牙,双核 240MHz 主频,有 520KB SRAM,外设接口覆盖了机器人常用的大部分需求。配合 Arduino 框架,十几分钟就能跑起第一个点灯程序;配合 Micro-ROS,又能直接和 ROS 2 生态打通,做 SLAM 导航也不是不可能。
这篇文章要解决的真正问题是:如何不用昂贵的开发板,只靠 ESP32 加上常见模块,就造出一台能远程控制、能避障、能和用户做基础交互的 AI 机器人小伙伴。
读完你会掌握:
- ESP32 在机器人项目中的架构定位和选型建议。
- 从零搭建 ESP32 Arduino 开发环境并烧录的完整流程。
- 使用 L298N 或 DRV8833 驱动直流电机、读取超声波传感器数据的代码写法。
- 如何用 Web 页面和手机蓝牙控制机器人,并扩展语音、屏幕等 AI 交互能力。
- 如何用 Micro-ROS 把 ESP32 接入 ROS 2,为后续做自主导航铺路。
2. ESP32 为什么适合做机器人主控
2.1 它到底是什么
ESP32 是乐鑫推出的一系列 WiFi 蓝牙 SoC,不是单纯的单片机。它内部集成了 Xtensa 双核处理器、WiFi 802.11 b/g/n、蓝牙 4.2/5.0,以及大量 GPIO 外设。不同型号差异不小,做机器人最常用的是 ESP32 DevKitC 这类经典板,或者 ESP32-S3 这种带 AI 加速的型号。
用一句话概括:ESP32 是“一块带 WiFi 蓝牙的单片机”,它的地位介于传统 MCU 和 Linux 单板机之间。
2.2 和常见开发板对比
| 对比项 | ESP32 DevKitC | STM32F407 探索者 | Arduino Uno | 树莓派 4B |
|---|---|---|---|---|
| 主频 | 240MHz 双核 | 168MHz 单核 | 16MHz | 1.5GHz 四核 |
| WiFi 蓝牙 | 原生集成 | 需外挂模块 | 需外挂模块 | 原生集成 |
| 内存 | 520KB SRAM | 192KB SRAM | 2KB SRAM | 2GB-8GB |
| 开发难度 | 低(Arduino/Python) | 高(HAL/寄存器) | 最低 | 较高(Linux) |
| 实时性 | 较好 | 很好 | 一般 | 较差 |
| 系统启动时间 | 毫秒级 | 毫秒级 | 毫秒级 | 秒级 |
| 做桌面 AI 机器人适配度 | 高 | 中 | 低 | 高但成本高 |
从这个表能看出,ESP32 的最大优势不是算力,而是“集成度 + 实时性 + 成本”的平衡点。
2.3 开发板和单片机的区别
很多新手会问:开发板和单片机到底什么区别?
单片机是芯片本身,开发板是把单片机芯片、晶振、USB 转串口、电源电路、引脚排针集成在一起的电路板。买一块 ESP32 开发板,相当于拿到一个“最小可用的 ESP32 系统”,插上 USB 线就能烧录和调试。这也是为什么教程中我会刻意说“开发板”,而不是“芯片”。
对机器人项目来说,开发板的意义在于:不必自己设计复杂的外围电路,把时间花在逻辑和结构上。
2.4 结论:适合哪种项目
ESP32 适合做资源受限机器人。所谓资源受限,是指这台机器人不需要本地跑大模型或稠密建图,而是通过 WiFi 把图像或传感器数据传到上位机处理,或者只执行简单的避障、巡线、语音指令响应。
如果你要做的恰好是一台“眼睛靠摄像头、大脑靠云端或手机、身体靠电机、表情靠屏幕”的桌面机器人,ESP32 就是性价比最高的选择。
3. 总体架构与硬件选型
3.1 系统架构设计
在动手之前,先画出机器人小伙伴的功能框图。这一步很重要,否则焊完线才发现引脚冲突会非常痛苦。
桌面 AI 机器人小伙伴可以拆成下面几层:
- 感知层:超声波测距模块、温湿度传感器、可选摄像头。
- 决策层:ESP32 主控,负责传感器采样、控制逻辑、通信。
- 执行层:直流电机驱动板、舵机云台、OLED/LVGL 屏幕。
- 交互层:手机网页控制、蓝牙手柄、语音播报模块或离线语音识别。
- 通信层:WiFi 连接路由器或手机热点,通过 HTTP/WebSocket 与手机通信。
3.2 硬件清单
下面的清单不追求高性能,只追求容易买到、接线友好、资料多。
# 核心主控 1. ESP32 DevKitC V4 开发板(或 ESP32-S3 DevKitC-1) 2. 面包板 + 若干杜邦线 # 运动系统 3. 2 个 TT 直流减速电机 + 橡胶轮 4. 1 个 L298N 电机驱动板(或 DRV8833) 5. 5V 2A 移动电源(或 2 节 18650 电池 + 降压模块) # 传感器 6. HC-SR04 超声波传感器 7. DHT11 或 DHT22 温湿度传感器(可选) # 交互外设 8. 0.96 寸 SSD1306 OLED 屏幕 9. 1 或 2 个 SG90 舵机(做出头部摆动效果) 10. 按键、LED 若干3.3 引脚规划建议
ESP32 引脚并不像 Arduino Uno 那样“随便一个就能用”,有几个坑要提前知道:
- GPIO 6-11 默认连接闪存芯片,不能当普通 IO 用。
- GPIO 12 是 strapping 引脚,上电时如果拉高会影响进入下载模式。
- GPIO 34-39 是输入专用引脚,不能输出 PWM 或数字输出。
- 电机驱动板最好和传感器使用不同电源域,避免电机启动时拉低传感器供电。
一个稳妥的引脚分配如下:
| 设备 | ESP32 引脚 |
|---|---|
| 左电机 IN1/IN2 | GPIO 26 / GPIO 27 |
| 右电机 IN3/IN4 | GPIO 14 / GPIO 12(若担心,可换成 32/33) |
| 超声波 TRIG | GPIO 16 |
| 超声波 ECHO | GPIO 17 |
| OLED SDA / SCL | GPIO 21 / GPIO 22 |
| 舵机信号 | GPIO 15 |
| DHT11 数据 | GPIO 4 |
这里特别提醒:GPIO 12 上电电平和下载模式有关,如果插上拉模块导致无法烧录,请优先换引脚。
4. 开发环境搭建
4.1 安装 Arduino IDE
在 Arduino 官网下载 IDE 2.x 版本,安装完成并打开。
4.2 添加 ESP32 开发板支持
打开“文件 → 首选项 → 附加开发板管理器网址”,填入乐鑫官方地址:
https://espressif.github.io/arduino-esp32/package_esp32_index.json然后打开“工具 → 开发板 → 开发板管理器”,搜索esp32 by Espressif Systems,点击安装。安装包较大,首次安装可能需要较长时间。如果出现网络下载失败,优先检查网络环境,或使用国内镜像源。
从实际使用经验看,ESP32 Arduino 内核版本迭代较快,遇到编译报错时先确认你安装的是 2.x 还是 3.x 版本,因为不同版本对analogWrite、LEDC等 API 的兼容有明显差异。
4.3 测试烧录
把 ESP32 开发板插入 USB,选择开发板型号为ESP32 Dev Module,选择对应的 COM 口,上传一个最简代码:
// 文件路径:test_blink/test_blink.ino #define LED_BUILTIN_PIN 2 void setup() { pinMode(LED_BUILTIN_PIN, OUTPUT); Serial.begin(115200); } void loop() { digitalWrite(LED_BUILTIN_PIN, HIGH); delay(500); digitalWrite(LED_BUILTIN_PIN, LOW); delay(500); Serial.println("ESP32 alive"); }如果板载 LED 以 1 秒周期闪烁,并且串口输出ESP32 alive,说明环境已经通了。
5. 电机控制与运动系统实现
5.1 电机驱动板接线
L298N 是经典电机驱动模块,接线时要特别注意共地问题:
ESP32 5V(或外部 5V) → L298N 12V 输入 L298N 5V 输出 → ESP32 VIN(可选,取决于供电方案) L298N GND → ESP32 GND GPIO 26 → L298N IN1 GPIO 27 → L298N IN2 GPIO 14 → L298N IN3 GPIO 12 → L298N IN4 电机 A 两线 → L298N OUT1 / OUT2 电机 B 两线 → L298N OUT3 / OUT4必须强调:电机驱动板的电源和 ESP32 的电源最好分开。如果强行用同一个 USB 5V 供电去驱动两个大电流电机,很容易出现开发板重启、串口断连、电机抖动等情况。新手最常见的错误是只插 USB 线,然后接上电机驱动板,期望它能带动机器人,结果屏幕直接黑掉。
5.2 电机控制代码
下面这段代码实现了前、后、左、右、停止五个动作。它通过 digitalWrite 控制电机方向,并通过 delay 控制运行时间。
// 文件路径:motor_test/motor_test.ino int motorA1 = 26; int motorA2 = 27; int motorB1 = 14; int motorB2 = 12; void setup() { pinMode(motorA1, OUTPUT); pinMode(motorA2, OUTPUT); pinMode(motorB1, OUTPUT); pinMode(motorB2, OUTPUT); Serial.begin(115200); Serial.println("Motor test start"); } void loop() { runForward(1000); runBackward(1000); turnLeft(800); turnRight(800); stopMotor(1000); } void runForward(int duration) { digitalWrite(motorA1, HIGH); digitalWrite(motorA2, LOW); digitalWrite(motorB1, HIGH); digitalWrite(motorB2, LOW); delay(duration); } void runBackward(int duration) { digitalWrite(motorA1, LOW); digitalWrite(motorA2, HIGH); digitalWrite(motorB1, LOW); digitalWrite(motorB2, HIGH); delay(duration); } void turnLeft(int duration) { digitalWrite(motorA1, LOW); digitalWrite(motorA2, HIGH); digitalWrite(motorB1, HIGH); digitalWrite(motorB2, LOW); delay(duration); } void turnRight(int duration) { digitalWrite(motorA1, HIGH); digitalWrite(motorA2, LOW); digitalWrite(motorB1, LOW); digitalWrite(motorB2, HIGH); delay(duration); } void stopMotor(int duration) { digitalWrite(motorA1, LOW); digitalWrite(motorA2, LOW); digitalWrite(motorB1, LOW); digitalWrite(motorB2, LOW); delay(duration); }代码里的左转右转是“原地差速转向”,也就是让两个轮子朝相反方向转动。真实小车如果安装的是带减速箱的 TT 电机,转向效果会比较明显。
5.3 如何验证运动是否正常
烧录后把机器人放在桌面,按顺序观察:前进 1 秒、后退 1 秒、左转 0.8 秒、右转 0.8 秒、停止 1 秒。如果发现动作顺序不对,只需要调整 IN1/IN2 或者 IN3/IN4 的接线位置就能解决,不用改代码。
如果电机完全没有反应,优先排查 L298N 上的电源指示灯是否亮起,以及 ESP32 的 GND 是否和 L298N 的 GND 接在一起。共地问题几乎占据电机控制故障的一半。
6. 传感器感知与自动避障
6.1 超声波测距原理
HC-SR04 的工作原理很简单:向 TRIG 引脚发送一个 10 微秒以上的高电平脉冲,模块就会发射超声波;当声波碰到障碍物返回时,ECHO 引脚输出高电平。测出这段高电平的时间,再乘以声速 340m/s 并除以 2,就是到障碍物的距离。
它在代码里的实现如下:
// 文件路径:ultrasonic_test/ultrasonic_test.ino #define TRIG_PIN 16 #define ECHO_PIN 17 void setup() { Serial.begin(115200); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); } void loop() { long duration; float distanceCm; digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); duration = pulseIn(ECHO_PIN, HIGH); distanceCm = duration * 0.034 / 2; Serial.print("Distance: "); Serial.println(distanceCm); delay(100); }如果串口显示距离一直在跳,排除环境噪声后,大概率是供电不稳。HC-SR04 在 5V 供电下更稳定,但它的 ECHO 引脚会返回 5V 电平,直接接 ESP32 的 GPIO 有电压不匹配风险,稳妥做法是加一个分压电路,或用 3.3V 供电的传感器模块。
6.2 添加避障逻辑
把测距和电机控制合在一起,就能写出最简单的“碰不到墙”的机器人:
// 文件路径:auto_avoid/auto_avoid.ino #define TRIG_PIN 16 #define ECHO_PIN 17 #define MOTOR_A1 26 #define MOTOR_A2 27 #define MOTOR_B1 14 #define MOTOR_B2 12 #define SAFE_DISTANCE 20.0 void setup() { Serial.begin(115200); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); pinMode(MOTOR_A1, OUTPUT); pinMode(MOTOR_A2, OUTPUT); pinMode(MOTOR_B1, OUTPUT); pinMode(MOTOR_B2, OUTPUT); } void loop() { float dist = readDistance(); Serial.print("Distance: "); Serial.println(dist); if (dist < SAFE_DISTANCE) { stopMotor(); delay(200); turnLeft(); delay(500); } else { runForward(); } } float readDistance() { digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); long duration = pulseIn(ECHO_PIN, HIGH); if (duration == 0) return 999; return duration * 0.034 / 2; } void runForward() { digitalWrite(MOTOR_A1, HIGH); digitalWrite(MOTOR_A2, LOW); digitalWrite(MOTOR_B1, HIGH); digitalWrite(MOTOR_B2, LOW); } void turnLeft() { digitalWrite(MOTOR_A1, LOW); digitalWrite(MOTOR_A2, HIGH); digitalWrite(MOTOR_B1, HIGH); digitalWrite(MOTOR_B2, LOW); } void stopMotor() { digitalWrite(MOTOR_A1, LOW); digitalWrite(MOTOR_A2, LOW); digitalWrite(MOTOR_B1, LOW); digitalWrite(MOTOR_B2, LOW); }这段代码已经能让你看到一个“会自己躲墙的小车”。如果你希望它更智能一点,可以把turnLeft()改成随机选择左转或右转,避免遇到墙角时卡死。
7. 接入 WiFi 和网页远程控制
7.1 为什么 ESP32 天然适合做远程控制
ESP32 自带 WiFi 是它比多数单片机更顺手的地方。你不需要额外购买 ESP8266 模块,也不用写复杂的 AT 指令。直接在 Arduino 代码里引入 WiFi.h,就能摇身一变成为一个 Web 服务器。
这里要澄清一个常见误解:很多人以为 ESP32 “开 WiFi 会占很多资源”,其实在简单的控制场景里,ESP32 的实时响应影响可以忽略。更需要注意的是你的网络环境是否稳定,以及机器人是否处于路由器信号覆盖范围内。
7.2 最小 Web 控制服务器
下面这个代码实现了一个网页控制台:手机连上同一个 WiFi 后,打开浏览器访问机器人 IP,点击方向按钮即可控制电机。同时它还提供了网页上的测距数据显示。
// 文件路径:web_control/web_control.ino #include <WiFi.h> const char* ssid = "YOUR_WIFI_SSID"; const char* password = "YOUR_WIFI_PASSWORD"; #define TRIG_PIN 16 #define ECHO_PIN 17 #define MOTOR_A1 26 #define MOTOR_A2 27 #define MOTOR_B1 14 #define MOTOR_B2 12 WiFiServer server(80); String html = "<!DOCTYPE html><html><head>" "<meta charset='utf-8'>" "<meta name='viewport' content='width=device-width, initial-scale=1'>" "<title>ESP32 Robot Control</title>" "<style>" "body { font-family: Arial; text-align: center; margin-top: 40px; }" ".btn { display: inline-block; width: 160px; height: 60px; margin: 10px; " "font-size: 24px; background: #2196F3; color: white; border: none; " "border-radius: 12px; }" ".btn:hover { background: #0b7dda; }" "</style></head><body>" "<h2>ESP32 AI Robot</h2>" "<a href='/forward'><button class='btn'>前进</button></a>" "<br>" "<a href='/left'><button class='btn'>左转</button></a>" "<a href='/stop'><button class='btn'>停止</button></a>" "<a href='/right'><button class='btn'>右转</button></a>" "<br>" "<a href='/backward'><button class='btn'>后退</button></a>" "<p><button class='btn' onclick=\"location.href='/distance'\">测量距离</button></p>" "</body></html>"; void setup() { Serial.begin(115200); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); pinMode(MOTOR_A1, OUTPUT); pinMode(MOTOR_A2, OUTPUT); pinMode(MOTOR_B1, OUTPUT); pinMode(MOTOR_B2, OUTPUT); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.print("WiFi connected, IP: "); Serial.println(WiFi.localIP()); server.begin(); } void loop() { WiFiClient client = server.available(); if (!client) return; String request = client.readStringUntil('\r'); Serial.println(request); if (request.indexOf("/forward") >= 0) runMotor(HIGH, LOW, HIGH, LOW); else if (request.indexOf("/backward") >= 0) runMotor(LOW, HIGH, LOW, HIGH); else if (request.indexOf("/left") >= 0) runMotor(LOW, HIGH, HIGH, LOW); else if (request.indexOf("/right") >= 0) runMotor(HIGH, LOW, LOW, HIGH); else if (request.indexOf("/stop") >= 0) runMotor(LOW, LOW, LOW, LOW); else if (request.indexOf("/distance") >= 0) { float dist = readDistance(); client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/plain"); client.println(); client.println("Distance: " + String(dist) + " cm"); client.stop(); delay(1); return; } client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); client.println(html); client.stop(); delay(1); } void runMotor(int a1, int a2, int b1, int b2) { digitalWrite(MOTOR_A1, a1); digitalWrite(MOTOR_A2, a2); digitalWrite(MOTOR_B1, b1); digitalWrite(MOTOR_B2, b2); } float readDistance() { digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); long duration = pulseIn(ECHO_PIN, HIGH); if (duration == 0) return 999; return duration * 0.034 / 2; }把YOUR_WIFI_SSID和YOUR_WIFI_PASSWORD替换成你自己的 WiFi 信息,烧录后查看串口打印的 IP 地址,在手机浏览器输入这个 IP,就能看到控制界面。
7.3 大型机器人或者多设备场景怎么处理
如果你的项目是搜索热词里提到的“机器人导航”“ROS2 机器人开发”,那就要面对“多设备协同”的问题。ESP32 不适合作为复杂导航的主节点,但它非常适合作为下位机,把车速和传感器数据通过 WiFi/串口发给上位机。上位机可以是 PC、Jetson Nano,也可以是另一块更强大的开发板。
这时候把 HTTP 换成更高效的 WebSocket 或 MQTT 是更好的选择。HTTP 请求每次都要建连和断连,控制频率高了会产生明显的延迟;MQTT 或 WebSocket 则能保持长连接,控制指令延迟能降到几十毫秒以内。
8. 用 OLED 和语音模块做 AI 交互
8.1 添加表情屏幕
机器人要有“小伙伴”的感觉,单单会跑是不够的。给它加一块 0.96 寸 OLED,用 U8g2 或 Adafruit SSD1306 库画一双眼睛,就能实现简单的情感化反馈。
在 Arduino 库管理器中搜索SSD1306并安装 Adafruit SSD1306 和 Adafruit GFX,然后看下面的示例:
// 文件路径:oled_face/oled_face.ino #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_ADDR 0x3C Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); void setup() { Wire.begin(21, 22); display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR); display.clearDisplay(); } void loop() { // 画一对“眼睛” display.clearDisplay(); drawEyes(); delay(2000); // 显示一个“开心”表情 display.clearDisplay(); drawHappyFace(); delay(2000); } void drawEyes() { display.fillCircle(40, 28, 8, SSD1306_WHITE); display.fillCircle(88, 28, 8, SSD1306_WHITE); display.display(); } void drawHappyFace() { display.drawCircle(64, 32, 20, SSD1306_WHITE); display.fillCircle(56, 28, 3, SSD1306_WHITE); display.fillCircle(72, 28, 3, SSD1306_WHITE); display.drawArc(56, 38, 16, 200, 340, SSD1306_WHITE); display.display(); }注意drawArc在 Adafruit GFX 的某些版本里不可用,如果你编译报错,可以换用display.drawLine手动画一条弧线。画表情不是重点,重点是你开始通过外设表达机器人“状态”,这就是交互体验的基础。
8.2 接入基础语音交互
ESP32 直接跑本地大模型不现实,但可以接离线语音识别模组,或者通过 WiFi 请求云端语音接口。这里给一个最简单的思路:使用 SU-03T 这类离线语音识别模块,通过串口把识别结果发给 ESP32,ESP32 根据结果执行动作。
接线方式:
SU-03T TX → ESP32 GPIO 3 (RX0) SU-03T RX → ESP32 GPIO 1 (TX0) SU-03T GND → ESP32 GND SU-03T VCC → ESP32 5V(根据模块要求)在 Arduino 里用Serial2接收串口数据,识别到“前进”“后退”“停止”等关键词后执行对应电机动作。这种方式没有网络延迟,也不依赖外部服务,适合离线演示。
真正做 AI 对话交互时,你更可能采用“ESP32 + 手机 App + 云端 API”的架构:ESP32 通过蓝牙或 WiFi 把语音上传到手机,手机调用大模型接口,再把返回的文本通过 TTS 播放出来。ESP32 在这里充当的是机器人底层执行器和语音前端。
8.3 LVGL 屏幕界面
如果你使用的是带屏幕的 ESP32 开发板,比如 ESP32-S3 配 ST7789 屏幕,可以在板上运行 LVGL。LVGL 是一个开源嵌入式图形库,能设计按钮、动画、仪表盘。搜索热词里出现的esp32 lvgl,是很多开发者在做智能面板项目时的主要方向。
用 LVGL 做机器人控制界面的好处是:它不需要依赖浏览器,直接在机器人本地面板上显示控制按钮和状态。缺点是开发量比 OLED 表情大不少,适合作为进阶挑战。
9. 进阶:用 Micro-ROS 把 ESP32 接入 ROS 2
9.1 Micro-ROS 是什么
如果你看到过esp32 micro-ros arduino这组关键词,说明你可能已经开始接触机器人操作系统 ROS 2。ROS 2 是机器人领域的主流软件框架,但它默认运行在 Linux 系统上,对单片机并不友好。Micro-ROS 的作用就是让内存受限的 MCU 也能成为 ROS 2 网络里的一个节点。
用大白话说:ROS 2 是一套机器人的“操作系统级通信框架”,Micro-ROS 是它的瘦身版,ESP32 通过 WiFi 或串口就能和跑着 ROS 2 的上位机通信。
9.2 ESP32 在 ROS 2 里的定位
在真实机器人项目中,ESP32 最适合做执行器节点和传感器节点,比如:
- 发布超声波测距话题。
- 发布 IMU 数据话题。
- 订阅
/cmd_vel话题,把速度指令转成电机 PWM。
这样上位机只需要负责 SLAM 建图、路径规划、AI 视觉,而 ESP32 负责实时响应和底层控制。
9.3 接入 Micro-ROS 的大致流程
第一步,在 Arduino IDE 中安装micro_ros_arduino库。可以通过 Library Manager 搜索micro_ros_arduino或从 GitHub 克隆到 Arduino/libraries 目录。
第二步,根据网络方式选择传输层。WiFi 场景下,需要先连接到热点,然后运行 Micro-ROS Agent。串口场景下则用 USB 转 TTL 连接上位机。
第三步,用MicroRosPublisher发布话题。下面是一个最小示例结构:
// 文件路径:micro_ros_demo/micro_ros_demo.ino(示意) #include <micro_ros_arduino.h> #include <stdio.h> #include <rcl/rcl.h> #include <rcl/error_handling.h> #include <std_msgs/msg/int32.h> rcl_publisher_t publisher; rcl_publisher_t publisher; std_msgs__msg__Int32 msg; void setup() { set_microros_wifi_transports("YOUR_WIFI_SSID", "YOUR_WIFI_PASSWORD", "192.168.1.100", 8888); // 其余初始化代码略 } void loop() { msg.data = analogRead(34); rcl_publish(&publisher, &msg, NULL); delay(100); }注意,这段代码只是示意,完整运行还需要在setup里创建节点、初始化 publisher,并且处理rclc_executor的 spin。不同版本的 micro_ros_arduino 库 API 有差异,编译报错时先从官方例程micro_ros_arduino_publisher开始修改。
9.4 ROS 2 上位机配置
在上位机上启动 Micro-ROS Agent:
source /opt/ros/humble/setup.bash ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0或者使用 WiFi 传输:
ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888启动后,在另一个终端执行ros2 topic list,如果能看到 ESP32 发布的话题名,说明两者已经打通。这一步对于后续做“机器人导航”至关重要,因为你可以让机器人跑在 ROS 2 的导航栈里。
10. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 烧录失败,串口无法连接 | 板子进入不了下载模式 | 按住 BOOT 键再点烧录 | 手动进入下载模式,或换 USB 数据线 |
烧录时报错A fatal error occurred | 驱动未装 / 端口错 / 线材问题 | 检查设备管理器,确认 COM 口 | 安装 CP210x 或 CH340 驱动 |
| 电机不转 | L298N 供电不足或未共地 | 检查电源指示灯、GND 连线 | 外部供电,接好共地 |
| 电机转向反了 | 电机线接反或代码方向不对 | 观察电机转向 | 调换 OUT 接线或改代码 |
| 超声波距离数据乱跳 | 供电不稳 / ECHO 电平不匹配 | 万用表测电压,串口打印通电后数据 | 独立供电或加分压电路 |
| OLED 无显示 | I2C 地址不对或 SDA/SCL 接反 | 运行 I2C 扫描程序 | 尝试 0x3C 和 0x3D 地址 |
| Web 控制页打不开 | WiFi 名密码错误 / 不在同一网段 | 串口打印 IP,手机 ping 该 IP | 重新配网,确保手机和开发板同一路由器 |
| Micro-ROS 连接失败 | Agent 未启动 / 端口被占用 | 查看 Agent 日志 | 关闭防火墙或换端口 |
| 外接电源后 ESP32 重启 | 电机电流过大导致电压跌落 | 测电机启动时电压 | 电机驱动单独供电,或加电容 |
除了这些,还有一个容易忽略的问题:GPIO 引脚冲突。很多 ESP32 开发板把 LED 接在 GPIO 2 上,如果你把电机引脚也用到 GPIO 2,跑起来可能看到 LED 异常闪烁。建议在项目一开始就维护一张引脚分配表,写完代码后逐项核对。
11. 最佳实践与工程建议
11.1 先分模块再拼接
我见过太多把代码一口气写完,最后烧录全部失败的人。正确做法是:
- 先点亮 LED,确认环境可用。
- 再跑电机测试,确认方向和 PWM 正常。
- 再测传感器,确认距离值准确。
- 再连 OLED,确认显示正常。
- 最后把模块拼到一起,加 WiFi 控制。
每拼一个模块就验证一次,能大幅降低排错成本。
11.2 供电设计是机器人的生命线
桌面机器人的故障,一半以上来自供电。建议:
- 电机驱动板和主控分开供电,或者使用大电流降压模块。
- 电机启动时瞬间电流可能达到 1-2A,电压会明显跌落。
- 如果使用 18650 电池,加一个 5V/3A 降压模块。
- 万用表测量各模块实际电压,而不是只看理论值。
11.3 代码组织和版本管理
即使是一个人做项目,也建议用 Git 管理代码。可以把每个功能写成单独的.h文件或tab文件,例如motor.h、ultrasonic.h、wifi_control.h,最后在.ino文件里组合调用。这样后续扩展麦克风、摄像头、屏幕时,不需要重写底层逻辑。
11.4 日志与调试习惯
在关键位置添加Serial.println打印状态,远比断点调试方便。尤其是 WiFi 连接、传感器读取、电机动作切换这几个节点,合理的日志输出能让你在 5 分钟内定位问题。
上生产环境或长时间运行时,可以把日志合并成一个状态字符串,通过 WebSocket 发给控制端显示,这样就能实时看到机器人“心路历程”。
11.5 安全边界与合法使用提醒
如果项目涉及摄像头、语音采集、云端 API,提醒自己遵守设备所属地区和所在机构的隐私与安全规定。不要采集未经授权的个人信息,不要绕过任何平台的安全机制。代码中默认只处理授权范围内的受控设备。
12. 总结与后续学习方向
到这里,你已经把一颗可能还在吃灰的 ESP32 开发板,变成了一台能跑、能躲墙、能被手机控制、能显示表情的基础机器人小伙伴。
回顾一下,这篇文章真正有用的是三块内容:一是 ESP32 在机器人架构中的定位,它适合做主控而非复杂计算节点;二是从电机、传感器到 WiFi 控制的完整代码链路,这些代码可以组合出非常多的玩法;三是 Micro-ROS 的接入思路,让你以后从“玩具”走向“真机器人”时有明确路径。
下一步建议你按这个顺序继续深入:
- 先跑通本文的 Web 控制页,让机器人在手机控制下自由移动。
- 给机器人添加一个舵机云台,挂上摄像头,做简单的 WiFi 图传。
- 接入 Micro-ROS 和上位机,用 ROS 2 的 Nav2 做 SLAM 导航。
- 如果对屏幕交互感兴趣,学习 LVGL,在机器人上做一个完整的控制面板。
- 如果对 AI 交互感兴趣,尝试把离线语音识别模块和云端大模型接进来。
说到底,机器人开发的乐趣不在于一步到位,而在于每次“换一个新模块,跑通一个新功能”的成就感。现在就从你最感兴趣的模块开始动手吧,你的第一个 AI 机器人小伙伴已经在桌面上等你了。