Kukko - robotyczny kurczak z termowizją

Kukko - robotyczny kurczak z termowizją

Zaprezentowana konstrukcja to prosty robot, który porusza się autonomicznie, śledząc ruchy człowieka z użyciem termowizji. Zaimplementowany algorytm imituje zachowanie jednego ze stworzeń z gry Legend of Zelda, które też było inspiracją dla zewnętrznej formy robota.

Stworzenia z serii gier Legend of Zelda, która publikowana jest od 1992 roku, były inspiracją do tego projektu. We wszystkich występują te przypominające kurczaki istotny. Jak można przeczytać w jednym z opisów gry „… jeśli Link [główny bohater gry – przyp.red.] zaatakuje Cucco wiele razy, Cucco wpadnie w furię i zacznie bardzo głośno piać, aby przywołać swoje stado i razem zaatakują go, lecąc w dół z różnych kierunków (dziobiąc go na śmierć). Po zaatakowaniu przez stado Cucco Link nie ma sposobu, aby się obronić, chyba, że uda mu się uciec na czas, uciekając z danej okolicy”.

Budowany w ramach tego projektu robot ma na celu udawanie tego stworzenia, włącznie z emulacją jego zachowań. Po wystawieniu go na działanie wystarczająco głośnego hałasu Cucco przechodzi w tryb ataku i zaczyna gonić najbliższy przedmiot, wysyłający sygnał cieplny kontrastujący z temperaturą otoczenia. Projekt został realizowany w ramach Computational Design and Digital Fabrication Seminar w programie International Master of Science (ITECH) na uniwersytecie w Stuttgarcie przez Matthiasa Hornunga, Otto Lindstama i Kalaivanana Amudhana.

Potrzebne elementy

Do zbudowania robota Kukko potrzebne będą:

  • dwa moduły Arduino UNO,
  • moduł Adafruit AMG8833,
  • mikrofon elektretowy (w module KY037 lub podobnym),
  • sensor ultradźwiękowy HC-SR04,
  • moduł audio MY-H1658,
  • sterownik silników Adafruit AMG8833,
  • dwa silniki DC z przekładniami i kołami,
  • dwa wyświetlacze OLED z kontrolerem SSD1306,
  • przewody do płytki stykowej/goldpinów,
  • płytka stykowa,
  • dwie baterie 9 V z klipsami.

Dodatkowo, do wykonania obudowy potrzebny będzie wydruk 3D, klej, taśma dwustronnie klejąca itp. Wymienione elementy oraz narzędzia zostały pokazane na rysunku 1. Część elementów z listy dostępnych jest, jako zestawy, np. w Internecie zakupić można zestaw składający się z dwóch sterowników silnika i czterech silników, zintegrowanych z przekładniami. Jeden taki zestaw zawiera elementy do skonstruowania dwóch takich robotów, jednak dobrze jest mieć jakieś części zamienne, konstruując taki pojazd.

Rysunek 1. Zebrane potrzebne elementy i moduły oraz narzędzia, potrzebne do zbudowania robotycznego Kukko

Na rysunku 2 pokazano rozmieszczenie niektórych komponentów w całej konstrukcji. Na liście potrzebnych elementów zapisano dwie płytki Arduino UNO, ponieważ druga płytka może być potrzebna do rozwiązania pewnych konfliktów w jednej z zastosowanych bibliotek.
Zasada działania i algorytm przetwarzania termowizji

Rysunek 2. Kukko, wraz z zaznaczonymi kluczowymi komponentami

Algorytm działania Kukko pokazano na rysunku 3. Robot może znajdować się w dwóch stanach – aktywnym lub pasywnym (idle). W stanie pasywnym porusza się on w losowy sposób, używając dodatkowo zainstalowany w nim ultradźwiękowy sensor, aby omijać ewentualne przeszkody. Jeśli przed nim nie ma żadnych obiektów, jedzie on do przodu i wydaje, co jakiś czas spokojne dźwięki, przekręcając się o losowy kąt. Jeśli wykryje przed sobą przeszkodę, obraca się, przed kontynuowaniem ruchu w przód. Gdy robot zostanie aktywowany za pomocą głośnego dźwięku zwiększa swój poziom agresji. Jeśli poziom ten przekroczy pewną predefiniowaną wartość, Kukko przechodzi do stanu aktywnego i wtedy poszukuje ofiar.

Rysunek 3. Algorytm postepowania Kukko

Głównym sensorem, z jakiego korzysta robot do wyszukiwania hałasujących ludzi jest termowizja. Prosty sensor termowizyjny (AMG8833) to macierz 8×8 pikseli. Nie nadaje się zbytnio do obrazowania, czy wyszukiwania obiektów, ale doskonale wykrywa poruszające się ciepłe przedmioty. Algorytm detekcji z pomocą termowizji pokazano w uproszczeniu na rysunku 4. W momencie, gdy robot nie widzi żadnych istotnych źródeł ciepła swoją kamerą, rozpoczyna poszukiwania poruszając się losowo. W momencie, gdy dostrzeże ciepły obiekt (cieplejszy od tła), przekręca się – w lewo, gdy jest on przy lewej krawędzi obrazu lub w prawo, gdy jest przy prawej krawędzi (rysunek 4). Gdy obiekt będzie symetrycznie na środku obrazu z kamery termowizyjnej, Kukko pojedzie w przód.

Rysunek 4. Algorytm nawigacji na źródło ciepła z użyciem sensora termowizyjnego AMG8833

Hardware

W pierwszej kolejności trzeba zgromadzić wszystkie elementy. Zamawiając poszczególne układy i moduły należy korzystać z listy i rysunku 1, aby upewnić się, że zamawiane są właściwe komponenty. Elementy można oczywiście zmieniać, w pewnym ograniczonym zakresie. Na przykład można zastąpić mikrofon, który uruchamia tryb wściekłości Kukko, innym czujnikiem.

Jako środek ostrożności autor zaleca użycie dwóch oddzielnych płytek Arduino zamiast jednej, aby uniknąć potencjalnych problemów z bibliotekami wyświetlacza LCD i kamery termowizyjnej (odpowiednio Adafruit_SSD1306.h i Adafruit_GFX.h), które w pewnych warunkach mogą się nawzajem zakłócać, uniemożliwiając sobie poprawne działanie.

Rysunek 5. Schemat robota

Na rysunku 5 pokazano schemat połączeń poszczególnych elementów w robocie. Moduły należy połączyć zgodnie z tym schematem. Z uwagi na montaż wyświetlaczy LCD i kamery termalnej wewnątrz kadłuba kurczaka, z daleka od płytki stykowej, do ich połączenia należy użyć długich przewodów.

Należy dostosować czułość mikrofonu w systemie, obracając śrubę potencjometru w kierunku przeciwnym do ruchu wskazówek zegara, aż dioda LED-2 na module zgaśnie. Można tę czułość jeszcze dopracować, gdy cała elektronika zostanie zmontowana i przetestowana.

Elementy wyszarzone na schemacie są potrzebne tylko wtedy, gdy chce się zaimplementować dodatkowy silnik prądu stałego do mechanizmu trzepotania skrzydłami, który jest opcjonalny.

Przed zmontowaniem wszystkich modułów, trzy z nich – dwa wyświetlacze i moduł audio należy przygotować tak, jak opisano w dalszej części artykułu. Dopiero po ich konfiguracji można podłączyć je do systemu, aby działały w pełni poprawnie.

Ekrany OLED

Aby móc niezależnie obsługiwać dwa wyświetlacze LCD, powinny one mieć różne adresy I²C. Dlatego w jednym z nich należy zmienić adres. W tym celu postępujemy zgodnie z dalszymi instrukcjami:

  1. Podłączamy Arduino UNO do wyświetlacza (VCC do 5 V, GND do GND, SDA do pinu A4 i SCL do pinu A5). Należy uważać, ponieważ niektóre płytki z wyświetlaczami SSD1306 mają piny w nietypowej kolejności – należy każdorazowo sprawdzić etykiety na PCB i nie sugerować się kolejnością.
  2. Należy następnie sprawdzić adresy I²C ekranów SSD1306. Można użyć do tego prostego skryptu, pokazanego na listingu 1.
  3. Uruchamiamy kod z podłączonym każdym wyświetlaczem (niezależnie) i zapisujemy adres.
  4. Jeśli adresy obu wyświetlaczy są takie same (np. 0×3C), należy przejść do kolejnego punktu tej instrukcji. Jeśli adresy różnią się od siebie (np. 0×3C i 0×3D), wystarczy je zanotować i można przejść dalej.
  5. Należy zmienić adres I²C jednego z wyświetlaczy LCD, przesuwając rezystor z tyłu PCB (rysunek 6).
  6. Po zakończeniu wracamy do kroku 2. by sprawdzić, czy adres został zmieniony poprawnie.
Listing 1. Prosty skrypt do skanowania urządzeń na interfejsie I2C

#include <Wire.h>

void setup(){
Wire.begin();
Serial.begin(9600);
Serial.println(“\nI2C Scanner”);
}

void loop(){
byte error, address;
int nDevices;
Serial.println(“Scanning...”);
nDevices = 0;
// Skanowanie po kolejnych adresach
for(address = 1; address < 127; address++ ){
// Sprawdzenie, czy są urządzenia pod adresem
Wire.beginTransmission(address);
// Odpowiedź urządzenia lub błąd
error = Wire.endTransmission();
// Brak błędu - wykryto urządzenie
if (error == 0){
Serial.print(“I2C device found at address 0x”);
if (address < 16) Serial.print(“0”);
Serial.print(address, HEX);
Serial.println(“ !”);
nDevices++;
}
// Brak urządzenia pod tym adresem
else if (error == 4){
Serial.print(“Unknown error at address 0x”);
if (address < 16) Serial.print(“0”);
Serial.println(address, HEX);
}
}
if (nDevices == 0)
Serial.println(“No I2C devices found\n”);
else Serial.println(“done\n”);
// Odczekaj 5 sekund przed kolejnym skanem
delay(5000);
}
Rysunek 6. Opornik ustalający adres na tyle płytki wyświetlacza OLED SSD1306

Moduł Audio

Moduł audio, pokazany na fotografii 1, wymaga zaprogramowania przed użyciem.

Fotografia 1. Moduł audio

Zanim będzie on zamontowany w naszym systemie, do jego pamięci muszą być wgrane dźwięki, jakie ma odtwarzać. Programowanie modułu audio jest bardzo proste. Wystarczy przejść przez cztery opisane poniżej kroki:

  1. Podłączamy moduł do komputera za pomocą dostarczonego kabla USB. Pojawi się on, jako urządzenie pamięci masowej w systemie.
  2. Pliki ładujemy do modułu po prostu przeciągając i upuszczając je na nowy dysk w komputerze. Ma on pojemność 8 MB, jednak wystarczy to do wgrania wymaganych plików. Po wgraniu usuwamy pamięć z systemu.
  3. Po wgraniu dźwięku sprawdzamy działanie modułu podłączając go do zasilania i naciskając dołączony przycisk.
  4. Po przetestowaniu systemu odłączamy przycisk i zamiast tego podłączamy przewody jak, aby zamontować komponent tak, jak pokazano na schemacie (rysunek 5).

Firmware

Oprogramowanie firmware Kukko jest bardzo proste. Są to dwa szkice Arduino, dla dwóch modułów. Jeden z nich odpowiedzialny jest za obsługę termowizji, a drugi kontroluje wszystkie pozostałe elementy robota.

Potrzebne biblioteki

W pierwszej kolejności należy zainstalować w Arduino IDE biblioteki, potrzebne w systemie do obsługi poszczególnych modułów:

Biblioteki można zainstalować w środowisku Arduino pobierając je z podanych wyżej adresów lub poprzez wbudowany w to IDE menedżer bibliotek. Jeśli chcemy instalować biblioteki manualnie, należy skorzystać z instrukcji, zawartych na stronach, z których je pobrano.

Kod Kukko

Aby przetestować działanie wszystkich sensorów, przed montażem całego układu, sięgnąć można po pierwszy szkic AngryEye.ino (listing 2) i przesłać go do Arduino IDE. Do uruchomienia tego skryptu wystarczy płytka Arduino ze wszystkimi czujnikami i elementami wykonawczymi, poza kamerą termowizyjną, którą obsługuje drugi moduł Arduino. W pierwszej kolejności musimy skompilować ten kod. Jeśli kompiluje się on poprawnie, oznacza to, że instalacja bibliotek zakończyła się pełnym sukcesem. Jeśli widoczny jest jakiś problem, należy sprawdzić jego źródło i ustalić, która z bibliotek jest odpowiedzialna za ten fakt.

Listing 2. Oprogramowanie dla pierwszego modułu Arduino UNO. W listingu pominięto definicje zmiennych globalnych i funkcje oraz tablice przeznaczone do wyświetlania własnych znaków na ekranie OLED – oczu Kukko

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Time.h>

#define OLED_RESET -1

Adafruit_SSD1306 display(OLED_RESET);
Adafruit_SSD1306 display2(OLED_RESET);

void setup() {
// Losowe ziarno do generacji liczb losowych
randomSeed(analogRead(0));
Serial.begin(9600);
// Inicjalizacja wyświetlacza o adresie 0x3c
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
delay(1000);
// Inicjalizacja wyświetlacza o adresie 0x3d
display2.begin(SSD1306_SWITCHCAPVCC, 0x3D);
display2.clearDisplay();
delay(1000);
// Konfiguracja wyzwalania sensora ultradźwiękowego
pinMode(trigPin, OUTPUT);
// Konfiguracja echa sensora ultradźwiękowego
pinMode(echoPin, INPUT);
// Konfiguracja pinów kontroli mostka H
pinMode(GSM1, OUTPUT);
pinMode(GSM2, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
// Konfiguracja kontroli modułu audio
pinMode(schp, OUTPUT);
digitalWrite(schp, HIGH);
// Odczyt danych z drugiej płytki Arduino
pinMode(digitalPin1, INPUT);
pinMode(digitalPin2, INPUT);
}

void loop() {
int mn = 1024;
int mx = 0;

for (int i = 0; i < 100; ++i) {
int val = analogRead(microphonePin);
mn = min(mn, val);
mx = max(mx, val);
}
int delta = mx - mn;
Serial.println(delta);
if (delta > 7) {
value = value + 1;
Serial.println(value);
long randomValue = random(2);
if (value > 3 - randomValue) {
display.drawBitmap(
0, 0, angryEye, 128, 64, WHITE);
display.display();
display2.drawBitmap(
0, 0, angryEyeInv, 128, 64, WHITE);
display2.display();
myTime = millis();
// Domyślnie 30
while (millis() < myTime + 20000){
// Przejście w tryb agresywny
AngryChicken();
}
StopMotion();
display.clearDisplay();
display2.clearDisplay();
value = 0;
}
else {
HappyChicken() ;
long randomValue = random(10);
if (randomValue < 7) {
}
}
}
}

Po załadowaniu firmware można sprawdzić, czy wszystkie czujniki i elementy wykonawcze działają prawidłowo. Moduł cały czas drukuje informacje przez port szeregowy (Serial.Println()) podczas ruchu przed ekranem termowizyjnym itd. Po sprawdzeniu programu, można zaprogramować drugi moduł Arduino dla Kukko.

Oprogramowanie drugiego modułu

Po zakończeniu pracy należy przesłać drugi plik kodu – thermalNavImplementation.ino (listing 3) na drugą płytę Arduino z kamerą termowizyjną po sprawdzeniu, czy kod ten kompiluje się bez żadnych problemów. Skrypt odczytuje z kamery termowizyjnej obraz, a następnie uruchamia jeden z 4 rodzajów ruchów: w lewo, w prawo i do przodu oraz ruch poszukujący celu. Odpowiedź szkicu można zweryfikować, przesuwając dłonią po przestrzeni przed czujnikiem, jednocześnie obserwując monitor szeregowy podłączony do modułu Arduino.

Listing 3. Oprogramowanie dla drugiego Kukko

#include <Wire.h>
#include <Adafruit_AMG88xx.h>

int digitalWritePin1 = 2;
int digitalWritePin2 = 13;
// Zmienna do przechowywania aktualnego odczytu
int val = 0;

Adafruit_AMG88xx amg;

void setup() {
Serial.begin(1000000);
Serial.println(F("AMG88xx pixels"));
bool status;
status = amg.begin();
amg.setMovingAverageMode(true);
if (!status) {
Serial.println("Could not find
a valid AMG88xx sensor, check wiring!");
while (1);
}
Serial.println("-- Pixels Test --");
Serial.println();
delay(100);
pinMode(digitalWritePin1, OUTPUT);
pinMode(digitalWritePin2, OUTPUT);
}

void loop() {
float pixels[AMG88xx_PIXEL_ARRAY_SIZE];
// Odczyt pikseli z kamery termowizyjnej
amg.readPixels(pixels);
char c = thermalNav(pixels);
if (c == ‘F’){
digitalWrite(digitalWritePin1, HIGH);
digitalWrite(digitalWritePin2, HIGH);
}
else if (c == ‘L’){
digitalWrite(digitalWritePin1, HIGH);
digitalWrite(digitalWritePin2, LOW);
}
else if (c == ‘R’){
digitalWrite(digitalWritePin1, LOW);
digitalWrite(digitalWritePin2, HIGH);
}
else if (c == ‘S’){
digitalWrite(digitalWritePin1, LOW);
digitalWrite(digitalWritePin2, LOW);
}
}

char thermalNav(float pixels[AMG88xx_PIXEL_ARRAY_SIZE]){
int const segments = 2;
float const threshold = 25;
float sumL = 0.00;
float sumR = 0.00;
float const turnThreshold = 4.0;
// Minimalna i maksymalna wartość pikseli
float min_val = pixels[0];
float max_val = pixels[0];
for (int i = 0; i < AMG88xx_PIXEL_ARRAY_SIZE; i++) {
if (pixels[i] < min_val) {
min_val = pixels[i];
}
if (pixels[i] > max_val) {
max_val = pixels[i];
}
Serial.print(String(pixels[i])+",");
}
Serial.print(‘\n’);
float matrix[8][8] = {};
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
matrix[i][j] = pixels[(i)*8 + (j)];
}
}
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 4; j++) {
if (matrix[i][j] < threshold){
matrix[i][j] = 0.00;
}
sumL += matrix[i][j];
}
for (int j = 4; j < 8; j++) {
if (matrix[i][j] < threshold){
matrix[i][j] = 0.00;
}
sumR += matrix[i][j];
}
}
// Sprawdzanie kierunku i zwracanie wyników
// Skręt w prawo
if (sumR - sumL > turnThreshold &&
(sumL+sumR) < turnThreshold*100){
return ‘R’;
}
// Skręt w lewo
else if (sumL - sumR > turnThreshold &&
(sumL+sumR) < turnThreshold*100){
return ‘L’;
}
// Jazda naprzód
else if ((sumL+sumR) > turnThreshold){
return ‘F’;
}
// Poszukiwanie
else {
return ‘S’;
}
}

Obudowa

W pierwszej kolejności należy wydrukować na drukarce 3D elementy podwozia i montażu kół. Do podwozia następnie należy zamontować koło obrotowe w tylnej części, wkręcając je na miejsce. Następnie należy wyciąć z grubego papieru pozostałe części obudowy. W ten sposób, na podstawie szablonów, znajdujących się na stronie z projektem, należy wyciąć elementy tułowia, ogona i głowy Kukko. Z wyciętych fragmentów sklejamy cały korpus, a następnie przyklejamy do niego ogon. Na rysunku 7 pokazano poszczególne sekcje do sklejenia.

Rysunek 7. Rozstrzelony widok elementów kartonowej obudowy Kukko

Ostatnim etapem jest instalacja sensorów. Należy w obudowie zrobić otwory dla sensora termicznego oraz ekranów OLED, pełniących funkcje oczu. Na fotografii 2 pokazano rezultat tego zabiegu.

Fotografia 2. Zainstalowany w obudowie detektor termiczny oraz oczy Kukko

Podsumowanie

Postępując zgodnie z powyższymi instrukcjami można złożyć i uruchomić robota Kukko. Nic nie stoi również na przeszkodzie, aby zmodyfikować konstrukcję lub algorytm, by dostosować robota do naszych potrzeb i pomysłów.

Nikodem Czechowski, EP

Bibliografia:
https://www.instructables.com/Robotic-Kukko/

Artykuł ukazał się w
Elektronika Praktyczna
lipiec 2023

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio maj - czerwiec 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje kwiecień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich maj 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów