
1. 为什么我建议你从 PlatformIO 换到 Arduino IDE1.1 卡在 downloading 0% 不是你的错我印象很深第一次用 VSCode 里的 PlatformIO 创建 ESP32 工程那个创建进度条尬在configuring project: downloading 0%整整一个下午。网上搜出来的解决方案无非是清缓存、换源、重装工具链但很多人卡住的核心原因其实是 PlatformIO 拉取平台包和工具链时走的不是让你能顺畅访问的线路。平台要到国外服务器下载 espressif32 的全套工具链网络稍有抖动整个创建过程就废了。这个问题的关键在于 PlatformIO 的原理它在platformio.ini里声明framework arduino或framework espidf后并不会直接使用你电脑里已有的 Arduino 环境而是重新下载一份独立的编译工具链。ESP32 的平台包动辄几百 MB第一次创建工程慢是常态。如果你的网络再弱一点看到的就只剩那个“0%”发呆。所以我并不是否定 PlatformIO 本身。它对大型多库工程的管理能力非常强lib_deps一行就能搞定依赖。但如果你只是想跑 micro-ROS它带来的学习成本远大于它节省的成本。1.2 Arduino IDE 做 micro-ROS 完全可行micro-ROS 官方仓库里确实维护了micro_ros_arduino这个库它不仅支持 ESP32还支持树莓派 Pico、STM32 等常见 MCU而且示例代码很完整。Arduino IDE 只要装好对应板卡和这个库编译、上传、串口打印一条龙。有人会担心 Arduino IDE 的项目管理太“玩具化”一个.ino文件写到底。但 micro-ROS 在 MCU 端的代码量通常不大无非是初始化节点、发布话题、订阅话题、跑定时器把这些放在一个文件里完全没问题。真到需要拆分的阶段Arduino IDE 也能通过*.h、*.cpp拆文件只是没有 PlatformIO 那么顺手而已。1.3 这条路线适合谁如果你满足下面任意一条我建议你换 Arduino IDE 来搞对 PlatformIO 的下载机制不熟悉而且网络环境不太好工程创建总是失败之前写过 Arduino不想再学一套 VSCode 插件生态只是想快速验证 micro-ROS 的串口传输和话题收发不想折腾工具链做项目时追求“上传就能跑”少一些环境玄学。反过来如果你的项目必须深度依赖自定义 ROS 接口或者要同时管理十几个源码文件那 PlatformIO 的优势才值得你去克服下载和配置的问题。2. 搭环境板卡、库和开发板选择2.1 Arduino IDE 2.x 添加 ESP32 板卡源打开 Arduino IDE 2.x先到“文件 首选项”在“附加开发板管理器网址”里填入https://espressif.github.io/arduino-esp32/package_esp32_index.json然后进入“开发板管理器”搜索 ESP32选择最新稳定版本安装。如果你用的是 ESP32-S3、C3 这类新芯片尽量装 arduino-esp32 官方支持的新版板卡包。旧版本板卡包对 S3 的 USB-Serial/JTAG 支持不完整会出现编译成功但串口完全没输出的问题排查起来很头疼。提醒URL 里的package_esp32_index.json不要漏掉package_前缀。加错之后开发板管理器怎么刷新都搜不到 ESP32。2.2 安装 micro-ROS 到 Arduino IDE在“库管理器”里搜索micro_ros_arduino选官方库安装。注意顺序先装板卡包再装库。如果顺序反了库管理器可能会因为缺少目标板卡的宏定义导致后续编译时找不到rcl系列头文件。库安装完成后示例菜单里会出现micro_ros_arduino里面提供了micro-ROS_publisher、micro-ROS_subscriber等现成模板。我后面写的代码本质上就是在这套模板上做减法把不需要的东西删掉把核心链路保留下来。2.3 选开发板型号和分区方案在 Arduino IDE 顶部的开发板选择里普通 ESP32 选ESP32 Dev Module对应平台标识为esp32:esp32:esp32ESP32-S3 则选择ESP32S3 Dev Module对应esp32:esp32:esp32s3这个 FQBN 用于告诉编译器使用哪一套引脚映射和链接脚本。很多人的板子是 ESP32-S3但选了普通 ESP32导致编译出来能用却无法正常启动。建议看一下板子背面丝印或原理图确认芯片型号再选。分区方案这一步我会优先选Huge APP (3MB No OTA / 1MB SPIFFS)因为 micro-ROS 生成的固件体积偏大默认分区虽然大多数情况够用但留出 3MB 应用空间会更踏实。Flash 大小和分区方案要对应别选 4MB 的板子却配了 8MB 的分区。3. 动手写第一个 micro-ROS 发布订阅程序3.1 你要先理解 micro-ROS 是怎么工作的micro-ROS 把 ROS 2 的客户端接口尽可能压缩到 MCU 端运行。ESP32 通过串口或无线网络选择一个 micro-ROS Agent 作为中间层Agent 再与 PC 上的 ROS 2 通信这样 PC 端看起来就像多了一个嵌入式 ROS 2 节点。这意味着你调试时有两端一端是电脑上的 Agent一端是 ESP32 的固件。两端必须匹配包括 ROS 2 发行版这里用 Humble、传输方式串口或 UDP、以及波特率或端口号。任何一端不对话题列表里就看不到设备。3.2 发布者的最小可运行代码新建 Arduino 工程把下面代码复制进去。我保持了最精简结构#include micro_ros_arduino.h #include stdio.h #include rcl/rcl.h #include rcl/error_handling.h #include rclc/rclc.h #include rclc/executor.h #include std_msgs/msg/int32.h rcl_publisher_t publisher; rclc_executor_t executor; rclc_support_t support; rcl_allocator_t allocator; rcl_node_t node; rcl_timer_t timer; std_msgs__msg__Int32 msg; #define RCCHECK(fn) { rcl_ret_t temp_rc fn; if((temp_rc ! RCL_RET_OK)){ return false; } } void timer_callback(rcl_timer_t * timer, int64_t last_call_time) { msg.data; rcl_publish(publisher, msg, NULL); Serial.printf(publish: %ld\n, msg.data); } bool create_node() { allocator rcl_get_default_allocator(); RCCHECK(rclc_support_init(support, 0, NULL, allocator)); RCCHECK(rclc_node_init_default(node, esp32_node, , support)); RCCHECK(rclc_publisher_init_default( publisher, node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), esp32_data)); RCCHECK(rclc_timer_init_default( timer, support, RCL_MS_TO_NS(1000), timer_callback)); executor rclc_executor_get_zero_initialized_executor(); RCCHECK(rclc_executor_init(executor, support.context, 1, allocator)); RCCHECK(rclc_executor_add_timer(executor, timer)); return true; } void setup() { Serial.begin(115200); set_microros_serial_transports(Serial); delay(2000); msg.data 0; create_node(); } void loop() { rclc_executor_spin_some(executor, RCL_MS_TO_NS(10)); }这段代码要做的事情很清楚set_microros_serial_transports(Serial)初始化串口传输create_node()里创建节点、发布者、定时器和执行器timer_callback每 1 秒把msg.data加一发布到esp32_data话题loop()不断调用rclc_executor_spin_some处理 ROS 2 回调。其中delay(2000)起到一个小作用板子启动后等待片刻让串口芯片稳定也让 Agent 有时间准备好接收。如果你上电后立刻连接失败可以把这个延时再拉长到 3000ms 试试。3.3 订阅者让 ESP32 接收 PC 端指令我再给出一个订阅示例用于接收上位机发来的简单控制指令。这里以整数指令为例#include std_msgs/msg/int32.h rcl_subscription_t subscriber; void subscription_callback(const void * msg_in) { const std_msgs__msg__Int32 * msg (const std_msgs__msg__Int32 *)msg_in; Serial.printf(recv: %ld\n, msg-data); } bool create_subscriber() { RCCHECK(rclc_subscription_init_default( subscriber, node, ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, Int32), cmd_echo)); RCCHECK(rclc_executor_add_subscription(executor, subscriber, msg, subscription_callback, ON_NEW_DATA)); return true; }注意创建订阅者后一定要把它添加到执行器里而且回调触发模式用ON_NEW_DATA。如果忘了添加订阅者到执行器话题数据其实已经收到但回调函数永远不会被调用这个问题非常隐蔽我刚开始排查了一晚上才找到原因。3.4 编译烧录的现场细节编译前确认开发板型号、分区方案和 Flash 大小都选对。点击上传后如果提示无法连接最常见的是 USB 驱动问题或者板子没进入下载模式。CH340 和 CP2102 是最常见的两个 USB 转串口芯片装好对应驱动后再在设备管理器里确认端口。上传时我会按住板上的BOOT键不放直到 IDE 输出显示Connecting....再松开。如果你用的是开发板自带的自动下载电路通常不用手动按但如果经常出现刚点上传就报A fatal error occurred: Failed to connect to ESP32手动按住BOOT是最直接的解决办法。上传成功后打开串口监视器波特率设成 115200就能看到每秒一条publish: N。到这里MCU 侧的最小闭环已经跑通。4. 把 Agent 拉起来让数据出现在 PC 上4.1 Docker 版 Agent先单独拉镜像启动 micro-ROS Agent 最省事的方式是用 Docker。对应 Humble 版镜像名是microros/micro-ros-agent:humble如果在执行启动命令时看到Unable to find image microros/micro-ros-agent:humble locally这通常只是 Docker 在告诉你本地没有这个镜像接下来会自动拉取。问题在于一些网络环境下自动拉取容易超时。这时候建议先把镜像显式拉下来docker pull microros/micro-ros-agent:humble镜像拉取成功后再启动串口模式的 Agentdocker run -it --rm --nethost -v /dev:/dev --privileged microros/micro-ros-agent:humble serial --dev /dev/ttyUSB0 -b 115200解释一下参数--nethost让容器共享宿主网络UDP 模式时会用到-v /dev:/dev --privileged把宿主机的设备节点映射进容器让 Agent 能打开/dev/ttyUSB0serial启动串口传输--dev /dev/ttyUSB0指定 ESP32 对应的串口设备。Windows 上可能是COM3不过 Docker 在 Windows 下对串口透传支持不友好更推荐用 WSL 2 或直接在 Linux 上跑-b 115200波特率和 Arduino 代码里的Serial.begin(115200)保持一致。如果你在 Linux 下运行第一次用 Docker 可能还要加sudo。如果系统提示没有权限访问/dev/ttyUSB0可以把当前用户加入dialout用户组或者临时用sudo docker run验证。4.2 非 Docker 方式用已经装好的 ROS 2 Humble如果你电脑上已经有 ROS 2 Humble也装过 micro-ROS Agent 相关的包可以直接这样启动source /opt/ros/humble/setup.bash ros2 run micro_ros_agent micro_ros_agent serial --dev /dev/ttyUSB0 -b 115200这种方式的好处是排查问题比较直观Agent 的日志会直接打印在终端里。即便 Agent 启动期间没有立刻看到 ESP32 连接也不要慌等 ESP32 重新上电或者重启程序Agent 会自动建立连接。4.3 验证数据链路Agent 启动后再开一个终端source /opt/ros/humble/setup.bash ros2 topic list应该能看到esp32_data然后ros2 topic echo /esp32_data正常情况下每秒钟会刷出一条data: 1、data: 2…… 和 ESP32 串口监视器同步增长。如果ros2 topic list里看不到话题或者 echo 没有输出从这几个方向排查Agent 是否正常打开串口有没有出现serial port errorESP32 的波特率和 Agent 的-b参数是否一致两边是不是同一个 ROS 2 发行版HumbleAgent 容器是否真的使用--nethost串口模式下也要确保设备权限足够。4.4 WiFi 透传让 ESP32 不用插线如果同一个局域网内我想让 ESP32 无线接入 ROS 2可以切换到 UDP 模式。ESP32 端先用 WiFi 连接路由器再调用#include WiFi.h const char * ssid your_wifi; const char * password your_password; IPAddress agent_ip(192, 168, 1, 100); uint16_t agent_port 8888; void setup() { WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); } set_microros_transports(agent_ip, agent_port); // 其他初始化代码 }Agent 端改成 UDP 监听docker run -it --rm --nethost --privileged microros/micro-ros-agent:humble udp4 --port 8888或者ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888无线方式的好处是省去串口线适合远程调试和移动机器人。但要注意如果路由器开启 AP 隔离或者电脑和 ESP32 不在同一网段UDP 会连不上甚至出现“时断时续”的现象。建议先用有线验证一次再切无线。5. 踩坑经验编译、烧录和稳定运行5.1 编译报错问题定位报错特征大概率原因处理方式找不到rcl/rcl.hmicro-ROS 库未正确安装卸载重装库重启 Arduino IDElwip/...或esp_...头文件缺失ESP32 板卡包版本过旧升级 arduino-esp32 到最新版编译时内存不足分区方案太小选 Huge APP 分区或换更大 Flash 板子上传失败Failed to connect to ESP32板子没进入下载模式或驱动问题按 BOOT 键重新插拔 USB检查驱动上传成功但串口无输出芯片型号选错或波特率不对确认 FQBN确认串口监视器波特率我特别想强调一下 FQBN 选错的问题。一些 ESP32-S3 开发板外观很像普通 ESP32 Dev Module如果只按外观选型编译可能成功但上电后程序根本不跑。这是因为两者启动流程、RAM 地址、外设映射不同。所以拿到开发板后先看丝印确认具体芯片型号这是十分钟的事却能省掉几小时排查时间。5.2 OTA 升级的注意点OTA 升级在 micro-ROS 场景里也很常见毕竟很多设备部署后不方便接串口。做 OTA 前分区方案必须改成带 OTA 的类型比如8MB with spiffs或OTA_minimal。如果分区不对上传固件时会出现Invalid app image或者能上传成功但重启后跑不起来。开发阶段我建议别开 OTA直接用 USB 烧录原因是 OTA 上传过程如果中途断电、网络断开固件写入一半板子可能变砖需要重新烧 bootloader 才能恢复。即便要测 OTA我也习惯先备份一套可用的 USB 烧录固件。关于烧录地址Arduino IDE 会自动根据分区表填写不需要手动指定。只有你用命令行的esptool.py手写烧录时才需要查一下分区表里的app0起始地址把应用固件烧到对应偏移位置。比如 ESP32 默认分区表里app0通常从0x10000开始但这只是一个常见默认值实际以分区表为准。5.3 让 ESP32 端运行更可靠micro-ROS 在串口模式下最怕串口缓冲溢出和供电不稳。如果 ESP32 与电机驱动共用一个电源电机启动瞬间会造成电压跌落轻则重启重则 Agent 侧报连接错误。我的做法是给 ESP32 单独接一路稳压电源或者至少在电源入口加一个大容量去耦电容。另外rclc_executor_spin_some的超时参数不建议设得太大。我用 10ms既能保证话题回调及时处理又不会让 CPU 占用率爆满。如果你同时订阅了很多话题执行器初始化的第 3 个参数handles 数量要对应增大否则运行一会儿会出现订阅回调不触发或者干脆崩溃。还有一个容易忽略的点Arduino IDE 的串口监视器一旦打开就会占用 COM 口。如果此时 Agent 也要打开同一个串口串口监视器会报打不开或者 Agent 报设备被占用。调试时先关掉串口监视器让 Agent 独占串口或者在 ESP32 上单独分配一个调试打印串口。5.4 什么时候我更推荐用 PlatformIO把 Arduino IDE 路线讲完之后我也补充一下边界。如果遇到这些情况我可能会切回 PlatformIO项目里需要大量自定义 ROS 消息类型需要自动生成头文件并统一编译工程文件很多需要清晰的src/、include/、lib/结构代码要同时在 ESP32 和本机做单元测试PlatformIO 的test框架更方便深度依赖 ESP-IDF 原生成分时PlatformIO 对 IDF 工具链的管理更直接。除去这些日常的传感器采集、执行器控制、多话题收发Arduino IDE 完全够用而且安装门槛低下载失败概率小是最适合先跑通流程的路线。6. 最后的实操心得我在实际调 micro-ROS 的过程中最大的体会是不要一上来就追求“一步到位”。先用 USB 串口 最简单的 Int32 话题跑通再逐步加 WiFi、加自定义消息、加多个话题。每增加一个变量就单独验证一次。这样即使出问题也知道该去查哪一段。如果你在 Arduino IDE 里已经能稳定打印publish: N又在 Agent 端能看到data不断递增那整个 micro-ROS 2.0.5Humble 版的核心链路就已经握在手里了。剩下的传感器接入、控制指令下发、OTA 升级都可以在这个最小闭环上慢慢长出来。最后再分享一个小技巧我会把“串口 Int32 Agent echo”这套最小验证模板存成一个独立工程每次换新板子、换新环境先烧这个模板确认链路正常再往里面加业务代码。这个习惯帮我省了很多排查时间也让我在从 PlatformIO 切换到 Arduino IDE 之后再也没有为环境问题焦虑过。