본문 바로가기

ESP32 CAM

ESP32-CAM과 Edge Impulse를 이용한 객체 탐지

반응형

ESP32-CAM과 Edge Impulse를 이용한 객체 탐지

 

이 프로젝트에서는 널리 사용되는 ESP32-CAM 모듈을 활용하여 다양한 채소를 식별할 수 있는 이미지 인식 시스템을 구축했습니다. Edge Impulse 플랫폼을 사용하여 모델을 학습시키고 ESP32CAM 모듈에 배포했으며, 모델의 결과는 OLED 화면에 출력됩니다.

 

이 튜토리얼에서는 엣지 컴퓨팅의 기본 개념과 ESP32-CAM과 Edge Impulse를 사용하여 AI 기반 객체 감지 모델을 학습시키고 배포하는 방법을 살펴봅니다. 또한, circuitdigest에서는 이와 유사한 AI 프로젝트 및 ESP32CAM 프로젝트를 다수 진행했으니 , 더 자세한 내용을 확인하고 싶으시면 참고하시기 바랍니다.

 

ESP32-CAM과 Edge Impulse를 이용한 객체 탐지

 

ESP32-CAM 객체 감지 프로젝트 작동 데모 영상입니다. I2C OLED 디스플레이를 통해 감지 결과를 보여줍니다.

 

위 이미지는 저희 프로젝트의 간단한 작동 시연 영상입니다. ESP32 카메라 모듈이 앞에 있는 물체를 감지하여 아래 OLED 화면에 표시하는 것을 확인할 수 있습니다.

 

엣지 AI란 무엇인가요?

 

엣지 컴퓨팅 또는 AI 온 엣지 는 인공지능 알고리즘을 마이크로컨트롤러나 마이크로프로세서와 같은 소형 저전력 장치에서 실행할 수 있도록 하는 기술입니다. 이 접근 방식은 데이터를 멀리 떨어진 서버로 전송하는 대신 로컬에서 처리함으로써 비용을 절감하고 시스템 성능을 향상시키는 것을 목표로 합니다. 클라우드 컴퓨팅은 초기 설정 및 유지 관리 비용이 저렴하여 오랫동안 인기를 누려왔지만, 기술 발전으로 이제 엣지 컴퓨팅이 실현 가능하고 효율적인 대안이 되었습니다. 본질적으로 엣지 컴퓨팅은 데이터가 생성되는 곳에 강력한 처리 능력을 직접 제공하여 시스템을 더 빠르고 효율적으로 만듭니다.

 

객체 인식이란 무엇인가요?

 

기술적인 용어로 "객체 인식은 컴퓨터 비전 및 인공지능 분야의 기술로, 이미지나 비디오 내의 객체를 식별하고 분류하는 것을 의미합니다. 이 과정을 통해 기계는 인간처럼 시각 세계를 해석하고 이해할 수 있습니다." 또한 객체 감지, 객체 인식, 이미지 인식과 같은 용어는 같은 의미로 자주 혼용된다는 점을 알아두세요. 이 ESP32 카메라 객체 감지 프로젝트에서는 ESP32 카메라 모듈 앞에 있는 채소가 양파, 감자, 토마토 중 어떤 종류인지 인식하는 방법을 보여드리겠습니다.

 

 

객체 인식

 

이미지 속 객체를 인식하는 데 필요한 과정을 이해해 봅시다.

 

  1. 데이터 수집: 이 과정은 데이터 수집으로 시작됩니다. 시스템이 인식하도록 하려는 물체의 이미지를 여러 장 수집하십시오.
  2. 획득한 데이터의 전처리: 수집된 이미지는 다음 단계를 지원하기 위해 전처리됩니다. 일반적으로 노이즈 감소, 대비 향상 및 크기 조정이 포함됩니다.
  3. 특징 추출: 이미지의 질감, ​​모양, 색상, 가장자리와 같은 주요 특징을 추출하여 시스템 학습에 사용합니다.
  4. 모델 학습: 합성곱 신경망(CNN)과 같은 머신러닝 모델은 레이블이 지정된 이미지로 구성된 대규모 데이터셋을 사용하여 학습됩니다. 모델은 추출된 특징을 특정 객체와 연결하는 방법을 학습합니다.
  5. 모델 테스트: 학습이 완료되면 모델을 테스트하고 배포할 수 있습니다. 모델은 개별 객체를 인식하고 학습 데이터셋에서 추출한 특징과 비교합니다. 그런 다음 감지된 객체의 위치를 ​​표시하고 이름을 지정합니다. 정확도를 높이려면 더 큰 데이터셋으로 모델을 학습시키세요.
  6. 모델 배포: 시스템 제약 조건에 따라 학습된 모델의 형식을 변환하여 시스템에서 실행합니다. 이 단계에서는 탐지 정확도가 높아야 하며, 그렇지 않을 경우 데이터 수집 및 모델 학습에 더 집중해야 합니다.

 

자, 이제 실험에 필요한 구성 요소를 살펴보겠습니다.

 

ESP32 CAM 객체 감지 프로젝트에 필요한 구성 요소

 

테스트 하드웨어로는 카메라가 내장된 저가형 마이크로컨트롤러인 ESP32-CAM을 선택했습니다. ESP32-CAM을 선택했기 때문에 USB-시리얼 변환기라는 추가 프로그래머가 필요했습니다. 또한, 객체 인식을 위해 몇 가지 채소를 선택했습니다. 처음에는 간단해 보였지만 생각보다 어려웠습니다. 자세한 내용은 아래에서 설명하겠습니다. 이제 필요한 부품 목록을 살펴보겠습니다.

 

ESP32-CAM x1

USB-시리얼 변환기 1개

브레드보드 1개

0.96인치 OLED 디스플레이 1개 (선택 사항)

점퍼선 (필요 수량)

 

채소 몇 가지를 준비했어요. 토마토, 감자, 양파를 각각 3개씩 사용할게요.

 

소프트웨어 부분에서는 크게 두 가지를 살펴보겠습니다. 바로 Edge Impulse와 Arduino IDE입니다. Edge Impulse는 모든 머신러닝 관련 작업에 사용할 것이고, Arduino IDE는 ESP32-CAM의 프로그래밍 및 디버깅에 사용할 것입니다. 자세한 내용은 아래 목록을 참조하세요.

 

엣지 임펄스

아두이노 IDE

 

ESP32CAM 이미지 인식 연결도

 

이 객체 감지 프로젝트에 필요한 회로도는 구성 요소가 많지 않아 매우 간단합니다. OLED는 선택 사항이므로 사용자의 선호에 따라 사용 여부를 결정할 수 있습니다. 저는 프로젝트 작동 방식을 쉽게 보여주기 위해 OLED를 사용했습니다. 간단하게 객체 감지 결과를 확인하려면 시리얼 모니터를 사용할 수도 있습니다.

 

 

물체 감지 회로도

 

위 회로도를 참고하여 연결하십시오. FTDI 프로그래머(빨간색 보드)를 ESP32에 연결하여 프로그래밍하는 방법도 나와 있습니다. ESP32CAM 모듈 사용이 처음이시라면 "ESP32-CAM 프로그래밍 방법" 관련 글을 참고하시면 프로그래밍에 대한 모든 것을 이해하는 데 도움이 될 것입니다.

 

Edge Impulse 소개

 

Edge Impulse는 개발자가 머신러닝 모델을 엣지 디바이스에 직접 원활하게 생성, 최적화 및 배포할 수 있도록 설계된 혁신적인 플랫폼입니다. 직관적인 인터페이스와 강력한 도구를 활용하여 개발자는 효율적으로 데이터를 수집하고, 모델을 학습시키고, 실제 애플리케이션에 통합할 수 있습니다. 또한 Edge Impulse는 다양한 하드웨어 플랫폼과 센서를 강력하게 지원하여 개발자의 업무 효율성을 극대화합니다.

 

 

엣지 임펄스 홈페이지

 

이를 통해 엣지 컴퓨팅 분야에서 빠른 혁신이 가능해지며, IoT, 로봇 공학, 모바일 기술과 같은 다양한 산업 분야에서 더욱 스마트하고 반응성이 뛰어난 디바이스를 구현할 수 있습니다. Edge Impulse에 대해 자세히 알아보고 강력한 기능을 살펴보려면 공식 웹사이트를 방문하세요. Edge Impulse를 사용한 구축 방법에 대한 추가 튜토리얼이 필요하면 저희 Edge Impulse 프로젝트도 확인해 보세요. 아래에 리스트를 가져왔어용.

 

ESP32-CAM Currency Recognition System using Edge Impulse

An Embedded AI Project Aimed at Easing Night-Time Baby Feeding

ESP32 Offline Voice Recognition Using Edge Impulse

Qualcomm to Unify Edge AI Stack with Arduino Acquisition

Using Thermal Cameras for Gesture Recognition with AI

ESP32-CAM Face Recognition using Edge Impulse

Object Detection using ESP32-CAM and Edge Impulse

Tomato Sorting Machine using Edge Impulse TinyML on Raspberry Pi

How to use Edge Impulse Signal based Model Training to train a Fever Detection Model

Object Classification using Edge Impulse TinyML on Raspberry Pi

 

 

ESP32-CAM 엣지 임펄스

 

엣지 임펄스(Edge Impulse)에 대해 어느 정도 이해하셨기를 바랍니다. 이 글에서는 시중에서 가장 저렴한 카메라 모듈 중 하나인 ESP32-CAM을 사용하여 엣지 임펄스를 활용한 객체 탐지 ​​시스템을 구현해 보겠습니다. 엣지 임펄스 개념을 쉽게 설명하기 위해 채소 분류를 예시로 들었습니다. 감자, 토마토, 양파 세 가지 채소를 분류하는 예제를 살펴보겠지만, 어떤 객체든 선택하여 객체 인식을 구현할 수 있습니다.

 

우리가 할 일은 다음과 같습니다.

  • ESP32-CAM에 내장된 카메라를 사용하여 데이터셋을 직접 수집합니다.
  • Edge Impulse를 활용하여 ESP32-CAM과 호환되는 머신러닝 모델을 구축합니다.
  • 머신러닝 모델을 실시간으로 검증하고 테스트합니다.

 

이 글에서는 Edge Impulse의 모든 기능을 다루지는 않을 것임을 유념해 주십시오. Edge Impulse는 이미 잘 정리된 문서를 제공하고 있습니다. 더 자세한 내용을 알고 싶으시면 Edge Impulse 공식 웹사이트( Edge Impulse Documentation)를 방문해 주시기 바랍니다 .

 

ESP32 CAM을 이용한 객체 인식 방법

 

이러한 절차는 크게 세 부분으로 나눌 수 있습니다. 데이터 수집, Edge Impulse를 사용한 머신러닝 모델 구축, 그리고 프로토타입 테스트입니다.

 

데이터 수집:

 

데이터 수집은 크게 세 가지 방법으로 할 수 있습니다. 본인이 편하게 느끼는 방식을 선택하시면 됩니다.

 

꼭 기억해야 할 체크리스트:

  1. 분류하려는 각 객체에 대해 최소 50장 이상의 이미지를 수집해야 합니다. 이미지가 많을수록 정확도가 높아지는 것은 보편적인 법칙입니다.
  2. 객체의 배경은 어떤 것이든 상관없지만, 아무것도 없는 빈 배경이 더 좋습니다.
  3. 해상도가 높을수록 학습 시간이 더 오래 걸리므로 최적의 해상도를 선택하세요. 저희의 경우 256x256픽셀이면 충분합니다.

이 글에서는 채소, 특히 토마토, 감자, 양파에 집중할 것입니다.

 

옵션 1: 온라인에서 데이터 세트 검색

 

데이터를 수집하는 가장 쉽고 첫 번째 방법은 인터넷에서 데이터셋을 직접 찾는 것입니다. Kaggle, Google Dataset Search, OpenML 등의 사이트를 이용하면 됩니다. 여기서 직접 검색할 수 있습니다. 우리의 경우 채소를 분류할 것이므로 토마토, 감자, 양파 이미지가 필요합니다.

 

 

Kaggle에서 채소 분류 데이터셋 검색 결과

 

앞서 설명드린 대로, 여러 웹사이트에서 데이터셋을 검색하여 필요에 맞는 데이터셋을 찾으세요. 데이터셋을 수집할 때는 체크리스트를 꼭 확인하는 것을 잊지 마세요. 위 이미지에서 제가 Kaggle에서 검색한 결과를 볼 수 있습니다. 적합한 데이터셋을 찾는 데 시간이 다소 걸릴 수 있습니다. 하지만 저는 몇 가지 이유로 이 방법을 사용하지 않을 예정이며, 이 부분은 곧 자세히 설명드리겠습니다.

 

옵션 2: 온라인에서 이미지를 하나씩 수집하기

 

다음 단계는 간단합니다. 구글에서 이미지를 검색하고 하나씩 다운로드하는 것입니다. 하지만 이 과정은 시간이 좀 더 걸립니다. 다운로드한 이미지에 워터마크가 없는지 확인하세요. 그 외에는 모두 괜찮습니다. 다시 한번 체크리스트를 확인하세요. 이제 제가 선호하는 다음 방법으로 넘어가 보겠습니다.

 

옵션 3: 직접 사진 촬영하기

 

이제 흥미로운 부분인, 직접 수집한 데이터를 사용하여 자체 데이터셋을 만드는 단계입니다. 여기에는 여러 가지 방법이 있습니다. 모바일 기기로 이미지를 캡처하거나, Edge Impulse 자체의 모바일 또는 웹캠 캡처 기능을 사용하거나, ESP32-CAM의 카메라를 사용하는 방법이 있습니다.

 

Edge Impulse를 사용하여 모바일폰이나 웹캠으로 이미지를 녹화하는 것은 간단하므로 이 부분은 설명하지 않겠습니다. 대신 ESP32-CAM 자체를 이용한 이미지 캡처에 초점을 맞추겠습니다. 이는 EloquentEsp32cam 라이브러리를 사용하면 가능하며, 이 라이브러리에는 ESP32-CAM을 웹 서버처럼 작동시켜 데이터셋 생성을 위한 사진 촬영에 필요한 기본 인터페이스를 제공하는 예제 코드가 포함되어 있습니다.

 

1단계: 라이브러리 설치

 

라이브러리 관리자에서 찾을 수 있는 EloquentEsp32cam 라이브러리를 설치해야 합니다.

 

 

EloquentEsp32cam 라이브러리 설치

 

위 이미지는 Eloquent ESP32-CAM 라이브러리의 설치 과정을 보여줍니다.

 

2단계: 예제 프로그램 열기 및 필요한 수정 작업 수행

 

아래 이미지에 표시된 것처럼 Examples -> EloquentEsp32cam -> Collect_Images_for_EdgeImpulse 에 있는 예제 프로그램을 엽니다 .

 

 

우리가 선택해야 할 예제 코드 위치

 

다음으로, 해당 프로그램에서 Wi-Fi 핫스팟 자격 증명을 입력해야 합니다.

 

#define WIFI_SSID "SSID"

#define WIFI_PASS "PASSWORD"

#define HOSTNAME "esp32cam"

 

반드시 수정할 곳이 하나 더 있는데 바로 카메라 모델을 설정하는 곳이다. 36라인을 아래와 같이 수정한다. 가지고 있는 카메라 모델에 맞게 수정한다. 

 

 

프로그램 시작 부분에 위의 내용이 표시됩니다. "SSID"에는 Wi-Fi 핫스팟 이름을, "PASSWORD"에는 비밀번호를 입력해 주세요. ESP32는 2.4GHz 네트워크에서만 작동합니다. 이제 모든 설정이 완료되었으며 코드를 업로드할 준비가 되었습니다. 업로드 방법을 모르시는 경우, ESP32-CAM 프로그래밍 방법을 설명하는 다음 글을 참고해 주세요.

 

3단계: ESP32-CAM 웹 서버 접속 및 데이터 캡처

 

IP 주소를 찾는 것은 간단합니다. 시리얼 모니터를 사용하면 IP 주소를 확인할 수 있습니다. 시스템이 성공적으로 부팅되면 로컬 IP 주소가 115200 보드 속도로 시리얼 출력됩니다. 이 IP 주소를 복사하여 동일 네트워크에 연결된 장치의 웹 브라우저에 붙여넣으면 ESP32-CAM에서 호스팅하는 웹 서버에 접속할 수 있습니다.

 

ESP32-CAM에서 서비스되는 웹 서버

 

연결이 성공적으로 완료되면 위와 유사한 창이 나타납니다. 사용자 인터페이스는 간단합니다. "수집 시작" 버튼을 눌러 영상을 캡처하세요. 캡처를 시작하면 "지우기"와 "다운로드"라는 두 개의 새로운 버튼이 추가됩니다. 충분한 데이터가 확보되면 "다운로드" 버튼을 클릭하고 파일 이름을 지정하세요. 그러면 압축 폴더가 다운로드됩니다.

 

촬영 대상을 잘 볼 수 있도록 시야를 확보하고 조명을 충분히 밝게 하세요. 배경은 단색, 특히 흰색의 빈 공간을 사용하는 것이 좋습니다.

 

 

ESP32-CAM을 이용한 이미지 획득 과정

 

마찬가지로 토마토와 감자에도 이 과정을 따르세요. 다음으로, 에지 임펄스 부분으로 넘어가겠습니다.

 

Edge Impulse 작동 방식

 

마지막으로, 프로젝트의 Edge Impulse 부분을 살펴보겠습니다. 이 과정은 간단하고 따라하기 쉽습니다. 전체 과정은 다음과 같습니다. Edge Impulse 가입, Edge Impulse를 통한 데이터 수집, Impulse 생성, 특징 추출, 머신러닝 모델 학습, 그리고 ESP32-CAM에 모델 배포까지 입니다. 자, 그럼 시작해 볼까요!

 

1단계: Edge Impulse 가입하기

 

간단한 과정입니다. 하지만 참고하실 수 있도록 아래에 스크린샷을 첨부했습니다.

 

 

EdgeImpluse 로그인 절차

 

먼저, Edge Impulse 웹사이트로 이동하여 "로그인" 옵션을 클릭한 다음 "가입" 옵션을 선택하여 가입 페이지로 이동합니다. 가입을 완료하면 계정에 로그인하세요.

 

2단계: Edge Impulse에서 새 프로젝트 생성

 

Edge Impulse에 로그인한 후 " 새 프로젝트 만들기 "를 클릭합니다. 그런 다음 프로젝트 제목을 입력합니다. 저는 " 채소 식별 "로 하겠습니다. 나머지 설정은 기본값으로 두고 " 새 프로젝트 만들기 "를 클릭합니다.

 

이제 프로젝트의 메인 대시보드를 보실 수 있습니다.

 

 

EdgeImplus에서 새 프로젝트를 생성하는 절차

 

위 이미지는 새 프로젝트를 생성할 때 사용되는 단계를 명확하게 보여줍니다.

 

3단계: Edge Impulse에서의 데이터 수집

 

왼쪽에는 "데이터 수집" 옵션이 있습니다. 이를 클릭하면 데이터를 추가하고 레이블을 지정하는 페이지로 이동합니다. 여기에서 데이터 세트에 대한 다양한 관점을 확인할 수 있습니다. 훈련 세트와 테스트 세트의 개별 데이터 등을 볼 수 있습니다.

 

여기에서 웹캠, 모바일 기기 또는 지원되는 개발 보드를 사용하여 데이터를 수집하는 "데이터 수집" 옵션을 확인할 수 있습니다.

 

 

EdgeImpluse의 새 프로젝트에 수집된 데이터를 추가하는 절차

 

위의 단계를 따라 각 폴더를 데이터셋 컬렉션에 추가하세요. 이제 다음 단계로 데이터에 레이블을 지정하겠습니다.

 

상단에 "라벨링 대기열" 메뉴가 있습니다. 이 메뉴를 선택하면 추가한 이미지에 라벨을 붙일 수 있는 창으로 이동합니다. 이 작업은 다소 시간이 걸립니다. 수집한 모든 데이터에 대해 경계 상자를 하나씩 그려야 하기 때문입니다.

 

 

EdgeImplus에서 객체에 레이블을 지정하는 절차

 

위 이미지는 객체에 라벨을 붙이는 단계를 보여줍니다. 라벨을 붙일 때는 데이터가 96 x 96 픽셀 크기로 처리되므로 정사각형 경계를 만드는 것이 좋습니다. 시스템의 정확도를 최대한 높이려면 정사각형으로 만드는 것이 가장 좋습니다.

 

라벨링 창에서 "라벨 제안"과 같은 옵션을 볼 수 있습니다. 고화질 이미지나 연속된 이미지가 있는 경우 매우 유용합니다. "프레임 간 객체 추적"이 선택되어 있으면, 한 번 경계 상자를 그린 객체가 다음 프레임에서도 추적되므로 작업 시간을 단축할 수 있습니다. 다음으로, "YOLOv5를 사용한 분류"를 사용하여 시스템에서 일반적인 객체를 자동으로 인식할 수 있습니다. 이 경우 시스템이 자동으로 라벨을 지정하고 경계 상자를 그립니다.

 

따라서 데이터 수집 영역에서의 작업은 완료되었습니다. 다음으로는 동기 부여 요소를 만드는 방법을 살펴보겠습니다.

 

4단계: 임펄스 생성

 

여기서는 에지 임펄스(Edge Impulse)에 따라 임펄스를 생성할 것입니다. 즉, 이전 단계에서 추가한 원시 데이터를 가져와 신호 처리를 통해 특징을 추출하고, 마지막으로 학습 블록을 사용하여 새로운 데이터를 분류하도록 에지 임펄스의 기본 설정을 수행할 것입니다.

 

 

에지 임펄스에서 임펄스를 생성하는 절차

 

  • 초기 단계이기 때문에 선택 가능한 옵션이 많지 않습니다. 이미지 데이터의 경우, 기본값인 96x96의 높이와 너비를 그대로 사용하겠습니다. 필요에 따라 크기를 변경할 수 있습니다. 크기를 늘리면 정확도는 향상되지만 학습 시간도 길어지므로 신중하게 선택하세요.
  • 다음으로, 크기 조정 모드에서 기본 설정은 "가장 짧은 길이에 맞추기"입니다. 사용자가 그린 경계 상자와 설정한 사진 해상도에 따라 이 설정을 선택할 수 있습니다.
  • 다음으로, 객체 탐지에서 이미지 처리를 할 것이므로 처리 블록을 "이미지"로 추가합니다. 그런 다음 학습 블록에 "객체 탐지"를 추가합니다.
  • 출력 특징에서 처리할 특징 배열을 확인할 수 있습니다.
  • 이제 "Impulse 저장"을 클릭하세요.

 

다음으로, 특징점 생성에 대해 살펴보겠습니다.

 

5단계: 특징 생성

 

지금은 처리 블록에 있습니다. 즉, 원시 특징을 처리하여 특징 중요도를 높이는 단계입니다. 어떻게 작동하는지 살펴보겠습니다.

 

 

에지 임펄스에서 특징을 생성하는 절차

 

늘 그렇듯, 전체 절차는 그림으로 참고하실 수 있도록 위에 추가했습니다.

 

  • 먼저 왼쪽 메뉴 열에서 "이미지" 옵션을 선택하세요. 이제 원본 특징, 매개변수 및 DSP 결과를 볼 수 있습니다.
  • 설정에서 "그레이스케일"을 선택하면 시스템 부하를 줄일 수 있습니다. 그러면 DSP 결과의 변화를 확인할 수 있습니다.
  • 이제 "매개변수 저장"을 클릭하면 "피처 생성" 옵션이 나타납니다. 그것을 클릭하세요.
  • 특징 생성이 시작되면 몇 초 후에 특징 탐색기에서 상세 보고서를 확인할 수 있습니다. 위 네 번째 단계에서는 제 데이터셋에서 생성된 특징들을 차트로 나타낸 것을 볼 수 있습니다. 이 차트에서 각 색상은 개별 레이블을 나타냅니다. 색상이 서로 섞여 있는 경우, 해당 레이블의 특징들이 매우 유사하다는 의미입니다. 이 경우, 제공된 데이터셋을 수정하거나 차원을 변경하여 문제를 해결해야 합니다. 각 색상이 분리되어 그룹화될 때만 해당 머신러닝 모델의 정확도가 높다는 것을 의미합니다.

 

다음으로, 모델 학습의 핵심 부분으로 넘어가겠습니다.

 

6단계: 모델 학습

 

여기서는 신경망을 구성하고 학습 과정을 시작하겠습니다.

 

 

Edge Impulse에서 모델 학습 절차

 

위 이미지는 신경망 구성 및 학습 과정을 단계별로 보여줍니다.

 

여기서 수정되는 중요한 매개변수를 살펴보겠습니다.

 

학습 주기 횟수: 에포크(epoch) 또는 학습 주기는 학습 알고리즘이 모든 학습 데이터를 한 번씩 처리하고 역전파를 사용하여 모델의 매개변수를 업데이트하는 과정을 말합니다. 에포크 수를 늘리면 모델 정확도가 향상될 수 있지만 과적합이 발생할 수 있고, 에포크 수를 줄이면 과적합을 방지할 수 있지만 과소적합이 발생할 수 있습니다.

 

학습률: 학습률은 각 학습 단계에서 모델의 매개변수가 얼마나 변하는지를 제어합니다. 이는 신경망의 학습 속도를 결정합니다. 모델이 너무 빨리 학습하여 과적합되는 경우, 학습률을 낮출 수 있습니다.

 

적절한 값을 설정하는 것은 상당히 어렵습니다. 시행착오를 거치면 최적의 값을 찾을 수 있습니다.

 

올바른 값을 설정한 후 "학습 시작"을 누르세요. 학습 주기 횟수와 학습 속도를 고려하면 완료하는 데 다소 시간이 걸리므로 편안하게 기다리세요.

 

학습이 완료되면 F1 점수로 시스템의 정확도를 나타내는 학습 성능을 확인할 수 있습니다. 숫자가 클수록 정확도가 높습니다.

 

마지막으로 모델 배포에 대해 알아보겠습니다.

 

7단계: 모델 배포

 

먼저 왼쪽 메뉴에서 "배포" 옵션을 클릭하면 배포 페이지가 나타납니다. 배포에는 여러 가지 옵션이 있으며, 브라우저를 통해 모델을 실시간으로 확인할 수도 있습니다. 자세한 내용은 해당 페이지를 참조하세요.

 

자, 이제 우리는 네 가지 일을 할 것입니다.

  1. 배포 페이지로 이동하세요.
  2. 배포 옵션으로 "Arduino 라이브러리"를 선택하세요.
  3. 대상을 "Espressif ESP-EYE (ESP32 240MHz)"로 변경하십시오.
  4. "빌드"를 클릭하세요.

 

이렇게 하면 아두이노 IDE에 설치할 전용 라이브러리를 구축하는 과정이 시작됩니다.

 

 

Edge Impulse에서 모델 배포 구성하기

 

위 이미지는 학습된 모델로 구성된 아두이노 라이브러리를 구축하는 과정을 보여줍니다. 다운로드한 파일은 ESP32 카메라 객체 감지 코드 입니다 . 이 파일을 아두이노 IDE에 바로 설치할 수 있습니다. 이제 자세한 단계를 살펴보겠습니다.

 

ESP32-CAM에 엣지 임펄스 코드 업로드

 

이제 아래 단계를 따라 Edge Impulse에서 다운로드한 Arduino 라이브러리를 Arduino IDE에 설치해 보겠습니다.

 

빌드가 완료되면 다운로드되는 zip 폴더의 압축을 푸세요. 제 경우에는 zip 폴더의 이름이 "ei-vegetables-identification-arduino-1.0.1.zip"입니다.

 

  1. 압축 해제된 폴더 안에는 프로젝트 제목 뒤에 "inferencing"이라는 단어가 붙은 폴더가 있습니다. 제 경우에는 "Vegetables_Identification_inferencing"입니다.
  2. 이 폴더를 복사하여 Documents->Arduino->libraries 폴더에 붙여넣으세요.
  3. 아두이노 IDE를 다시 시작하면 ESP32-CAM에 코드를 업로드할 준비가 완료됩니다.
  4. 아두이노 IDE를 다시 시작한 후, 설치된 라이브러리에서 예제 프로그램을 엽니다. 아래 이미지를 참조하십시오.

 

아두이노 IDE에서 설치된 라이브러리의 예제 프로그램을 여는 단계

 

프로그램에서 수정해야 할 부분은 단 하나뿐입니다. 코드 시작 부분에 있는 '#define CAMERA_MODEL_ESP_EYE' 줄을 주석 처리하고, '#define CAMERA_MODEL_AI_THINKER' 줄의 주석을 해제하는 것입니다. 참고를 위해 아래에 스크린샷을 첨부했습니다.

 

 

아두이노 코드 수정

 

다음 단계는 당연히 ESP32-CAM에 코드를 업로드하는 것입니다. 코드를 업로드하면 그 다음 단계에서 작동 시연을 진행할 수 있습니다.

 

ESP32 CAM 객체 인식 프로젝트 작동 시연

 

ESP32-CAM을 올바른 위치와 방향으로 배치하여 카메라가 피사체를 선명하게 촬영할 수 있도록 하세요. 저는 ESP32-CAM을 최적의 높이와 거리에 고정하기 위해 소형 휴대폰 삼각대를 사용할 예정입니다.

 

 

ESP32-CAM을 소형 삼각대에 고정하는 모습

 

위 그림에서 하드웨어가 삼각대에 연결된 것을 볼 수 있습니다. 다음으로, 예제 프로그램을 업로드한 후 Arduino IDE에서 시리얼 모니터를 엽니다. 장치가 감지된 객체를 보여주는 일련의 메시지를 출력하기 시작한 것을 확인할 수 있습니다.

 

 

객체 감지 임시 데모

 

위 이미지는 업로드된 코드가 실시간으로 작동하는 모습을 보여줍니다. 네, 실제로 토마토를 문제없이 감지했습니다. 이렇게 해서 프로젝트가 완료되었습니다. 하지만 앞서 말씀드린 것처럼, 이 프로젝트를 더욱 효과적으로 만들기 위해 OLED 디스플레이를 사용할 예정입니다. 그래서 ESP32 카메라 객체 감지 코드를 OLED 디스플레이를 포함하도록 수정했습니다. 전체 코드는 이 페이지 하단에서 확인하실 수 있습니다. ESP32 카메라 객체 감지 GitHub 페이지에서도 코드를 다운로드할 수 있습니다.

 

필요한 모든 변경 작업을 완료한 후 수정된 코드를 업로드하면 최상의 결과를 얻을 수 있습니다.

 

 

OLED 디스플레이를 사용한 데모

 

위 이미지는 수정된 코드의 실행 결과를 보여주며, OLED 디스플레이에 출력되는 모습을 나타냅니다.

 

결론

 

저희는 Edge Impulse를 활용한 머신러닝 프로젝트를 성공적으로 완료했습니다. 하지만 Edge Impulse의 기능은 이 프로젝트를 훨씬 뛰어넘습니다. 이 강력한 플랫폼을 활용할 수 있는 가능성은 무궁무진합니다. 앞으로 진행하실 프로젝트에서 Edge Impulse의 잠재력을 최대한 활용해 보시기를 권장합니다. 즐거운 학습 되세요!

 

이 글의 마지막 시연에 사용된 전체 코드와 회로도는 다음 GitHub 저장소에서 확인할 수 있습니다.

 

전체 프로젝트 코드

 

 

이 튜토리얼의 원문 기사를 참고하시려면 이 링크를 따라가세요. 

 

 

전체 코드를 아래에 올립니다.

 

/*
*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

 

 

아~ 길기도 길다.

 

반응형

캐어랩 고객 지원

취업, 창업의 막막함, 외주 관리, 제품 부재!

당신의 고민은 무엇입니까? 현실과 동떨어진 교육, 실패만 반복하는 외주 계약, 아이디어는 있지만 구현할 기술이 없는 막막함.

우리는 알고 있습니다. 문제의 원인은 '명확한 학습, 실전 경험과 신뢰할 수 있는 기술력의 부재'에서 시작됩니다.

이제 고민을 멈추고, 캐어랩을 만나세요!

코딩(펌웨어), 전자부품과 디지털 회로설계, PCB 설계 제작, 고객(시장/수출) 발굴과 마케팅 전략으로 당신을 지원합니다.

제품 설계의 고수는 성공이 만든 게 아니라 실패가 만듭니다. 아이디어를 양산 가능한 제품으로!

귀사의 제품을 만드세요. 교육과 개발 실적으로 신뢰할 수 있는 파트너를 확보하세요.

지난 30년 여정, 캐어랩이 얻은 모든 것을 함께 나누고 싶습니다.

카카오 채널 추가하기

카톡 채팅방에서 무엇이든 물어보세요

당신의 성공을 위해 캐어랩과 함께 하세요.

캐어랩 온라인 채널 바로가기

캐어랩