1. 环境搭建与Hello World

任务目标

在ESP32开发板上搭建micro-ROS环境,实现一个发布节点,周期性地向ROS 2主机发送包含"Hello World"字符串的自定义消息,并在主机端通过ros2 topic echo验证接收。

环境与依赖

  • 硬件:ESP32开发板(如ESP32-DevKitC)、USB转串口线
  • 主机:Ubuntu 22.04,已安装ROS 2 Humble
  • 固件框架:ESP-IDF v5.1(需预先安装)
  • micro-ROS组件:micro_ros_espidf_component(v2.0.0及以上)

实现步骤

在ESP-IDF工程中通过组件管理器添加micro-ROS依赖,编写发布节点代码,配置Wi-Fi连接与agent地址,编译烧录后运行。

关键完整代码

// main.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "micro_ros_espidf_component.h"

static const char *TAG = "micro_ros_node";

// 自定义消息类型(与主机端一致)
#include "std_msgs/msg/string.h"

// Wi-Fi配置
#define WIFI_SSID "YOUR_SSID"
#define WIFI_PASS "YOUR_PASSWORD"

// micro-ROS agent IP与端口
#define ROS_AGENT_IP "192.168.1.100"
#define ROS_AGENT_PORT 8888

static void wifi_init(void) {
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASS,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());
    ESP_ERROR_CHECK(esp_wifi_connect());
}

static void micro_ros_task(void *arg) {
    // 初始化micro-ROS
    micro_ros_espidf_component_init();

    // 创建发布者
    rcl_publisher_t publisher = rcl_get_zero_initialized_publisher();
    std_msgs__msg__String msg;
    std_msgs__msg__String__init(&msg);

    const char *node_name = "esp32_hello_publisher";
    const char *topic_name = "hello_world_topic";

    // 创建节点
    rcl_node_t node = rcl_get_zero_initialized_node();
    rcl_allocator_t allocator = rcl_get_default_allocator();
    rclc_support_t support;
    rcl_ret_t rc = rclc_support_init(&support, 0, NULL, allocator);
    if (rc != RCL_RET_OK) {
        ESP_LOGE(TAG, "rclc_support_init failed: %d", rc);
        vTaskDelete(NULL);
    }

    rc = rclc_node_init_default(&node, node_name, "", &support);
    if (rc != RCL_RET_OK) {
        ESP_LOGE(TAG, "rclc_node_init_default failed: %d", rc);
        vTaskDelete(NULL);
    }

    rc = rclc_publisher_init_default(
        &publisher,
        &node,
        ROSIDL_GET_MSG_TYPE_SUPPORT(std_msgs, msg, String),
        topic_name
    );
    if (rc != RCL_RET_OK) {
        ESP_LOGE(TAG, "rclc_publisher_init_default failed: %d", rc);
        vTaskDelete(NULL);
    }

    // 周期发布
    char hello_msg[50];
    int count = 0;
    while (1) {
        snprintf(hello_msg, sizeof(hello_msg), "Hello World from ESP32 #%d", count++);
        msg.data.data = hello_msg;
        msg.data.size = strlen(hello_msg);
        msg.data.capacity = sizeof(hello_msg);

        rc = rcl_publish(&publisher, &msg, NULL);
        if (rc == RCL_RET_OK) {
            ESP_LOGI(TAG, "Published: %s", hello_msg);
        } else {
            ESP_LOGE(TAG, "Publish failed: %d", rc);
        }

        vTaskDelay(pdMS_TO_TICKS(1000)); // 1Hz
    }

    // 清理(实际不会到达)
    rcl_publisher_fini(&publisher, &node);
    rcl_node_fini(&node);
    vTaskDelete(NULL);
}

void app_main(void) {
    wifi_init();
    xTaskCreate(micro_ros_task, "micro_ros_task", 8192, NULL, 5, NULL);
}

工程配置

在idf_component.yml中添加依赖:

dependencies:
  micro_ros_espidf_component:
    version: "*"
    git: https://github.com/micro-ROS/micro_ros_espidf_component.git

运行与验证

  1. 在主机启动micro-ROS Agent:ros2 run micro_ros_agent micro_ros_agent udp4 --port 8888
  2. 编译烧录:idf.py build flash monitor
  3. 在主机新终端执行:ros2 topic echo /hello_world_topic std_msgs/msg/String

通过条件:主机终端每秒显示一条data: Hello World from ESP32 #N(N为递增序号),且ESP32串口日志同步输出Published: Hello World from ESP32 #N,即表示micro-ROS通信链路正常。

常见问题

  • Wi-Fi连接失败:确认SSID/密码正确,ESP32与主机在同一网段。
  • Agent无法发现设备:检查主机防火墙是否放行UDP 8888端口,确认Agent先于设备启动。
  • 编译报错找不到micro_ros头文件:执行idf.py reconfigure重新解析组件依赖。