Samochodzik, czyli jak wykorzystać modem Xbee.

Jakiś czas temu wpadłem na pomysł budowy quadrocoptera. Jednak, że nie od razu Rzym zbudowano, stwierdziłem, że zacznę od skonstruowania czegoś mniejszego, tańszego i bezpieczniejszego, czegoś na czym mógłbym spokojnie testować zdalne sterowanie. Tak oto urodziła się idea trójkołowego samochodzika.

Od czego zacząć

Konstrukcja nie powala, po prostu wyciąłem z kartonu prostokąt o wymiarach 10×20 i doczepiłem do niego zaciskami dwa sześciowoltowe silniczki.

Samochodzik wersja bardzo alfa
Samochodzik wersja bardzo alfa

Jego sercem i rozumem jest Arduino UNO z nałożonym Motor Shieldem. W zasadzie w tak prostym modelu można pominąć ów shield, gdyby założyć, że nie będziemy korzystać z biegu wstecznego i sterować silnikami za pomocą PWM,, ale akurat miałem go pod ręką i grzechem byłoby tego nie wykorzystać ;) Poza tym wykorzystałem dwa modemy Xbee S2 oraz, znaleziony na dnie szafy, joystick analogowy, który z powodzeniem można zastąpić Joystick Shield Kitem. Wykorzystanie go ma tą zaletę, że oszczędzi Wam wiele pracy i zmniejszy ilość walających się kabli.

Rozbieranie joysticka

Josticki analogowe to tak naprawdę dwa potencjometry, ustawione prostopadle do siebie. Jeden odpowiada za odchylenie w osi OX, drugi w osi OY. Wychylenie drążka powoduje zmianę rezystancji tych potencjometrów, zwykle mają one rezystancje od około 0 do 100k Ohm.

Joystick
Joystick

Specjalnie na potrzeby padów, joysticków, etc. w komputerach pojawiły się GamePorty. Jak sami pewnie się domyślacie, najpierw musiałem dojść do tego który przewód za co odpowiada. Odciąłem wtyczkę od kabla i posiłkując się opisem pinów z wikipedii stwierdziłem, że widoczny na zdjęciu przewód pomarańczowy odpowiada za oś OX, a brązowy – OY. Dlatego polecam wykorzystanie shielda – oszczędzicie sobie pracy. Kolejnym problemem był sam odczyt spadku napięcia na potencjometrze. Niestety, nie mogłem mierzyć go bezpośrednio. Dlatego też wykorzystałem opisany TU dzielnik napięcia, tak samo jak tam wykorzystałem rezystor 10k i mierzyłem spadek napięcia zamiast na joysticku – na ów rezystorze. To z kolei spowodowało, że napięcie nie zmieniało się liniowo, lecz hiperbolicznie – w związku z tym ustawiłem cztery możliwe wychylenia (zero/lekko/średnio/mocno) i zbadałem napięcia dla tych wychyleń. W ten sposób ustaliłem, że dla odczytu poniżej 190 (190*5/1023 = 0.92 V) drążek jest wychylony do przodu, dla odczytu powyżej 240 (1,17V) drążek odchylony jest do tyłu, itd.

Skoro już mamy czym sterować…

Przyszedł czas na zastanowienie się jak przekazywać polecenia do Arduino na pokładzie samochodzika. Po podłączeniu (RX do DOUT, TX do DIN) i skonfigurowaniu Xbee wedle tutorialu, nadszedł etap na napisanie programu. Dzięki blogowi Sprae, który w bardzo czytelny sposób tłumaczy tajniki wysyłania struktur, w bólach powstały dwa programy, nadawaczy i odbiorczy (linki na końcu postu):
Jak sami pewnie zauważycie, są to w zasadzie odpowiednio przerobione programy zamieszczone na blogu Sprae, nie wynajdywałem koła od nowa ;) On też tłumaczy tam w bardzo dobry sposób jak działa proces wysyłania i odczytywania poszczególnych bitów. W związku z tym objaśnię w tym poście tylko wybrane fragmenty kodu. Aha, ważna sprawa: program odbiorczy nie chce się kompilować (przynajmniej u mnie) w Arduino IDE 1.0.1 ! Do wgrania programu na Uno wykorzystałem IDE 0.22.

int Speed[] = {0, 100, 175, 255};
int currentSpeed;
int currentForce;

Tutaj deklaruję zmienne pomocnicze, które wykorzystam w dalszych częściach kodu.

struct paczka
{
  int dir;
  int vel; /*velocity*/
  int turn;
  int force;
} pack;

Oto struktura, przechowująca odczyt wychylenia joysticka, która zostanie wysłana do pojazdu. Pole:

  • ” dir” odpowiada za kierunek (255 – przód, 0 – tył)
  •  „vel”  odpowiada za prędkośc (0 – zero/100 – mała/175 – pół/255 – cała)
  •  „turn” to kierunek skrętu, czyli czy skręcamy w lewo czy w prawo (0 – nie skręcamy/125 – lewo/255 – prawo)
  •   „force” to siła z jaką skręcamy (0/100/175/255), a dokładniej jaki jest stosunek wypełnienia PWM sterującego jednym kołem, do wypełnienia PWM odpowiadającego z drugie koło

Całą strukturę zbudowałem w oparciu o wartości typu „int”, gdyż miałem pewne problemy w przesyłaniu do struktury znaków i tablic znaków („char”). Równie dobrze zamiast 225 oznaczającego kierunek w przód, mogłoby być tam np. 5, to są tylko przyjęte przeze mnie oznaczenia. Oznaczenia to tylko umowa. A propos umów i oznaczeń, który wzór na moc czynną jest poprawny?

 

   czy        ??

Odpowiedź na dole strony.

    if(NAPY <190) /*forward*/
    {
      if(NAPY >= 150 && NAPY < 190)
      {
        currentSpeed = Speed[1];
      }
      else if(NAPY >=100 && NAPY < 150)
      {
        currentSpeed = Speed[2];
      }
       else
      {
        currentSpeed = Speed[3];
      }
      pack.dir = 255;
      pack.vel = currentSpeed;
    }

Ten fragment kodu odpowiada założeniu o który pisałem w poprzednim akapicie. Dla odczytu poniżej 190 (0.92 V) program zapisuje w polu struktury odpowiadającej za kierunek wartość „255”, a następnie sprawdza jak bardzo wychylony jest joystick. Im mniejsze jest zmierzone napięcie tym większa jest wartość wychylenia. Kolejne przedziały są przyporządkowane wartościom z kolejnych komórek pomocniczej tablicy.

Wybrane fragmenty programu odbiorczego:

Deklaracja zmiennych oraz bibliotek.

#include <MotorShield.h>
#include <EEPROM.h>

MS_DCMotor right(MOTOR_A);
MS_DCMotor left(MOTOR_B);
int Speed[] = {0, 100, 175, 255};
int currentSpeed;

Biblioteka EEPROM nie jest konieczna do działania samochodzika, w sumie spokojnie można powiedzieć, że jest zbędna ;). Po prostu w czasie tworzenia programu chciałem sprawdzić jak działa przesyłanie danych między XBEE, a najprostsze było napisanie funkcji, opisanej poniżej, zapisującej odebrane dane w pamięci EEPROM.

void pack_save(struct paczka *pack, unsigned int address)
{
  byte *data = (byte *) pack;
  unsigned int size = sizeof(paczka);
  for (unsigned int i=0; i<size; i++)
  {
    EEPROM.write(address+i, data[i]);
  }
}

W ten sposób mogłem sprawdzić co zostało odebrane. Jeśli chcecie sami sprawdzić co odbiera Wasze Xbee usuńcie symbole „/*” oraz „*/”. Wszystko co jest pomiędzy nimi, w języku C, oznacza komentarz i jest pomijane przez kompilator. Ponieważ prędkość zapisu do pamięci EEPROM jest relatywnie niska, należy zwiększyć opóźnienie w wykonaniu następnej pętli programu (delay). Z doświadczenia wiem, że przy zapisywaniu odebranych danych i opóźnieniu rzędu 10~30 ms, Arduino wiesza się po paru cyklach. Pamiętajcie też, że trwałość pamięci EEPROM jest obliczona na ok 100000 cykli zapisu i odczytu, więc korzystajcie z niej rozważnie.

void rotator_controller(struct paczka pack)
{
    if(pack.dir == 255)
      {
      right.run(RELEASE);
      left.run(RELEASE); 
      right.run(FORWARD);
      left.run(FORWARD);
      right.setSpeed(pack.vel);
      left.setSpeed(pack.vel);
       }
   if(pack.dir == 0)
      {
      right.run(RELEASE);
      left.run(RELEASE); 
      right.run(BACKWARD);
      left.run(BACKWARD);
      right.setSpeed(pack.vel);
      left.setSpeed(pack.vel);
      }
    if(pack.dir == 125)
    {
      right.run(BRAKE);
      left.run(BRAKE);
      right.setSpeed(Speed[0]);
      left.setSpeed(Speed[0]);

    }

Tak jak widać, jeśli „pack.dir” odpowiadające kierunkowi ma odpowiednią wartość, w tym przypadku, „255” pojazd jedzie do przodu. Na tej samej zasadzie dla „0” będzie jechał do tyłu, a dla „125” zostanie włączony hamulec.

    if(pack.turn == 125) /*left*/
    {
      right.setSpeed(pack.vel);
      if(pack.force == 100)
        left.setSpeed(pack.vel/1.5);
      if(pack.force == 175)
        left.setSpeed(pack.vel/2);
      if(pack.force == 255)
        left.setSpeed(pack.vel/3);
    }

Tak jak napisałem wcześniej, skręcanie polega tu na różnicy wypełnienia PWM sterującego jednym kołem, a drugim. Dzięki temu promień skrętu jest niewielki, a gdyby zmienić odrobinę kod, tak by dla maksymalnego wychylenia koła obracały się w przeciwnych kierunkach, samochodzik będzie się obracał w miejscu. Jak czołg, który zajmuje zaszczytne miejsce na liście „TO DO”.

W zasadzie już wszystko mamy

Do sterowania silnikami wykorzystałem, ze względu na wygodę MotorShielda oraz napisaną do nie go bibliotekę. Biblioteka znakomicie upraszcza cały program, a poza tym zauważyłem ciekawą zależność. Przy własnoręcznie napisanym programie obsługującym silniki  wyły nieludzko, z kolei przy tej bibliotece pracują w miarę cicho. W ten sposób można sterować np tym robotem.

Efekty pracy:

Odpowiedź do pytania:

Oba są poprawne. Wszystko zależy od umowy, od tego jakie oznaczenia się przyjmuje. Przykładowo, na wydziale Elektrycznym mojej Alma Mater obowiązuje wzór pierwszy, z kolei na wydziale Elektroniki, stosuje się drugi wzór bo operują oni wielkościami amplitudy sygnału, a do liczenia mocy wykorzystuje się wartość skuteczną. A wartość skuteczna, dla sygnału przemiennego, jest opisywana wzorem:

Programy nadawcze i odbiorcze

Użyte elementy:

Ten samochodzik zbudowany został z następujących elementów z oferty Nettigo: