▼點擊下方名片,關注公眾號,獲取更多精彩內容▼
這里是《賀老師講嵌入式AI》,我是《嵌入式AI:讓單片機學會思考》課程主理人,專注AI在MCU上的落地實踐。
在單片機工程里,我們每天都在寫函數。讀取 ADC 是函數,計算溫度是函數,判斷按鍵狀態也是函數。神經網絡推理也可以先按這個方式理解:給它一段輸入數組,它按照固定順序計算,最后返回一段輸出數組。
比如一個姿態識別模型,輸入可能是 1 秒鐘的三軸加速度數據;一個故障檢測模型,輸入可能是 256 個采樣點的振動波形;一個關鍵詞識別模型,輸入可能是一小段音頻特征。模型并不會在單片機上重新學習,它只會把這段輸入按訓練后確定下來的參數算一遍。
推理階段最關鍵的一句話:權重和偏置已經確定,MCU 只負責把輸入代入固定計算流程,得到輸出結果。
如果用 C 語言寫一個極簡接口,它可能長這樣:
void model_run(const int8_t *input, int8_t *output)
{
/* 真實工程里,這里通常由 TFLite Micro、STM32Cube.AI、
CMSIS-NN 或廠商推理庫生成/調用。 */
dense_layer_0(input, hidden_buffer);
relu(hidden_buffer);
dense_layer_1(hidden_buffer, output);
}這段代碼沒有“思考”的過程,只有確定的輸入、確定的參數和確定的計算順序。它和普通業務函數的差別,不在于執行方式突然變神秘,而在于函數內部的參數不是程序員手寫出來的規則,而是訓練階段從數據中擬合出來的數值。

神經網絡里經常出現兩個詞:權重和偏置。它們聽起來像算法術語,落到固件里其實就是一批數字。權重決定輸入特征之間怎么組合,偏置用于調整計算結果的基準位置。訓練結束后,這些數字會被導出成模型文件,再被轉換工具變成 MCU 能鏈接進去的數據。
最小的例子可以從一個全連接層看起。假設輸入有 3 個數,輸出有 2 個數,那么每個輸出都要和 3 個輸入做乘加,再加上一個偏置:
static const int8_t W[2][3] = {
{ 12, -7, 4 },
{ -3, 9, 11 }
};
static const int32_t B[2] = {
128,
-64
};
void dense_3_to_2(const int8_t x[3], int32_t y[2])
{
for (int out = 0; out < 2; out++) {
int32_t acc = B[out];
for (int in = 0; in < 3; in++) {
acc += (int32_t)x[in] * (int32_t)W[out][in];
}
y[out] = acc;
}
}這就是很多神經網絡算子的核心影子:數組讀取、乘法、加法、循環、結果寫回。卷積層比這個例子多了窗口滑動和通道循環,深度可分離卷積多了不同的計算組織方式,激活函數會把結果限制到某個范圍,但底層仍然離不開“按固定順序讀數組并做數值計算”。
所以單片機能夠運行神經網絡,并不是因為它具備了某種抽象智能,而是因為神經網絡推理可以被編譯成 MCU 能執行的指令:讀 Flash 里的權重,讀 RAM 里的輸入和中間結果,用 CPU/DSP/NPU 做乘加,最后把輸出寫回 RAM。
普通 C 函數只要參數類型對得上,很多時候就能運行。模型推理函數也有輸入輸出接口,但它對數據格式更敏感。輸入長度、排列順序、量化比例、預處理方式必須和訓練時保持一致,否則模型會正常運行,卻輸出不可信的結果。
輸入長度
訓練時用 128 個采樣點,板端就不能臨時改成 100 個點。長度變了,后面的權重矩陣維度就對不上。
數據順序
訓練時按 ax、ay、az 排列,板端不能變成 az、ay、ax。順序錯了,模型會把同一組物理信號理解成另一種特征。
數值范圍
訓練時做過歸一化或量化,板端也要執行同樣的縮放。尤其是 int8 模型,零點和縮放系數不能隨意處理。
輸出含義
輸出不是天然等于動作命令。分類分數、異常分數、回歸值需要經過閾值、連續幀確認或狀態機再進入業務邏輯。
這也是很多 MCU AI 項目失敗的位置:模型函數本身可以跑,串口也能打印結果,但輸入數據和訓練數據不是同一種格式。由于模型只會執行固定計算,它沒有辦法自動識別“這次輸入的單位變了”“這次窗口沒對齊”“這次零點沒處理”。這些問題必須由固件的數據鏈路負責。
一個更接近工程現場的推理入口,通常不是簡單傳入一段數組,而是包含采樣、窗口、預處理、推理和后處理:
bool run_vibration_model(float adc_voltage)
{
ring_buffer_push(adc_voltage);
if (!window_ready()) {
return false;
}
build_window_feature(input_tensor);
quantize_to_int8(input_tensor, model_input);
model_run(model_input, model_output);
float score = dequantize_score(model_output[FAULT_CLASS]);
return state_machine_update(score);
}這段代碼里,真正的模型推理只是中間一環。前面要保證采樣窗口穩定,后面要保證輸出結果不會直接觸發危險動作。單片機能跑神經網絡,和單片機能把神經網絡用好,是兩個層級的問題。
普通函數通常是工程師根據經驗寫出來的。比如溫度超過 80℃ 報警,電流超過 2A 限流,振動 RMS 超過閾值提示異常。這類函數的規則清晰、可解釋、可手工調整,適合邊界明確的問題。
模型函數的計算流程同樣固定,但內部參數來自訓練數據。它不要求工程師把每條規則都寫出來,而是用大量樣本把“哪些組合更像某一類狀態”壓縮進權重和偏置里。對于多傳感器、多特征、邊界模糊、閾值難以穩定覆蓋的問題,這種方式更容易表達復雜模式。
程序員明確寫出判斷條件,例如 if (rms > threshold)。好處是行為清楚,壞處是復雜場景下規則會越來越多,并且很難覆蓋邊界狀態。
程序員確定輸入格式和運行鏈路,訓練過程確定權重和偏置。部署后,MCU 仍然執行固定代碼,只是這段代碼內部包含大量由數據生成的常量參數。
用一句工程化的話總結:普通函數的核心資產是代碼邏輯,模型函數的核心資產是參數。代碼邏輯決定它怎么計算,模型參數決定它對輸入模式的判斷傾向。部署到單片機后,這兩者都必須變成可編譯、可鏈接、可運行、可測試的固件內容。
既然神經網絡推理可以看成 C 函數,接下來就能解釋 MCU 部署最常見的三個限制。
Flash 決定模型參數能不能放下。 權重和偏置通常以常量數組形式存在,模型越大,參數越多,占用的 Flash 或外部 Flash 就越多。INT8 量化后,參數體積通常會明顯下降,這也是 MCU 部署經常優先使用量化模型的原因。
RAM 決定中間結果能不能周轉。 輸入張量、輸出張量、中間激活值、臨時緩沖區都要占 RAM。很多模型看起來權重不大,但運行時中間激活很大,最終卡在 RAM,而不是卡在 Flash。
算子支持決定固定流程能不能生成有效代碼。 如果模型里用了部署框架不支持的層,轉換工具就無法把它穩定變成 MCU 端可執行流程。工程上經常要反過來設計模型結構,優先使用 MCU 推理庫支持成熟、優化充分的算子。
這三個限制對應到 C 函數的角度非常直觀:常量表放哪里,臨時數組放哪里,函數里的每一種計算有沒有可用實現。只要這三件事成立,神經網絡就不再是“云端算法”,而是一段可以被 MCU 調度的本地計算任務。
單片機能運行神經網絡,并不是因為單片機突然擁有了“智能”。它能運行,是因為推理階段已經被壓縮成確定的輸入、確定的參數、確定的計算流程和確定的輸出接口。
做 MCU AI 項目時,最實用的判斷方式不是先討論網絡名字,而是把它拆回固件問題:輸入數組怎么來,權重和偏置放在哪里,中間緩沖夠不夠,推理函數多久跑完,輸出結果怎么進入狀態機。
如果這些問題都能回答清楚,單片機運行神經網絡就不是一句口號,而是一個可以驗證的工程鏈路。如果這些問題回答不清楚,即使模型在電腦上準確率很高,到了板端也可能只是一個能編譯、能打印、但無法穩定用于產品判斷的 demo。
由于微信公眾號規則發生改變,點贊和收藏后,才能持續推送給您更多相近的文章,歡迎點贊和推薦。
