niedziela, 20 marca 2011

DIY: Tweete - robot mobilny


Autor: Marek
Redakcja: Dondu

„Zdaje się, że widziałem kotecka! O tak! Na pewno widziałem kotecka!"

Tweete jest gąsienicowym robotem sterowanym za pomocą telefonu z Androidem przez interfejs bluetooth. Pomysł na robota jak i nazwa wzięła się z chęci zrobienia mojemu kotu zabawki.

Przy okazji chciałem zrealizować od dawna planowany projekt zdalnie sterowanego pojazdu gąsienicowego.





Tweete zaprojektowany został dla gumowych gąsienic modelu czołgu Sherman. Szkielet oraz koła wydrukowane zostały na drukarce 3D. Napęd stanowią 2 silniki HL149 z przekładniami redukcyjnymi.


Szkielet Tweete

Tweete


Każdy napędza jedną gąsienicę. Silniki umieszczone są niesymetrycznie po przeciwnych stronach pojazdu. Takie coś nie wpływa negatywnie na przeniesienie napędu, a umożliwia zmniejszenie szerokości pojazdu, nie powoduje konieczności kombinowania z przekładniami/paskami, przez co konstrukcja bardzo się upraszcza.


Elektronika

Elektronika - warstwa górna

Elektronika - warstwa dolna

Częścią głównie tu opisywaną będzie elektronika. Działa ona w oparciu o mikrokontroler ATMega88a, która steruje silnikami poprzez wysyłanie sygnałów PWM do podwójnego mostka H kryjącego się w układzie L293D.

Możliwość kontroli zdalnej zapewnia moduł bluetooth HC-06 (działający jako slave), który to komunikuje się z mikrokontrolerem za pośrednictwem interfejsu USART oraz radiowo z pilotem w postaci telefonu z Androidem i oczywiście bluetooth'em na pokładzie.

Całość zasilana jest z akumulatora li-pol 2S (2 ogniwa połączone szeregowo, czyli mające nominalnie 7.4V) o pojemności 500mAh. Z uwagi na fakt, że nie posiadam żadnej ładowarki nadającej się do ładowania tego typu pakietów, oraz przewiduję, że robot posiądzie kiedyś mniej lub bardziej autonomię, postanowiłem taką ładowarkę w nim zintegrować.

Posłużył do tego układ MCP73862. Pozwala on na regulację prądu ładowania, czasu zabezpieczającego poszczególne etapy ładowania oraz kontrolę temperatury pakietu. Autonomia z tą ładowarką jest związana planami, by robot sam udawał się do stacji ładowania, gdy zajdzie taka potrzeba.

Oprócz wymienionych peryferiów zostały na płytce stworzone wyprowadzenia do innych, na chwilę obecną nie używanych, takich jak SPI, I2C, przerwania zewnętrzne INT0 i INT1, wejścia do konwertera ADC oraz dodatkowa para PWMów.


Zasilanie części logicznej

Podstawową decyzją na wstępie projektu jest dobór napięcia zasilania części logicznych układu. Na płytce znajdują się 3 układy, które muszą ze sobą współpracować: mikrokontroler, mostek H oraz moduł bluetooth. O ile atmega może być zasilana napięciem z przedziału 1.8-5.5V, tak moduł bluetooth operuje na napięciu maksymalnym 3.3V, a mostek L293D potrzebuje minimum 4.5V.

Należy dodać, że sygnały logiczne wysyłane na moduł bluetooth nie mogą przekraczać napięcia większego od napięcia zasilania, co powoduje konieczność zastosowania napięcia zasilania mikrokontrolera „rozmawiającego” z modułem na poziomie nie wyższym, niż dopuszczalnym dla modułu, albo poprzez zastosowanie układu separującego, np. translatora napięć.

Z kolei układ L293D wymaga wyższego napięcia zasilania, ale komunikacja z mikrokontrolerem jest jednokierunkowa i możliwa przy niższym napięciu. W związku z tym zdecydowałem się na zastosowanie dwóch stabilizatorów napięcia: dla 5V układu LM1117 w obudowie SOT223 oraz dla 3.3V układu MCP1700T3302 w obudowie SOT23.

Z uwagi na fakt, że stabilizator MCP1700T3302 ma maksymalny prąd wyjściowy na poziomie 250mA, a układ LM1117 aż 1A, zdecydowałem się na zasilenie modułu bluetooth napięciem 5V, aby ograniczyć nagrzewanie się drugiego układu. Jest to możliwe, dzięki wbudowanemu w moduł układowi obniżającego napięcie (moduł może być dzięki niemu zasilany napięciem z przedziału 3.3-5V)


Mikrokontroler


Schemat połączeń mikrokontrolera

Z układów wewnętrznych atmegi wykorzystałem Timer0 do generowania sygnałów PWM, interfejs USART do komunikacji oraz jeden kanał przetwornika ADC w celu kontroli napięcia akumulatora. Oprócz tego konieczne było użycie kilku pinów I/O. Jak wcześniej wspomniałem, zostały dodatkowo dodane złącza do w tej chwili nieużywanych (które kiedyś mogą się przydać) interfejsów mikrokontrolera.

Kontrola stanu naładowania akumulatora polega na pomiarze napięcia na niej. Jako, że nominalne napięcie akumulatora wynosi 7.4V, a maksymalne napięcie, jakie ATMega może w tej konfiguracji zmierzyć to 3.3V, zastosowany został dzielnik napięcia.

Wykorzystując w obliczeniach najbardziej podstawowe prawo związane z elektrycznością - prawo Ohma, wyznaczone zostały wartości dwóch rezystorów, które powodują, że po podłączeniu akumulatora do układu na nóżce przetwornika A-C mikrokontrolera napięcie zmienia się w zakresie ok. 3.25-0V, co pozwala określić stan naładowania akumulatora.



Ładowarka


Schemat podłączenia ładowarki


Baterie litowo polimerowe lub litowo jonowe są powszechnie stosowane w przenośnych układach elektronicznych, np. telefonach komórkowych. Wymagają jednak określonego algorytmu ładowania – nie można do nich po prostu podłączyć napięcia i czekać na naładowanie. Bateria może się w ten sposób uszkodzić, albo nie będzie pracować zbyt długo.

Z uwagi na bardzo szerokie zastosowanie tych akumulatorów powstało wiele dedykowanych układów ładowarek, które dodatkowo umożliwiają dopasowanie parametrów procesu ładowania. Przykładem takiego układu jest MCP73862, który umożliwia ładowanie pakietów 2S.

Do układu można podłączyć termistor, którym sprawdza się temperaturę ogniw, a za pomocą rezystora i kondensatora ustala się maksymalny prąd ładowania oraz czas zabezpieczający ładowanie. Dodatkowo układ posiada 2 wyjścia STAT, które informują o stanie procesu ładowania. Można je podłączyć do diod LED, które sygnalizują status ładowania.

W projekcie pominięta została kwestia kontroli temperatury pakietu. W związku z tym, zastosowano odpowiedni układ rezystorów, który „oszukuje” układ, sprawiając, że odczytuje on cały czas poprawna temperaturę. Kontrola temperatury wiąże się z koniecznością montażu odpowiedniego termistora, a jednocześnie nie jest na tyle niezbędna i w wielu aplikacjach jest pomijana.

Głównym problemem związanym z procesem ładowania jest dobór prądu ładowania akumulatora. Realizuje się to za pomącą podpięcia pinu PROG do GND przez rezystor o określonej wartości. Układ pozwala na dobór prądu do maksymalnej wartości 1200mA. Maksymalny prąd ładowania jest na ogół podany na baterii albo w jej danych katalogowych.

Z reguły wartość ta podana jest w odniesieniu do pojemności akumulatora C, np. wartość 1C w przypadku baterii o pojemności 500mAh oznacza maksymalny prąd o wartości 500mA. Warto jednak ładować baterię nieco mniejszym prądem niż dopuszczalny, ponieważ przedłuży to jej żywotności. Z uwagi na to, że maksymalny prąd ładowania zastosowanej przeze mnie baterii wynosi 500mA, zastosowałem rezystor o wartości 2.2kΩ, który to sprawia, że ładowarka ładuje baterię maksymalnym prądem w okolicy 400mA.

Rezystor dobrać można na podstawie wykresu zamieszczonego w nocie katalogowej


Prąd ładowania w funkcji rezystora programującego


lub też obliczyć jego wartość przy użyciu wzoru:


Wzór na wartość rezystora
programującego


By układ działał poprawnie należy również dołączyć do niego kondensator, który będzie determinował czas, po którym w ramach bezpieczeństwa przerwane zostaną poszczególne etapy ładowania, o ile nie zakończyły się wcześniej. Kondensator dobiera się na podstawie trzech wzorów w danych katalogowych.


Dobór kondensatorów dla czasu przerwań

W tym przypadku zastosowany został kondensator 100nF, który spowoduje przerwanie procesu ładowania, jeśli ten będzie trwał dłużej niż 3 godziny. Oprócz tego w swoim czasie przerwane zostaną również poszczególne etapy ładowania akumulatora określone dwoma wcześniejszymi wzorami.


L293D


Schemat połączeń układu L293D

Zasady działania mostka H nie będę tłumaczył, opiszę pokrótce cechy układu L293D. Scalak ten ma w sobie dwa mostki H, co pozwala na sterowanie dwoma silnikami DC lub jednym bipolarnym silnikiem krokowym.

Układ posiada dwa oddzielne wejścia zasilania: VCC1 - zasilanie logiki, VCC2 - zasilanie silników. Napięcie zasilania obu części musi mieścić się w granicach 4.5-36V. Maksymalny prąd ciągły jaki może znieść układ to 600mA na kanał, prąd chwilowy może sięgnąć wartości do 1.2A na kanał. Wyjścia na silniki są dodatkowo zabezpieczone diodami (stąd końcówka D w nazwie układu), które zabezpieczają układ przed niepożądanym prądem indukowanym przez silnik (np. podczas ręcznego obracania wirnikiem silnika).

Po każdej stronie obudowy znajdują się jeden pin zasilania (dla jednej ze stron zasilanie logiki, dla drugiej silników), dwa piny GND, dwa piny wyprowadzeń do silnika oraz 3 piny sterowania.

Sterowanie silnikiem odbywa się poprzez zmianę prędkości i kierunku obrotów. Dla silnika pierwszego dając stan wysoki na jeden z pinów DIR1A lub DIR2A nadaje się określony kierunek obrotów silnika. W sytuacji, gdy na obu pinach będzie taki sam stan, silnik nie będzie reagował.

Pin 1,2EN odpowiada z kolei za załączenie przepływu prądu przez mostek. Domyślnie jest on wyłączony. Aby więc ruszyć silnikiem trzeba przyłożyć stan wysoki na jeden z pinów DIR oraz na pin 1,2EN. Z kolei połączenie tego ostatniego pinu z kanałem PWM pozwoli po prostu na regulowanie prędkości silnika poprzez zmianę wypełnienia sygnału. Silnik jest wtedy zasilany bardzo krótkimi seriami, co z uwagi na pewną pojemność elektryczną całego układu, bezwładność silnika i wysoką częstotliwość sygnału powoduje, że silnik zamiast szarpać z dużą częstotliwością, po prostu pracuje wolniej lub szybciej.

Schemat całego układu jest zaprezentowany poniżej:


Schemat całości układu

Do pobrania pliki KiCad: tweete.rar (kopia)

Na płytce dodatkowo został zaplanowany przełącznik, do załączania napięcia do układu oraz przycisk wyzwalający przerwanie od resetu.


HC-06

Moduł bluetooth, który został wykorzystany posiada interfejs komunikacyjny USART. Za pomocą tego interfejsu do mikrokontrolera przesyłane są informacje, które do modułu trafiają drogą bezprzewodową.

Komunikacja w tym standardzie odbywa się po dwóch liniach TXD (transmisja) oraz RXD (odbiór). Aby możliwa była współpraca, linia TXD jednego urządzenia musi być podpięta do linii RXD drugiego i na odwrót. USART jest najbardziej rozbudowanym ze wszystkich wbudowanych układów mikrokontrolera interfejsem komunikacyjnym.

Możliwy jest wybór prędkości transmisji oraz kształtu ramki danych. Z tego powodu konieczne jest ujednolicenie ustawień obu urządzeń komunikujących się ze sobą. Jako, że parametry pracy USARTA nie mają większego znaczenia, postanowiłem dostosować je do domyślnych parametrów modułu bluetooth - baudrate 9600, 8-bitowy znak i jeden bit stopu.


Program

Program został napisany w języku C. Sprowadza się on do pętli, w której odczytywane są dane znajdujące się w rejestrze UDR0 (dane przychodzące USARTA), podjęcie decyzji na podstawie tych danych oraz informacji o napięciu akumulatora (uniemożliwienie jazdy z rozładowanym akumulatorem).

Dane trafiające do robota są pojedynczymi bajtami, czyli liczbą z zakresu 0-255. Z telefonu wysyłane są one pojedynczo, a każda oznacza konkretną komendę taką jak jazda na wprost, do tyłu, obrót wokół własnej osi w prawo lub lewo lub stop. Dodatkowo jest możliwość regulowania prędkości poprzez zadawanie trzech różnych wartości wypełnienia sygnału PWM.

Dodam, że z uwagi na zasilanie silników napięciem wyższym niż dopuszczalne 6V, maksymalne wypełnienie sygnału PWM ustawiłem na 180, czyli jakieś 70%.


//#define F_CPU 1000000UL 
//F_CPU zdefiniuj w opcjach projektu zgodnie z: http://mikrokontrolery.blogspot.com/2011/03/fcpu-gcc-gdzie-definiowac.html

#include <avr/io.h>
#include <stdio.h>  
#include <util/delay.h>

volatile uint8_t dir;
volatile uint8_t wychodzace;
volatile uint8_t pwr;

 
void USART_Init(void)
{
 UCSR0A|=_BV(U2X0);   //podwójna prędkosc transmisji
 UBRR0H=0;
 UBRR0L=12;    //baudrate 9600 
 UCSR0B=(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0); //włączenie transmisji i odbioru
 UCSR0C=(1<<UCSZ00)|(1<<UCSZ01);  //tryb asynchroniczny, 8 bitowa ramka danych

}

void USART_Transmit(void)   //procedura umożliwiająca wysyłanie danych do telefonu
{
 while((UCSR0A & (1<<UDRE0)) == 0);
 UDR0= wychodzace;
}

void move(volatile uint8_t dir)  //procedura wkonania ruchu
{
 switch(dir)
 {
  case 97:PORTD = 0b10000000;  //przod
    PORTC = 0b00000001;
    OCR0A=pwr;
    OCR0B=pwr;
    break;
    
  case 98:PORTD = 0b00010000;  //tyl
    PORTC = 0b00000010;
    OCR0A=pwr;
    OCR0B=pwr;
    break;
    
  case 99:PORTD = 0b00010000;  //lewo
    PORTC = 0b00000001;
    OCR1A=pwr;
    OCR1B=pwr;
    break;
    
  case 100:PORTD = 0b10000000;  //prawo
    PORTC = 0b00000010;
    OCR1A=pwr;
    OCR1B=pwr;
    break;
    
  case 101:PORTD = 0b00000000;
    PORTC = 0b00000000; //stop
    OCR1A=0;
    OCR1B=0;
    break;
    
  case 102:pwr=70;
    OCR0A=pwr;
    OCR0B=pwr;  //moc poziom 1
    break;
    
  case 103: pwr=130;
    OCR0A=pwr;
    OCR0B=pwr;  //moc poziom 2
    break;
    
  case 104: pwr=180;
    OCR0A=pwr;
    OCR0B=pwr;  //moc poziom 3
    break;
    
 }
}



void init()  //procedura inicjalizacji podstawowych funkcji
{

 /* USTAWIANIE WYJŚĆ */
 DDRC |= (1<<PC0)|(1<<PC1);       // wyjścia pwm i dir
 DDRD |= (1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
 
 /* INICJALIZACJA PWM - TIMER0 */
 TCCR0A |= (1<<COM0A1) | (1<<COM0B1) | (1<<WGM00); //PWM z korekcją fazy, clear on compare
 TCCR0B |= (1<<CS00)|(1<<CS01);    //Prescaler x64, przy wyższej częstotliwości silniki piszczą
 
 OCR0A = 0;
 OCR0B = 0; //wyzerowanie rejestrów kanałów PWM
 
 /*INICJALIZACJA ADC */
 
     ADCSRA = (1<<ADEN) |(1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);
   ADMUX  = (1<<REFS0)|(1<<MUX1);   //ustawienie Vref=vcc i ADC2
   DDRC &=~ (1<<PC4);   //ADC2 jako wejście

}

void pomiar()  //procedura wykonywanioa pojedynczej konwersji przy użyciu ADC
{
 ADCSRA |= (1<<ADSC);
 while(ADCSRA & (1<<ADSC));
}


int main(void)
{
 volatile uint8_t dir;
 
 
 init();  // inicjalizacja
 USART_Init(); // inicjalizacja usarta
 for(;;)
 { 
  dir=UDR0;
  pomiar();
  if(ADC<863) move(101) //jeśli napięcie spadnie poniżej 7.2V(2,78 na kanale ADC), zatrzymaj pojazd
  else move(dir);  //jeśli napięcie jest odpowiednie, wykonaj określoną czynność
  

 }
 return 0;
}

Do pobrania: main.c (kopia)


Telefon

Ostatnim elementem zestawu jest aplikacja umożliwiająca wysyłanie danych do robota z telefonu. Posłużyła mi do tego aplikacja Bluetooth SPP dostępna za darmo w sklepie Google Play. Jest to terminal umożliwiający wysyłanie i odbieranie bajtów danych przez bluetooth. Używam tej aplikacji, ponieważ umożliwia ona dostosowanie w prosty sposób klawiatury do własnych potrzeb.

Każdy przycisk został opisany oraz przypisana została mu wartość, którą wysyła do urządzenia. Przykładowo jeździe do przodu odpowiada liczba 97, co w kodzie ASCII oznacza znak 'a' (ten znak jest wysyłany przez terminal).


Wygląd aplikacji



Możliwości rozwoju

Pojazd ten jest świetną platformą do rozwijania i stworzenia autonomicznego robota mogącego mieć różne funkcje. Dzięki dodatkowej parze PWM można zamontować serwo, które sterowałoby np. ultradźwiękowym czujnikiem odległości. Pozwoliłby on na możliwość samodzielnego wyszukiwania przeszkód i omijania ich, lub nawet atakowania. Interfejsy komunikacyjne SPI i I2C dają niemal nieograniczone możliwości rozwijania urządzenia.

Jak też wcześniej wspomniałem, możliwe jest wykonanie stacji dokującej, w której robot mógłby samodzielnie ładować akumulator.


4 komentarze:

  1. Świetnie, materiał kompletny

    OdpowiedzUsuń
  2. Jaki zasięg udało się osiągnąć?

    OdpowiedzUsuń
    Odpowiedzi
    1. Dla tego typu modułów, 10 metrów w zależności od warunków.

      Usuń
  3. Witam, mam kilka pytań dotyczących Twojego poradnika: jak zrobić robota Tweete? Otóż nie wiem jak zdefiniować przyciski w aplikacji bluetooth spp na telefonie aby telefon wysyłał do modułu (mikrokontrolera) odpowiednie sygnały (silnik na przód, do tyłu, itp). Nie wiem jak przypisać danemu przycisku wartość np. 97 czyli ‘a’. Dodatkowo bardzo bym prosił o pomoc dotyczącą atmegi8. Ponieważ w artykule używasz atmegi88pa rozumiem, że aby całość działała na atmedze8 muszę zmienić kod. Pytam się ponieważ przy próbie kompilacji programu na zwykłą ‘8’ wyskakuje wiele błędów, a pierwszy zaraz przy linijce:
    UCSR0A|=_BV(U2X0); //podwójna prędkosc transmisji
    Bardzo prosił bym o pomoc. Bardzo dziękuje.

    OdpowiedzUsuń