首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >我花了一个周末,用 Rust 让 ESP32 跑起了 AI 推理

我花了一个周末,用 Rust 让 ESP32 跑起了 AI 推理

作者头像
不吃草的牛德
发布2026-07-21 09:15:44
发布2026-07-21 09:15:44
1010
举报
文章被收录于专栏:RustRust

最近无聊,于是我折腾了一块 ESP32,想做个能识别手势的小设备。先用 C 写了一遍,折腾到凌晨一点多,最后靠加 printf 找到的 bug。

那天晚上我盯着终端发呆,突然想:这都2026年了,嵌入式开发还是这副德性?

第二天,我了解到 Rust 在 ESP32 上的支持已经相当成熟了。做了个测试,感觉比我预期的好太多。

先说结论

ESP32 不是什么都能干。它的双核 Xtensa 跑到 240MHz,SRAM 只有 520KB。你指望在上面跑 LLaMA,那属于想多了。

但它能干的事情其实不少:

唤醒词检测(比如"嘿小X"),传感器数据的异常检测,简单的图像分类(配合 ESP32-CAM),还有最实际的——做一个物理接口,把传感器数据和云端 AI API 串起来。

我最终做的东西不复杂:一个带麦克风的小盒子,本地做关键词检测,听到唤醒词后录音,发到云端做语音识别,再调大模型 API 返回结果。说白了就是个自制的小爱同学,但你可以完全控制数据的流向。

用 Rust 写这个,花了大概三天。同样的功能用 C 写,我估计得一周,而且大概率会有内存问题。

环境搭建:比想象中顺利

先说工具链。你需要装这些东西:

代码语言:javascript
复制
# 装 Rust 不多说了
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# esp-rs 的工具链管理器
cargo install espup
espup install

# 项目生成器
cargo install esp-generate

装完之后,创建项目:

代码语言:javascript
复制
esp-generate --chip esp32 my-ai-device

它会问你几个问题,选 std 模式(基于 ESP-IDF)还是 no_std(裸机)。如果你要用 WiFi 和各种外设,选 std。如果你要做极致优化、抠每一字节内存,选 no_std。

我选的 std,因为要联网调 API,没必要跟自己过不去。

项目结构长这样:

代码语言:javascript
复制
my-ai-device/
├── Cargo.toml
├── src/
│   └── main.rs
├── .cargo/
│   └── config.toml
└── sdkconfig.defaults

点个灯先

嵌入式老规矩,先点灯,确认工具链没问题:

代码语言:javascript
复制
use esp_idf_svc::hal::delay::FreeRtos;
use esp_idf_svc::hal::gpio::*;
use esp_idf_svc::hal::peripherals::Peripherals;

fn main() {
    // 初始化 ESP-IDF(必须)
    esp_idf_svc::sys::link_patches();

    let peripherals = Peripherals::take().unwrap();
    let mut led = PinDriver::output(peripherals.pins.gpio2).unwrap();

    loop {
        led.set_high().unwrap();
        FreeRtos::delay_ms(500);
        led.set_low().unwrap();
        FreeRtos::delay_ms(500);
    }
}

编译烧录一行命令搞定:

代码语言:javascript
复制
cargo run

灯亮了,开始干正事。

接麦克风,做唤醒词检测

ESP32 本地跑 AI,目前最现实的方案是 TensorFlow Lite Micro。Rust 这边可以通过 C FFI 调用,也有一些社区封装。

唤醒词检测我用的是 Google 的 MicroNet 模型,量化后大概 18KB,ESP32 跑起来帧率还行。核心逻辑就几步:

代码语言:javascript
复制
// 采集音频 -> 特征提取 -> 模型推理 -> 判断是否命中唤醒词
fn detect_wake_word(audio_buffer: &[i16], model: &mut TfLiteInterpreter) -> bool {
    // 提取 MFCC 特征
    let features = extract_mfcc(audio_buffer);

    // 喂给 TFLite Micro 模型
    model.set_input(&features);
    model.invoke();

    // 输出是一个概率值,0 到 1 之间
    let confidence = model.get_output()[0];
    confidence > 0.85
}

实际代码比这个复杂得多,要处理环形缓冲区、采样率转换、特征窗口滑动这些细节。但核心就是这几步。

我测了一下,从音频进来到判断完成,大概 30ms 延迟。够用了。

联网调云端 AI

检测到唤醒词后,开始录音,5 秒后发到云端:

代码语言:javascript
复制
use esp_idf_svc::http::client::*;
use std::io::Read;

fn send_audio_to_cloud(audio: &[u8]) -> anyhow::Result<String> {
    let url = "https://your-api.com/transcribe";

    let client = HttpClient::new()?;
    let mut req = client.post(url)?;

    req.header("Content-Type", "audio/wav")?;
    req.header("Authorization", "Bearer YOUR_TOKEN")?;

    let mut resp = req.send(audio)?;
    let mut buf = String::new();
    resp.read_to_string(&mut buf)?;

    Ok(buf)
}

拿到识别结果后,再调一轮大模型 API,把回复通过扬声器播出来。整个链路大概 2 到 3 秒,大部分时间花在云端。本地那 30ms 几乎可以忽略。

踩过的坑

说点实在的,不是所有东西都顺。

内存是真紧张。 520KB SRAM,TFLite 模型占一点,音频缓冲区占一点,HTTP 客户端再占一点,留给你的空间没多少。我有一版代码音频缓冲区开大了,直接栈溢出,板子重启了。后来改用 PSRAM(外挂的 4MB 伪静态内存)才解决。如果你也遇到莫名其妙的重启,先检查内存。

no_std 生态还有缺口。 如果你选了裸机模式,很多 std 库的东西用不了,一些 crate 也不支持 no_std。我做音频处理的时候想用 rustfft,结果它不支持 no_std,最后自己手写了个简单的 DFT。不难,但浪费时间。

调试体验一般。 ESP32 的调试需要 JTAG,而且 Rust 的调试符号有时候对不上。大部分时候我还是靠日志输出排查问题,跟写 C 时候差不多。这点上 Rust 并没有带来明显改善。

编译慢。 第一次编译 esp-idf-svc 的时候,我等了快十分钟。后面增量编译快了,但第一次确实有点劝退。如果你网络不好,下载 ESP-IDF 的子模块也可能卡很久。

值不值得

说几个我自己的判断。

如果你只是做原型验证,C/C++ 其实也够了。ESP-IDF 的文档和示例都是 C 的,社区更大,遇到问题搜得到答案。Rust 这边的资料还在积累中,有些边角问题你得自己啃源码。

但如果你打算做一个长期维护的项目,或者固件逻辑比较复杂(状态机多、并发多),Rust 的优势就出来了。编译器帮你挡掉了一大类内存安全问题,这一点在嵌入式上太重要了。嵌入式的 bug 不好调,因为你看不到崩溃日志,板子可能直接死给你看。Rust 让你在编译阶段就消灭大部分这类问题。

另外 esp-rs 的文档质量不错,esp-idf-svc 封装得很到位,基本上 ESP-IDF 能干的事情它都能干。如果你已经会写 Rust,上手成本不高。

我的建议是:如果你会 Rust 并且又对硬件感兴趣,可以花个周末试一下。ESP32 开发板三十来块,Rust 工具链免费,试错成本很低。万一你有了创意,可能会喜欢上这条路呢。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-07-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust火箭工坊 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 最近无聊,于是我折腾了一块 ESP32,想做个能识别手势的小设备。先用 C 写了一遍,折腾到凌晨一点多,最后靠加 printf 找到的 bug。
    • 先说结论
    • 环境搭建:比想象中顺利
    • 点个灯先
    • 接麦克风,做唤醒词检测
    • 联网调云端 AI
    • 踩过的坑
    • 值不值得
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档