ورکشاپ آموزشی - آیسی مدار
رفتن به محتوا

دستیار هوشمند یخچال با هوش مصنوعی و ESP32-CAM

رباتیک و هوشمندسازی
۰ نظر
۵۱ نفر
کد ۳۰۷۱

در زندگی پرمشغله امروزی خیلی پیش میاد که میوه‌ها، سبزیجات و غذاهای باقیمانده رو داخل یخچال می‌ذاریم و کاملا فراموششون می‌کنیم تا زمانی که فاسد بشن. امروز در آیسی‌مدار قراره یک گجت هوشمند و فوق‌العاده کاربردی بسازیم که دقیقا برای حل همین مشکل طراحی شده. پروژه DecayDock یک دستیار هوشمند یخچاله که با استفاده از بینایی ماشین و هوش مصنوعی، مواد غذایی رو تشخیص می‌ده و میزان تازگی اون‌ها رو برات پیگیری می‌کنه.

مغز متفکر این گجت یک ماژول ESP32-CAM هست که در کنار یک نمایشگر کوچک TFT قرار گرفته. برای بخش هوش مصنوعی از تکنولوژی TinyML استفاده می‌کنیم؛ این یعنی تمام پردازش‌های مربوط به تشخیص تصویر مستقیما روی خود برد و به صورت آفلاین انجام می‌شه، در نتیجه دستگاه با سرعت بالا و بدون نیاز به پردازش ابری یا اینترنت کار می‌کنه.

این دستگاه به صورت مگنتی روی در یخچال نصب می‌شه. فقط کافیه ماده غذایی رو جلوی دوربینش بگیری تا اون رو شناسایی کنه و با یک نوار وضعیت رنگی بهت بگه که چقدر تا خراب شدنش مونده و کدوم غذا باید زودتر مصرف بشه. قطعات رو آماده کن تا بریم سراغ ساخت این دستیار هوشمند و جذاب آشپزخانه!

  1. دسته‌بندی مواد غذایی
  2. مدت زمان نگهداری در یخچال
  3. عمر مفید پیش‌بینی‌شده
  1. رنگ سبز ← تازه
  2. رنگ زرد ← زودتر مصرف شود
  3. رنگ قرمز ← در حال انقضا و خرابی

قطعات موردنیاز

  • نرم‌افزار Arduino IDE
  • برای برنامه‌نویسی و آپلود فرم‌ور
  • پلتفرم Edge Impulse
  • مدل تشخیص غذای TinyML
  • کتابخانه TFT_eSPI
  • رابط کاربری و گرافیک TFT
  • کتابخانه دوربین ESP32
  • رابط دوربین و ثبت تصویر
  • هویه و ابزار لحیم‌کاری
  • برای ایجاد اتصالات الکتریکی پایدار
  • تفنگ چسب حرارتی
  • برای نصب و ثابت کردن قطعات
  • مجموعه پیچ‌گوشتی
  • برای مونتاژ قاب
  • سیم‌چین و سیم‌لخت‌کن
  • برای آماده‌سازی کابل‌ها
  • لپ‌تاپ یا کامپیوتر
  • برای برنامه‌نویسی و آموزش هوش مصنوعی
  • پرینتر سه‌بعدی (اختیاری)
  • برای ساخت قاب اختصاصی
  • محفظه باز برای لنز ESP32-CAM
  • نمایشگر TFT لمسی
  • بخش نشانگر وضعیت
  • ماژول ESP32-CAM
  • سیم‌کشی و اتصالات
  • اتصالات تغذیه و برق
  • ال‌ای‌دی‌ها و دکمه‌های اختیاری
  • طراحی جمع‌وجور
  • قابل حمل
  • ظاهر تمیز و مرتب
  • کاربردی برای استفاده روزمره
مرحله ۱

نحوه عملکرد سیستم دستیار هوشمند

قدم اول: شناخت منطق پروژه

قبل از دست گرفتن هویه، بهتره درک کنیم کل این سیستم چطور کار می‌کنه. ایده اصلی اینه که یک گجت کوچک بسازیم که با استفاده از هوش مصنوعی، مواد غذایی رو بشناسه و با مدیریت موجودی، جلوی اسراف رو بگیره. هسته اصلی این گجت برد ESP32-CAM هست که ویژگی‌های زیر رو به صورت همزمان داره:

  1. یک میکروکنترلر قدرتمند
  2. دوربین برای پردازش تصویر
  3. قابلیت اتصال به وای‌فای

همه این‌ها تو یک برد کوچک و اقتصادی جمع شدن. به جای استفاده از سرویس‌های گران‌قیمت هوش مصنوعی ابری، ما از تکنولوژی TinyML و پلتفرم Edge Impulse استفاده می‌کنیم تا مدل تشخیص تصویر مستقیما روی خود ESP32-CAM اجرا بشه. این کار باعث می‌شه سیستم سریع‌تر، سبک‌تر و برای پروژه‌های امبدد ایده‌آل باشه.

مراحل انجام کار سیستم

  1. کاربر ماده غذایی رو روبه‌روی دوربین قرار می‌ده.
  2. برد ESP32-CAM یک عکس از اون ثبت می‌کنه.
  3. مدل هوش مصنوعی (TinyML) نوع غذا رو تحلیل و شناسایی می‌کنه.
  4. مورد شناسایی‌شده روی صفحه نمایش TFT ظاهر می‌شه.
  5. یک درصد برای میزان تازگی و یک نوار رنگی تولید می‌شه.
  6. سیستم با این رابط بصری به کاربر یادآوری می‌کنه که کدوم غذا رو باید زودتر بخوره.

این پروژه دقیقا برای حل یک مشکل واقعی طراحی شده؛ سبزیجات و غذاهایی که اونقدر تو یخچال می‌مونن تا خراب بشن. با تعاملی کردن فرآیند مدیریت غذا، این گجت بهت کمک می‌کنه سبک زندگی سالم‌تر و پایدارتری داشته باشی. ساختار سخت‌افزاری ما هم شامل این موارد می‌شه:

  1. ماژول ESP32-CAM برای پردازش هوش مصنوعی و تصویر
  2. نمایشگر TFT برای نشان دادن لیست موجودی و میزان تازگی
  3. یک قاب مگنتی برای چسبیدن به در یخچال
مرحله ۲

سیم‌کشی و اتصالات قطعات

حالا که معماری سیستم رو فهمیدیم، باید ارتباط بین ESP32-CAM و نمایشگر TFT رو برقرار کنیم و مدار رو برای پردازش تصویر آماده کنیم. هدف اینه که مدار تا جای ممکن ساده، جمع‌وجور و بدون قطعی باشه تا راحت داخل قاب مگنتی جا بشه. برد ESP32-CAM به عنوان کنترلر اصلی وظایف زیر رو بر عهده داره:

  1. ثبت تصویر از طریق دوربین
  2. پردازش مدل هوش مصنوعی (Edge AI)
  3. ارتباط با صفحه نمایش
  4. مدیریت منطق موجودی‌ها

نمایشگر TFT هم با استفاده از پروتکل SPI متصل می‌شه و این اطلاعات رو نشون می‌ده:

  1. نام غذای تشخیص داده شده
  2. درصد تازگی غذا
  3. نوارهای رنگی نشانگر وضعیت
  4. اطلاعات مربوط به موجودی یخچال

قطعات مورد نیاز برای مدار

  1. برد ESP32-CAM
  2. نمایشگر TFT
  3. ماژول پروگرامر FTDI
  4. سیم‌های جامپر
  5. منبع تغذیه 5 ولت مطمئن

اتصالات نمایشگر TFT به ESP32-CAM

برای ارتباط پین‌های نمایشگر به این شکل عمل کن: پین VCC به 3.3 ولت پین GND به GND پین SCK به GPIO14 پین MOSI به GPIO15 پین CS به GPIO13 پین DC به GPIO2 پین RST به GPIO12

اتصالات پروگرامر FTDI

چون ESP32-CAM پورت USB نداره، برای آپلود کدها باید از پروگرامر FTDI استفاده کنیم: پین 5 ولت به 5 ولت پین GND به GND پین TX پروگرامر به U0 اهم برد پین RX پروگرامر به U0T برد

  1. برای رفتن به حالت آپلود، حتما باید پین GPIO0 به GND وصل باشه.
  2. بعد از شروع فرآیند آپلود تو نرم‌افزار، دکمه ریست روی برد رو یک بار فشار بده.

تست اولیه سخت‌افزار

بعد از این که سیم‌کشی‌ها تموم شد، باید این موارد رو تست کنی:

  1. راه‌اندازی صحیح دوربین
  2. ارتباط بدون مشکل با نمایشگر TFT
  3. پایداری منبع تغذیه
  4. ارتباط سریال (Serial Monitor)

این مرحله خیلی مهمه، چون قبل از این که مدل سنگین هوش مصنوعی رو روی برد بریزیم، باید از پایداری سخت‌افزار مطمئن بشیم. وقتی دوربین و نمایشگر درست کار کردن، یعنی سخت‌افزار آماده پذیرش مدل AI و شروع تشخیص بلادرنگ غذاست.

مرحله ۳

نصب و تنظیم کتابخانه TFT_eSPI

بعد از سیم‌کشی نمایشگر، باید کتابخانه TFT_eSPI رو تو محیط Arduino IDE تنظیم کنیم. این کتابخانه قدرتمند قراره کارهای زیر رو برامون انجام بده:

  1. نمایش نام مواد غذایی
  2. رسم نوارهای پیشرفت برای میزان تازگی
  3. ایجاد رابط کاربری برای لیست موجودی
  4. رندر کردن تمام محیط گرافیکی یخچال هوشمند

کتابخانه TFT_eSPI بسیار سبک و مخصوص بردهای خانواده ESP طراحی شده، برای همین سرعت فوق‌العاده‌ای تو رندر گرافیک بلادرنگ داره.

مراحل نصب کتابخانه TFT_eSPI

مرحله اول

نرم‌افزار Arduino IDE رو باز کن.

مرحله دوم

از منوی بالا به این مسیر برو:

TXT
Sketch  Include Library  Manage Libraries

مرحله سوم

این عبارت رو جستجو کن:

TXT
TFT_eSPI

و کتابخانه‌ای که نویسنده‌اش Bodmer هست رو نصب کن.

ویرایش فایل تنظیمات کتابخانه

برای این که نمایشگر درست با ESP32-CAM کار کنه، باید تنظیمات پین‌ها رو داخل یکی از فایل‌های کتابخانه تغییر بدی. به مسیر پوشه کتابخانه تو سیستم برو و این فایل رو باز کن:

TXT
Documents/Arduino/libraries/TFT_eSPI/User_Setup.h

تنظیم درایور نمایشگر

بسته به چیپ درایور نمایشگری که خریدی، باید خط مربوط به اون رو از حالت کامنت (//) خارج کنی. مثلا برای درایور ILI9341:

TXT
#define ILI9341_DRIVER

تنظیم پین‌های SPI

پین‌ها رو دقیقا مطابق با سیم‌کشی که انجام دادیم، تو فایل تنظیم کن:

TXT
#define TFT_MOSI 15
#define TFT_SCLK 14
#define TFT_CS 13
#define TFT_DC 2
#define TFT_RST 12

تنظیم رزولوشن تصویر

رزولوشن رو هم بر اساس نمایشگرت وارد کن. مثال:

TXT
#define TFT_WIDTH 240
#define TFT_HEIGHT 320

تست نهایی نمایشگر

بعد از ذخیره کردن فایل تنظیمات، یک کد ساده تست گرافیک آپلود کن تا این موارد رو چک کنی:

  1. کیفیت رندر شدن متن‌ها
  2. صحت رنگ‌های تولید شده
  3. سرعت رفرش شدن صفحه
  4. پایداری ارتباط SPI

کد تست نمونه:

Arduino
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
void setup() {
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_GREEN);
tft.setTextSize(2);
tft.setCursor(20,40);
tft.println("DecayDock Ready");
}
void loop() {
}

چرا این مرحله انقدر مهمه؟

تنظیم درست TFT_eSPI حیاتیه چون نمایشگر، تنها پل ارتباطی بین دستگاه و کاربره. تمام خروجی‌های مهم دستگاه از این طریق نمایش داده می‌شن:

  1. غذاهای تشخیص‌داده‌شده
  2. وضعیت لحظه‌ای تازگی مواد
  3. لیست موجودی یخچال
  4. نوارهای گرافیکی و بصری پیشرفت
  5. یادآورهای هوشمند مصرف
مرحله ۴

راه‌اندازی Edge Impulse و جمع‌آوری داده‌ها (دیتاست)

بعد از این که سخت‌افزار رو بستیم، نوبت به آموزش هوش مصنوعی تو پلتفرم Edge Impulse می‌رسه. این مهم‌ترین بخش پروژه‌ست، چون دقت تشخیص غذاها کاملا به نحوه جمع‌آوری تصاویر و آموزش مدل بستگی داره. ما می‌خوایم این مدل بتونه رایج‌ترین خوراکی‌های یخچال رو بشناسه، مثل:

  1. گوجه‌فرنگی
  2. پیاز
  3. موز
  4. پاکت شیر
  5. اسفناج و سبزیجات
  6. غذاهای باقیمانده

روند کار رو طوری سفارشی کردیم که کاملا برای مدیریت موجودی یخچال بهینه‌سازی بشه.

چرا Edge Impulse؟

این پلتفرم توسعه TinyML رو برای بردهای کوچکی مثل ESP32-CAM به شدت ساده می‌کنه. امکاناتی که بهت می‌ده شامل:

  1. مدیریت مجموعه‌داده‌های (دیتاست) تصویری
  2. برچسب‌گذاری (لیبل‌گذاری) راحت تصاویر
  3. آموزش مدل هوش مصنوعی
  4. تست و اعتبارسنجی مدل
  5. خروجی گرفتن به صورت یک کتابخانه ساده آردوینو

همه این‌ها تو یک محیط یکپارچه انجام می‌شه. دلیل مهم دیگه اینه که مدل‌های ساخته‌شده تو این پلتفرم کاملا آفلاین روی خود برد اجرا می‌شن، پس سیستم ما ویژگی‌های زیر رو خواهد داشت:

  1. سرعت بسیار بالا
  2. مصرف انرژی کم
  3. بدون نیاز به اینترنت
  4. کاملا کاربردی برای استفاده‌های روزمره

ایجاد پروژه در Edge Impulse

مرحله اول - ساخت حساب کاربری

وارد سایت Edge Impulse Studio شو، یک اکانت بساز و لاگین کن.

مرحله دوم - ایجاد پروژه جدید

روی این دکمه کلیک کن:

TXT
Create New Project

یک نام برای پروژه‌ات انتخاب کن:

TXT
DecayDock Food Recognition

و نوع پروژه رو مشخص کن:

TXT
Image Classification

آماده‌سازی مجموعه‌داده (دیتاست)

به جای دانلود عکس‌های آماده از اینترنت، باید با خود دوربین ESP32-CAM از خوراکی‌ها عکس بگیری. چون می‌خوایم مدل دقیقا با همون لنز و شرایط نوری آشپزخانه کار کنه. این کار باعث می‌شه:

  1. دقت عملی سیستم خیلی بالا بره
  2. با نور محیط سازگار بشه
  3. تو شرایط واقعی عملکرد بهتری نشون بده

دسته‌بندی‌هایی که انتخاب کردیم

برای این که مدل سبک بمونه و به حافظه ESP فشار نیاره، تعداد دسته‌بندی‌ها رو محدود نگه می‌داریم: گوجه‌فرنگی: بالای 50 عکس پیاز: بالای 50 عکس موز: بالای 45 عکس پاکت شیر: بالای 40 عکس اسفناج: بالای 40 عکس غذاهای باقیمانده: بالای 35 عکس کم نگه داشتن تعداد کلاس‌ها به این موارد کمک می‌کنه:

  1. افزایش سرعت تشخیص
  2. کاهش مصرف حافظه
  3. بالا رفتن دقت کلی مدل

ثبت تصاویر

فرآیند عکس‌گرفتن

با استفاده از ESP32-CAM، از هر ماده غذایی تو شرایط زیر عکس بگیر:

  1. زاویه‌های مختلف
  2. فاصله‌های دور و نزدیک
  3. شرایط نوری متفاوت

سعی کن عکس‌ها رو تو این محیط‌ها بگیری:

  1. زیر نور واقعی آشپزخانه
  2. نزدیک به فضای یخچال
  3. با پس‌زمینه‌های کمی شلوغ

این روش خیلی بهتر از عکس گرفتن تو یک استودیوی تمیز با پس‌زمینه سفیده و باعث می‌شه هوش مصنوعی در محیط‌های واقعی گیج نشه. گرفتن عکس از زوایای مختلف، دقت مدل TinyML رو تضمین می‌کنه. ( Edge Impulse Forum)

نکات طلایی برای ثبت عکس

1. موقعیت ثابت دوربین

موقع عکس گرفتن، سعی کن دوربین رو ثابت نگه داری تا کیفیت تصاویر افت نکنه و تار نشن.

2. پس‌زمینه ساده برای شروع

برای اولین عکس‌ها از یک پس‌زمینه خلوت استفاده کن تا مدل بتونه راحت‌تر شکل غذا رو یاد بگیره و خطاهای تشخیص (False Detections) کم بشه.

3. نورپردازی مناسب

نور خوب باعث شفافیت عکس‌ها می‌شه و مدل سریع‌تر الگوها رو یاد می‌گیره.

4. رزولوشن پایین تصاویر

اندازه تصاویر رو روی این مقدار تنظیم کن:

TXT
96  96

رزولوشن‌های پایین برای سیستم‌های امبدد بهترین گزینه هستن و زمان آموزش (Training) رو هم به‌شدت کم می‌کنن.

آپلود کردن تصاویر در پلتفرم

بعد از این که عکس‌هات آماده شد:

مرحله اول

به این بخش برو:

TXT
Data Acquisition

تو محیط کاربری Edge Impulse.

مرحله دوم

تمام عکس‌ها رو آپلود کن و به این شکل تقسیمشون کن:

  1. 80 درصد برای آموزش (Training)
  2. 20 درصد برای تست (Testing)

برچسب‌گذاری (لیبل‌گذاری) تصاویر

در مرحله بعد، وارد این بخش شو:

TXT
Labeling Queue

حالا باید دور هر خوراکی تو عکس‌ها یک کادر بکشی و اسمش رو به صورت دستی وارد کنی. مثال:

  1. Tomato (گوجه‌فرنگی)
  2. Onion (پیاز)
  3. Banana (موز)
  4. Milk (شیر)

این کار به هوش مصنوعی می‌فهمونه که تو هر عکس دقیقا باید دنبال چی بگرده.

ساخت ایمپالس (مدل پردازشی)

بعد از لیبل‌گذاری:

به این منو برو:

TXT
Create Impulse

این تنظیمات رو اعمال کن: اندازه تصویر: 96 در 96 بلوک پردازشی: Image بلوک یادگیری: Object Detection

آموزش دادن مدل هوش مصنوعی

حالا به بخش زیر برو:

TXT
Object Detection

آموزش مدل رو با استفاده از معماری FOMO انجام بده. این معماری مخصوص دستگاه‌های ضعیفی مثل ESP32-CAM طراحی شده. تو این مرحله مدل ویژگی‌های زیر رو یاد می‌گیره:

  1. شکل و فرم
  2. رنگ‌ها
  3. بافت سطحی
  4. خطوط محیطی اشیاء

تا بتونه به راحتی خوراکی‌ها رو تشخیص بده.

تست دقت مدل

بعد از این که پروسه آموزش تموم شد، مدل رو مستقیما تو همون پلتفرم تست کن. باید ببینی که آیا مدل می‌تونه با دقت بالا این خوراکی‌ها رو پیدا کنه:

  1. گوجه‌فرنگی
  2. پیاز
  3. موز
  4. پاکت شیر

اگر زیر نور معمولی اتاق، تشخیص پایداری داشت، یعنی کارت رو درست انجام دادی.

خروجی گرفتن به صورت کتابخانه آردوینو

وقتی از دقت مدل راضی بودی:

مرحله اول

وارد این بخش شو:

TXT
Deployment

مرحله دوم

این گزینه رو انتخاب کن:

TXT
Arduino Library

مرحله سوم

فایل ZIP تولیدشده رو دانلود کن.

نصب کتابخانه AI در آردوینو

فایل ZIP که دانلود کردی رو از حالت فشرده خارج کن و پوشه کتابخانه رو به این مسیر ببر:

TXT
Documents  Arduino  Libraries

نرم‌افزار آردوینو رو ببند و دوباره باز کن. الان مدل هوش مصنوعی تو آماده‌ست تا مستقیما روی برد ESP32-CAM ران بشه.

چرا این مرحله یک نقطه عطف بود؟

انجام این مراحل باعث شد پروژه DecayDock از یک دوربین ساده متصل به وای‌فای، به یک سیستم کامل هوش مصنوعی (Edge AI) ارتقا پیدا کنه. به جای این که نام خوراکی‌ها رو دستی وارد کنی، دستگاه حالا می‌تونه:

  1. مواد غذایی رو کاملا خودکار بشناسه
  2. تصاویر رو به صورت محلی پردازش کنه
  3. بدون نیاز به اینترنت و سرویس‌های ابری کار کنه
  4. مدیریت موجودی یخچال رو در لحظه و هوشمند انجام بده

این همون ویژگی‌ایه که باعث می‌شه این پروژه:

  1. کاملا کاربردی باشه
  2. هوشمند عمل کنه
  3. سبک و بهینه باشه
  4. به حفظ منابع و پایداری محیط زیست کمک کنه

و در عین حال، ساختش برای علاقه‌مندان به الکترونیک ارزان و لذت‌بخش بمونه.

مرحله ۵

تست عملی سیستم تشخیص غذا

بعد از این که مدل خروجی‌گرفته از Edge Impulse رو به کتابخانه‌های آردوینو اضافه کردی، نوبت به تست این هوش مصنوعی تو دنیای واقعی می‌رسه.

این مرحله خیلی حیاتیه، چون باید مطمئن بشیم دستگاه تو یک آشپزخانه واقعی همون‌قدر خوب کار می‌کنه که تو محیط تست ایده‌آل کار می‌کرد. هدف اصلی ما در این مرحله بررسی موارد زیر است:

  1. دقت تشخیص نوع غذا
  2. سرعت و کیفیت پاسخ‌دهی نمایشگر
  3. سرعت پردازش و تشخیص در لحظه (Real-time)
  4. پایداری سیستم تو شرایط نوری مختلف

آپلود کردن کدهای نهایی

کتابخانه دانلود شده رو باید تو کد اصلی پروژه (Firmware) در محیط آردوینو فراخوانی کنی. این کد قرار این کارها رو به صورت همزمان هندل کنه:

  1. راه‌اندازی ماژول دوربین
  2. اجرای پردازش‌های TinyML
  3. تشخیص وجود غذا در تصویر
  4. آپدیت کردن محتوای نمایشگر TFT
  5. رندر کردن نوارهای وضعیت تازگی

بعد از آپلود این کد، ESP32-CAM بدون نیاز به هیچ پردازنده خارجی یا فضای ابری، شروع به اجرای مستقیم مدل هوش مصنوعی می‌کنه.

تست تشخیص در لحظه (Real-Time)

برای تست دستگاه، خوراکی‌های مختلفی رو دونه به دونه جلوی دوربین قرار دادم. مواردی که تست کردم:

  1. گوجه‌فرنگی
  2. پیاز
  3. موز
  4. پاکت شیر
  5. اسفناج

برد ESP32-CAM تونست با موفقیت این خوراکی‌ها رو تشخیص بده و فورا این اطلاعات رو روی صفحه نشون بده:

  1. نام ماده غذایی
  2. درصد تخمینی تازگی
  3. نشانگر رنگی وضعیت

همه این‌ها به شکل زیبایی روی صفحه TFT رندر می‌شه.

تست رابط کاربری وضعیت تازگی

سیستم تازه‌سنجی رو با شبیه‌سازی مدت‌زمان‌های مختلف ماندگاری تو یخچال، تست کردیم. برای مثال:

  1. یک گوجه‌فرنگی تازه وارد شده ← نوار وضعیت سبز
  2. اسفناجی که چند روز مونده ← نوار وضعیت زرد (اخطار مصرف)
  3. شیری که تاریخش گذشته ← نشانگر قرمز و هشدار

این مکانیزم یک سیستم کاملا بصری و قابل فهم ایجاد می‌کنه که کاربر تو یک نگاه متوجه وضعیت موجودی یخچال می‌شه.

چالش شرایط نوری مختلف

یکی از بزرگترین چالش‌ها زمان تست دستگاه، تغییر مداوم نور در محیط آشپزخانه و سایه‌های یخچال بود. بهترین عملکرد مدل در این شرایط به دست اومد:

  1. نورپردازی متوسط و یکنواخت داخل اتاق
  2. ثابت بودن زاویه و موقعیت دوربین
  3. کمترین میزان بازتاب نور از سطوح

برای بالا بردن پایداری سیستم، این کارها رو انجام دادیم:

  1. تنظیم بهتر زاویه دید دوربین
  2. افزایش تنوع عکس‌ها تو مرحله آموزش (Training)
  3. تست و بهینه‌سازی تو ساعت‌های مختلف روز

این کارها خطای تشخیص رو به حداقل رسوند.

عملکرد نهایی و سرعت سیستم

خروجی این تست‌ها نشون داد که دستگاه تو این موارد فوق‌العاده عمل می‌کنه:

  1. سرعت بالای تشخیص اشیاء
  2. آپدیت روان و بدون لگ نمایشگر TFT
  3. پایداری پردازش Edge AI بدون افت فریم
  4. مصرف پایین و بهینه انرژی توسط سخت‌افزار

و چون تمام این مغز متفکر روی خود برد ESP32-CAM جاسازی شده، دستگاه کاملا آفلاین و بدون هیچ نیازی به وای‌فای یا اینترنت وظایفش رو انجام می‌ده.

مرحله ۶

طراحی رابط کاربری روی نمایشگر

بعد از این که خیال‌مون از کارکرد درست هوش مصنوعی راحت شد، نوبت به طراحی یک رابط کاربری (UI) تمیز و جذاب برای نمایشگر TFT می‌رسه. هدف اصلی اینه که گجت ما شبیه یک محصول هوشمند واقعی به نظر برسه، نه صرفا یک کاردستی الکترونیکی. باید کاربر با یک نگاه متوجه این موارد بشه:

  1. کدوم ماده غذایی توسط دوربین شناسایی شده
  2. درصد تازگی و کیفیت اون چقدره
  3. کدوم خوراکی باید در اولویت مصرف قرار بگیره

و همه این‌ها باید با استفاده از المان‌های گرافیکی ساده بیان بشه.

سبک طراحی رابط کاربری

برای طراحی این محیط، از استایل‌های مینیمال و مدرن الهام گرفتیم، مثل:

  1. لوازم هوشمند و مدرن آشپزخانه
  2. داشبوردهای سیستم‌های اینترنت اشیاء (IoT)
  3. اپلیکیشن‌های سفارش غذا

اطلاعاتی که روی صفحه نشون داده می‌شه شامل این موارده:

  1. تصویر نمادین ماده غذایی
  2. نام غذای تشخیص‌داده‌شده
  3. میزان اطمینان هوش مصنوعی به تشخیصش (Confidence Score)
  4. درصد تخمینی تازگی
  5. نوار پیشرفت (Progress bar) متحرک
  6. رنگ نشانگر وضعیت سلامت غذا

برای مثال: 🥬 اسفناج | میزان تازگی: 72٪ | 🟩🟩🟩🟩🟩🟨⬜⬜⬜⬜ این مدل نمایش، حس خیلی بهتری نسبت به چاپ کردن چند خط متن خشک و خالی به کاربر می‌ده.

نرم‌افزارها و کتابخانه‌های استفاده‌شده

نرم‌افزار Arduino IDE برای محیط اصلی برنامه‌نویسی. کتابخانه TFT_eSPI برای رندر کردن گرافیک روی صفحه. کتابخانه TJpg_Decoder برای لود کردن آیکون‌های تصویری.

کتابخانه SPI برای ارتباط بین برد و نمایشگر. کتابخانه Edge Impulse برای تشخیص هوشمند غذا. کتابخانه ESP32 Camera برای دریافت دیتای لنز.

دلیل انتخاب TFT_eSPI

این کتابخانه رو انتخاب کردیم چون:

  1. بسیار سبک و کم‌حجمه
  2. سرعت بالایی داره
  3. به‌طور خاص برای بردهای خانواده ESP32 بهینه‌سازی شده
  4. برای ساخت رابط‌های گرافیکی تو سیستم‌های امبدد ایده‌آله

با کمک این کتابخونه می‌تونیم کارهای زیر رو انجام بدیم:

  1. رسم اشکال هندسی
  2. رندر کردن فونت‌ها و متن‌ها
  3. ساخت نوارهای پیشرفت داینامیک
  4. نمایش عکس‌های گرافیکی
  5. به‌روزرسانی روان و بدون پرش صفحه

این ویژگی‌ها باعث می‌شه ظاهر منوها خیلی پخته‌تر و حرفه‌ای‌تر به نظر برسه.

نمایش تصاویر گرافیکی خوراکی‌ها

برای جذاب‌تر شدن محیط، تصمیم گرفتیم آیکون‌های کوچکی از خوراکی‌ها رو روی صفحه نشون بدیم. مثال:

  1. آیکون گوجه‌فرنگی
  2. آیکون پیاز
  3. آیکون موز

این عکس‌ها ابتدا به آرایه‌های هگزادسیمال تبدیل شدن:

TXT
JPEG format

و سپس با فراخوانی کتابخانه دکودر روی صفحه چاپ می‌شن:

TXT
TJpg_Decoder

این آرایه‌های تصویری مستقیما داخل حافظه فلش ESP32 ذخیره می‌شن و بلافاصله بعد از تشخیص غذا روی صفحه لود می‌شن.

طراحی نوار پیشرفت (میزان تازگی)

این نوار یکی از مهم‌ترین المان‌های DecayDock هست که وضعیت خوراکی‌ها رو با رنگ‌بندی بهت نشون می‌ده: رنگ سبز: کاملا تازه رنگ زرد: نیاز به مصرف سریع رنگ قرمز: نزدیک به انقضا این نوار بر اساس پارامترهای زیر با گذر زمان کاهش پیدا می‌کنه:

  1. نوع ماده غذایی
  2. عمر مفید استانداردی که براش تعریف شده
  3. مدت زمانی که تو یخچال مونده

این روش باعث می‌شه کاربر بدون نیاز به خوندن توضیحات طولانی، با یک نگاه متوجه وضعیت موجودی یخچال بشه.

ساختار کلی چیدمان صفحه

ما صفحه نمایش رو به این بخش‌ها تقسیم کردیم:

بخش بالایی

  1. نام غذایی که هوش مصنوعی تشخیص داده
  2. امتیاز اطمینان به درستی تشخیص

بخش میانی

  1. آیکون یا عکس شماتیک غذا

بخش پایینی

  1. درصد تازگی
  2. نوار پیشرفت رنگی
  3. متن یادآور مصرف

به همین سادگی و کارایی.

پیاده‌سازی کدهای گرافیک

تمام این چیدمان مستقیما با استفاده از توابع گرافیکی آردوینو نوشته شده. مهم‌ترین توابعی که استفاده کردیم:

  1. fillScreen() برای پاک کردن صفحه
  2. drawRect() برای رسم حاشیه مستطیل
  3. fillRect() برای رسم مستطیل توپر
  4. drawBitmap() برای لود کردن عکس‌ها
  5. setCursor() برای تعیین محل نوشتن
  6. print() برای چاپ کردن متن

این دستورات به ما کمک می‌کنن تا این المان‌ها رو بسازیم:

  1. کادربندی‌ها
  2. لیبل‌های متنی
  3. نوارهای پیشرفت متغیر
  4. محل قرارگیری عکس‌ها
  5. نشانگرهای متحرک

نمونه کدهای رابط کاربری

Arduino
#include <TFT_eSPI.h>
TFT_eSPI tft = TFT_eSPI();
String food = "Onion";
int freshness = 82;
void setup() {
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
// Food name
tft.setTextColor(TFT_WHITE);
tft.setTextSize(2);
tft.setCursor(20,20);
tft.println(food);
// Freshness text
tft.setCursor(20,220);
tft.print("Freshness: ");
tft.print(freshness);
tft.println("%");
// Progress bar outline
tft.drawRect(20,250,200,20,TFT_WHITE);
// Progress fill
tft.fillRect(20,250,freshness*2,20,TFT_GREEN);
}
void loop() {
}

به‌روزرسانی‌های بلادرنگ (Real-Time)

به محض این که مدل هوش مصنوعی یک ماده غذایی جدید رو شناسایی کنه:

  1. اطلاعات قبلی صفحه پاک می‌شه
  2. آیکون خوراکی جدید روی صفحه لود می‌شه
  3. نوار پیشرفت بازنشانی می‌شه
  4. مقدار درصد تازگی آپدیت می‌شه

و همه این‌ها انقدر نرم اتفاق میفته که حس کار با یک گجت تجاری رو بهت می‌ده.

چرا طراحی این رابط انقدر مهم بود؟

این رابط گرافیکی باعث شد پروژه از این حالت:

یک برد آزمایشی برای اثبات عملکرد هوش مصنوعی

به این حالت تبدیل بشه:

نمونه اولیه یک محصول هوشمند و کاربردی برای آشپزخانه.

به جای این که با خروجی‌های متنی و گیج‌کننده سریال مانیتور سروکله بزنی، سیستم با تو از این طریق حرف می‌زنه:

  1. تصاویر آشنا
  2. رنگ‌های معنادار
  3. نوارهای گرافیکی
  4. یادآورهای ساده

و این تغییرات دستگاه رو به ابزاری تبدیل می‌کنه که:

  1. استفاده ازش خیلی راحته
  2. از نظر بصری جذابه
  3. برای افراد مبتدی کاملا مفهومه
  4. برای استفاده روزمره تو یک خونه واقعی آماده‌ست
مرحله ۷

مونتاژ قطعات داخل قاب مگنتی

بعد از پشت سر گذاشتن تست‌های سخت‌افزاری و طراحی رابط کاربری گرافیکی، وقتشه تمام این قطعات رو داخل یک قاب جمع‌وجور و مگنتی جاسازی کنیم تا پروژه ظاهر نهایی خودش رو پیدا کنه.

هدف از این بخش اینه که گجت ما دقیقا حس یک لوازم جانبی هوشمند رو به کاربر بده و از شر ظاهر به‌هم‌ریخته سیم‌ها و بردبوردها خلاص بشیم. طراحی قاب باید طوری باشه که:

  1. خیلی راحت روی در یخچال نصب بشه
  2. سبک و جمع‌وجور باقی بمونه
  3. قطعات الکترونیکی رو در برابر ضربه محافظت کنه
  4. سیم‌کشی‌ها رو مخفی و مرتب نگه داره
  5. ظاهر کلی گجت رو شیک و حرفه‌ای کنه

طراحی بدنه دستگاه

قاب دستگاه رو به شکل یک ماژول مستطیلی مخصوص نصب روی یخچال طراحی کردیم که ویژگی‌های زیر رو داره:

  1. حفره دایره‌ای در جلو برای لنز دوربین
  2. بریدگی مربعی برای صفحه نمایش TFT
  3. فضای داخلی کافی برای قرار دادن برد ESP32-CAM
  4. مسیرهای مدیریت کابل در داخل
  5. پنل پشتی برای چسباندن آهنرباها

با این طراحی، نمای جلوی دستگاه کاملا خلوت و تمیز می‌مونه و تمام سیم‌کشی‌ها داخل بدنه پنهان می‌شن.

چیدمان قطعات داخل بدنه

قطعات رو به این شکل داخل قاب نصب می‌کنیم: برد ESP32-CAM: تو قسمت داخلی پشت بدنه نصب می‌شه. نمایشگر TFT: از داخل به بریدگی پنل جلویی می‌چسبه. سیم‌کشی‌ها: از شیارهای کناری عبور می‌کنن. کابل تغذیه USB: از شیار پایینی بدنه خارج می‌شه. آهنرباها: روی پنل پشتی قرار می‌گیرن.

نصب و فیکس کردن نمایشگر

نمایشگر TFT رو با دقت روی بریدگی پنل جلویی تنظیم کن تا تصویر کاملا تراز و مشخص باشه. برای محکم کردنش از این وسایل استفاده کردیم:

  1. چسب حرارتی تفنگی
  2. نوار چسب دوطرفه فومی
  3. پایه‌های کوچک نگه‌دارنده (در صورت پرینت 3D)

من عمدا یک حاشیه نازک اطراف صفحه نمایش گذاشتم تا ظاهر دستگاه بیشتر شبیه به تبلت‌ها یا گجت‌های تجاری بازار بشه.

جانمایی لنز ESP32-CAM

ماژول ESP32-CAM پشت پنل جلویی قرار می‌گیره، طوری که لنز دوربین دقیقا وسط حفره دایره‌ای باشه. این چیدمان باعث می‌شه:

  1. زاویه دید دوربین برای تشخیص خوراکی‌ها بهینه‌تر بشه
  2. لنز دوربین از آسیب‌های فیزیکی در امان بمونه
  3. سیم‌های متصل به برد کمتر دیده بشن
  4. یکپارچگی و زیبایی بدنه حفظ بشه

نکته مهم: زاویه لنز رو کمی رو به پایین تنظیم کردیم، چون کاربرها معمولا مواد غذایی رو از زاویه پایین‌تر جلوی دستگاه یخچال می‌گیرن.

سیستم نصب مگنتی

برای این که نصب گجت روی در یخچال راحت باشه و نیازی به سوراخ‌کاری نداشته باشیم، از چند آهنربای نئودیمیوم (Neodymium) قوی در پنل پشتی استفاده کردیم. این آهنرباها به دستگاه اجازه می‌دن:

  1. مستقیما و محکم به سطح فلزی یخچال بچسبه
  2. در صورت نیاز به راحتی جابه‌جا بشه
  3. بدون هیچ‌گونه نصابی یا استفاده از دریل نصب بشه

همین قابلیت مگنتی، حس داشتن یک لوازم جانبی هوشمند آشپزخانه رو چند برابر می‌کنه.

مدیریت سیم‌ها (Cable Management)

موقع مونتاژ، حتما روی مدیریت کابل‌ها وقت بذار؛ چون سیم‌های آویزون و شلخته، ظاهر هر پروژه حرفه‌ای رو خراب می‌کنن. برای تمیزتر شدن کار:

  1. طول سیم‌ها رو تا جای ممکن کوتاه کردیم
  2. از بست‌های کمربندی کوچک استفاده کردیم
  3. مسیر سیم‌کشی‌ها رو داخل قاب سازماندهی کردیم
  4. سیم‌های جامپر اضافی و بی‌مصرف رو حذف کردیم

این کار به ظاهر داخلی پروژه نظم فوق‌العاده‌ای می‌ده.

تأمین برق دستگاه

برق کل این سیستم با پورت:

TXT
5V USB power

از طریق خود ماژول ESP تامین می‌شه. کابل شارژر USB از یک بریدگی کوچک در پایین قاب خارج می‌شه تا ظاهر جلویی دستگاه کاملا خلوت و مینیمال باقی بمونه.

تست نهایی پس از مونتاژ

وقتی درپوش قاب رو بستی، یک بار دیگه سیستم رو تست کن تا مطمئن بشی:

  1. میدان دید دوربین مسدود نشده باشه
  2. صفحه نمایش TFT به وضوح خوانا باشه
  3. اتصال کابل برق قطع و وصل نشه
  4. کیفیت تشخیص غذا مثل قبل بالا باشه
  5. دستگاه داخل قاب بیش از حد داغ نشه

اگر همه چی مرتبه، حالا دستگاهت با قدرت به در یخچال چسبیده و آماده خدمت‌رسانیه!

مرحله ۸

کدهای پروژه

Arduino
/*
*ESP32 cam object detection code
* by circuirdigest on 27-June-2024
*/
#include <xxxx_inferencing.h> // modify with your project title, Replace the xxxx
#include "edge-impulse-sdk/dsp/image/image.hpp"
#include "esp_camera.h"
// Select camera model - find more camera models in camera_pins.h file here
// https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/…
// #define CAMERA_MODEL_ESP_EYE // Has PSRAM
#define CAMERA_MODEL_AI_THINKER // Has PSRAM
#if defined(CAMERA_MODEL_ESP_EYE)
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 4
#define SIOD_GPIO_NUM 18
#define SIOC_GPIO_NUM 23
#define Y9_GPIO_NUM 36
#define Y8_GPIO_NUM 37
#define Y7_GPIO_NUM 38
#define Y6_GPIO_NUM 39
#define Y5_GPIO_NUM 35
#define Y4_GPIO_NUM 14
#define Y3_GPIO_NUM 13
#define Y2_GPIO_NUM 34
#define VSYNC_GPIO_NUM 5
#define HREF_GPIO_NUM 27
#define PCLK_GPIO_NUM 25
#elif defined(CAMERA_MODEL_AI_THINKER)
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
#else
#error "Camera model not selected"
#endif
/* Constant defines -------------------------------------------------------- */
#define EI_CAMERA_RAW_FRAME_BUFFER_COLS 320
#define EI_CAMERA_RAW_FRAME_BUFFER_ROWS 240
#define EI_CAMERA_FRAME_BYTE_SIZE 3
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ESP32-CAM doesn't have dedicated i2c pins, so we define our own. Let's choose 15 and 14
#define I2C_SDA 15
#define I2C_SCL 14
TwoWire I2Cbus = TwoWire(0);
// Display defines
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &I2Cbus, OLED_RESET);
/* Private variables ------------------------------------------------------- */
static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal
static bool is_initialised = false;
uint8_t *snapshot_buf; //points to the output of the capture
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sscb_sda = SIOD_GPIO_NUM,
.pin_sscb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
//XCLK 20MHz or 10MHz for OV2640 double FPS (Experimental)
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG, //YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_QVGA, //QQVGA-UXGA Do not use sizes above QVGA when not JPEG
.jpeg_quality = 12, //0-63 lower number means higher quality
.fb_count = 1, //if more than one, i2s runs in continuous mode. Use only with JPEG
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
};
/* Function definitions ------------------------------------------------------- */
bool ei_camera_init(void);
void ei_camera_deinit(void);
bool ei_camera_capture(uint32_t img_width, uint32_t img_height, uint8_t *out_buf);
/**
* @brief Arduino setup function
*/
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
// Initialize I2C with our defined pins
I2Cbus.begin(I2C_SDA, I2C_SCL, 100000);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.printf("SSD1306 OLED display failed to initalize.\nCheck that display SDA is connected to pin %d and SCL connected to pin %d\n", I2C_SDA, I2C_SCL);
while (true)
;
}
//comment out the below line to start inference immediately after upload
while (!Serial)
;
Serial.println("Edge Impulse Inferencing Demo");
if (ei_camera_init() == false) {
ei_printf("Failed to initialize Camera!\r\n");
} else {
ei_printf("Camera initialized\r\n");
}
ei_printf("\nStarting continious inference in 2 seconds...\n");
display.clearDisplay();
display.setCursor(0, 0);
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.print("Starting continious\n inference in\n 2 seconds...");
display.display();
ei_sleep(2000);
display.clearDisplay();
}
/**
* @brief Get data and run inferencing
*
* @param[in] debug Get debug info if true
*/
void loop() {
display.clearDisplay();
// instead of wait_ms, we'll wait on the signal, this allows threads to cancel us...
if (ei_sleep(5) != EI_IMPULSE_OK) {
return;
}
snapshot_buf = (uint8_t *)malloc(EI_CAMERA_RAW_FRAME_BUFFER_COLS * EI_CAMERA_RAW_FRAME_BUFFER_ROWS * EI_CAMERA_FRAME_BYTE_SIZE);
// check if allocation was successful
if (snapshot_buf == nullptr) {
ei_printf("ERR: Failed to allocate snapshot buffer!\n");
return;
}
ei::signal_t signal;
signal.total_length = EI_CLASSIFIER_INPUT_WIDTH * EI_CLASSIFIER_INPUT_HEIGHT;
signal.get_data = &ei_camera_get_data;
if (ei_camera_capture((size_t)EI_CLASSIFIER_INPUT_WIDTH, (size_t)EI_CLASSIFIER_INPUT_HEIGHT, snapshot_buf) == false) {
ei_printf("Failed to capture image\r\n");
free(snapshot_buf);
return;
}
// Run the classifier
ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR err = run_classifier(&signal, &result, debug_nn);
if (err != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", err);
return;
}
// print the predictions
ei_printf("Predictions (DSP: %d ms., Classification: %d ms., Anomaly: %d ms.): \n",
result.timing.dsp, result.timing.classification, result.timing.anomaly);
#if EI_CLASSIFIER_OBJECT_DETECTION == 1
bool bb_found = result.bounding_boxes[0].value > 0;
for (size_t ix = 0; ix < result.bounding_boxes_count; ix++) {
auto bb = result.bounding_boxes[ix];
if (bb.value == 0) {
continue;
}
ei_printf(" %s (%f) [ x: %u, y: %u, width: %u, height: %u ]\n", bb.label, bb.value, bb.x, bb.y, bb.width, bb.height);
display.setCursor(0, 20 * ix);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.print(bb.label);
display.print("-");
display.print(int((bb.value)*100));
display.print("%");
display.display();
}
if (!bb_found) {
ei_printf(" No objects found\n");
display.setCursor(0, 16);
display.setTextSize(2);
display.setTextColor(SSD1306_WHITE);
display.print("No objects found");
display.display();
}
#else
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: %.5f\n", result.classification[ix].label,
result.classification[ix].value);
}
#endif
#if EI_CLASSIFIER_HAS_ANOMALY == 1
ei_printf(" anomaly score: %.3f\n", result.anomaly);
#endif
free(snapshot_buf);
}
/**
* @brief Setup image sensor & start streaming
*
* @retval false if initialisation failed
*/
bool ei_camera_init(void) {
if (is_initialised) return true;
#if defined(CAMERA_MODEL_ESP_EYE)
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
#endif
//initialize the camera
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x\n", err);
return false;
}
sensor_t *s = esp_camera_sensor_get();
// initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_vflip(s, 1); // flip it back
s->set_brightness(s, 1); // up the brightness just a bit
s->set_saturation(s, 0); // lower the saturation
}
#if defined(CAMERA_MODEL_M5STACK_WIDE)
s->set_vflip(s, 1);
s->set_hmirror(s, 1);
#elif defined(CAMERA_MODEL_ESP_EYE)
s->set_vflip(s, 1);
s->set_hmirror(s, 1);
s->set_awb_gain(s, 1);
#endif
is_initialised = true;
return true;
}
/**
* @brief Stop streaming of sensor data
*/
void ei_camera_deinit(void) {
//deinitialize the camera
esp_err_t err = esp_camera_deinit();
if (err != ESP_OK) {
ei_printf("Camera deinit failed\n");
return;
}
is_initialised = false;
return;
}
/**
* @brief Capture, rescale and crop image
*
* @param[in] img_width width of output image
* @param[in] img_height height of output image
* @param[in] out_buf pointer to store output image, NULL may be used
* if ei_camera_frame_buffer is to be used for capture and resize/cropping.
*
* @retval false if not initialised, image captured, rescaled or cropped failed
*
*/
bool ei_camera_capture(uint32_t img_width, uint32_t img_height, uint8_t *out_buf) {
bool do_resize = false;
if (!is_initialised) {
ei_printf("ERR: Camera is not initialized\r\n");
return false;
}
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
ei_printf("Camera capture failed\n");
return false;
}
bool converted = fmt2rgb888(fb->buf, fb->len, PIXFORMAT_JPEG, snapshot_buf);
esp_camera_fb_return(fb);
if (!converted) {
ei_printf("Conversion failed\n");
return false;
}
if ((img_width != EI_CAMERA_RAW_FRAME_BUFFER_COLS)
|| (img_height != EI_CAMERA_RAW_FRAME_BUFFER_ROWS)) {
do_resize = true;
}
if (do_resize) {
ei::image::processing::crop_and_interpolate_rgb888(
out_buf,
EI_CAMERA_RAW_FRAME_BUFFER_COLS,
EI_CAMERA_RAW_FRAME_BUFFER_ROWS,
out_buf,
img_width,
img_height);
}
return true;
}
static int ei_camera_get_data(size_t offset, size_t length, float *out_ptr) {
// we already have a RGB888 buffer, so recalculate offset into pixel index
size_t pixel_ix = offset * 3;
size_t pixels_left = length;
size_t out_ptr_ix = 0;
while (pixels_left != 0) {
// Swap BGR to RGB here
// due to https://github.com/espressif/esp32-camera/issues/379
out_ptr[out_ptr_ix] = (snapshot_buf[pixel_ix + 2] << 16) + (snapshot_buf[pixel_ix + 1] << 8) + snapshot_buf[pixel_ix];
// go to the next pixel
out_ptr_ix++;
pixel_ix += 3;
pixels_left--;
}
// and done!
return 0;
}
#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_CAMERA
#error "Invalid model for current sensor"
#endif
مرحله ۹

جمع‌بندی و نتیجه‌گیری

پروژه DecayDock چیزی فراتر از ساخت یک کاردستی الکترونیکی بود. این ایده از یک مشاهده ساده تو زندگی روزمره شکل گرفت؛ دیدن سبزیجات و غذاهایی که گوشه یخچال فراموش می‌شدن و در نهایت سر از سطل زباله درمیاوردن.

شاید دور ریختن یک گوجه‌فرنگی مسئله کوچکی به نظر برسه، اما وقتی به حجم هدررفت مواد غذایی در سطح جهانی و ارتباطش با تغییرات اقلیمی، تولید گاز متان و مصرف بی‌رویه منابع نگاه می‌کنی، متوجه ابعاد بزرگ این مشکل می‌شی.

نکته جذاب این پروژه، تبدیل یک ایده ساده به یک گجت کاربردی با استفاده از سخت‌افزارهای ارزان‌قیمت و در دسترس بود که با قدرت هوش مصنوعی Edge AI ترکیب شده.

از جمع‌آوری عکس‌ها با دوربین ESP32-CAM گرفته تا آموزش مدل تو Edge Impulse، دیباگ کردن سیم‌کشی‌ها، طراحی رابط گرافیکی روی نمایشگر و مونتاژ نهایی داخل قاب مگنتی، هر مرحله یک درس جدید به همراه داشت. در طول ساخت قطعا با چالش‌هایی روبه‌رو می‌شی:

  1. تشخیص‌های اشتباه دوربین
  2. قطعی و ناپایداری سیم‌ها
  3. مشکلات لود شدن تصویر در نمایشگر
  4. تغییرات نوری محیط هنگام تست

اما برطرف کردن همین مشکلات، لذت‌بخش‌ترین قسمت ماجراست. لحظه‌ای که دستگاه برای اولین بار یک خوراکی رو در لحظه تشخیص می‌ده و روی صفحه نشون می‌ده، متوجه می‌شی که یک مشت کد و سیم رو به یک ابزار تعاملی و هدفمند تبدیل کردی.

این پروژه نشون داد که برای حفظ محیط زیست و داشتن زندگی پایدارتر، همیشه نیازی به سیستم‌های صنعتی بزرگ یا تکنولوژی‌های میلیون تومانی نداریم. گاهی یک گجت کوچک روی در یخچال می‌تونه عادات روزمره ما رو اصلاح کنه و جلوی اسراف رو بگیره.

پروژه DecayDock نمادی از این تفکره که تکنولوژی باید علاوه بر هوشمند بودن، مسئولیت‌پذیر و در خدمت انسان‌ها باشه. این دستگاه ترکیبی است از:

  1. هوش مصنوعی امبدد (توکار)
  2. حفظ پایداری منابع
  3. قابلیت استفاده در زندگی روزمره
  4. اصلاح رفتارهای ساده انسانی

امیدوارم از ساخت این دستیار هوشمند آشپزخانه در آیسی‌مدار لذت برده باشی و این پروژه بهت انگیزه داده باشه تا با خلاقیت خودت، مشکلات دنیای واقعی رو با الکترونیک حل کنی. حتما تجربیاتت رو تو بخش نظرات برامون بنویس و در آیسی‌مدار منتظر پروژه‌های بعدی ما باش!

تو میتونی اولین سازنده باشی!

اگه پروژه رو ساختی به اشتراک بزار و اعتبار هدیه بگیر

این آموزش با استانداردهای اختصاصی آیسی‌مدار بازنویسی و بهینه‌سازی شده و تمامی حقوق انتشار آن متعلق به این مجموعه است و در صورت کپی یا بازنشر پیگرد قانونی خواهد داشت.

آموزش‌های مشابه

گفتگو‌های کاربران

هنوز نظری ثبت نشده؛ تو شروع کن!