Researching TinyML Part 2: Deteksi dan Rekognisi Gestur Tangan Menggunakan Raspberry Pi PICO (RP2040)

Selamat pagi/siang/sore/malam untuk semua pembaca jurnal gulo. Pada jurnal kali ini akan dilakukan implementasi TinyML dengan menggunakan edge impulse untuk melakukan rekognisi terhadap gestur tangan pada Raspberry Pi PICO (RP2040). Jurnal ini merupakan kelanjutan dari jurnal sebelumnya mengenai Instalasi dan konfigurasi edge-impulse. Pada implementasi kali ini digunakan sensor IMU BNO055 sebagai acuan rekognisi. Contoh sensor BNO055 yang saya gunakan dapat dilihat pada gambar 1.

Gambar 1. IMU Sensor BNO055

Sensor IMU BNO memiliki degree of freedom sebanyak 9. Nilai 9 tersebut berarti sensor BNO055 terdiri dari 3-dof accelerometer, 3-dof gyroscope, dan 3-dof magnetometer. Namun pada implementasi kali ini kita hanya gunakan bagian 3-dof accelerometernya saja. Namun untuk pengembangan lebih lanjut dan apabila teman-teman pembaca menginginkan hasil yang lebih baik bisa juga digunakan 3-dof gyroscope untuk membantu merekognisi gestur tangan. Accelerometer merupakan sensor yang digunakan untuk mendeteksi percepatan pada suatu arah tertentu dalam vektor. Ilustrasi accelerometer dapat dilihat pada gambar 2.

Gambar 2. Ilustrasi Accelerometer (sumber: Robot Magazine)

Dari accelerometer terdapat 3 buah arah vektor yakni X, Y, dan Z. Vektor X akan fluktuatif pada pergerakan maju-mundur, Vektor ya akan fluktuatif pada pergerakan kanan-kiri, dan Vektor Z akan fluktuatif pada pergerakan naik-turun. Dasar pergerakan inilah yang akan kita gunakan sebagai data training untuk model TinyML yang akan digunakan untuk merekognisi gestur tangan. Gestur tangan yang akan di rekognisi pada implementasi kali ini adalah: naik-turun, kanan-kiri, maju-mundur, dan lingkaran. Ilustrasi gestur tangan yang digunakan dapat dilihat pada gambar 3, gambar 4, gambar 5, dan gambar 6.

Gambar 3. Gestur tangan naik-turun
Gambar 4. Gestur tangan kiri-kanan
Gambar 5. Gestur Maju Mundur
Gambar 6. Gestur tangan circle.

Instalasi Compiler Raspberry Pi PICO

Raspberry pi pico merupakan perangkat microcontroller yang berbeda jenis dengan Raspberry Pi pada umumnya. Pada umumnya perangkat raspberry pi merupakan perangkat mini pc yang bisa menjalankan operating system. Karena dapat menjalankan operating system maka pemrograman raspberry pi mini pc dapat digunakan dengan menggunakan beberapa bahasa pemrograman seperti C++, Python, JS, dan lainnya. Pada perangkat microcontroller seperti raspberry pi pico paradigma yang digunakan berbeda dari mini pc. Pada raspberry pi pico untuk melakukan pemrograman perangkat kita dapat menggunakan C maupun python dengan micropython. Untuk bahasa pemrograman C dibutuhkan sebuah compiler yang mampun mengcompile program C menjadi file uf2. File uf2 merupakan file firmware yang nantinya akan dijalankan pada perangkat raspberry pi pico. Sementara apabila kita menggunakan micropython untuk memprogram raspberry pi pico maka cukup untuk melakukan flashing firmware python di awal pada saja, setelahnya kita dapat langsung melakukan upload file .py . Pada implementasi ini akan digunakan bahasa C untuk melakukan pemrograman pada raspberry pi pico. Dalam melakukan implementasi ini terdapat beberapa langkah yang harus dilakukan terlebih dahulu, berikut adalah step-step yang dilakukan:

  • Pertama-tama dilakukan instalasi visual studio code pada perangkat windows anda. Pada tutorial ini digunakan perangkat dengan OS windows untuk implementasi percobaan. Namun jika menggunakan perangkat linux maka dapat menyesuaikan dengan tutorial lain yang ada di internet. Selain pada implementasi ini tidak menggunakan arduino IDE dikarenakan penulis ingin mencoba cara lain untuk dapat melakukan pemrograman lain dengan mengkompile secara manual program yang dibutuhkan. File master visual studio code dapat di download pada link berikut vscode . Setelah selesai melakukan download file selanjutnya lakukan instalasi visual studio code seperti biasa.
  • Setelah dilakukan instalasi visual studio code selanjutnya buat sebuah folder pada drive C: di laptop teman-teman dan beri nama folder tersebut VSARM. Selanjutnya buat folder lain di dalam folder VSARM tersebut, folder yang dibuat adalah : armcc, lib, mingw, dan sdk. Folder armcc digunakan untuk menyimpan GCC dari arm, folder lib digunakan untuk menyimpan library yang akan digunakan pada raspberry pi pico, mingw merupakan folder yang berisi Minimalist GNU untuk mengeksekusi compiler pada gcc, sementara sdk berisi sdk dari raspberry pi pico. Dari langkah ini akan dihasilkan susunan folder seperti gambar 7.
Gambar 7. Susunan folder VSARM.
  • Setelah dilakukan pembuatan folder seperti gambar 7 selanjutnya kita akan mengisi tiap folder dengan beberapa file. Pertama-tama kita akan isi folder armcc dengan file gnu toolchain. Installer GNU toolchain bisa di download pada link berikut . Dari file yang ada di link tersebut teman-teman bisa mendownload file bernama gcc-arm-none-eabi-10.3-2021.10-win32.exe. Setelah file berhasil terdownload selanjutnya dilakukan instalasi file tersebut. Pada tahap instalasi apabila terdapat window konfigurasi path instalasi maka dapat diarahkan ke folder C:\VSARM\armcc seperti gambar 8. Sementara itu pada akhir instalasi akan terdapat window untuk menambahkan path ke variable system seperti gambar 9. Pada window ini teman-teman dapat memilih semua pilihan.
Gambar 8. Konfigurasi path ke C:\VSARM\armcc
Gambar 9. Konfigurasi Path ke Environment Variable.
  • Setelah toolchain berhasil diinstall selanjutnya dilakukan instalasi dan konfigurasi MinGW pada komputer. File MinGW dapat di download pada link berikut MinGW. Setelah file MinGW terdownload selanjutnya lakukan ekstrasi file tersebut dengan unzip/winrar. Folder hasil ekstraksi selanjutnya dapat di copy pada folder C:\VSARM\mingw. Pada komputer pribadi saya hasil copy MinGW ke folder C:\VSARM\mingw dapat dilihat pada gambar 10.
Gambar 10. Folder mingw32 hasil ekstraksi MinGW pad path C:\VSARM\mingw.
  • Selanjutnya setelah folder dipindahkan ke C:\VSARM\mingw, teman-teman bisa membuka CMD sebagai administrator dan memasukan code berikut untuk melakukan konfigurasi pada mingw.
echo mingw32-make %* > C:\VSARM\mingw\mingw32\bin\make.bat
  • Setelah konfigurasi mingw selesai dilakukan, teman-teman bisa melakukan instalasi CMAKE sebagai compiler program. Proses instalasi dilakukan seperti software pada umumnya, dan tidak ada penambahan konfigurasi lainnya. Program master untuk CMAKE dapat di download pada link berikut cmake . Dari link tersebut teman-teman bisa mendownload file cmake-3.24.0-rc4-windows-x86_64.msi.
  • Tahap berikutnya yang dilakukan apabila CMAKE sudah terinstall adalah instalasi python interpreter pada komputer teman-teman. Python yang digunakan pada implementasi ini adalah python 3.9. untuk instalasi python dilakukan seperti biasa dan tidak ada penambahan konfigurasi lainnya. File master python dapat di download dari link berikut python. Teman-teman bisa memilih file python 3.9.13.
  • Kemudian lakukan instalasi GIT untuk windows supaya mempermudah kita dalam melakukann pulling dan update SDK raspberry pi pico. Instalasi GIT dilakukan dengan mendownload file dari link berikut GIT .
  • Setelah GIT berhasil terinstall teman-teman dapat membuka aplikasi GIT BASH seperti gambar 11. Selanjutnya pada git bash teman-teman dapat melakukan cloning repository SDK pada raspberry pi pico dengan syntax sebagai berikut
cd /c/VSARM/sdk/pico 
git clone -b master https://github.com/raspberrypi/pico-sdk.git 
cd pico-sdk 
git submodule update --init 
cd .. 
git clone -b master https://github.com/raspberrypi/pico-examples.git
Gambar 11. GIT Bash
  • Setelah file Raspberry Pi Pico SDK sudah terdownload dan terkonfigurasi di local drive, selanjutnya kita akan menambahkan beberapa konfigurasi di enviroment variable. Tujuan dari menambahkan konfigurasi ini adalah agar kita dapat mengakses semua command maupun konfigurasi dari cmd. Untuk menambahkan konfigurasi environment bisa dilakukan dengan melakukan search “edit the system environment variables” seperti gambar 12. Selanjutnya pilih menu environment variables. Selanjutnya pada bagian User Variables pilih path dan tambahkan variable: C:\VSARM\mingw\mingw32\bin, dan C:\VSARM\armcc\10 2021.10\bin. Untuk direktori armcc namanya bisa mengikuti sesuai dengan versi dari toolchain teman-teman. Contoh konfigurasi dapat dilihat seperti gambar 13.
Gambar 12. Menu edit variable
Gambar 13. Environment Variable.
  • Setelah itu, jangan lupa menyimpan konfigurasi dengan menekan tombol OK. Kemudian konfigurasi PICO_SDK_PATH pada user variable seperti gambar 14. Isi dengan letak direktori dari SDK raspberry pi pico.
Gambar 14. Konfiguras PICO_SDK_PATH.
  • Untuk mengecek apakah konfigurasi sudah benar, teman-teman bisa membuka CMD seperti gambar 15 dan mengetikkan beberapa perintah berikut:
make
mingw32-make
echo %PICO_SDK_PATH%
Gambar 15. Pengetesan syntax pada CMD.
  • Selanjutnya setelah instalasi compiler selesai, lakukan instalasi nodejs yang nantinya akan digunakan untuk menjalankan edge-impulse-cli. Instalasi NodeJS dapat dilakukan dengan mendownload master file dari link berikut nodejs. Dalam melakukan download pilih file dengan ekstensi .msi apabila teman-teman menggunakan windows. Setelah terdownload lakukan instalasi nodejs seperti biasa. Kemudian buka kembali environment setting seperti gambar 13 dan pastikan bahwa nodejs sudah tertambah di bagian path. Jika belum teman-teman bisa menambahkan path nodejs di user variable tersebut.
  • Setelah nodejs terinstall pastikan dengan membuka cmd seperti gambar 16 dan jalankan beberapa perintah berikut:
node -v
npm 
Gambar 16. Testing Nodejs pada CMD.
  • setelah nodejs dan npm bisa dipastikan berjalan seperti gambar 16, selanjutnya lakukan instalasi edge-impulse-cli dengan cara mengetikkan syntax berikut:
npm install -g edge-impulse-cli

Pemrograman Raspberry Pi Pico untuk mengakuisisi data

Sebelum melakukan pemrograman pada raspberry pi buat rangkaian pada raspberry pi seperti gambar 17.

Gambar 17. Rangkaian Raspberry Pi Pico dan BNO055.

Setelah selesai dibuat rangkaian seperti gambar 17, pemrograman di lakukan dengan menggunakan visual studio code. Code program dapat diperoleh dari link github berikut github. Teman-teman bisa melakukan cloning atau mendownload langsung dari link tersebut. Dari link tersebut terdapat 2 buah direktori, dimana direktori Data_acquisition berisi program untuk akuisisi data training dan Inferencing berisi program untuk menjalankan TinyML. Dari keduah program tersebut pertama-tama kita akan menjalankan program Data_acquisition. Pada program data acquisition terdapat 2 buah file seperti gambar 18.

Gambar 18. File Program Data_acquisition

Pertama-tama terdapat file CMakeLists.txt yang beriksi instruksi compile dari program kita. File CMakeLists.txt dibutuhkan untuk mendifinisakan bagaimana sebuah program akan di compile, apa saja library yang akan digunakan, dan output yang diinginkan dari kompilasi. Pada file ini berisi data seperti syntax di bawah ini:

cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(picobno)
pico_sdk_init()
add_executable(picobno picobno.c)
target_link_libraries(picobno 
    pico_stdlib
    hardware_i2c
)
pico_enable_stdio_usb(picobno 1)
pico_enable_stdio_uart(picobno 0)
pico_add_extra_outputs(picobno)

Dari program diatas dapat dilihat bahwa kita menggunakan pico_stdlib dan hardware_i2c library untuk mengakses BNO055 dan memprogram raspberry pi pico. Selain itu pada program di atas dilakukan konfigurasi untuk mengoutputkan serial melalui usb dan bukan uart. Opsi ini dilakukan dengan syntax berikut:

pico_enable_stdio_usb(picobno 1)
pico_enable_stdio_uart(picobno 0)

Kemudian untuk mendapatkan hasil kompilasi berupa file uf2 yang nantinya kita akan upload di raspberry pi pico, digunakan syntax berikut:

pico_add_extra_outputs(picobno)

Selanjutnya terdapat file picobno.c yang merupakan file program untuk memprogram raspberry pi pico. Isi dari file tersebut adalah syntax berikut:

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"

#define I2C_PORT i2c0

static int addr = 0x28;

// Initialise Accelerometer Function
void accel_init(void){
    // Check to see if connection is correct
    sleep_ms(1000); // Add a short delay to help BNO005 boot up
    uint8_t reg = 0x00;
    uint8_t chipID[1];
    i2c_write_blocking(I2C_PORT, addr, &reg, 1, true);
    i2c_read_blocking(I2C_PORT, addr, chipID, 1, false);

    if(chipID[0] != 0xA0){
        while(1){
            printf("Chip ID Not Correct - Check Connection!");
            sleep_ms(5000);
        }
    }

    // Use internal oscillator
    uint8_t data[2];
    data[0] = 0x3F;
    data[1] = 0x40;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Reset all interrupt status bits
    data[0] = 0x3F;
    data[1] = 0x01;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Configure Power Mode
    data[0] = 0x3E;
    data[1] = 0x00;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(50);

    // Defaul Axis Configuration
    data[0] = 0x41;
    data[1] = 0x24;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Default Axis Signs
    data[0] = 0x42;
    data[1] = 0x00;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Set units to m/s^2
    data[0] = 0x3B;
    data[1] = 0b0001000;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(30);

    // Set operation to acceleration only
    data[0] = 0x3D;
    data[1] = 0x0C;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(100);
}

int main(void){
    stdio_init_all(); // Initialise STD I/O for printing over serial

    // Configure the I2C Communication
    i2c_init(I2C_PORT, 400 * 1000);
    gpio_set_function(4, GPIO_FUNC_I2C);
    gpio_set_function(5, GPIO_FUNC_I2C);
    gpio_pull_up(4);
    gpio_pull_up(5);

    // Call accelerometer initialisation function
    accel_init();

    uint8_t accel[6]; // Store data from the 6 acceleration registers
    int16_t accelX, accelY, accelZ; // Combined 3 axis data
    float f_accelX, f_accelY, f_accelZ; // Float type of acceleration data
    uint8_t val = 0x08; // Start register address

    // Infinite Loop
    while(1){
        i2c_write_blocking(I2C_PORT, addr, &val, 1, true);
        i2c_read_blocking(I2C_PORT, addr, accel, 6, false);

        accelX = ((accel[1]<<8) | accel[0]);
        accelY = ((accel[3]<<8) | accel[2]);
        accelZ = ((accel[5]<<8) | accel[4]);

        f_accelX = accelX / 100.00;
        f_accelY = accelY / 100.00;
        f_accelZ = accelZ / 100.00;

        // Print to serial monitor
        printf("%.2f,%.2f,%.2f\n", f_accelX, f_accelY, f_accelZ);
        sleep_ms(100);
    }
}

Dari syntax diatas kita akan mengakuisisi nilai accelerometer dari sensor BNO055. Program ini dikembangkan dari program yang dibuat oleh learnembeddedsystems. Program pembacaan sensor BNO055 yang digunakan tidak menggunakan library yang existing namun dengan menggunakan hex payload untuk melakukan konfigurasi terhadap beberapa parameter pada sensor BNO055 seperti pada syntax berikut:

void accel_init(void){
    // Check to see if connection is correct
    sleep_ms(1000); // Add a short delay to help BNO005 boot up
    uint8_t reg = 0x00;
    uint8_t chipID[1];
    i2c_write_blocking(I2C_PORT, addr, ®, 1, true);
    i2c_read_blocking(I2C_PORT, addr, chipID, 1, false);

    if(chipID[0] != 0xA0){
        while(1){
            printf("Chip ID Not Correct - Check Connection!");
            sleep_ms(5000);
        }
    }

    // Use internal oscillator
    uint8_t data[2];
    data[0] = 0x3F;
    data[1] = 0x40;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Reset all interrupt status bits
    data[0] = 0x3F;
    data[1] = 0x01;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Configure Power Mode
    data[0] = 0x3E;
    data[1] = 0x00;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(50);

    // Defaul Axis Configuration
    data[0] = 0x41;
    data[1] = 0x24;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Default Axis Signs
    data[0] = 0x42;
    data[1] = 0x00;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Set units to m/s^2
    data[0] = 0x3B;
    data[1] = 0b0001000;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(30);

    // Set operation to acceleration only
    data[0] = 0x3D;
    data[1] = 0x0C;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(100);
}

Hasil dari program tersebut adalah 3 buah nilai acclerometer x,y,z yang akan di print secara berurutan sesuai dengan syntax berikut:

printf("%.2f,%.2f,%.2f\n", f_accelX, f_accelY, f_accelZ);

Sedikit berbagi tips, pada pemrograman raspberry pi pico memiliki struktur yang berbeda dengan menggunakan arduino ide. Apabila kita dengan menggunakan arduino IDE struktur syntax akan berupa sebagai berikut:

#include library.h

void setup(){
.
.
}

void loop(){
.
.
}

Pada struktur kode arduino ide seperti syntax di atas, kita tau bahwa terdapat 2 buah fungsi utama yaitu setup dan loop. Fungsi setup merupakan fungsi yang akan dijalankan pertama kali dan utama. Kemudian setelah itu fungsi loop akan dijalankan untuk melakukan looping program. Sementara itu pada raspbery pi pico struktur akan terlihat seperti berikut:

#include library.h

int main(void){
.
.
while (1){
   .
   .
   .
 }
}

Dari syntax dapat dilihat bahwa looping akan dijalankan di dalam main function yang merupakan fungsi utama dalam program. Hal ini terjadi dikarenakan pemrograman dilakukan dengan bahasa C murni untuk raspberry pi pico.

Selanjutnya file terakhir yang berada di dalam folder Data_acquisition adalah file pic_sdk_import.cmake. File ini merupakan file konfigurasi CMAKE untuk sdk raspberry pi pico yang sebelumnya sudah kita download dan clone di direktori C:\VSARM\sdk. Syntax di dalam file ini tidak akan kita ubah-ubah, jadi kita hanya menggunakan apa adanya karena syntax tersebut merupakan standard dari file konfigurasi cmake.

kemudian untuk melakukan kompilasi file program dapat dilakukan dengan cara membuka terminal pada visual studio code teman-teman, seperti gambar 19.

Gambar 19. Terminal pada visual studio code.

Secara default ketika teman-teman membuka terminal baru maka terminal akan selalu mengarah ke direktori dimana program teman-teman berada. Selanjutnya pada terminal tersebut ketikkan beberapa syntax berikut untuk mengkompilasi program:

mkdir build
cd build
cmake -G "MinGW Makefiles" ..
mingw32-make

dari syntax di atas, pertama-tama akan dibuat folder build tempat dimana kita akan mengkompile program. Selanjutnya kita pindah direktori ke dalam direktori build tersebut, setelah berada di dalam direktori build kita jalankan perintah cmake untuk mengkonfigurasi dan menyiapkan seluruh file yang dibutuhkan untuk kompilasi. Dan selanjutnya perintah mingw32-make merupakan perintah kompilasi terhadap keseluruhan file menjadi sebuah file uf2. Apabila kompilasi berhasil maka akan didapatkan tampilan terminal seperti gambar 20 dan akan terdapat file berekstensi .uf2 didalam direktori build seperti gambar 21.

Gambar 20. kompilasi program yang berhasil.
Gambar 21. File ekstensi .uf2 di dalam folder build.

Pada gambar 21, file .uf2 berada di paling bawah dari daftar file. Selanjutnya untuk mengupload file tersebut pada raspberry pi pico bisa dilakukan dengan menekan Tombol BOOT pada raspberry pi pico, baru setelah itu menghubungkan raspberry pi pico dengan laptop atau komputer teman-teman. Hasil dari proses ini adalah sebuah drive baru ter-mount ke komputer kita seperti gambar 22. Selanjutnya untuk melakukan upload file bisa dilakukan dengan mengcopy file ekstensi .uf2 ke drive raspberry pi pico yang termount di komputer kita seperti gambar 23.

Gambar 22. Drive Raspberry Pi Pico termounting di komputer kita.
Gambar 23. Hasil menyalin file .uf2 ke dalam drive raspberry pi pico.

Pada gambar 23, apabila kita sudah selesai mengcopy ke dalam drive tersebut maka biasanya drive raspberry pi akan langsung disconnected dan menjalankan program yang sudah kita copy. Untuk melihat hasil dari program ini, kita bisa menggunakan serial monitor menggunakan aplikasi putty atau arduino IDE. pertama-tama teman-teman harus mengecek port nomor berapa yang merupakan serial com port dari raspberry pi pico pada device manager. Pada komputer saya, raspberry pi pico terkoneksi serial pada COM5 seperti gambar 24.

Gambar 24. Serial COM Port Raspberry Pi Pico.

Selanjutnya apabila teman-teman ingin menggunakan putty teman-teman bisa melakukan konfigurasi seperti gambar 25.

Gambar 25. Konfigurasi pembacaan serial pada putty.

Dari gambar 25, pada serial line saya isi dengan COM5 yang merupakan line dari raspberry pi pico di komputer saya. Dan speed saya isi dengan 115200, karena default baudrate yang digunakan oleh serial raspberry pi pico sebesar 115200. Kemudian klik open untuk mulai membaca data dari raspberry pi pico. hasil pembacaan data dapat dilihat pada gambar 26.

Gambar 26. Data IMU dari raspberry pi pico.

Selanjutnya kita akan melakukan forwarding data IMU raspi ke edge impulse yang nantinya kita akan gunakan untuk mengakuisisi data training. Dalam proses akuisisi ini akan digunakan edge-impulse-cli untuk melakukan forwarding data dari serial ke edge-impulse platform. Sebelum melakukan forwarding teman-teman harus signup / signin pada edge-impulse terlebih dahulu. Untuk sign-in atau sign-up teman-teman dapat masuk ke link berikut edge-impulse. Kemudian pilih menu login pada web tersebut. Pada menu login tersebut teman-teman akan diberikan pilihan untuk sign-in atau sign-up. Teman-teman bisa melakukan login atau sign-up terlebih dahulu. Kemudian jika teman-teman sudah masuk teman-teman harap membuat project baru dengan mengeklik tombol hijau create project seperti gambar 27.

Gambar 27. Create Project Edge-Impulse.

Setelah itu teman-teman bisa memberikan nama project baru, nama ini terserah teman-teman. Contoh dari proses ini dapat dilihat pada gambar 28.

Gambar 28. Project Baru edge-impulse

Setelah project edge-impulse berhasil dibuat biasanya teman-teman akan mendapatkan tampilan window seperti gambar 29. Pada tahap ini teman-teman bisa memilihi accelerometer.

Gambar 29. Accelerometer

Dalam melakukan proses development model TinyML pada edge-impulse, pertama-tama lakukan akuisisi data dari alat ke edge-impulse studio(website). Untuk mengakuisisi data dari alat digunakan menu data acquisition pada edge-empulse. Namun untuk menggunakan menu tersebut kita harus menghubungkan device dengan studio edge-impulse terlebih dahulu. Cara untuk menghubungkan device dengan studio edge impulse adalah dengan membuka cmd dan jalankan edge-impulse-cli data forwarder seperti gambar 30.

Gambar 30. Edge impulse data forwarder.

Data forwarder merupakan fitur dari edge-impulse untuk memforwardkan data dari alat ke studio. Untuk menggunakan edge fitur ini jalankan syntax :

edge-impulse-data-forwarder

Seperti gambar 30 pertama-tama teman-teman akan diminta untuk memasukan alamat email dan password dari akun edge-impulse teman-teman. kemudian setelah terkoneksi teman-teman akan diminta untuk memilih serial com port dari raspberry pi pico yang sudah di program sebelumnya. Setelah terdapat informasi serial connected selanjutnya teman-teman akan diminta untuk memilih project yang teman-teman sudah buat sebelumnya. Setelah itu program edge impulse cli akan membaca contoh data serial dari raspberry pi pico. Pada proses ini teman-teman akan diminta untuk memberi nama setiap data yang terbaca pada edge-impulse, disini saya menamai data dengan x,y, dan z. Setelah semua terkonfigurasi maka akan terdapat informasi connected antara device dengan edge-impulse seperti gambar 30.

Langkah selanjutnya teman-teman akan mengakuisisi data pada menu data acquisition di edge-impulse. Disini teman-teman bisa masuk ke menu tersebut dan mengecek apakah device teman-teman sudah terhubung atau belum. Apabila sudah terhubung maka pada value record new data akan berisi data seperti gambar 31.

Gambar 31. Informasi device pada record data.

Dari gambar 31 didapatkan informasi nama device, label data, sample, sensor, dan frequency. Nama device merupaka identifier dari device kita. Label merupakan sebuah string yang digunakan untuk melabeli data kita, label ini menentukan jenis output rekognisi. Sample length merupakan waktu yang digunakan untuk mengambil sample data dari device. Sensor merupakan initial dari setiap data yang didapatkan dari device. frequency merupakan sampling dari pengambilan data pada device. Disini karena device sudah terdeteksi maka akan dilakukan sampling. Untuk sampling sendiri kita akan menggunakan 5 label yaitu: updown untuk gestur tangan naik turun, leftright untuk gestur tangan kanan kiri, forwardbackward untuk gestur tangan maju mundur, circle untuk gestur tangan membentuk lingkaran, dan idle untuk gestur tangan diam. Untuk pengambilan data langsung saja dapat dilakukan mengklik tombol start sampling. Contoh pengambilan data dapat dilihat pada gambar 32, gambar 33, gambar 34, gambar 35, dan gambar 36.

Gambar 32. Acquisition data Forward Backward.
Gambar 33. Acquaisition data circle.
Gambar 34. Acquisition data left-right.
Gambar 35. Acquisition data up-down
Gambar 36. Acquisition data idle.

Pengambilan data seperti gambar 32 hingga gambar 36 dilakukan sebanyak masing-masing 20 kali. Hal ini bertujuan untuk memperkaya data yang kita akan proses. Selanjutnya setelah data terakusisi selanjutnya lakukan proses splitting data untuk membagi data training dan testing pada menu data acquisition. Cara splitting adalah masuk ke submenu train split data seperti gambar 37

Gambar 37. ilustrasi train/split data

Pada data yang tidak balance antara penerima dan pengirim, submenu train test split akan membantu mu untuk melakukan splitting pada dataset dengan memilih tombol train/test split seperti gambar 38.

Gambar 38. Perform Train Test Split

Dari situ kemudian studio otomatis akan melakukan spliting dataset dan merebalance data. Pastikan setelah dataset di splitting warna submenu train/test berwarna hijau dan tidak ada error seperti gambar 39.

Gambar 39. data train test sudah balance.

Kemudian apabila data acquisition telah selesai dan data balance, proses selanjutnya adalah membuat impulse. Pada proses ini data yang telah diakuisisi akan di proses menjadi sebuah model. Proses pembuatan impulse untuk rekognisi gestur tangan dapat dilihat pada gambar 40.

Gambar 40. Pembuatan impulse.

Pada gambar 40, dapat kita lihat terdapat 3 buah proses yang akan dibangun pada impulse. Proses yang pertama merupakan proses preprocessing data, dimana pada proses ini diwakili dengan kotak berwarna merah pada gambar 40. Pada proses ini data akan disampling ukuran windownya, ukuran window yang akan digunakan pada proses ini adalah 1 detik. Sehingga pemrosesan nantinya akan dilakukan setiap 1 detik. Sementara itu untuk window increase kita gunakan sebesar 80ms, window increase akan digunakan untuk membuat window sliding dengan waktu 80ms pada setiap 1 detik data. Kemudian bagian terakhir yang disetting adalah frequency. Pada frequency, nilai tersebut disamakan dengan nilai saat mengakuisisi data.

Kemudian setelah dalam fase pembuatan impulse dilakukan pembuatan feature analysis dan selection yang diwakiliki oleh kotak putih. Pada tahap ini kita akan menggunakan spectral features untuk selection features dari data kita. Pada proses ini kita juga akan memilih semua axis untuk digunakan sebagai input dari model kita.

Proses terakhir dalam pembuatan input adalah Classification neural network dan anomaly detection yang diwakili warna biru. Pada proses ini nantinya kita akan membahas lebih banyak soal neural network. Neural network akan digunakan untuk melakukan klasifikasi dari data kita. Klasifikasi dilakukan untuk merekognisi setiap gerakan yang dilakukan oleh tangan kita apakah itu termasuk naik-turun (updown), kanan-kiri(leftright), Lingkaran (circle), maju-mundur(forwardbackward), dan berhenti(idle). Sementara itu untuk anomaly detection digunakan untuk mendeteksi anomaly dari data yang digunakan. Kemudian setelah selesai melakukan konfigurasi pada create impulse teman-teman bisa melakukan saving konfigurasi dan masuk ke menu spectral features seperti gambar 41

Gambar 41. Menu Spectral Features

Pada menu ini teman-teman dapat melihat spectral features dari setiap data sinyal IMU teman-teman. Pada menu ini terdapat window raw data yang berisi data mentah sinyal IMU, Parameters yang berisi nilai parameter dari spectral features, dan DSP result yang merupakan hasil dari pemrosesan spectral features. Pada konfigurasi parameter spectral features dapat disamakan terlebih dahulu dengan gambar 42.

Gambar 42. Konfigurasi parameter Spectral Features

Parameter pada gambar 42 terdiri dari 3 bagian yaitu scaling , filter dan spectral power. Spectral power merupakan parameter yang mengubah parameter domain waktu menjadi frequency menggunakan FFT. Sementara filter digunakan untuk memfilter sinyal IMU dengan pilihan LPF, HPF, maupun BPF. Sementara scaling digunakan untuk melakukan scaling x axis dari sinyal. Jika teman-teman penasaran dengan maksud dari parameter gambar 42, teman-teman bisa membaca mengenai hal tersebut pada bidang ilmu pemrosesan sinyal digital. Sementara ini kita akan menggunakan parameter yang sesuai dengan gambar 42. hasil dari parameter ini adalah sinyal pada DSP result seperti gambar 43 dan gambar 44.

Gambar 43. DSP result untuk frequency domain dan filter time domain.
Gambar 44. DSP result untuk Spectral power dan process features.

Selanjutnya setelah konfigurasi selesai dilakukan teman-teman harus mengeklik tombol save parameter pada spectral features. Hasil dari save parameter adalah teman-teman akan diarahkan pada submenu generate features dimana pada submenu ini teman-teman nantinya harus memilih tombol generate features untuk menghasilkan features seperti gambar 45.

Gambar 45. Generates Features.

Pada gambar 45 dapat kita lihat bahwa features yang digenerasi akan menghasilkan plot seperti feature explorer. Pada featurer explorer kita dapat melihat bahwa tiap-tiap data memiliki tempat di plot tersebut dan setiap data yang memiliki label sama akan mengelompok berdekatan. Selanjutnya akan dilakukan training Neural network pada menu NN classifier. Parameter model yang digunakan pada training neural network disini dapat dilihat pada gambar 46.

Gambar 46. Training Model Parameter.

Dari gambar 46 kita dapat melihat bahwa kita membuat sebuah model sederhana dengan 3 buah hidden layer yang masing-masing berisi Dense Layer 20 neuron, Dense Layer 10 Neuron, dan Dense Layer 5 Neuron. Dalam pembuatan arsitektur model sebenarnya tidak terdapat aturan yang baku, sehingga teman-teman bisa mengeksplor lebih lanjut dengan arsitektur yang berbeda-beda. Kebetulan pada proses ini saya membuat model dengan arsitektur yang sederhana agar tidak berat pada proses di raspberry pi pico. Kemudian pada konfigurasi gambar 46 kita dapat melihat bahwa iterasi yang digunakan sebanyak 30 dan learning rate sebesar 0.0005 serta validation set size sebesar 20%. Dari beberapa konfigurasi ini teman-teman dapat eksplor lebih lanjut. Menggunakan learning rate yang lebih besar dapat membuat model menjadi semakin robust, namun juga dapat mengakibatkan overfitting. Selain itu learning rate yang lebih besar mengakibatkan model akan lebih lama di training. Kemudian untuk learning rate yang lebih besar maka model akan di training lebih cepat namun akan kurang dalam hal robustness, sementara untuk learning rate yang lebih kecil maka model akan lebih cepat mencapai global maxima dari namun training akan lebih lama. Jika semua parameter sudah selesai di konfigurasi maka selanjutnya dilakukan training dengan memilih tombol start training. Hasil dari proses training dapat dilihat pada gambar 47.

Gambar 47. Hasil training Neural Network

Dari hasil training model gambar 47 dapat kita lihat bahwa akurasi model sebesar 99%. Disini kita mendapatkan akurasi yang sangat bagus dari model yang kita training. Dari hasil akurasi ini kita bisa katakan bahwa model kita sangat robust. Apabila kita lihat dari hasil confusion matrix maka nilai TP sangat besar untuk tiap tiap kelas dan F1 Score bernilai 99%. Selanjutnya dilakukan anomaly detection model training setelah NN classifier. Hal ini bertujuan untuk mencari anomaly pada data yang didapatkan dari perangkat IMU nantinya. Pada proses ini kita akan mengcluster kan data secara default dengan jumlah cluster sebesar 32 dan parameter yang digunakan menggunakan suggested parameter. Konfigurasi anomaly detection dapat dilihat pada gambar 48.

Gambar 48. Anomaly Detection Training.

Setelah parameter terkonfigurasi selanjutnya pilih start training pada anomaly detection. Hasil dari proses ini dapat dilihat pada gambar 49.

Gambar 49. Plot anomaly Detection.

Gambar 49 merupakan salah satu plot dengan 2 axis untuk anomaly detection. Plot ini dimaksudkan apabila data yang digunakan nantinya berada diluar cluster maka akan dianggap sebagai anomaly data dan model dapat memberitahukan kepada kita bahwa terjadi anomaly data.

Setelah semua model berhasil di training selanjutnya dilakukan proses deployment, pada proses ini model yang telah kita training akan kita deploy di perangkat raspberry pi pico. Untuk proses ini kita akan masuk ke menu deployment pada edge-impulse dan memilih untuk menjadikan model menjadi sebuah C/C++ library seperti gambar 50.

Gambar 50. Deployment Model pada library C.

Penggunaan model menjadi library c dan c++ dikarenakan kita melakukan pemrograman raspberry pi pico dengan menggunakan bahasa c. Kemudian setelah kita pilih C++ library pada menu deployment, kita pilih tombol build pada bagian bawah halaman. Setelah menekan build edge-impulse kemudian akan membuild model menjadi sebuah file.zip yang berisi library C++. File .zip yang telah ready akan otomatis terdownload dan dapat kita lakukan proses deployment dengan komputer kita. Setelah kita lakukan ekstraksi model dalam library, kita akan mendapatkan susunan file seperti gambar 51.

Gambar 51. Susunan file model edge-impulse.

Dari gambar 51 terdapat 3 direktori dan 2 files, 3 direktori tersebut adalah tflite model yang berisi model tflite yang sudah kita training, model-parameters berisi parameter model tflite, edge-impulse-sdk yang berisi sdk untuk menjalankan model edge-impulse dan file CMakeLists.txt serta Readme.txt. Dari semua file tersebut kita nantinya hanya akan menggunakan 3 buah folder saja untuk di copy ke dalam file dari github yang sudah di share sebelumnya.

Folder tflite-model, model-parameters, dan edge-impulse-sdk di copy pada direktori Inferencing pada file dari github. Hasil copy akan menghasilkan direktori seperti gambar 52.

Gambar 52. Direktori inferencing lengkap.

Dari direktori ini program yang digunakan terdapat pada folder source. Program yang digunakan memiliki syntax sebagai berikut:

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/i2c.h"
#include "ei_run_classifier.h"

const uint LED_PIN = 25;
#define I2C_PORT i2c0
static bool debug_nn = false;
static int addr = 0x28;

// Initialise Accelerometer Function
void accel_init(void){
    // Check to see if connection is correct
    sleep_ms(1000); // Add a short delay to help BNO005 boot up
    uint8_t reg = 0x00;
    uint8_t chipID[1];
    i2c_write_blocking(I2C_PORT, addr, &reg, 1, true);
    i2c_read_blocking(I2C_PORT, addr, chipID, 1, false);

    if(chipID[0] != 0xA0){
        while(1){
            printf("Chip ID Not Correct - Check Connection!");
            sleep_ms(5000);
        }
    }

    // Use internal oscillator
    uint8_t data[2];
    data[0] = 0x3F;
    data[1] = 0x40;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Reset all interrupt status bits
    data[0] = 0x3F;
    data[1] = 0x01;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Configure Power Mode
    data[0] = 0x3E;
    data[1] = 0x00;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(50);

    // Defaul Axis Configuration
    data[0] = 0x41;
    data[1] = 0x24;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Default Axis Signs
    data[0] = 0x42;
    data[1] = 0x00;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);

    // Set units to m/s^2
    data[0] = 0x3B;
    data[1] = 0b0001000;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(30);

    // Set operation to acceleration only
    data[0] = 0x3D;
    data[1] = 0x0C;
    i2c_write_blocking(I2C_PORT, addr, data, 2, true);
    sleep_ms(100);
}

int main()
{
  stdio_init_all();


  gpio_init(LED_PIN);
  gpio_set_dir(LED_PIN, GPIO_OUT);
  i2c_init(I2C_PORT, 400 * 1000);
  gpio_set_function(4, GPIO_FUNC_I2C);
  gpio_set_function(5, GPIO_FUNC_I2C);
  gpio_pull_up(4);
  gpio_pull_up(5);

  // Call accelerometer initialisation function
  accel_init();

  uint8_t accel[6]; // Store data from the 6 acceleration registers
  int16_t accelX, accelY, accelZ; // Combined 3 axis data
  float f_accelX, f_accelY, f_accelZ; // Float type of acceleration data
  uint8_t val = 0x08;
  ei_impulse_result_t result = {0};

  while (true)
  {
    ei_printf("Edge Impulse standalone inferencing (Raspberry Pi Pico)\n");


    while (1)
    {
      // blink LED
      gpio_put(LED_PIN, !gpio_get(LED_PIN));
      float buffer[EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE] = { 0 };

    for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) {
        // Determine the next tick (and then sleep later)
        uint64_t next_tick = ei_read_timer_us() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
        i2c_write_blocking(I2C_PORT, addr, &val, 1, true);
        i2c_read_blocking(I2C_PORT, addr, accel, 6, false);

        accelX = ((accel[1]<<8) | accel[0]);
        accelY = ((accel[3]<<8) | accel[2]);
        accelZ = ((accel[5]<<8) | accel[4]);

        f_accelX = accelX / 100.00;
        f_accelY = accelY / 100.00;
        f_accelZ = accelZ / 100.00;
        buffer[ix + 0]= f_accelX;
        buffer[ix + 1]= f_accelY;
        buffer[ix + 2]= f_accelZ;
        sleep_us(next_tick - ei_read_timer_us());
    }

      // the features are stored into flash, and we don't want to load everything into RAM
        signal_t signal;
        int err = numpy::signal_from_buffer(buffer, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE, &signal);
        if (err != 0) {
            ei_printf("Failed to create signal from buffer (%d)\n", err);
            return 1;
        }

        // Run the classifier
        ei_impulse_result_t result = { 0 };

        err = run_classifier(&signal, &result, debug_nn);
        if (err != EI_IMPULSE_OK) {
            ei_printf("ERR: Failed to run classifier (%d)\n", err);
            return 1;
        }

        // print the predictions
        ei_printf("Predictions ");
        ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
            result.timing.dsp, result.timing.classification, result.timing.anomaly);
        ei_printf(": \n");
        for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
            ei_printf("->  %s: %.4f%s\n", result.classification[ix].label, (result.classification[ix].value), "%");
        }
    #if EI_CLASSIFIER_HAS_ANOMALY == 1
        ei_printf("    anomaly score: %.3f\n", result.anomaly);
    #endif
        gpio_put(LED_PIN, 0);
    }

// #if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_ACCELEROMETER
//     #error "Invalid model for current sensor"
// #endif

    return 0;
}
}

Program di atas sama seperti program untuk akuisisi data. Namun yang berbeda terdapat proses inferencing untuk input IMU menjadi sebuah output rekognisi. Pada program diatas terdapat proses buffering untuk mengumpulkan sinyal IMU selama 1 detik sebelum di inference. Hal ini dilakukan karena model di training dengan window size sebesar 1 detik. Proses ini dijumpai pada syntax berikut:

 for (size_t ix = 0; ix < EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE; ix += 3) {
        // Determine the next tick (and then sleep later)
        uint64_t next_tick = ei_read_timer_us() + (EI_CLASSIFIER_INTERVAL_MS * 1000);
        i2c_write_blocking(I2C_PORT, addr, &val, 1, true);
        i2c_read_blocking(I2C_PORT, addr, accel, 6, false);

        accelX = ((accel[1]<<8) | accel[0]);
        accelY = ((accel[3]<<8) | accel[2]);
        accelZ = ((accel[5]<<8) | accel[4]);

        f_accelX = accelX / 100.00;
        f_accelY = accelY / 100.00;
        f_accelZ = accelZ / 100.00;
        buffer[ix + 0]= f_accelX;
        buffer[ix + 1]= f_accelY;
        buffer[ix + 2]= f_accelZ;
        sleep_us(next_tick - ei_read_timer_us());
    }

Selanjutnya program ini akan mengoutputkan nama probabilitas setiap kelas dan anomaly. Syntax untuk mengoutputkan probabilitas setiap kelas dan anomaly dapat dilihat pada syntax berikut:

 ei_printf("Predictions ");
        ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
            result.timing.dsp, result.timing.classification, result.timing.anomaly);
        ei_printf(": \n");
        for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
            ei_printf("->  %s: %.4f%s\n", result.classification[ix].label, (result.classification[ix].value), "%");
        }

Selanjutnya setelah direktori inferencing sudah lengkap dengan berbagai parameter yang dibutuhkan. Dilakukan proses compile program. Cara untuk mengcompile sama dengan sebelumnya, teman-teman cukup membuka terminal dan jalankan beberapa syntax berikut:

mkdir build
cd build
cmake -G "MinGW Makefiles" ..
mingw32-make

Apabila berhasil mengkompilasi maka akan menghasilkan tampilan seperti gambar 53.

Gambar 53. Kompilasi model berhasil.

Selanjutnya apabila sudah berhasil melakukan kompilasi, lakukan copy file .uf2 di dalam folder build ke raspberry pi pico. Untuk cara copy file dilakukan sama seperti sebelumnya dengan menekan tombol boot raspberry pi pico terlebih dahulu sebelum menghubungkan dengan komputer kita. Kemudian lepaskan tombol boot setelah drive raspberry pi pico berhasil termounting. Selanjutnya lakukan copy file .uf2 kedalam raspberry pi pico.

Untuk melakukan test terhadap raspberry pi pico dan model yang sudah berhasil kita deploy, teman-teman dapat menggunakan putty kembali maupun arduino IDE. Ketika dengan putty gunakan parameter seperti gambar 25 kembali.

Selanjutnya teman-teman dapat melakukan testing pada raspberry pi pico dengan beberapa gerakan yang tangan yang sebelumnya sudah kita training seperti maju-mundur, atas-bawah, kanan-kiri, dan lingkaran. Selain itu teman-teman juga dapat mengamati bahwa model akan mengenali kondisi dimana tidak terjadi apa-apa atau idle. Contoh testing dapat dilihat pada gambar 54.

Gambar 54. Pengetesan model dengan circle gesture.

Testing alat dapat juga dilihat pada video youtube dibawah ini:

Demikian jurnal kali ini mengenai implementasi hand gesture recognition menggunakan TinyML pada Raspberry Pi Pico dengan platform Edge Impulse. Terimakasih sudah membaca 🙂

Tinggalkan komentar