ESC
输入关键词搜索文章标题和内容

嵌入式RTOS全解 Zephyr与RT-Thread与FreeRTOS与NuttX与micro-ROS

本文由 linuxROS 整理发布,首发于 linuxros.cn,转载请注明出处。

嵌入式RTOS全解 Zephyr与RT-Thread与FreeRTOS与NuttX与micro-ROS

导读:MCU上跑裸机还是RTOS?Zephyr想做物联网的Linux,FreeRTOS已经统治市场,RT-Thread是国内生态最全的,NuttX把POSIX搬上了单片机,micro-ROS让MCU直接接入ROS2。五大系统怎么选,看这篇就够了。


一、原理简析

RTOS是什么?

RTOS(Real-Time Operating System)是运行在MCU上的操作系统内核,核心任务是在确定的时间内响应外部事件。和桌面OS不同,RTOS的关键指标不是吞吐量,而是确定性——高优先级任务必须在可预测的延迟内得到CPU。

部件 作用 类比
任务调度器 决定哪个任务获得CPU 交通指挥员
任务间通信 队列、信号量、互斥锁 任务之间的"对讲机"
内存管理 静态/动态分配,MPU保护 内存管家
中断管理 快速响应硬件事件 紧急通道
时钟管理 软件定时器、Tick 系统心跳

裸机 vs RTOS:什么时候该上RTOS?

裸机(Super Loop)只适合单一功能、时序简单的场景。一旦出现以下信号,就该上RTOS了:

信号 说明
任务数量超过3个 裸机的中断+主循环模式开始失控
有实时性要求 传感器采样必须在固定周期内完成
需要网络协议栈 TCP/IP、BLE、MQTT等协议栈天然依赖多任务
有文件系统需求 FAT、LittleFS等需要任务配合
需要OTA升级 后台下载+前台运行,必然多任务

抢占式调度的核心逻辑

RTOS的核心是抢占式调度:内核在每个Tick中断中检查是否有更高优先级任务就绪,如果有,立即切换。任务有四种状态:

flowchart LR Ready["就绪态<br/>等待CPU"] --> Running["运行态<br/>占用CPU"] Running --> Blocked["阻塞态<br/>等待事件"] Blocked --> Ready Running --> Ready Running --> Suspended["挂起态<br/>主动暂停"] Suspended --> Ready style Ready fill:#E3F2FD,stroke:#1976D2 style Running fill:#E8F5E9,stroke:#388E3C style Blocked fill:#FFF8E1,stroke:#F57C00 style Suspended fill:#FFEBEE,stroke:#D32F2F

优先级反转是RTOS设计中的经典问题:低优先级任务持有锁,中优先级任务抢占CPU,低优先级间接阻塞了高优先级任务。解决方案是优先级继承——持有锁的低优先级任务临时提升到等待者的优先级。

五大RTOS的定位

系统 本质 定位 一句话理解
Zephyr Linux基金会的物联网OS 厂商中立的通用平台 MCU界的Linux,想统一嵌入式生态
RT-Thread 中国开源社区主导 物联网全栈平台 MCU上的"小OpenWrt",软件包生态最全
FreeRTOS AWS维护的RTOS内核 极简内核 嵌入式界的"C标准库",装机量第一
NuttX Apache基金会的POSIX RTOS 类Linux的MCU环境 把POSIX搬上单片机,能跑Linux应用
micro-ROS ROS2官方MCU客户端 机器人MCU通信 让MCU直接加入ROS2话题网络

二、核心架构对比

五系统架构全景图

flowchart TB subgraph 应用层["应用层"] App["用户任务 / 业务逻辑"] end subgraph 中间件["中间件与服务"] Zeph["Zephyr<br/>Bluetooth, Networking,<br/>USB, Filesystem, Sensor"] RTT["RT-Thread<br/>FinSH, DFS, SAL,<br/>AT Command, 500+ Packages"] Free["FreeRTOS+<br/>TCP, FAT, CLI<br/>(可选附加库)"] NuttX["NuttX<br/>POSIX APIs, VFS,<br/>ROMFS, NFS, CAN, Audio"] uROS["micro-ROS<br/>RCLC, DDS-XRCE,<br/>Executor, Lifecycle"] end subgraph 内核层["内核层"] ZK["Zephyr Kernel<br/>Preemptive, SMP,<br/>MPU, MMU"] RTK["RT-Thread Kernel<br/>256优先级,<br/>抢占式"] FK["FreeRTOS Kernel<br/>3文件内核,<br/>抢占/协作"] NK["NuttX Kernel<br/>POSIX线程,<br/>MMU-less Linux"] uRK["底层RTOS<br/>FreeRTOS/Zephyr/<br/>NuttX/Bare-metal"] end subgraph 硬件层["硬件抽象层"] HAL["ARM Cortex-M/A/R, RISC-V, x86, Xtensa, MIPS, ARC"] end 应用层 --> 中间件 Zeph --> ZK RTT --> RTK Free --> FK NuttX --> NK uROS --> uRK ZK --> HAL RTK --> HAL FK --> HAL NK --> HAL uRK --> HAL style 应用层 fill:#F3E5F5,stroke:#7B1FA2 style 中间件 fill:#E3F2FD,stroke:#1976D2 style 内核层 fill:#E8F5E9,stroke:#388E3C style 硬件层 fill:#FFF8E1,stroke:#F57C00

Zephyr架构

Zephyr的设计哲学是厂商中立 + 可配置性。它借鉴了Linux的Kconfig和Devicetree,用一套统一的API屏蔽底层硬件差异。

zephyrproject/
├── zephyr/
│   ├── arch/           # 架构支持(ARM, RISC-V, x86, ARC, Xtensa等)
│   ├── boards/         # 500+板级支持
│   ├── drivers/        # 设备驱动(传感器、外设、通信)
│   ├── subsys/         # 子系统(蓝牙、网络、USB、文件系统、电源管理)
│   ├── kernel/         # 内核(调度、同步、IPC、内存管理)
│   ├── include/        # 公共API头文件(POSIX-like接口)
│   └── cmake/          # CMake构建系统
├── modules/            # 第三方模块(MCUboot, TF-M, LVGL等)
└── west.yml            # 多仓库管理清单

Zephyr用west工具统一管理源码、编译、烧录。west init拉取全部依赖,west build一路编译到底。有Linux开发经验的,这套CMake + Kconfig + Devicetree组合上手快。

关键特性:Zephyr是五者中唯一原生支持SMP多核调度和用户态/内核态隔离的系统。LTS版本(v3.7)保证了工业场景的稳定性。

RT-Thread架构

RT-Thread的核心是软件包生态 + 中国本土化。它不仅是内核,而是一个完整的物联网平台。

rt-thread/
├── src/               # 内核源码(调度、IPC、时钟、内存)
├── include/           # 内核头文件
├── components/        # 组件层
│   ├── dfs/           # 虚拟文件系统
│   ├── finsh/         # 命令行Shell
│   ├── net/           # 网络协议栈(SAL抽象层)
│   ├── at_device/     # AT指令设备框架
│   └── drivers/       # 驱动框架
├── bsp/               # 200+板级支持包
├── libcpu/            # CPU架构适配
└── packages/          # 500+第三方软件包

RT-Thread的FinSH Shell是调试利器——通过串口直接输入命令查看任务状态、内存使用、寄存器值,不用接调试器。SAL网络抽象层则让应用层代码不关心底层是WiFi、4G还是有线以太网。

RT-Thread Studio IDE提供图形化配置、一键编译、在线调试,上手快。资源占用极低:3KB ROM + 1.2KB RAM就能跑起来。

FreeRTOS架构

FreeRTOS的哲学是极简主义。内核只有三个文件:tasks.c、queue.c、list.c。没有构建系统,没有配置工具,直接把源码文件复制到工程里就能用。

FreeRTOS/
├── FreeRTOS/
│   ├── Source/
│   │   ├── tasks.c           # 任务管理
│   │   ├── queue.c           # 队列(信号量/互斥锁基于队列实现)
│   │   ├── list.c            # 链表(调度器内部数据结构)
│   │   ├── timers.c          # 软件定时器
│   │   ├── event_groups.c    # 事件组
│   │   ├── stream_buffer.c   # 流缓冲区
│   │   └── portable/         # 硬件移植层(GCC/IAR/Keil等编译器+架构)
│   ├── include/
│   └── Demo/                 # 各平台示例
├── FreeRTOS-Plus/
│   ├── TCP/                  # FreeRTOS+TCP
│   ├── FAT/                  # FreeRTOS+FAT
│   └── CLI/                  # FreeRTOS+CLI

FreeRTOS+TCP和FreeRTOS+FAT是附加库,不是内核的一部分。和AWS IoT Core的原生集成是它的一大卖点——用FreeRTOS的设备可以直接推送遥测数据到云端。

Tickless Idle在低功耗场景下效果好:空闲时完全关闭SysTick,靠外部中断唤醒,功耗降到最低。

来自 linuxros.cn · linuxROS

NuttX架构

NuttX的核心是POSIX兼容。它把Linux的API(pthread、socket、open/read/write、/dev/、/proc/)搬到了MCU上,熟悉Linux的开发者直接上手。

nuttx/
├── arch/               # 架构支持(ARM, RISC-V, x86, MIPS, Xtensa等)
├── drivers/            # 设备驱动(USB, CAN, Audio, Video, Graphics)
├── fs/                 # 文件系统(VFS, ROMFS, NFS, procfs, tmpfs)
├── net/                # 网络协议栈(TCP/IP, UDP, Socket API)
├── include/            # POSIX标准头文件(pthread.h, socket.h等)
├── sched/              # 调度器(POSIX线程模型)
├── mm/                 # 内存管理(支持MMU-less)
├── audio/              # 音频子系统
└── graphics/           # 图形子系统(NxWidgets, LVGL)

NuttX的VFS虚拟文件系统让所有设备都表现为文件节点——/dev/ttyS0、/dev/i2c0、/dev/spi0,和Linux一模一样。ROMFS可以把文件系统编译进固件,NFS支持挂载远程网络文件系统。

小米的Vela物联网平台基于NuttX构建,NuttX已经过了大规模商用验证。Sony和Samsung也在部分产品中使用NuttX。

micro-ROS架构

micro-ROS不是独立RTOS,而是运行在RTOS之上的ROS2客户端。它让MCU能像Linux主机一样,在ROS2的分布式话题网络中发布和订阅消息。

micro_ros/
├── micro_ros_setup/          # 环境搭建脚本
├── rclc/                     # ROS2 C客户端库(轻量版rclcpp)
├── rclc_lifecycle/           # 节点生命周期管理
├── rclc_parameter/           # 参数服务器客户端
├── rmw_microxrcedds/         # DDS-XRCE中间件(eProsima)
├── micro_ros_agent/          # 宿主机代理(连接MCU到ROS2网络)
└── micro_ros_arduino/        # Arduino平台集成

micro-ROS的通信链路是:MCU节点 → Micro XRCE-DDS(串口/UDP) → micro-ROS Agent → ROS2 Graph。Agent运行在Linux主机上,把MCU的消息桥接到ROS2的DDS网络中,其他节点无需关心消息来源是MCU还是主机。

micro-ROS需要底层RTOS支撑,通常搭配FreeRTOS(资源最省)或Zephyr(功能最全)。资源占用约20KB RAM + 100KB+ Flash。


三、对比总表

核心维度对比

维度 Zephyr RT-Thread FreeRTOS NuttX micro-ROS
开源指标 140K commits, 128分支, 218标签 18K commits 3.6K commits, 100标签 61K commits, 25分支, 199标签 371 commits (setup)
许可证 Apache 2.0 Apache 2.0 MIT Apache 2.0 Apache 2.0
维护方 Linux基金会 中国开源社区 AWS/Amazon Apache基金会 ROS2官方
语言 C (95%+) C (95%+) C (99%) C (95%+) C (95%+)
构建工具 CMake + west scons / RT-Thread Studio 无(直接复制源文件) Make + Kconfig CMake + colcon
配置方式 Kconfig + Devicetree Kconfig + env FreeRTOSConfig.h Kconfig CMake + colcon.meta
最小ROM \~50KB 3KB \~6KB 32KB 100KB+
最小RAM \~10KB 1.2KB \~2KB 8KB 20KB
支持架构 ARM, RISC-V, x86, ARC, Xtensa ARM, RISC-V, MIPS ARM Cortex-M/A/R, RISC-V, Xtensa, MIPS, x86 ARM, RISC-V, x86, MIPS, Xtensa 依赖底层RTOS
板级支持 500+ 200+ 40+架构 广泛 STM32, ESP32, Pico, Arduino
调度模型 抢占式, SMP 抢占式, 256优先级 抢占式/协作式, SMP POSIX线程, 抢占式 依赖底层RTOS
IPC 信号量, 互斥锁, 队列, 消息队列, 事件, 管道 信号量, 互斥锁, 事件, 邮箱, 消息队列 信号量, 互斥锁, 队列, 事件组, 任务通知, 流缓冲区 POSIX信号量, 互斥锁, 消息队列, 信号 ROS2话题/服务/参数
网络协议栈 原生TCP/IP, CoAP, MQTT, LwM2M SAL抽象层, 多协议栈 FreeRTOS+TCP (附加) 原生TCP/IP, Socket API 依赖底层RTOS
文件系统 LittleFS, FAT, NVS DFS虚拟文件系统 FreeRTOS+FAT (附加) VFS, ROMFS, NFS, procfs 无
OTA支持 MCUboot 内置OTA, 差分升级 AWS OTA 通过bootloader 无
学习曲线 中高 中 低 中高 低(需ROS2基础)
中文文档 少量 丰富 丰富 极少 极少
代表用户 Google, Intel, Nordic, NXP, ST 中国IoT厂商广泛使用 全球部署量最大 Xiaomi Vela, Sony, Samsung ROS2机器人社区

定位差异矩阵

场景 推荐 原因
资源极度受限(<5KB RAM) RT-Thread / FreeRTOS 二者均可在1-2KB RAM运行
蓝牙/WiFi/USB一应俱全 Zephyr 原生子系统最完整
中国社区生态 + 中文文档 RT-Thread 500+软件包,中文社区活跃
极简入门 / 通用场景 FreeRTOS 3个文件,70%市场份额
从Linux平滑迁移到MCU NuttX POSIX兼容,pthread/socket直接可用
机器人MCU接入ROS2 micro-ROS 官方ROS2客户端,开箱即用
工业认证 / 安全关键系统 Zephyr / FreeRTOS Zephyr有LTS,FreeRTOS有SIL认证
需要SMP多核 Zephyr / FreeRTOS 二者均原生支持SMP

选型决策流程

flowchart TB A["你的项目类型?"] --> B{"需要接入ROS2?"} B -->|"是"| C["micro-ROS<br/>直接接入ROS2话题网络"] B -->|"否"| D{"资源情况?"} D -->|"<5KB RAM<br/>极度受限"| E{"是否有中文文档需求?"} E -->|"是"| F["RT-Thread<br/>3KB ROM, 1.2KB RAM<br/>中文社区活跃"] E -->|"否"| G["FreeRTOS<br/>2KB RAM, 3个文件<br/>全球部署量最大"] D -->|"50KB+ ROM<br/>资源充裕"| H{"功能需求?"} H -->|"蓝牙/WiFi/USB<br/>传感器/电源管理"| I["Zephyr<br/>500+板级支持<br/>子系统最完整"] H -->|"跑Linux应用<br/>POSIX兼容"| J["NuttX<br/>POSIX API<br/>小米Vela背书"] H -->|"IoT全栈<br/>软件包生态"| K["RT-Thread<br/>500+软件包<br/>AT指令/云SDK"] style C fill:#E8F5E9,stroke:#388E3C style F fill:#FFF8E1,stroke:#F57C00 style G fill:#E3F2FD,stroke:#1976D2 style I fill:#F3E5F5,stroke:#7B1FA2 style J fill:#FFEBEE,stroke:#D32F2F style K fill:#E1F5FE,stroke:#0288D1

四、实战步骤

Zephyr实战(创建任务 + 编译烧录)

# ===== 安装 west 工具 =====
pip install west

# ===== 初始化Zephyr工作区 =====
west init -m https://github.com/zephyrproject-rtos/zephyr my-zephyr-app
cd my-zephyr-app
west update

# ===== 安装SDK和依赖 =====
west sdk install
pip install -r zephyr/scripts/requirements.txt

# ===== 创建应用目录 =====
mkdir -p app/src
// app/src/main.c —— Zephyr双任务示例
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#define STACK_SIZE 1024
#define TASK1_PRIO 5
#define TASK2_PRIO 6

K_THREAD_STACK_DEFINE(task1_stack, STACK_SIZE);
K_THREAD_STACK_DEFINE(task2_stack, STACK_SIZE);

struct k_thread task1_data, task2_data;
struct k_mutex my_mutex;

void task1_entry(void *p1, void *p2, void *p3)
{
    while (1) {
        k_mutex_lock(&my_mutex, K_FOREVER);
        printk("[TASK1] Sensor reading at %d ms\n", k_uptime_get_32());
        k_mutex_unlock(&my_mutex);
        k_msleep(100);
    }
}

void task2_entry(void *p1, void *p2, void *p3)
{
    while (1) {
        k_mutex_lock(&my_mutex, K_FOREVER);
        printk("[TASK2] Data processing at %d ms\n", k_uptime_get_32());
        k_mutex_unlock(&my_mutex);
        k_msleep(250);
    }
}

void main(void)
{
    k_mutex_init(&my_mutex);

    k_thread_create(&task1_data, task1_stack,
        K_THREAD_STACK_SIZEOF(task1_stack),
        task1_entry, NULL, NULL, NULL,
        TASK1_PRIO, 0, K_NO_WAIT);

    k_thread_create(&task2_data, task2_stack,
        K_THREAD_STACK_SIZEOF(task2_stack),
        task2_entry, NULL, NULL, NULL,
        TASK2_PRIO, 0, K_NO_WAIT);

    printk("Zephyr RTOS started\n");
}
# app/CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(my_app)

target_sources(app PRIVATE src/main.c)
# ===== 编译(以STM32F4为例)=====
west build -b stm32f4_disco app/

# ===== 烧录 =====
west flash

# ===== 查看串口输出 =====
west build -t run

RT-Thread实战(创建任务 + FinSH调试)

# ===== 克隆RT-Thread =====
git clone https://github.com/RT-Thread/rt-thread.git
cd rt-thread/bsp/stm32/stm32f407-atk-explorer

# ===== 使用env工具配置(或RT-Thread Studio图形化)=====
scons --menuconfig
# 勾选:RT-Thread Kernel → Kernel Device Object
# 勾选:RT-Thread Components → Command shell (FinSH)
// applications/main.c —— RT-Thread双线程示例
#include <rtthread.h>

#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5

static struct rt_mutex dynamic_mutex;
static rt_thread_t tid1 = RT_NULL;
static rt_thread_t tid2 = RT_NULL;

static void thread1_entry(void *parameter)
{
    while (1) {
        rt_mutex_take(&dynamic_mutex, RT_WAITING_FOREVER);
        rt_kprintf("[TASK1] Sensor reading at %d ms\n",
            rt_tick_get());
        rt_mutex_release(&dynamic_mutex);
        rt_thread_mdelay(100);
    }
}

static void thread2_entry(void *parameter)
{
    while (1) {
        rt_mutex_take(&dynamic_mutex, RT_WAITING_FOREVER);
        rt_kprintf("[TASK2] Data processing at %d ms\n",
            rt_tick_get());
        rt_mutex_release(&dynamic_mutex);
        rt_thread_mdelay(250);
    }
}

int main(void)
{
    rt_mutex_init(&dynamic_mutex, "dmutex", RT_IPC_FLAG_FIFO);

    tid1 = rt_thread_create("sensor",
        thread1_entry, RT_NULL,
        THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE);
    if (tid1 != RT_NULL) rt_thread_startup(tid1);

    tid2 = rt_thread_create("process",
        thread2_entry, RT_NULL,
        THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE);
    if (tid2 != RT_NULL) rt_thread_startup(tid2);

    rt_kprintf("RT-Thread started\n");
    return 0;
}
# ===== 编译(生成Keil MDK工程)=====
scons --target=mdk5

# ===== 或用scons直接编译(需要工具链)=====
scons -j4

# ===== 烧录后打开串口,进入FinSH Shell =====
# 常用FinSH命令:
list_thread          # 列出所有线程
list_sem             # 列出所有信号量
list_mutex           # 列出所有互斥锁
free                 # 查看内存使用
ps                   # 查看线程状态

FreeRTOS实战(创建任务 + 编译烧录)

# ===== 克隆FreeRTOS =====
git clone https://github.com/FreeRTOS/FreeRTOS.git --depth 1
cd FreeRTOS/FreeRTOS/Demo/CORTEX_M4F_STM32F407ZG-SK

# FreeRTOS没有构建系统,直接把源文件加入工程
# 核心文件:tasks.c, queue.c, list.c, timers.c
# 移植文件:portable/GCC/ARM_CM4F/port.c
# 内存管理:portable/MemMang/heap_4.c
// main.c —— FreeRTOS双任务示例
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"

SemaphoreHandle_t xMutex;

void vTask1(void *pvParameters)
{
    const char *pcTaskName = "Sensor";
    for (;;) {
        if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            printf("[%s] Reading at %lu ms\n",
                pcTaskName, xTaskGetTickCount());
            xSemaphoreGive(xMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}

void vTask2(void *pvParameters)
{
    const char *pcTaskName = "Process";
    for (;;) {
        if (xSemaphoreTake(xMutex, portMAX_DELAY) == pdTRUE) {
            printf("[%s] Processing at %lu ms\n",
                pcTaskName, xTaskGetTickCount());
            xSemaphoreGive(xMutex);
        }
        vTaskDelay(pdMS_TO_TICKS(250));
    }
}

int main(void)
{
    xMutex = xSemaphoreCreateMutex();

    xTaskCreate(vTask1, "Sensor",
        configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    xTaskCreate(vTask2, "Process",
        configMINIMAL_STACK_SIZE, NULL, 2, NULL);

    printf("FreeRTOS started\n");
    vTaskStartScheduler();

    for (;;);  /* 不应该到达这里 */
    return 0;
}
// FreeRTOSConfig.h —— 关键配置项
#define configUSE_PREEMPTION            1
#define configUSE_IDLE_HOOK             0
#define configUSE_TICK_HOOK             0
#define configCPU_CLOCK_HZ              ( ( unsigned long ) 168000000 )
#define configTICK_RATE_HZ              ( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES            ( 5 )
#define configMINIMAL_STACK_SIZE        ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE           ( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN         ( 16 )
#define configUSE_MUTEXES               1
# ===== 编译(以GCC为例)=====
arm-none-eabi-gcc -mcpu=cortex-m4 -mthumb \
    -I FreeRTOS/Source/include \
    -I FreeRTOS/Source/portable/GCC/ARM_CM4F \
    main.c \
    FreeRTOS/Source/tasks.c \
    FreeRTOS/Source/queue.c \
    FreeRTOS/Source/list.c \
    FreeRTOS/Source/timers.c \
    FreeRTOS/Source/portable/GCC/ARM_CM4F/port.c \
    FreeRTOS/Source/portable/MemMang/heap_4.c \
    -o firmware.elf

# ===== 烧录(以STM32为例)=====
openocd -f board/stm32f4discovery.cfg \
    -c "program firmware.elf verify reset exit"

NuttX实战(创建任务 + 编译烧录)

# ===== 克隆NuttX =====
git clone https://github.com/apache/nuttx.git
git clone https://github.com/apache/nuttx-apps.git apps

cd nuttx

# ===== 配置板级支持(以STM32F4Discovery为例)=====
./tools/configure.sh stm32f4discovery:nsh

# ===== 图形化配置 =====
make menuconfig
# 勾选:Build Setup → Debug Options → Enable Debug Symbols
# 勾选:Application Configuration → Examples → Hello World
// 创建自定义应用 —— apps/examples/myapp/myapp_main.c
#include <nuttx/config.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

static sem_t my_sem;

void *task1_routine(void *arg)
{
    while (1) {
        sem_wait(&my_sem);
        printf("[TASK1] Sensor reading at %lu ms\n",
            (unsigned long)clock());
        sem_post(&my_sem);
        usleep(100000);  /* 100ms */
    }
    return NULL;
}

void *task2_routine(void *arg)
{
    while (1) {
        sem_wait(&my_sem);
        printf("[TASK2] Data processing at %lu ms\n",
            (unsigned long)clock());
        sem_post(&my_sem);
        usleep(250000);  /* 250ms */
    }
    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid1, tid2;
    sem_init(&my_sem, 0, 1);

    pthread_create(&tid1, NULL, task1_routine, NULL);
    pthread_create(&tid2, NULL, task2_routine, NULL);

    printf("NuttX POSIX RTOS started\n");

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    return 0;
}
# ===== 编译 =====
make -j$(nproc)

# ===== 产物位置 =====
ls nuttx.bin     # 固件镜像

# ===== 烧录(以STM32为例)=====
openocd -f board/stm32f4discovery.cfg \
    -c "program nuttx.bin 0x08000000 verify reset exit"

# ===== 串口连接后,进入NSH Shell =====
# NuttX Shell (NSH)
nsh> help
nsh> ps         # 查看进程
nsh> free       # 查看内存
nsh> ls /dev    # 查看设备节点(POSIX风格!)

micro-ROS实战(MCU发布话题到ROS2)

# ===== 安装micro-ROS工具 =====
source /opt/ros/humble/setup.bash
mkdir -p ~/microros_ws/src
cd ~/microros_ws
git clone -b humble https://github.com/micro-ROS/micro_ros_setup.git \
    src/micro_ros_setup
rosdep update && rosdep install --from-paths src --ignore-src -y
colcon build
source install/setup.bash

# ===== 创建固件工作区(以ESP32为例)=====
ros2 run micro_ros_setup create_firmware_ws.sh freertos esp32
ros2 run micro_ros_setup configure_firmware.sh int32_publisher_app
// main.c —— micro-ROS话题发布示例
#include <rcl/rcl.h>
#include <rcl/error_handling.h>
#include <rclc/rclc.h>
#include <rclc/executor.h>
#include <std_msgs/msg/int32.h>

#include <freertos/FreeRTOS.h>
#include <freertos/task.h>

#define RCCHECK(fn) { rcl_ret_t temp_rc = fn; \
    if ((temp_rc != RCL_RET_OK)) { \
        printf("Failed status on line %d: %d\n", \
            __LINE__, (int)temp_rc); \
        vTaskDelete(NULL); }}

rcl_publisher_t publisher;
std_msgs__msg__int32 msg;

void timer_callback(rcl_timer_t *timer, int64_t last_call_time)
{
    (void) last_call_time;
    msg.data++;
    RCSOFTCHECK(rcl_publish(&publisher, &msg, NULL));
    printf("Published: %d\n", msg.data);
}

void appMain(void *arg)
{
    rcl_allocator_t allocator = rcl_get_default_allocator();
    rclc_support_t support;
    rcl_node_t node;

    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_sensor_data"));

    rcl_timer_t timer;
    RCCHECK(rclc_timer_init_default(&timer, &support,
        RCL_MS_TO_NS(500), timer_callback));

    rclc_executor_t executor;
    RCCHECK(rclc_executor_init(&executor,
        &support.context, 1, &allocator));
    RCCHECK(rclc_executor_add_timer(&executor, &timer));

    msg.data = 0;
    printf("micro-ROS node started\n");

    rclc_executor_spin(&executor);

    RCCHECK(rcl_publisher_fini(&publisher, &node));
    RCCHECK(rcl_node_fini(&node));
    vTaskDelete(NULL);
}

void app_main(void)
{
    xTaskCreate(appMain, "uros_task",
        20000, NULL, 5, NULL);
}
# ===== 编译固件 =====
ros2 run micro_ros_setup build_firmware.sh

# ===== 烧录到ESP32 =====
ros2 run micro_ros_setup flash_firmware.sh

# ===== 在宿主机启动Agent(串口方式)=====
ros2 run micro_ros_agent micro_ros_agent serial \
    --dev /dev/ttyUSB0 -b 115200

# ===== 验证:在宿主机上查看话题 =====
ros2 topic list
# 输出:/esp32_sensor_data

ros2 topic echo /esp32_sensor_data
# 输出:data: 1
#       data: 2
#       data: 3
#       ...

五、常见问题解决

Zephyr常见问题

现象 根因 解决
west update 卡住不动 网络拉取GitHub仓库慢 设置代理或配置镜像源
编译报 DT_xxx not found Devicetree缺少节点定义 检查 boards/xxx.overlay 是否完整
Kconfig循环依赖 配置项互相引用 west build -t guiconfig 图形化排查
烧录失败 "no debug probe" 缺少调试器驱动 安装JLink/OpenOCD,检查 boards/xxx.yaml 的 runner 配置
线程栈溢出 栈空间分配不足 开启 CONFIG_THREAD_STACK_INFO,用 k_thread_stack_space_get() 查看

RT-Thread常见问题

现象 根因 解决
scons --menuconfig 报错 缺少Python依赖 pip install scons requests tqdm
FinSH无响应 串口中断配置错误 检查 rt_hw_console_output() 的底层串口驱动
软件包下载失败 env工具网络问题 手动 git clone 到 packages/ 目录
线程创建失败 堆内存不足 free 命令查看,增大 RT_HEAP_SIZE
硬件定时器不准 时钟配置错误 检查 board.c 中的 SystemCoreClock 设置

FreeRTOS常见问题

现象 根因 解决
configASSERT 触发 栈溢出或非法API调用 启用 configCHECK_FOR_STACK_OVERFLOW,检查 uxTaskGetStackHighWaterMark()
调度器不启动 中断优先级未正确配置 ARM Cortex-M:确保 configMAX_SYSCALL_INTERRUPT_PRIORITY 与NVIC优先级匹配
内存分配失败 heap太小 增大 configTOTAL_HEAP_SIZE,或换用 heap_4.c(支持碎片合并)
任务间通信死锁 互斥锁嵌套或获取顺序不一致 统一锁的获取顺序,使用 xSemaphoreTake(xMutex, timeout) 避免永久阻塞
空闲任务钩子不执行 高优先级任务占满CPU 确保至少有一个任务调用 vTaskDelay() 释放CPU

NuttX常见问题

现象 根因 解决
./tools/configure.sh 找不到配置 板级配置不存在 ./tools/configure.sh -L 列出所有可用配置
编译报 undefined reference to xxx Kconfig未启用该功能 make menuconfig 勾选对应模块
NSH Shell无法输入命令 串口驱动未正确初始化 检查 arch/xxx/src/xxx/xxx_serial.c 中的 up_earlyserialinit()
pthread创建失败 任务栈/堆不足 增大 CONFIG_PTHREAD_STACK_DEFAULT 或 CONFIG_MM_HEAP_SIZE
文件系统挂载失败 存储驱动未初始化 先执行 mkrd -s 512 128 创建RAM盘,再 mount -t vfat /dev/ram0 /mnt

micro-ROS常见问题

现象 根因 解决
Agent连接不上 串口参数不匹配 确认波特率一致,Agent端 -b 参数与固件端一致
话题发布成功但宿主机收不到 ROS2的DDS网络配置问题 检查 ROS_DOMAIN_ID,确保 rmw 实现一致
固件编译失败 交叉工具链未安装 安装对应平台的工具链:apt install gcc-arm-none-eabi
内存不足 micro-ROS占用RAM大 减小 RCL_UXRCE_MAX_SESSIONS 和 RCL_UXRCE_MAX_TOPICS
定时器回调不触发 executor未正确添加 确认 rclc_executor_add_timer() 调用在 rclc_executor_spin() 之前

调试命令速查

# ===== Zephyr调试 =====
west build -t menuconfig       # 图形化配置
west build -t guiconfig        # GUI配置
west debug                     # 启动调试器
west build -t ram_report       # 查看RAM占用
west build -t rom_report       # 查看ROM占用

# ===== RT-Thread调试 =====
scons --menuconfig             # 配置
scons --target=mdk5            # 生成MDK工程
scons -c                       # 清理

# ===== FreeRTOS调试 =====
# 无专用构建工具,依赖IDE(Keil/IAR/STM32CubeIDE)
# 运行时API:
# uxTaskGetStackHighWaterMark() # 查看栈余量
# vTaskList()                   # 打印任务列表
# xPortGetFreeHeapSize()        # 查看剩余堆

# ===== NuttX调试 =====
./tools/configure.sh -L        # 列出所有配置
make menuconfig                # 配置
make clean                     # 清理
make distclean                 # 完全清理

# ===== micro-ROS调试 =====
ros2 run micro_ros_setup create_agent_ws.sh   # 创建Agent工作区
ros2 run micro_ros_setup build_agent.sh       # 编译Agent
ros2 topic list                               # 查看话题
ros2 topic echo /topic_name                   # 查看话题内容

六、总结

选型建议

场景 推荐 理由
通用MCU项目 / 入门学习 FreeRTOS 3个文件,70%市场占有率,资料最多
中国IoT产品 / 中文社区 RT-Thread 500+软件包,中文文档,AT指令框架
蓝牙/WiFi/USB一应俱全 Zephyr 原生子系统最完整,500+板支持
从Linux迁移到MCU NuttX POSIX兼容,pthread/socket即插即用
机器人MCU接入ROS2 micro-ROS ROS2官方集成,话题/服务/参数全支持
工业安全认证 FreeRTOS / Zephyr FreeRTOS有SIL认证,Zephyr有LTS

核心口诀

FreeRTOS是嵌入式界的C标准库——3个文件,哪里都能用,装机量第一。

Zephyr是MCU上的Linux——Kconfig + Devicetree + CMake,Linux开发者零转换成本。

RT-Thread是MCU上的小OpenWrt——软件包生态 + FinSH Shell + 中文社区,开箱即用。

NuttX把POSIX搬上了单片机——pthread、socket、/dev/、/proc/,Linux应用直接跑。

micro-ROS是MCU到ROS2的直通车——一个Agent,MCU节点直接加入ROS2话题网络。

资源占用参考

系统 最小ROM 最小RAM 典型ROM 典型RAM
FreeRTOS \~6KB \~2KB 10-15KB 5-10KB
RT-Thread 3KB 1.2KB 15-50KB 5-20KB
Zephyr \~50KB \~10KB 100-300KB 30-80KB
NuttX \~32KB \~8KB 100-500KB 50-200KB
micro-ROS 100KB+ 20KB 200-500KB 50-100KB

以上数值为内核+最小功能集,实际占用取决于启用的组件。micro-ROS的数值包含底层RTOS。

生产环境避坑要点

要点 说明
版本选择 生产用稳定版本:Zephyr选LTS(v3.7),FreeRTOS选长期支持版,RT-Thread用release版,NuttX用stable分支,micro-ROS版本与ROS2发行版匹配
编译环境 固定工具链版本。推荐Ubuntu 22.04 LTS,32GB内存,SSD必备。Zephyr和NuttX编译需要大量磁盘空间(10GB+)
源码管理 不要直接改源码。Zephyr用Devicetree overlay,RT-Thread用env+软件包,FreeRTOS用FreeRTOSConfig.h,NuttX用Kconfig,micro-ROS用colcon.meta
栈大小 量产前必须实测每个任务的最大栈使用量。FreeRTOS用uxTaskGetStackHighWaterMark(),Zephyr用CONFIG_THREAD_STACK_INFO,RT-Thread用list_thread查看
中断优先级 ARM Cortex-M上,FreeRTOS的configMAX_SYSCALL_INTERRUPT_PRIORITY是最容易踩的坑。中断优先级数值越低优先级越高,确保调用RTOS API的中断优先级不高于此值

五个系统没有绝对的好坏,只有是否适合你的场景。资源极度受限选FreeRTOS或RT-Thread,需要完整生态选Zephyr,从Linux迁移选NuttX,机器人场景选micro-ROS。选型的第一原则永远是:够用就好,不要过度设计。


版权声明

作者linuxROS
协议本作品采用 CC BY-NC-SA 4.0 许可协议:署名-非商业性使用-相同方式共享
关注欢迎关注微信公众号 linuxROS,获取更多机器人 / 嵌入式 / Linux 干货
返回首页