Projekt RoboCZOŁG jest naszym pierwszym tego typu projektem. Jest on sterowany za pomocą sieci WI-FI poprzez stronę internetową. Jego główną funkcjonalnością jest wykrywanie kolorów za pomocą kamery.
Niestety nie udało nam się połączyć obydwu funkcjonalności, czyli poruszania i kamery, ale to będzie opisane w późniejszej części.
1. Płytka ESP32-CAM,
2. 16 kabli damsko-meskich,
3. 2 silniki TT z przekłądnią 1:48 dwustronny,
4. Micro Servo 9g SG90,
5. Kabel do zasilania,
6. Płytka z robota Sharky,
7. Filament PLA (można też jakiś drugi trochę miększy)
STEROWANIE
Aby zaprogramować funkcje naszego robota użyliśmy C++ w programie Arduino, którego można używać do kontrolowania płytek ESP32-CAM.
Kod stanowiący podstawę do dalszego programowania został pożyczony z instrukcji na stronie https://start.sic.edu.pl/course/view.php?id=143. będziemy potrzebwoac długiej listy bibliotek:
"esp_camera.h"
<WiFi.h>
"esp_timer.h"
"img_converters.h"
"Arduino.h"
"fb_gfx.h"
"soc/soc.h"
#include "soc/rtc_cntl_reg.h"
"esp_http_server.h"
Należy również wejść w zakladkę "Plik" --> "Preferencje" i w okienku "Dodatkowe URL" wkleić link: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json - ma to na celu umożliwienie Arduino IDE zainstalowanie dodatkowych pakietów narzędziowych (toolchains) i bibliotek, które są niezbędne do pracy z mikrokontrolerami ESP32.
Kod składa się z siedmiu współpracujących ze sobą sekcji, których działanie zostania krótko wyjaśnione w dalszej części instrukcji
1. Esp32-cam-RC-Car.ino - tak właśnie nazywa się "główna" część kodu zaraz po jego pobraniu (my potem zmieniliśmy tą nazwę na "RoboCzolg"), w której zamieszczona jest funckja void setup wywołująca wszystkie funkcje potrzebne do sterowania robotem. sterowanania robotem
Inicjowana jest kamerka oraz serwer wifi emitowany przez płytkę.
2. camera.h - służy do konfiguracji i inicjalizacji kamery podłączonej do mikrokontrolera. Definiuje piny GPIO (General Purpose Input/Output) - odpowiednie piny przypisane do funkcji takich jak zasilanie, resetowanie, sygnały danych i sygnały synchronizacji.
3. http.h - tutaj za pomocą HTMLa tworzona jest strona zawierająca przyciski do sterowania ruchem robota:
Forward (do przodu)
Left (w lewo)
Stop (zatrzymaj)
Right (w prawo)
Backward (do tyłu)
a także okno pokazujące widok z kamerki na żywo. Sposób, w jaki kamerka przekazuje swój obraz na stronę sprowadza się do bardzo szybkiego przesyłania obrazów poprzez Wifi wytworzone na płytce, w związku z tym w zaleznośći od naszych wymagań możemy zmieniać rodzielczość, jakość oraz częstotliwość, z jaką kamerka przesyła obraz.
4.motor.h i motor.cpp - tu ma miejsce zdefiniowanie pinów oraz funkcji kontrolujących sekwencje silnika napędzającego ruch robota, w zależności od zadanych przez nas komend.
5. config.h - bardzo krótka biblioteka ustawiająca stały parametr opisujący prędkośc silnika i wyłązajacy latarkę wbudowaną w płytkę ESP
6. camera_serwer.cpp - tutaj dzieje się cała magia, ponieważ mimo, że nie ma tu funcji setup, to tutaj jest opisywana interakcja robota z komendami wysyłanamy przez stronę internetową. Bibliotekę tą można podzileić an trzy główne części:
-static esp_err_t stream_handler(httpd_req_t *req) - kluczowa funkcja do obsługi strumieniowania obrazu z kamery na żywo do przeglądarki internetowej. Pobiera ona ramki z kamery, przekształca je w format JPEG (jeśli to konieczne), a następnie wysyła te ramki jako strumień MJPEG do klienta HTTP. Pozwala to na zdalne monitorowanie obrazu z kamery w czasie rzeczywistym.
-void startCameraServer() - uruchamia dwa serwery HTTP:
na porcie 80 słyżący do obsługi strony głównej i poleceń do robota.
na porcie 81 służący do obsługi strumieniowania wideo.
Funckja ta jest wywoływana później w void setup().
- static esp_err_t cmd_handler(httpd_req_t *req) - sterowania kierunkiem ajzdy robota - będziemy w szczególności rozwijać ją dodając kolejne funkcje robota.
Niezbędne jest aby pamiętać o odpowiednich ustawieniach jakie musimy wybrać przed uruchomieniem kodu, inaczej nam nie zadziała. Po pobraniu wsyztkich bibliotek wymienionych wyżej należy wejść w zakłądkę "Tools", z menedżera płytek pobrać "ESP32 Arduino" i wybrać opcję ESP32 Dev Module. Cały czas operując w zakładce Tools wybieramy odpowiedni Port, ustawiamy Erase All Flash na "Enabled", i Partition Scheme na "Huge APP". Na koniec otwieramy "Serial Monitor/Monitor Portu szeregowego" i w odpowiednim okienku wybieramy wartość taką, aby pokrywała się ona z tym o mamy wpisane w kodzie w "ESP32-cam-RC-Car.ino". Polecam obie te rzeczy ustawić na wartość 115200.
Mając to wszystko za sobą mozemy przetestować aktualną wersję programu - robot powinien być w stanie jeździć do przodu i do tyłu, skręcać obracając się dookoła własnej osi oraz na bierząco przesyłać na stronę obraz z kamerki.
Pierwszą modyfikacją jaką chcieliśmy wprowadzić do robota to zdalnie sterowana wieżyczka czołgu.
Jej ruch jest z pomocą serwomechanizmu, podłączonego do trzech pinów (zasilanie, uziemienie i pin do sterowania, w naszym przypadku jest to pin 16).
Programowanie:
stworzymy dwie nowe biblioteki:
serwo.h i serwo.cpp
Tworzymy je analogicznie do bibliotek obsługujących silniki, mimo że działanie tych dwóch części różni się od siebie.
Teraz musimy odpowiednio te biblioteki połączyć z resztą kodu.
W Esp32-cam-RC-Car.ino, teraz już przemianowanej na roboczolg.ino deklarujemy void servo_begin(void) gdzieś na początku i wywołujemy ją NA KOŃCU void setup() wpisując servo_begin(). Bardzo ważne jest aby wstawić to na koniec setupu, ponieważ jeśli wstawimy to wcześniej nie stworzy się serwer Wifi, który jest inicjowany wcześniej w tej funkcji.
Przechodzimy do camera_serwer. Deklarujemy #include "serwo.h", a niżej
void servo_left(); // Obrót serwomechanizmu w lewo
void servo_right(); // Obrót serwomechanizmu w prawo
void servo_stop(); // Zatrzymanie ruchu serwomechanizmu
void servo_mid();
Niżej w kodzie w funckcji static esp_err_t cmd_handler(httpd_req_t *req) znajdziemy długą liste else if-ów, które po spełnieniu swojego warunku powodują ruch silników. Analogicznie stworzymy fragmenty podobnie obsługujące nasze serwo.
Oto przykład:
else if (!strcmp(variable, "servoleft")) {
// Tutaj umieść kod aktywujący sterowanie serwem w lewo
servo_left(); // Wywołanie funkcji sterującej serwomechanizmem w lewo
servoActive = true; // Dodane: ustawienie flagi na true
}
Ten fragment zostanie aktywowany gdy na stronie zostanie wciśnięty przycisk przypisany jako zmienna "servoleft". Jak nazwa wskazuje jest to przycisk, który będzie skręcał naszą wieżyczkę w lewo.
Analogicznie tworzymy jeszcze podobne fragmenty obsługujące "servoright" - kręcące w prawo, oraz "servomid" - wieża wraca do pozycji wyjściowej.
Musimy zainicjować te zmienne w HTMLu.
Kod tworzący pojedynczy przycisk wygląda nastepująco:
<button class="button" onmousedown="toggleCheckbox('right');" ontouchstart="toggleCheckbox('right');" onmouseup="toggleCheckbox('stop');" ontouchend="toggleCheckbox('stop');">Right</button>
Są one odpowiednio ułożone, tak aby na stronie prezentowały się w wygodny i intuicyjny w użytku sposób. Sekcja przycisków obsługujących sterowanie jest zakończona </tr>.
Tuż pod tym należy wkleić:
<tr>
<td colspan="3" align="center">
<button class="button" style="background-color: black; border-radius: 100%;" onmousedown="toggleCheckbox('servoleft');" ontouchstart="toggleCheckbox('servoleft');" onmouseup="toggleCheckbox('servostop');" ontouchend="toggleCheckbox('servostop');">Rotate Left</button>
<button class="button" style="background-color: black; border-radius: 100%;" onmousedown="toggleCheckbox('servomid');" ontouchstart="toggleCheckbox('servomid');" onmouseup="toggleCheckbox('servostop');" ontouchend="toggleCheckbox('servostop');">Rotate Mid</button>
<button class="button" style="background-color: black; border-radius: 100%;" onmousedown="toggleCheckbox('servoright');" ontouchstart="toggleCheckbox('servoright');" onmouseup="toggleCheckbox('servostop');" ontouchend="toggleCheckbox('servostop');">Rotate Right</button>
</td>
</tr>
Wprowadzi nam to do interfejsu strony internetowej trzy nowe przyciski obsługujące serwo.
Aby na bierząco sprawdzać, czy nasze polecenia trafiają w odpowiednie miejsca bez konieczności uruchamiania robota raz po raz, mozemy wykorzystać Serial Monitor/Monitor Portu Szeregowego.
Wystarczy we fragmencie kodu, który chcemy sprawdzić czy jest on wywoływany, wstawić frazę Serial.prinf("komunikat"). Po skompilowaniu w pierszej kolejności wyświetli sie informacja o stanie kamery, serwera razem z adresem IP. Później możemy testować np. nowo wprowadzony przycisk. Przykład:
else if (!strcmp(variable, "servomid")) {
servo_mid(); // Załóżmy, że masz funkcję sterującą zatrzymywaniem serwomechanizmu
servoActive = true; // Dodane: ustawienie flagi na false
Serial.println("Serwo mid");
}
W ten sposób po wciśnięciu prawidłowo zadeklarowanego przecisku "mid" w oknie monitra portu szeregowego powinien pojawić sie komunikat "Serwo mid" informujący nas, że w istocie odpowiednia funckja została wywołana. Jest to szczególnie pomocne gdy na przykład komunikat się pojawił, ale robot nie zadziałał poprawnie - wtedy wiemy, że problem prawdopodobnie leży w samej funckji, a nie w jej wywołaniu.
WYKRYWANIE KOLORÓW
Rozpoznawanie kolorów
W celu rozpaznawania kolorów z pomocą kamerki ESP32-CAM robot używa programu w arduino w celu wysyłania obrazu z kamerki na strone internetową, z której drugi program napisany w Pythonie zczytuje obraz i rozpoznaje oraz izoluje poszczególne kolory, pokazując to w oknie. Oba te kody są bazowane na poradniku ze strony: https://how2electronics.com/color-detection-tracking-with-esp32-cam-opencv/ .
W celu wykorzystania tej funkcji należy pobrać program TANK.ino i włączyć go w programie Arduino.
Na początek należy znależć ten fragment kodu:
const char* WIFI_SSID = "ssid";
const char* WIFI_PASS = "password";
i w miejscu ssid i password wpisz nazwe i hasło swojej sieci Wi-Fi.
Następnie w zakładce "Narzędzia" wybrać płytkę "ESP32 Wrover Module" oraz upewnić się, że jest wybrany odpowiedni port. Po wgraniu programu na nasz mikroprocesor trzeba włączyć Serial monitor i po sprawdzeniu czy mamy ustawiony baud na 115200, zresetować płytkę, po zrobieniu tego powinny zostać wypisane komunikaty w tym "CAMERA OK", który oznacza poprawne funkcjonowanie kamery. Po krótkiej chwili powinno również wypisać adres do strony, na którą jest zsyłany obraz z kamerki. Możemy skopiować ten adres + /cam-lo.jpg i sprawdzić czy wszystko działa jak należy, obraz powinien się aktualizować po odświeżeniu strony.
Kolejnym krokiem jest zainstalowanie Pythona wraz z bibliotekami na nasz komputer. Należy wejść na strone https://www.python.org/downloads/ i pobrać pythona. Po pobraniu musimy zainstalować go włączając plik .exe. Następnie musimy zainstalować dwie biblioteki NumPy oraz OpenCV. Robimy to z pomocą wiersza polecenia. Włączamy go wpisując w wyszukiwarke windows cmd i klikając pierwszą opcje.
W wierszu polecenia wpisujemy:
py -m pip install numpy
i klikamy enter, po zainstalowaniu biblioteki wpisujemy:
py -m pip install opencv-python
i ponownie klikamy enter, to powinno poprawnie zainstalować biblioteki na naszym komputerze.
Teraz możemy zająć się kodem napisanym w Pythonie, pobieramy plik nazwany "wykrywanie kolorów.sln" i włączamy go. Klikamy w zakładke kolory.py i w 12 wierszu programu w miejscu 'adres strony' kopiujemy adres strony otrzymany wcześniej z programu w arduino. Nareszcie możesz włączyć program (upewniając się uprzednio, że płytka jest włączona) i pokazując różne kolory kamerce podziwiać działanie programu. Program powinien wykrywać kolory: niebieski, żółty, czerwony oraz zielony.
Projektowanie i mechanika
Model naszego projektu został zaprojektowany w programie NX SIEMENS. Kadłub, pokrywa, wieża i wałki są w całości zaprojektowane przez nas, natomiast koła i gąsienice są wzorowanie na gotowych projektach opartych na klockach lego. Do drukowania użyliśmy filamentu PLA natomiast zalecane byłoby użycie jakiegoś miększego do drukowania gąsienic, ponieważ mają one haczyki, które służą do łączenia z sobą elementów w jeden łańcuch, i gdy były za duże to się łamały, a gdy za małe to przy jeździe się łamały. Silniki zamocowane są w kadłubie za pomocą śrub, serwo jest umieszczone w pokrywie i połączone jest z wierzą za pomocą pasowania ciasnego jak i dociągnięte śrubą. Otwory w wierzy i kadłubie są specjalnie dostosowane do możliwości wykonywania ruchu wierzą w zakresie od -90 do 90 stopni (zakładając że 0 to gdy wierza skierowana jest do przodu). W podłodze kadłuba znajdują się otwory w celu zaoszczędzenia filamentu.
#include "esp_camera.h"
#include <WiFi.h>
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "fb_gfx.h"
#include "soc/soc.h" // disable brownout problems
#include "soc/rtc_cntl_reg.h" // disable brownout problems
#include "esp_http_server.h"
#include "camera.h"
#include "ESP32Servo.h"
#define USE_AP_MODE //if you don't have a router
#define VFLIP_MIRROR // if your camera shows fliped image1q
#if defined USE_AP_MODE
const char *soft_ap_ssid = "ROBOCZOŁG";
//const char *soft_ap_password = "12345678";
#else
//const char* wifi_network_ssid = "KT_GiGA_7EA2";
//const char* wifi_network_password = "7bdc00fi91";
//const char* wifi_network_ssid = "U+Net900F";
//const char* wifi_network_password = "908F7F99M!";
#endif
void startCameraServer();
void motor_begin(void);
void servo_begin(void);
void turnOnRed();
void turnOnGreen();
void turnOnBlue();
void turnOnYellow();
void turnOffLED();
void setup() {
//WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
Serial.begin(115200);
Serial.setDebugOutput(true);
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
if (psramFound()) {
Serial.printf("FRAMESIZE_QVGA");
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 10;
config.fb_count = 1;
} else {
Serial.printf("FRAMESIZE_SVGA");
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
// Camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
#if defined VFLIP_MIRROR
sensor_t *s = esp_camera_sensor_get();
s->set_hmirror(s, 1); // 0 = disable , 1 = enable
s->set_vflip(s, 1); // 0 = disable , 1 = enable
#endif
#if defined USE_AP_MODE
WiFi.softAP(soft_ap_ssid);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
#else
// Wi-FI connect
WiFi.begin(wifi_network_ssid, wifi_network_password);
Serial.println("Connecting to WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("WiFi connected : ");
Serial.println(WiFi.localIP());
#endif
// Start streaming web server
startCameraServer();
Serial.printf("kamera aktywna2");
motor_begin();
turnOnRed();
turnOnGreen();
turnOnBlue();
turnOnYellow();
turnOffLED();
}
void loop() {
}
https://youtube.com/shorts/K7p2dDDbdGs?feature=share
https://youtube.com/shorts/MSwgXOEatEQ?feature=share
https://youtube.com/shorts/mTv8SocsqVw?feature=share
https://grabcad.com/library/lego-wide-tank-track-with-2-holes-1
https://grabcad.com/library/88383-lego-technic-link-tread-wide