// 导入模型
#include "sine_model.h"

// 正弦波参数
constexpr int led_pin = 2;
constexpr float pi = 3.14159265;
constexpr float freq = 0.5;
constexpr float period = (1 / freq) * 1000000;

// TensorFlow Lite 全局变量
namespace {
  tflite::ErrorReporter* error_reporter = nullptr;
  const tflite::Model* model = nullptr;
  tflite::MicroInterpreter* interpreter = nullptr;
  TfLiteTensor* model_input = nullptr;
  TfLiteTensor* model_output = nullptr;

  // Tensor 内存池
  constexpr int kTensorArenaSize = 5 * 1024;
  uint8_t tensor_arena[kTensorArenaSize];
}

void setup() {

  // 初始化日志
  static tflite::MicroErrorReporter micro_error_reporter;
  error_reporter = &micro_error_reporter;

  // 加载模型
  model = tflite::GetModel(sine_model);

  // 注册模型使用的算子
  static tflite::MicroMutableOpResolver micro_mutable_op_resolver;
  micro_mutable_op_resolver.AddBuiltin(
      tflite::BuiltinOperator_FULLY_CONNECTED,
      tflite::ops::micro::Register_FULLY_CONNECTED(),
      1, 3);

  // 创建解释器
  static tflite::MicroInterpreter static_interpreter(
      model,
      micro_mutable_op_resolver,
      tensor_arena,
      kTensorArenaSize,
      error_reporter);

  interpreter = &static_interpreter;

  // 分配 Tensor 内存
  interpreter->AllocateTensors();

  // 获取输入输出 Tensor
  model_input = interpreter->input(0);
  model_output = interpreter->output(0);
}

void loop() {

  // 生成输入数据
  unsigned long timestamp = micros() % (unsigned long)period;
  float x_val = ((float)timestamp * 2 * pi) / period;

  // 写入模型输入
  model_input->data.f[0] = x_val;

  // 执行推理
  interpreter->Invoke();

  // 获取模型输出
  float y_val = model_output->data.f[0];

  // 输出到 LED
  int brightness = (int)(255 * y_val);
  analogWrite(led_pin, brightness);
}

整体流程:

一:加载模型
    ↓
二:创建解释器
    ↓
三:分配 Tensor Arena 内存
    ↓
四:不断获取时间
    ↓
五:把时间转换成 x
    ↓
六:送入神经网络
    ↓
七:得到 y = sin(x)
    ↓
八:控制 LED 亮度

#一:加载模型

加载模型最关键的代码只有两步:

第一步:包含模型文件

#include "sine_model.h"

这个文件通常长这样:

const unsigned char sine_model[] = {
    0x20, 0x00, 0x00, ...
};

const int sine_model_len = 2488;

第二步:加载模型

model = tflite::GetModel(sine_model);

这里的:

sine_model  就是模型数组的地址

GetModel()干了什么?

作用:

解析模型
读取网络结构
读取权重
建立Model对象

返回:

const tflite::Model* #指针

接下来为什么检查版本? 

if (model->version() != TFLITE_SCHEMA_VERSION)
{
    error_reporter->Report(
        "Model version does not match Schema");
}

作用:

检查:

模型版本
=
解释器版本

例如:

模型: 3.0
解释器: 4.0

可能无法运行。

所以先检查。


model 里面到底有什么?

可以简单理解为:

model
├── 网络结构
├── 权重参数
├── 输入信息
├── 输出信息
└── 算子信息

例如:

Input(1)
    ↓
Dense(16)
    ↓
Dense(16)
    ↓
Dense(1)

这些信息都在里面。


后面为什么要创建解释器?

仅仅:

model = tflite::GetModel(...)

只是把模型解析出来。

还不能运行。

必须创建:

MicroInterpreter

如果用一句话概括:

GetModel()并没有把模型“加载到内存里”,
它只是把 Flash 中的 tflite 二进制数据解释成 TensorFlow Lite 能认识的 Model 结构,
后面由 MicroInterpreter 使用这个 Model 来完成推理。

二:创建解释器

创建解释器的代码

// 创建 TensorFlow Lite Micro 解释器
static tflite::MicroInterpreter static_interpreter(
    model,                      // 模型
    micro_mutable_op_resolver,  // 已注册的算子
    tensor_arena,               // Tensor内存池地址
    kTensorArenaSize,           // Tensor内存池大小
    error_reporter);            // 错误报告器
                                // 这些参数的内容都是提前写好的,这里只传入即可
 
// 保存解释器指针,方便后续调用
interpreter = &static_interpreter;

解释器是什么?

可以把它理解成:

模型(Model)      = 图纸
解释器(Interpreter) = 工人

仅有模型:

model = tflite::GetModel(sine_model);

只是拿到了神经网络结构和权重。

并不会运行。


解释器负责:

读取模型
    ↓
申请Tensor内存
    ↓
执行各层计算
    ↓
输出结果

即:

interpreter->Invoke();

真正干活的是解释器。


创建解释器时传入的参数

📌1. model

model

前面加载好的模型。

例如:

Input
  ↓
Dense
  ↓
Dense
  ↓
Output

解释器需要知道网络长什么样。


📌2. micro_mutable_op_resolver

作用:

你的模型会用到哪些算子(Operator)

例如(手动注册需要使用的算子):

resolver.AddBuiltin(
    tflite::BuiltinOperator_FULLY_CONNECTED,
    tflite::ops::micro::Register_FULLY_CONNECTED()
);

因为,TensorFlow Lite Micro 不会把所有算子都编译进去,因为 MCU 内存太小。 


📌3. tensor_arena(内存的首地址)和 kTensorArenaSize(内存大小)

代码位置

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

这里创建了:

5 × 1024 = 5120 字节   5KB 内存

为什么需要它?

模型文件(.tflite)只保存:

权重
偏置
网络结构

但是推理时还需要存放:

输入张量
输出张量
中间层结果
算子工作区

这些数据都放在:

tensor_arena

运行时的内存布局

tensor_arena
┌───────────────┐
│ Input Tensor  │
├───────────────┤
│ Dense1 Output │
├───────────────┤
│ Dense2 Output │
├───────────────┤
│ Output Tensor │
├───────────────┤
│ Scratch Buffer│
└───────────────┘

📌4. error_reporter

error_reporter

错误输出接口。

例如:

AllocateTensors failed

会打印到串口。


创建完成后

interpreter = &static_interpreter;     #让全局指针指向解释器

以后直接通过interpreter 操作模型

interpreter->AllocateTensors();
interpreter->Invoke();
interpreter->input(0);
interpreter->output(0);

实际上等价于:

static_interpreter.AllocateTensors();
static_interpreter.Invoke();
static_interpreter.input(0);
static_interpreter.output(0);

#三:分配 Tensor Arena 内存

// 从 tensor_arena 中为模型的张量分配内存
TfLiteStatus allocate_status = interpreter->AllocateTensors();

if (allocate_status != kTfLiteOk) {
    error_reporter->Report("AllocateTensors() failed");
    while (1);
}

先理解什么是 Tensor

神经网络运行时,数据会不断流动:

输入
 ↓
第一层
 ↓
第二层
 ↓
输出

例如:

x = 1.57

进入网络后:

输入Tensor
 ↓
隐藏层Tensor
 ↓
输出Tensor

这些过程数据都需要内存存放。


AllocateTensors() 做什么

执行:

interpreter->AllocateTensors();

时,

解释器会查看模型结构(例如):

Input(1)
 ↓
Dense(8)
 ↓
Dense(1)

它会计算:

输入需要多少字节
中间层需要多少字节
输出需要多少字节

然后在:

tensor_arena

里划分空间。

类似:

tensor_arena

┌────────────┐
│ Input      │
├────────────┤
│ Hidden     │
├────────────┤
│ Output     │
├────────────┤
│ Scratch    │
└────────────┘

分配前是什么样

刚创建时:

MicroInterpreter interpreter(...);

只是知道:

模型在哪
arena在哪
有哪些算子

但是:

Input Tensor
Output Tensor
中间Tensor

都还没有地址。

类似:

Input Tensor
地址:未知

Output Tensor
地址:未知

AllocateTensors 后

执行:

AllocateTensors();

之后:

Input Tensor
地址:0x3FFC0000

Output Tensor
地址:0x3FFC0040

Hidden Tensor
地址:0x3FFC0080

都确定了。

所以后面才能:

interpreter->input(0);     //返回的就是内存的地址

interpreter->output(0);    //返回的就是内存的地址

一句话理解

interpreter->AllocateTensors();

作用就是:

根据模型结构,在 tensor_arena 内存池中给输入、输出和中间计算结果划分内存空间,并把所有 Tensor 的地址建立好。

没有这一步,模型根本不知道数据该放在哪里,也无法执行 Invoke()


四:不断获取时间

五:把时间转换成 x

六:送入神经网络

七:运行推理

TfLiteStatus invoke_status = interpreter->Invoke();

if (invoke_status != kTfLiteOk) {
    error_reporter->Report("Invoke failed on input %f\n", x_val);
}

解释

前面已经把输入数据放进去了:

model_input->data.f[0] = x_val;

例如:

x_val = 1.57

此时数据已经在输入 Tensor 里,但模型还没开始计算。


执行:

interpreter->Invoke();

后,TensorFlow Lite Micro 会:

读取输入 Tensor
      ↓
执行神经网络各层
      ↓
计算输出结果
      ↓
写入输出 Tensor

八:得到 y = sin(x) 


九:控制 LED 亮度

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐