LinkedIn YouTube Facebook
Szukaj

Wstecz
Artykuły

Robot „Orangutan” – budowa krok po kroku

Tworzenie oprogramowania dla robota

Najbardziej kłopotliwa część za nami. Do tej pory zbudowaliśmy urządzenie, które nie wykonuje żadnych bliżej określonych czynności. Musimy teraz „powiedzieć” robotowi co właściwie ma robić – innymi słowy: zaprogramować go.

Zakładam, że środowisko Atmel Studio 6 jest już zainstalowane, podobnie biblioteki Pololu. Skoro wszystko jest gotowe, to wczytujemy plik „KAMAMI-orangutan-robot.atsln” z rozpakowanego załącznika spod tego artykułu.

Poniżej przedstawiam kod, który powinien być widoczny po wybraniu pliku main.c z okienka Solution Explorer w Atmel Studio 6.

#include 

signed int roznica;
unsigned int obrot;
unsigned int prawysharp;
unsigned int lewysharp;
unsigned int prawysharp1;
unsigned int lewysharp1;
unsigned int prawysharp2;
unsigned int lewysharp2;
signed int silniklewy = 1;
signed int silnikprawy = 1;
signed int kierunek = 0;
float predkoscserwa = 1.0;

const int UWAGA=300;                           //wartosc, dla ktorej robot ma sie zatrzymac (zeby nie wpasc na przeszkode)



int main()
{
const unsigned char servoPinsB[] = {IO_D1};    //konfiguracja serwa
  servos_start_extended((unsigned char[]){}, 0, servoPinsB, sizeof(servoPinsB));
  set_servo_speed_b(0, 0);                     //zerowa wartosc oznacza maksymalna predkosc obrotu serwa
  set_servo_target_b(0, 1400);                 //obrot serwa do pozycji 1400 (neutralnej)
  
  clear();  
  set_analog_mode(MODE_10_BIT);                //konfiguracja przetwornika ADC

  while(1)     
  {
    
    lewysharp = analog_read_average(2, 5);     //wartosc zmierzona przez lewy czujnik
    prawysharp = analog_read_average(3, 5);    //wartosc zmierzona przez prawy czujnik
    
    clear;                                     //czyszczenie ekranu LCD
    lcd_goto_xy(0, 0);                         //ustawienie kursora w zadanych wspolrzednych
    print("R:");                               //drukowanie litery "R"
    print_long(roznica);                       //drukowanie zmiennej "roznica"
    print("    ");
    lcd_goto_xy(0, 1); 
    print("K:");  
    print_long(kierunek);
    print("    ");
    
    roznica = (prawysharp*2) - (lewysharp*2);
    obrot = 1400 + roznica;
    
    if (obrot>=1800) {                         //przekroczenie wybranego zakresu obrotu serwa
    obrot = 1800;
    } else if (obrot<=1000) { obrot = 1000; } predkoscserwa = abs(roznica)/3 +20; //im wieksza roznica miedzy wartosciami zmierzonymi przez lewy i prawy czujnik, tym szybciej serwo ma sie obracac if (predkoscserwa>=148.5) {                //przekroczenie zakresu predkosci serwa
    predkoscserwa=148.5;
    } else if (predkoscserwa<=30.5) {
    predkoscserwa=30.5;
    }  
    
    set_servo_speed_b(0, (int)(predkoscserwa+0.5));
    set_servo_target_b(0,obrot);
    
    if (lewysharp < UWAGA || prawysharp < UWAGA){ //brak przeszkody kierunek = (obrot - 1400); kierunek = (int)(kierunek/4); set_motors((kierunek+100), (-kierunek+100)); //zadanie predkosci dla lewego i prawego silnika delay_ms(150); //czekanie 150ms } else { //wykryta przeszkoda w niewielkiej odleglosci //cofniecie sie set_motors(-100, -100); delay_ms(300); set_motors(1,1); //rozgladanie sie set_servo_speed_b(0,0); set_servo_target_b(0,1000); //spojrzenie w lewo delay_ms(600); lewysharp1 = analog_read_average(2, 5); prawysharp1 = analog_read_average(3, 5); set_servo_speed_b(0,0); set_servo_target_b(0,1800); //spojrzenie w prawo delay_ms(800); lewysharp2 = analog_read_average(2, 5); prawysharp2 = analog_read_average(3, 5); set_servo_speed_b(0,0); //spojrzenie do przodu set_servo_target_b(0,1400); delay_ms(300); //okreslanie kierunku obrotu if (lewysharp1 > UWAGA) {                      //bliska przeszkoda po lewej stronie...
      
          if (prawysharp2 > UWAGA) {                   //bliska przeszkoda po prawej stronie...
       
            set_servo_speed_b(0, 0);
            set_servo_target_b(0,1000);                //spojrzenie w lewo
            set_motors(100, -100);                     //obrot w lewo w miejscu (zawracanie)
            delay_ms(350);
            set_servo_speed_b(0, 300);                 //wolne prostowanie "glowy"
            set_servo_target_b(0,1400);
            delay_ms(350);
            set_motors(1,1);
       
          } else {                                     //brak lub daleka przeszkoda po prawej stronie
       
            set_servo_speed_b(0, 0);
            set_servo_target_b(0,1600);                //spojrzenie w prawo
            set_motors(-100, 100);                     //obrot w prawo
            delay_ms(150);
            servo_speed_b(0, 300);                     //wolne prostowanie "glowy"
            set_servo_target_b(0,1400);
            delay_ms(150);
          }
      
          } else {                                    //brak lub daleka przeszkoda po lewej stronie
      
          if (prawysharp2 > UWAGA) {                  //bliska przeszkoda po prawej stronie
       
            set_servo_speed_b(0, 0);
            set_servo_target_b(0,1200);               //spojrzenie w lewo
            set_motors(100, -100);                    //obrot w lewo
            delay_ms(150);
            set_servo_speed_b(0, 300);                //wolne prostowanie "glowy"
            set_servo_target_b(0,1400);
            delay_ms(150);
       
          } else {                                    //brak lub daleka przeszkoda po prawej stronie
       
                                                      //sprawdzenie po której stronie jest więcej miejsca
       
          if ((lewysharp1-prawysharp2)>0) {           //po lewej stronie wiecej miejsca
        
            set_servo_speed_b(0, 0);
            set_servo_target_b(0,1200);               //spojrzenie w lewo
            set_motors(100, -100);                    //obrot w lewo
            delay_ms(150);
            set_servo_speed_b(0, 300);                //wolne prostowanie "glowy"
            set_servo_target_b(0,1400);
            delay_ms(150);
        
          } else {                                   //po prawej stronie wiecej miejsca
        
            set_servo_speed_b(0, 0);
            set_servo_target_b(0,1600);              //spojrzenie w prawo
            set_motors(-100, 100);                   //obrot w prawo
            delay_ms(150);
            set_servo_speed_b(0, 300);               //wolne prostowanie "glowy"
            set_servo_target_b(0,1400);
            delay_ms(150);
         
          }
        }
      }

    }
  }
}

Większość wykorzystanych funkcji została krótko opisana w komentarzach, ale warto zwrócić szczególną uwagę na najważniejsze z nich. Nie są one typowe dla programów pisanych w języku C dla mikrokontrolerów AVR. Pochodzą z bibliotek Pololu i są ściśle powiązane z platformą sprzętową, jaką jest sterownik Orangutan SV-328. Dostęp do wszystkich peryferiów został ułatwiony w taki sposób, aby uprościć początkującym naukę podstaw programowania. Bardzo podobnie wygląda programowanie sterowników bazujących na platformie Arduino. Oczywiście, nic nie stoi też na przeszkodzie, by zaprogramować robota w klasycznym C lub dowolnym innym języku programowania mikrokontrolerów (Basic, assembler itp.). Poniżej najważniejsze funkcje i ich przeznaczenie:

  • set_servo_speed_b(X, Y) – określa prędkość obrotu serwa – Y to prędkość, a X to numer serwa, którego ta wartość ma dotyczyć; Y=0 oznacza wyłączenie kontroli prędkości – wszystkie obroty wykonywane są najszybciej, jak to możliwe,
  • set_servo_target_b(X, Y) – zadanie obrotu serwa – X to numer serwa, a Y to pozycja
  • zmienna = analog_read_average(X, Y) – pomiar wartości zmierzonych przez przetwornik ADC – X to numer kanału ADC, a Y oznacza ilość pomiarów, spośród których wyznaczana jest wartość średnia, przypisywana następnie do zmiennej,
  • clear() – czyszczenie ekranu LCD – znikają wszystkie znaki wyświetlane wcześniej na LCD
  • lcd_goto_xy(X, Y) – umieszczenie kursora na ekranie LCD w określonych współrzędnych – X oznacza kolumnę, a Y wiersz,
  • print(tekst) – „drukowanie” na ekranie LCD wartości zmiennej tekst lub dowolnego ciągu znaków, napisanych w cudzysłowiu,
  • set_motors(X, Y) – określa prędkość obrotową dwóch kanałów silników (X dla jednego, Y dla drugiego), wartości ujemne oznaczają obrót w przeciwnym kierunku,
  • delay_ms(X) – zawieszenie działania programu na X ms,
  • red_led(X) – gaszenie lub zapalanie czerwonej diody LED – X może przyjmować wartość 0 (gaszenie) lub 1 (zapalanie),
  • green_led(X) – gaszenie lub zapalanie zielonej diody LED – X może przyjmować wartość 0 (gaszenie) lub 1 (zapalanie),
  • wait_for_button_press(TOP_BUTTON | BOTTOM_BUTTON) – zawieszanie działania programu, dopóki nie zostanie wciśnięty górny lub dolny przycisk,
  • wait_for_button_release(TOP_BUTTON | BOTTOM_BUTTON) – zawieszanie działania programu, dopóki nie zostanie puszczony górny lub dolny przycisk,
  • play_note(X, Y, Z) – sterownik gra określony dźwięk – X to wysokość dźwięku podawana w specjalnej notacji, np. E(5), C(4), F_SHARP(2) itp., Y oznacza czas trwania dźwięku, a Z głośność (maksymalna głośność to 15); funkcja korzysta z tego samego timera co serwo, więc równoczesne korzystanie z obu peryferiów jest kłopotliwe,

Więcej funkcji wraz z praktycznymi przykładami można znaleźć w katalogu bibliotek Pololu: [wybrana podczas instalacji ścieżka dostępu]/libpololu-avr/examples/atmega328p.

 

Autor: Damian Nowak