המאמר הזה הוא השלישי בסדרת המאמרים על הרצה של מודל בינה מלאכותית על מיקרו בקר מסוג ESP32. למי שבמקרה נוחת כאן בפעם הראשונה – מומלץ להתחיל דווקא עם פוסט המבוא על AIoT – השילוב המהמם של AI ו-IoT שבאמצעותו אנו מריצים מודלים של למידת מכונה וגם LLMים על יחידות קצה זולות ופשוטות ללא אינטרנט.
בפוסט הקודם יצרנו מודל שמזהה אנומליה של מתאן כדי… נו… כדי לתפוס פלצנים עצרנו בשלב שבו יצרנו tflite. בפוסט הזה, שהוא הפוסט המסכם, אנו נדפלט (מלשון deploy – העברה לעולם האמיתיח) את המודל אל esp32 שיכול להפעיל צופר או באזר או נורות אזהרה (או גם וגם) והכל ללא אינטרנט, ללא תשתית, רק חשמל, מודל למידת מכונה וזהו. ואז יש לנו מכונה שעושה דברים ויכולה לרוץ למשך הרבה זמן.
במקרה הזה, אנו נזנח את הפייתון ונחזור לעולם ה-C. למדנו איך עובדים עם ארדואינו IDE בפוסט הראשון בסדרה.
דיפלוי מודול למידת מכונה דוגמה לראות שהכל עובד ומנגן
לפני שצוללים אל עולם הדיפלוימנט האמיתי, אני בד״כ ממליץ לעבוד עם קוד דוגמה כדי לראות שהכל מנגן כמו שצריך. קודם כל זה נועד לשלול בעיות חיבור, בעיות זכרון או סתם מיקרובקר מקולקל (זה יכול להיות). קוד פשוט שעובד מאפשר לנו לבדוק שהמכונה עצמה עובדת וזה חשוב. שנית, אפשר לקחת את הקוד הזה אל ChatGPT ולהציג אותו כדוגמה וכך למנוע ממנו להטריף אתכם בהזיות וקוד שלא עובד. ברגע שנותנים לו דוגמה ואומרים לסקיינט המודרנית ״זה עובד, עכשיו תעשה שהדבר הזה יעשה כך וכך״ – זה יותר טוב ויגרום לכם פחות כאב ראש.
אנחנו מחפשים ספריות TinyML ל-ESP32. אני אשתמש ב-MicroTFLite. למרות שיש כמה כאלו. ראשית, נתקין אותה בארדואינו IDE שלנו. איך? ניכנס ל-Sketch, נלחץ על Include Library ונחפש את Manage Libraries למעלה:

ייפתח לנו חלון צדדי שבו אנו יכולים לחפש ספריות. נחפש את MicroTFLite ונלחץ על Install

השלב הבא הוא לשמור את ה-Sketch במיקום כלשהו. זהו! אנחנו מוכנים לעבודה! נוכל לבצע import לספריה הזו בלי בעיה. אם לא נבצע import, הספריה לא תיכללץ
ניכנס אל תיקית examples אל Hello world. יש שם גם דוגמת קוד וגם מודל שמבצע חיזוי – המודל הוא בסיומת h ושם הקובץ שלו הוא model.h. אנו נוריד אותו ונשמור אותו בתיקיה שבה שמרנו את ה-sketch שלנו. זה קריטי.
השלב הבא הוא להעתיק את הקוד שמופיע בדוגמת ה-Hello World, אני מציב אותו פה כדוגמה, אבל עדיף לקחת אותו משם:
/* Copyright 2024 johnos, TensorFlow Authors. All Rights Reserved.
This sketch is derived from the classic Hello World example of the general
TensorFlow Lite Micro library.
It was adapted and simplified by (based on original work by Chirale)
to conform to the typical style of Arduino sketches.
It has been tested on an ESP32 Dev Board.
The sketch implements a Deep Neural Network pre-trained on calculating
the function sin(x).
By sending a value between 0 and 2*Pi via the Serial Monitor,
both the value inferred by the DNN model and the actual value
calculated using the Arduino math library are displayed.
It shows how to use MicroTFLite Library to run a TensorFlow Lite model.
For more information read the library documentation
at: https://github.com/johnosbb/MicroTFLite
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
// include main library header file
#include <MicroTFLite.h>
// include static array definition of pre-trained model
#include "model.h"
// The Tensor Arena memory area is used by TensorFlow Lite to store input, output and intermediate tensors
constexpr int kTensorArenaSize = 2000;
alignas(16) uint8_t tensor_arena[kTensorArenaSize];
void setup()
{
// Initialize serial communications and wait for Serial Monitor to be opened
Serial.begin(115200);
while (!Serial)
;
Serial.println("Sine(x) function inference example.");
Serial.println("Initializing TensorFlow Lite Micro Interpreter...");
// Initialize the model
if (!ModelInit(model, tensor_arena, kTensorArenaSize))
{
Serial.println("Model initialization failed!");
while (true)
;
}
Serial.println("Model initialization done.");
// Optional: Print model metadata
ModelPrintMetadata();
// Optional: Print input and output tensor dimensions
ModelPrintInputTensorDimensions();
ModelPrintOutputTensorDimensions();
Serial.println("");
Serial.println("Please, input a float number between 0 and 6.28");
}
void loop()
{
// Check if a value was sent from Serial Monitor
if (Serial.available())
{
String inputValue = Serial.readString();
float x = inputValue.toFloat(); // Evaluates to zero if the user input is not a valid number
Serial.print("Your input value: ");
Serial.println(x);
// The model was trained in range 0 to 2*Pi, ensure value stays within this range
if (x < 0)
x = 0;
if (x > 6.28)
x = 6.28;
Serial.print("Adapted input value: ");
Serial.println(x);
// Set the value in the model's input tensor
if (!ModelSetInput(x, 0, true))
{
Serial.println("Failed to set input!");
return;
}
// Run inference, and report if an error occurs
if (!ModelRunInference())
{
Serial.println("RunInference Failed!");
return;
}
// Obtain and print the output from the model's output tensor
float y = ModelGetOutput(0);
Serial.print("Inferred Sin of ");
Serial.print(x);
Serial.print(" = ");
Serial.println(y, 2);
Serial.print("Actual Sin of ");
Serial.print(x);
Serial.print(" = ");
Serial.println(sin(x), 2);
}
}
ואז… זה הזמן ללחוץ על כפתור ה-Play ולהעלות את הקוד, המודל והספריה. זה יקח כמה דקות עד שהכל ייארז. השלב הבא הוא לפתוח את ה-Activity monitor – לוודא שהבאוד רייט תקין 0- ולראות שאפשר להזין נתון והוא יחזה אותו.

אם זה לא עובד לכם בפעם הראשונה – תנתקו ותחברו את המיקרובקר למחשב. זה אמור לעבוד. אם לא עובד – זה הזמן לבדוק למה – לנסות עם מיקרובקר אחר? לבדוק את החיבור ואת הכבל? לרוץ ל-ChatGPT? הכל הולך. אבל טוב לבדוק את זה בקוד דוגמה שאתם יודעים שהוא עובד מאשר באמצע הפעילות על מודל שלכם. זה מודל Hello World, הוא אמור לעבוד מהקופסה. אם לא – כדאי לנסות מודל אחר. אבל מה שחשוב הוא להצמד לדוגמה הפשוטה ולוודא שהיא עובדת לפני שרצים הלאה.
זה עובד? מעולה! עכשיו בואו וניקח את זה למודל שלנו. ניקח את ה-tflite שיצרנו – במקרה שלי זה המודל שמזהה אנומליות של גזים. אצלכם זה יכול להיות כל דבר אחר.
המרת המודל ל-C
אם יש לנו מק או לינוקס, יש לנו בשורת הפקודה את xxd -i mq4_autoencoder.tflite mq4_autoencoder.h שממיר את המודל שלנו לפורמט ש-sketch יכול לעבוד איתו. נקליד את השורה:
xxd -i mq4_autoencoder.tflite mq4_autoencoder.h
יווצר קובץ עם סיומת h. יש להעתיק את הקובץ לתיקיה של ה-sketch שלנו. בדיוק כפי שעשינו בדוגמה. אם נסתכל עם הארדואינו IDE על הקובץ, נראה שהוא מערכים של מספרים. בסופו של דבר כל הבינה המלאכותית זו מתמטיקה, חשוב לזכור
אם יש לכם חלונות, תצטרכו להעזר בפייתון עם הקוד הזה:
with open("model.tflite", "rb") as f:
model_data = f.read()
with open("model_data.h", "w") as f:
f.write("const unsigned char model_data[] = {")
f.write(",".join(["0x{:02x}".format(b) for b in model_data]))
f.write("};\n")
f.write(f"const unsigned int model_data_len = {len(model_data)};")
כך או אחרת, יש לכם מודל בתיקיה, אנחנו מוכנים לבדוק איך הוא יראה.
כתיבת הקוד
חשוב: למדתי שכדאי לתת ל-ChatGPT קוד שעובד. נעתיק את הקוד שעבד מדוגמת ה-Hello World ל-ChatGPT ונבקש ממנו ליצור קוד שטוען את המודל שלנו על פי התבנית של הקוד הזה ומריץ בדיקה של המודל. משהו בסגנון הזה:
#include <Arduino.h>
#include <MicroTFLite.h>
#include "mq4_autoencoder.h" // Ensure this file exists
// Define Tensor Arena Size (adjust based on model size)
constexpr int kTensorArenaSize = 64 * 1024;
alignas(16) uint8_t tensor_arena[kTensorArenaSize];
void setup()
{
Serial.begin(115200);
while (!Serial); // Wait for serial monitor to be ready
Serial.println("MQ4 Autoencoder - Anomaly Detection");
Serial.println("Initializing TensorFlow Lite Micro Interpreter...");
Serial.print("Model size: ");
Serial.println(mq4_autoencoder_tflite_len);
// Check if model is correctly loaded
if (mq4_autoencoder_tflite == NULL || mq4_autoencoder_tflite_len == 0)
{
Serial.println("ERROR: Model data is NULL or size is 0!");
while (true); // Halt execution
}
// Corrected Model Initialization (use correct model name)
if (!ModelInit(mq4_autoencoder_tflite, tensor_arena, kTensorArenaSize))
{
Serial.println("Model initialization failed!");
while (true); // Halt execution
}
Serial.println("Model initialization done.");
Serial.print("Model size: ");
Serial.println(mq4_autoencoder_tflite_len);
// Optional: Print Model Metadata & Tensor Info
ModelPrintMetadata();
ModelPrintInputTensorDimensions();
ModelPrintOutputTensorDimensions();
}
void loop()
{
float input_data[50] = {0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0,
2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 3.0,
3.1, 3.2, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0,
4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, 5.0};
// Set input tensor
for (int i = 0; i < 50; i++)
{
if (!ModelSetInput(input_data[i], i, true))
{
Serial.println("Failed to set input!");
return;
}
}
// Run inference
if (!ModelRunInference())
{
Serial.println("RunInference Failed!");
return;
}
// Retrieve model output (50 values)
Serial.println("Anomaly Detection Output:");
for (int i = 0; i < 50; i++)
{
float output_value = ModelGetOutput(i);
Serial.print("Output[");
Serial.print(i);
Serial.print("] = ");
Serial.println(output_value, 4); // Print output with 4 decimal places
}
Serial.println("Inference completed.\n");
delay(2000); // Run inference every 2 seconds
}
בעידן ה-ChatGPT וה-LLMים האחרים, כתיבת הקוד פחות משמעותית. מה שחשוב הוא ההנחיה והחשיבה מראש. אם תנסו לגשת ישירות אל בניית הקוד שעושה את מה שאתם רוצים/צריכים – סביר להניח שתיכשלו. חשוב לעבוד מתודית ובצעדים קטנים. כתיבת קוד למיקרובקרים היא יותר מסובכת והסיכוי ליפול הוא גדול. זו הסיבה שבפוסט הזה מה שחשוב לי להדגיש הוא יותר הכוונה ודרך העבודה ופחות הקוד שאותו אני מביא כדוגמה.
אם יש לכם תקלות, ויהיו, זה הזמן להפעיל את ה-LLM ולהתחיל לדבג. אבל זה חלק מהלימוד.
אם הכל תקין, תוכלו לראות שהקוד הזה עובד. עכשיו מתחילה החגיגה האמיתית בעצם – איסוף 50 דוגמאות ושליחה למודל לניתוח – אפשר כל 50 שניות, או כל 50 דקות. אבל זה כבר קל בעידן ה-LLM של היום. כל מה שצריך לעשות זה רק לבקש ולבדוק. חשוב לעבוד בצעדים קטנים. ומה קורה אם מוצאים? זה הזמן לחבר אמצעי פלט למיקרובקר שלנו – מנורה עד זמזם או צפצפה ולראות שהכל מתפוצץ ברגע שיש אנומליה והסרחנים הקטנים נמלטים מהסלון