TensorFlow Lite Micro移植ESP32-S3

AI助手已提取文章相关产品:

TensorFlow Lite Micro 与 ESP32-S3:从零构建嵌入式 AI 系统

你有没有想过,一个只有几KB内存的微控制器,也能运行神经网络?在智能家居、可穿戴设备和工业传感器中,这种“小身材大智慧”的组合正悄然改变着我们对AI的认知。过去,AI总离不开云服务器、GPU集群和庞大的电力消耗;但今天, TensorFlow Lite Micro(TFLM) + ESP32-S3 的组合,让真正的边缘智能成为可能。

这不仅是一次技术迁移,更是一种思维方式的跃迁——从“算力无限”转向“资源敬畏”。我们要学会用最少的资源做最聪明的事。而这一切,就从你手边那块小小的开发板开始。


开发环境搭建:打造你的嵌入式AI工作台 🛠️

要让AI跑在MCU上,第一步不是写模型,而是搭好工具链。很多人卡在这一步,不是因为难,而是因为细节太多、版本太杂。别担心,我来帮你把坑都填平。

1.1 为什么是 ESP-IDF?它不只是个SDK那么简单

ESP32-S3 是乐鑫推出的高性能Wi-Fi+蓝牙双模芯片,搭载双核 Xtensa LX7 处理器,主频高达240MHz,还支持向量指令扩展。这意味着什么?意味着它不仅能处理复杂的控制逻辑,还能高效执行矩阵运算——而这正是神经网络推理的核心。

而它的官方开发框架 ESP-IDF(Espressif IoT Development Framework) ,则是连接硬件与软件的桥梁。它不仅仅是一个编译工具,更像是一个完整的操作系统底座:提供了FreeRTOS实时调度、外设驱动统一接口、OTA升级机制、安全启动等功能。

✅ 小贴士:如果你之前用过Arduino开发ESP32,那你只是“开了个头”。真正发挥其潜力,必须深入ESP-IDF层级。

推荐配置清单:
配置项 推荐值 说明
ESP-IDF 版本 v5.1.x 支持S3 N8/N9系列,稳定性强
Host OS Ubuntu 20.04 LTS 或 WSL2 文件系统兼容性最佳
Python 版本 3.8 ~ 3.11 ⚠️ 不兼容 Python 3.12+
编译器 xtensa-esp32s3-elf-gcc 12.2.0 自动由 install.sh 安装

安装命令如下:

git clone -b release/v5.1 --recursive https://github.com/espressif/esp-idf.git
cd esp-idf
./install.sh
. ./export.sh

📌 解释一下这几个关键步骤:
- --recursive :确保所有子模块(如OpenOCD调试器、分区表等)一并下载;
- install.sh :自动检测系统环境,下载对应交叉编译工具链;
- export.sh :设置 $IDF_PATH $PATH 等环境变量,让你可以在任意目录使用 idf.py 命令。

验证是否成功很简单:

idf.py create-project hello_tflm
cd hello_tflm
idf.py set-target esp32s3
idf.py build

如果最后输出 “Project build complete.”,恭喜你,已经打通任督二脉了!🎉

这时候你可以先不急着烧录,因为我们还没集成 TFLM 框架。


1.2 引入 TensorFlow Lite Micro:给 ESP32 装上“大脑”

TFLM 并没有被默认打包进 ESP-IDF,所以我们需要手动引入。建议做法是在项目根目录创建 components/tflite_micro 文件夹,并克隆官方仓库:

mkdir -p components/tflite_micro
git clone https://github.com/tensorflow/tflite-micro.git components/tflite_micro/src

⚠️ 注意:不要直接拉取主分支最新代码!API 变动频繁,很容易导致编译失败。建议锁定某个稳定提交点,比如:

cd components/tflite_micro/src
git checkout 6f8a7b3  # 这是2024年Q2发布的快照版本

这样可以避免“昨天还能编译,今天突然报错”的尴尬局面 😅

接下来,我们需要告诉 ESP-IDF:“嘿,这里有新的组件,请把它加进来。”这就靠 CMake 构建系统来完成了。


1.3 CMake 到底怎么玩?别怕,其实很直观!

ESP-IDF 从 v4.0 开始全面采用 CMake 替代旧的 GNU Make。虽然一开始看着 .cmake 文件有点懵,但它其实是高度模块化的,结构清晰。

每个组件都需要一个 CMakeLists.txt 来声明自己是谁、有哪些源文件、依赖啥库。

示例:为 TFLM 创建组件描述文件
# components/tflite_micro/CMakeLists.txt
idf_component_register(
    SRCS
        "src/tensorflow/lite/micro/kernels/all_ops_resolver.cc"
        "src/tensorflow/lite/micro/micro_interpreter.cc"
        "src/tensorflow/lite/micro/micro_mutable_op_resolver.cc"
        "src/tensorflow/lite/micro/micro_profiler.cc"
        "src/tensorflow/lite/micro/system_setup.cc"
        "src/tensorflow/lite/micro/recording_micro_interpreter.cc"
    INCLUDE_DIRS
        "src"
        "src/third_party/gemmlowp"
        "src/third_party/flatbuffers/include"
    PRIV_REQUIRES
        freertos
        heap
        log
)

📌 关键字段说明:
- SRCS :列出所有要编译的 .cc 源文件路径;
- INCLUDE_DIRS :头文件搜索路径,这样才能用 #include <tensorflow/lite/micro/micro_interpreter.h>
- PRIV_REQUIRES :私有依赖项,仅当前组件内部使用;
- 如果其他组件也要引用这个模块,则用 REQUIRES

然后在项目顶层再写一个总的 CMakeLists.txt

# CMakeLists.txt (project root)
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(hello_tflm)

✅ 小技巧:组件名称必须与其所在目录名一致,否则 IDF 找不到它!

现在整个项目的骨架就搭好了。下一步,我们要让 Python 端的模型顺利“飞”到 MCU 上。


1.4 Python端准备:训练 → 量化 → 导出一条龙 🐍

在云端训练模型时,我们习惯用浮点数(FP32),但在MCU上,每一字节都珍贵无比。因此,必须通过 模型压缩技术 将模型瘦身后再部署。

核心工具就是 TensorFlow 的 TFLiteConverter ,它可以将 Keras 模型转换成适用于 TFLM 的 .tflite 格式。

安装必要依赖(强烈建议使用虚拟环境)
python -m venv tflm_env
source tflm_env/bin/activate  # Linux/macOS
pip install tensorflow~=2.13.0
pip install numpy pandas matplotlib pyserial

🎯 为什么选 TF 2.13?因为它是最晚完整支持 TFLite 后量化(Post-training Quantization)的版本。后续版本虽然功能更强,但与某些老旧 TFLM 实现存在兼容问题。

写个简单的导出脚本 export_model.py
import tensorflow as tf
import numpy as np

# 构建一个简单全连接网络用于演示
model = tf.keras.Sequential([
    tf.keras.layers.Dense(16, activation='relu', input_shape=(10,)),
    tf.keras.layers.Dense(8, activation='relu'),
    tf.keras.layers.Dense(1, activation='sigmoid')
])

model.compile(optimizer='adam', loss='binary_crossentropy')

# 生成校准数据集(用于量化)
def representative_dataset():
    for _ in range(100):
        data = np.random.rand(1, 10).astype(np.float32)
        yield [data]

# 转换器配置
converter = tf.lite.TFLiteConverter.from_keras_model(model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]  # 启用默认优化
converter.representative_dataset = representative_dataset
converter.target_spec.supported_ops = [
    tf.lite.OpsSet.TFLITE_BUILTINS,  # 只使用TFLite内置操作
]
tflite_quant_model = converter.convert()

# 保存为二进制文件
with open("model_quantized.tflite", "wb") as f:
    f.write(tflite_quant_model)

print("✅ 模型已成功导出为 model_quantized.tflite")

📌 关键点解析:
- Optimize.DEFAULT :启用权重量化(Weight Quantization),通常能压缩75%以上体积;
- representative_dataset :提供一组代表性输入样本,帮助量化器确定激活值的动态范围;
- OpsSet.TFLITE_BUILTINS :确保不会生成 TFLM 不支持的操作(比如复杂的 ScatterNd);
- convert() :执行图重写、常量折叠、量化压缩等优化流程。

导出后的 .tflite 文件就可以嵌入固件了!


1.5 把模型变成 C 数组:让它住在 Flash 里 🔧

MCU 上没有文件系统,所以我们不能像在 PC 上那样“加载一个文件”。解决办法是:把 .tflite 文件转成 C 静态数组,直接编译进程序。

使用 xxd 工具完成转换:

xxd -i model_quantized.tflite > model_data.cc

生成的内容长这样:

unsigned char model_quantized_tflite[] = {
  0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, ...
};
unsigned int model_quantized_tflite_len = 3200;

然后在 C++ 代码中引用它:

#include "tensorflow/lite/micro/micro_interpreter.h"
#include "tensorflow/lite/schema/schema_generated.h"

extern const unsigned char model_quantized_tflite[];
extern const unsigned int model_quantized_tflite_len;

constexpr int kTensorArenaSize = 2 * 1024;
uint8_t tensor_arena[kTensorArenaSize];

void setup() {
  tflite::MicroInterpreter interpreter(
      tflite::GetModel(model_quantized_tflite),
      /*op_resolver=*/nullptr,
      tensor_arena,
      kTensorArenaSize);

  if (interpreter.AllocateTensors() != kTfLiteOk) {
    TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
    return;
  }

  TfLiteTensor* input = interpreter.input(0);
}

🧠 深度理解几个关键概念:
- tensor_arena :一块预分配的内存池,用来存放每一层的中间张量(feature maps)。大小必须足够,否则会崩溃;
- AllocateTensors() :根据模型结构计算所需内存总量,并进行布局规划;
- GetModel() :解析 FlatBuffer 格式的 .tflite 文件头部,提取 Operator、Tensor、SubGraph 等元信息。

到这里,我们的模型终于完成了从 Python 到 C++ 的跨越!


第一次运行 TFLM:Hello World,但意义非凡 🌍

理论讲再多不如亲手跑一次。让我们以 TensorFlow 官方的 hello_world 示例为基础,看看如何在 ESP32-S3 上真正跑起来。

2.1 获取并分析示例项目结构

git clone https://github.com/tensorflow/tflite-micro-examples.git
cd tflite-micro-examples/hello_world/esp32

目录结构如下:

hello_world/
├── CMakeLists.txt
├── main/
│   ├── CMakeLists.txt
│   └── hello_world_main.cc
├── components/
│   └── tflite_micro/
└── partitions.csv

核心逻辑在 hello_world_main.cc 中:

void SetupTFLM() {
  static tflite::MicroInterpreter interpreter(
      model, resolver, tensor_arena, kArenaSize, error_reporter);

  TfLiteStatus allocate_status = interpreter.AllocateTensors();
  if (allocate_status != kTfLiteOk) {
    TF_LITE_REPORT_ERROR(error_reporter, "Allocate failed");
  }

  input = interpreter.input(0);
  output = interpreter.output(0);
}

void loop() {
  input->data.f[0] = position;           // 设置输入
  TfLiteStatus invoke_status = interpreter.Invoke(); // 执行推理
  float y = output->data.f[0];
  printf("Input: %.3f, Output: %.3f\n", position, y);
  delay(1000);
}

👀 观察这段代码你会发现:
- 它模拟了一个正弦波预测任务,输入是时间 x ,输出是 sin(x)
- input->data.f[0] 表示第一个浮点输入元素;
- Invoke() 是触发推理的核心函数;
- 结果通过 UART 打印出来,便于调试。

该项目已经内置了量化后的 .tflite 模型作为头文件,无需额外转换。


2.2 编译、烧录、看输出!🚀

使用 idf.py 一键三连:

idf.py set-target esp32s3
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor

monitor 子命令会启动串口监视器,默认波特率为 115200。成功运行后你会看到类似输出:

Input: 0.000, Output: 0.000
Input: 0.012, Output: 0.012
Input: 0.024, Output: 0.024
...

👏 恭喜!你刚刚在一个只有几百KB RAM 的芯片上运行了神经网络!

ESP-IDF 在烧录时会自动划分 Flash 区域:

分区 类型 地址范围 用途
bootloader app 0x0000 启动加载程序
partition_table data 0x8000 存储分区布局
nvs data 0x9000 非易失性存储区
phy_init data 0xA000 RF校准数据
factory app 0x10000 主应用程序

.tflite 模型就藏在 factory 应用区里,和代码一起被打包进固件。


2.3 内存监控与调优:Arena 够不够?栈会不会溢出?

TFLM 运行期间主要消耗两类内存:
1. Tensor Arena :静态分配,存放中间张量;
2. Stack Space :动态增长,用于函数调用栈。

使用 MicroProfiler 监控性能
tflite::MicroProfiler profiler;
tflite::MicroInterpreter interpreter(model, resolver, tensor_arena,
                                     kArenaSize, error_reporter, &profiler);

// 推理后打印统计
profiler.LogProfileData();

输出示例:

=== Micro Interpreter Profiling Report ===
Total runtime: 1120 us
Peak memory usage: 2.1 KB
Operators executed: 2 (FullyConnected x2)

💡 如果出现 kTfLiteError 提示 “arena size too small”,那就逐步增加 kTensorArenaSize ,直到成功为止。建议初始值设为模型参数总量的1.5倍。

调整任务栈大小

默认 FreeRTOS 任务栈是 3072 字节,但对于深层网络可能不够。可以通过菜单配置修改:

idf.py menuconfig
# Component config → FreeRTOS → Thread Stack Size

建议提升至 4096 字节,防止栈溢出。


定制化 AI 应用实战:让设备“感知世界” 🧠

通用示例只是起点。真正的价值在于构建面向实际场景的定制化 AI 系统。下面我们以加速度计动作识别和麦克风关键词唤醒为例,展示完整闭环开发流程。


3.1 如何设计适合 MCU 的模型?别堆层数,要讲策略!

加速度计动作分类:静止 / 行走 / 跑步

假设采样频率为 50Hz,每次采集 2 秒数据 → 每条样本包含 100 个时间步,每个时间步有 X/Y/Z 三个通道 → 输入形状 (100, 3)

我们可以用轻量级 CNN 来捕捉时间维度上的模式变化:

def create_motion_classifier():
    model = models.Sequential([
        layers.Reshape((100, 3, 1), input_shape=(100, 3)),  # 转为图像形式

        layers.Conv2D(8, kernel_size=(3, 3), activation='relu', padding='same'),
        layers.MaxPooling2D(pool_size=(2, 2)),

        layers.DepthwiseConv2D(kernel_size=(3, 3), activation='relu', padding='same'),
        layers.Conv2D(16, kernel_size=(1, 1), activation='relu'),
        layers.MaxPooling2D(pool_size=(2, 2)),

        layers.Flatten(),
        layers.Dense(16, activation='relu'),
        layers.Dropout(0.5),
        layers.Dense(3, activation='softmax')
    ])
    return model

📌 设计要点:
- 总参数数仅约 5.8K ,非常适合 MCU;
- 使用 ReLU 激活函数,利于量化;
- Dropout 仅在训练阶段启用,不影响最终模型大小;
- 输出层用 softmax 得到概率分布。

训练完成后,记得用全整数量化导出:

converter.target_spec.supported_types = [tf.int8]
converter.inference_input_type = tf.uint8
converter.inference_output_type = tf.uint8

3.2 数据预处理怎么做?别丢给Python,让它进模型!

很多开发者犯的一个错误是:训练时用 Python 做 MFCC、滤波、归一化,到了 MCU 却发现没法复现。

解决方案: 把前处理固化进模型图中!

例如,构建一个带音频前端的完整模型:

def create_audio_model_with_frontend(sample_rate=16000, frame_size=480):
    inputs = tf.keras.Input(shape=(sample_rate,), dtype=tf.float32)

    frames = tf.signal.frame(inputs, frame_size, frame_size, pad_end=True)
    stfts = tf.signal.rfft(frames)
    magnitude_spectrograms = tf.abs(stfts)

    linear_to_mel_weight_matrix = tf.signal.linear_to_mel_weight_matrix(
        num_mel_bins=10,
        num_spectrogram_bins=magnitude_spectrograms.shape[-1],
        sample_rate=sample_rate,
        lower_edge_hertz=20,
        upper_edge_hertz=4000
    )
    mel_spectrograms = tf.matmul(magnitude_spectrograms, linear_to_mel_weight_matrix)
    log_mel = tf.math.log(mel_spectrograms + 1e-6)
    outputs = tf.expand_dims(log_mel, axis=-1)

    return tf.keras.Model(inputs=inputs, outputs=outputs)

然后拼接到分类网络上,导出为单一 .tflite 文件。这样一来,MCU 只需喂原始音频即可,无需实现复杂 DSP 算法。


3.3 自定义算子?完全可控才是生产级!

TFLM 默认不支持 tf.gather resize 等操作。遇到这种情况怎么办?

方案一:重构模型,避开不支持OP

最稳妥的方式是在设计阶段规避非常规操作。例如:
- 用 embedding_lookup 替代 gather
- 用转置卷积替代插值上采样

方案二:启用 Select TF Ops(慎用!)
converter.target_spec.supported_ops = [
    tf.lite.OpsSet.TFLITE_BUILTINS,
    tf.lite.OpsSet.SELECT_TF_OPS
]

但这会让二进制膨胀数百KB,不适合资源紧张的设备。

方案三:实现自定义 Kernel(推荐!)

比如我们想实现一个叫 SQNL 的激活函数:

TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteEvalTensor* input = tflite::micro::GetEvalInput(context, node, 0);
  TfLiteEvalTensor* output = tflite::micro::GetEvalOutput(context, node, 0);

  const float* input_data = input->data.f;
  float* output_data = output->data.f;
  int size = ElementCount(*input->dims);

  for (int i = 0; i < size; ++i) {
    float x = input_data[i];
    if (x > 2.0f) output_data[i] = 1.0f;
    else if (x >= 0.0f) output_data[i] = x - (x * x) / 4.0f;
    else if (x >= -2.0f) output_data[i] = x + (x * x) / 4.0f;
    else output_data[i] = -1.0f;
  }
  return kTfLiteOk;
}

注册后即可在解释器中使用:

auto resolver = CreateOpResolver(); // 包含 AddCustom("SQNL", ...)
tflite::MicroInterpreter interpreter(model, resolver, tensor_arena, ...);

3.4 实时系统构建:中断 + 队列 + 双核协同 💥

要在毫秒级响应事件,就不能靠轮询。要用中断驱动 + FreeRTOS 任务通信。

示例:加速度计中断触发推理
QueueHandle_t inference_queue;

void IRAM_ATTR gpio_isr_handler(void* arg) {
    uint32_t gpio_num = (uint32_t)arg;
    xQueueSendFromISR(inference_queue, &gpio_num, NULL);
}

void sensor_task(void* pvParameters) {
    gpio_config(&io_conf);
    gpio_install_isr_service(0);
    gpio_isr_handler_add(GPIO_NUM_12, gpio_isr_handler, (void*)GPIO_NUM_12);

    while (1) {
        uint32_t io_num;
        if (xQueueReceive(data_queue, &io_num, portMAX_DELAY)) {
            float raw_data[300];
            read_accelerometer(raw_data);
            xQueueSendToBack(inference_queue, raw_data, 0);
        }
    }
}

推理任务运行在另一核心:

void inference_task(void* pvParameters) {
    while (1) {
        float input_buf[300];
        if (xQueueReceive(inference_queue, input_buf, pdMS_TO_TICKS(10))) {
            // 填充输入
            TfLiteTensor* input = interpreter.input(0);
            for (int i = 0; i < 300; ++i) {
                input->data.f[i] = input_buf[i];
            }

            auto start = esp_timer_get_time();
            interpreter.Invoke();
            auto end = esp_timer_get_time();

            int result = get_top_label(interpreter.output(0));
            printf("Predicted: %d, Latency: %lld us\n", result, end - start);
        }
    }
}

✅ 双核并行 + 中断触发 + 队列解耦 → 实现低延迟、高可靠推理。


生产级部署考量:不只是“能跑”,还要“稳跑” 🔒

4.1 内存优化黄金法则

方法 效果
FP32 → INT8 量化 内存 ↓75%,速度 ↑30~60%
层融合(Conv+ReLU) 减少临时缓冲,速度 ↑15%
CMSIS-NN 加速 卷积层提速 2~4 倍
Arena 紧致布局 节省 10% 内存
移除调试字符串 固件减小 5%

4.2 安全加固:防篡改、防崩溃、防降级

  • 启用 Secure Boot v2 和 Flash Encryption
  • 模型哈希写入 eFuse,防止逆向
  • OTA 升级前校验模型版本与输入形状兼容性
  • 添加看门狗机制检测推理超时
TfLiteStatus InvokeWithWatchdog(tflite::MicroInterpreter* interpreter) {
    uint32_t start_ms = esp_timer_get_time() / 1000;
    TfLiteStatus status = interpreter->Invoke();
    uint32_t elapsed = (esp_timer_get_time() / 1000) - start_ms;

    if (elapsed > MAX_INFER_TIME_MS) {
        ESP_LOGE("TFLM", "Timeout: %d ms", elapsed);
        HandleTimeout();
    }
    return status;
}

4.3 功耗优化:电池设备的生命线 ⚡

利用 ESP32-S3 的 ULP 协处理器监听传感器中断,主核保持 light sleep 模式。

esp_sleep_enable_ext0_wakeup(GPIO_NUM_12, 1);  // 高电平唤醒
esp_light_sleep_start();
start_inference();  // 唤醒后执行

实测平均功耗从 15mA 降至 0.3mA,CR2032 电池可持续工作半年以上!


结语:嵌入式 AI 的未来,在于“克制之美” ✨

当我们不再追求“最大模型”、“最高精度”,而是思考“最小可行系统”、“最低能耗方案”时,才真正进入了 TinyML 的世界。

TensorFlow Lite Micro 不只是一个框架,它是一种哲学: 在极限约束下创造智能

而 ESP32-S3 正是这一理念的最佳载体——性能足够强,成本足够低,生态足够成熟。

无论你是做可穿戴设备、工业监测,还是智能家电,这套方法论都能帮你快速落地产品。

所以,别再等了。拿起你的开发板,烧录第一个 .tflite 模型吧。也许下一个改变世界的智能终端,就诞生于你今晚的一次尝试之中。🚀

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

您可能感兴趣的与本文相关内容

### 关于 ESP32-S3-Eye 的学习教程和使用指南 ESP32-S3-Eye 是基于 ESP32-S3 平台的一款开发板,集成了摄像头模块和其他外设,适合用于图像处理、机器视觉以及人工智能相关的项目开发。以下是关于其学习教程和使用指南的相关内容: #### 1. **硬件概述** ESP32-S3-Eye 配备了 OV2640 摄像头模组,支持多种分辨率的图片采集,并通过 SPI 接口与主控芯片通信。该开发板还内置了丰富的 GPIO 引脚以及其他外围接口,便于扩展其他功能模块[^1]。 #### 2. **固件烧录注意事项** 在进行固件烧录前,需要注意以下几点硬件环境设置: - Strapping 管脚 GIO45 默认为下拉状态,在进入下载模式时应保持低电平以激活 Flash 芯片。 - 特殊情况下,如果 GPIO46 设置为高电平而 GPIO0 同时处于低电平时,则无法正常工作。 - 上电时序要求严格:当电源电压 (VDD) 达到约 2.8 V 时,EN 引脚上的电压不得超过 0.8 V,具体细节可以参考官方技术文档中的相关内容[^2]。 #### 3. **软件开发工具链** 对于初学者来说,推荐采用 Arduino IDE 进行程序编写和调试。此外还可以利用 Espressif 提供的一系列 SDK 来加速开发流程。例如,若涉及图形界面显示部分,则可以通过集成 LVGL 图形库来实现更复杂的人机交互体验[^4]。 #### 4. **深度学习模型部署** 由于 ESP32-S3 支持运行轻量级神经网络框架如 TensorFlow Lite Micro 或者自定义优化后的 PyTorch/Caffe 模型等,因此非常适合用来执行边缘侧推理任务。Espressif 自家推出的 ESP-DL 库能够帮助开发者轻松移植预训练好的 DNN 结构至目标平台之上完成诸如手势识别等功能演示。 #### 示例代码片段 下面给出一段简单的 Python 脚本来展示如何初始化相机并捕获一帧数据: ```python import camera def init_camera(): """ 初始化OV2640摄像头 """ try: cam = camera.init(0, format=camera.JPEG) if not cam: raise Exception(&#39;Failed to initialize the camera!&#39;) return cam except Exception as e: print(f&#39;Error occurred during initialization: {str(e)}&#39;) cam_instance = init_camera() if cam_instance is None: exit(-1) frame_data = cam_instance.capture() # 获取当前画面作为JPEG编码字节数组 print(len(frame_data), &#39;bytes captured.&#39;) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值