WILL-I V2.0
A továbbfejlesztett verzióban WILL-I-t egy mikroszervóra szerelt Sharp infravörös távolságérzékelő szenzorral szereltem fel. Így nemcsak lát (érzékeli az előtte lévő tárgyak távolságát), de képes körülnézni is.
Sharp szenzor
Egy 3-30 cm méréstartományú GP2D120XJ00F szenzort használtam. Az első dolog, amit meg kellett tenni, hogy ki kell mérni a szenzor feszültség-távolság görbéjét, mert nincs két egyforma szenzor. Ugyanazon típuson belül is mindegyiknek kicsit eltérő a görbéje. Az alábbi ábráról látható, hogy észrevehetően eltér a gyártó által megadott referenciagörbe az adott szenzorok tényleges, mért görbéjétől (akár 1 cm-es eltérés is lehet a mért és a referencia adatok között).
A fenti ábráról látható, hogy a Sharp szenzor maximális feszültsége 3,1V (ez nagyobb mint a belső 2,56V-os belső referenciafeszültség). Azt is lehetne használni, de akkor a 2,56V-nál nagyobb jeleket nem tudjuk mérni és a méréstartomány leszűkül kb. 6-30 cm-re.
Ha referenciafeszültségnek a stabilizált 5V-ot választjuk (0 és 5V között mérünk) , akkor nem használjuk ki a teljes méréstartományt mert a szenzor maximális jele 3,1 V.
10 bites ADC felbontás esetén a mért ADC értékek 0-634 közé esnek.
8 bites ADC felbontás esetén pedig 0-158 közötti értékeket kapunk.
Az általam használt feladatra a 8 bites felbontás is bőven elegendő lenne (elég csak 1-2cm-es pontossággal mérni), de korábban RoboMoly programjánál a 8 bites ADC-t már használtam, ezért most példaként a 10 bites felbontást fogom használni.
Mikroszervó
A szenzort a mikroszervó fogja mozgatni. WILL-I esetben elég csak 3 pozíció (jobb, bal, középállás).
A szervót hardveresen generált PWM jellel fogom vezérelni (a PWM-ről részletesebben az előző cikkben esett szó LINK).
A hardveres PWM előállítás előnye, hogy néhány utasítással beállítható, és a háttérben fut (nem terheli a mikrokontrollert). Az Atmega8-nak 3 lába van, ami képes PWM jel generálására: 15-ös és a 16-os lábat (OC1A és OC1B) a 16 bites Timer1 vezérli. (A Timer1-nek 2 csatornája és 8 bites működési módja is van.) A 17-es lábat (OC2) pedig a 8 bites Timer2 vezérli.
A szervó nagyfelbontású meghajtására a 16 bites Timer1 lenne az ideális. Mivel Timer1-nek 2 csatornája van, és azt már elhasználtam a DC motorok vezérlésére, ezért kénytelen leszek a 8 bites Timer2-t használni, ami csak alacsony felbontású szervóvezérlést tesz lehetővé (kevés lépésszám van a két szélső érték között). Szerencsére az infravörös szenzor mozgatásához nem is kell nagy felbontás. (Láthatjuk hogy elértük az Atmega8 határait, mert ha később olyan irányba fejlesztjük a robotunkat, hogy szeretnénk egynél több szervót vezérelni ( pl. karokat, érzékelőket mozgatni), akkor két választásunk marad:
- Szoftveresen oldjuk meg a PVM jel előállítását (ekkor bármelyik digitális kimenetre generálhatunk PWM jelet).
- kicseréljük az Atmega8-at egy olyan AVR-re, amelyiknek több timere és több PWM kimenete van.
Pl. az Atmega168-nak szintén 28 lába van (belefér a motorvezérlő panelbe), 2 db 8 bites és 1 db 10 bites timer-je és összesen 6db PWM csatornája van (4db kisfelbontású (8bit-es) és 2 nagyfelbontasú (10bit-es))
Nézzük meg hogyan lehet a szervót Timer2-vel vezérelni:
Láthattuk, hogy a szervót egy 1-2ms hosszú négyszögjellel vezérelhetjük, amelyeknek kb. 20ms-onként ismétlődniük kell. Tehát a vezérlőjel kitöltési tényezője 5-10% között változik (a teljes periódusidő 5%-át használjuk ki). Egy 8 bites timer esetén kevés lépésszám lehetséges a szervó két szélső pozíciója között.
Timer2 számlálója 0-255-ig számol, 255 után a számláló túlcsordul és lenullázódik, ezzel kell egy nagyjábol 20 ms-onként ismétlődő vezérlőjelet előállítani. Az alábbi ábráról látható, hogy a Timer2 előosztását 64-nek választva egy 61Hz-es, 16,4 ms-onként ismétlődő impulzust tudunk generálni, ami egy kicsit gyors a referencia 20ms-hoz viszonyítva, de az általam használt mikroszervó minden gond nélkül vezérelhető vele.
A négyszögimpulzus hosszát az OCR2 regiszter értékével tudjuk változtatni. Ahhoz hogy az impulzus hossza 1 és 2 ms között legyen OCR2 értéke 16-31 között lehet csak (lásd az alábbi táblázatot), ezért a szervó tűrésétől függően csak kb. 12-15 pozíciót tudunk beállítani (256-nak az 5%-a kb. 13). Egy 180°-os szervónal ez kb. 12-15°-os lépésközt jelent. Az általam használt mikroszervó szögtartománya 90°, ezért 6-8°-os lépésekben tudom a szervót pozícionálni, ami a célnak bőven megfelel.
A fentiek alapján Timer2 előosztását 64-nek megadva, OCR2 értékét pedig 16-31 közötti változtatva vezérelhetjük a PB3-as lábra kötött szervót a különböző pozíciókba.
WILL-I összeállítása
A Sharp IR szenzor kimenetét a PC0 (ADC0) lábra csatlakoztattam, a szervót a PB3-as lábra a nyomógombot pedig a PD2-es lábra.
WILL-I programja
Ez a vezérlőprogram sem bonyolult. Bekapcsolás után WILL-I gombnyomásra vár (PD2 lábra kötött gomb), majd elindul előre. A 10 bites ADC-vel beolvassuk a távolságérzékelő szenzor jelét. Ha WILL-I kb. 15 cm-nél közelebb (ADC >= 200) akadályt érzékel akkor:
- megáll
- balra néz
- távolságot mér (ADC méres)
- jobbra néz
- távolságot mér (ADC méres)
- majd arra fordul amerre nem érzékel akadályt, vagy az érzékelt akadály messzebb van mint a másik irányban érzékelt akadály.
A fenti algoritmus folyamatábrán ábrázolva így néz ki:
(minden ADC mérés és szervó forgatás után van egy fél másodperces késleltetés, hogy a szervónak legyen elég ideje az új pozícióba álláshoz, ez a folyamatábrán nincs feltüntetve)
(Ahhoz hogy az alábbi kód működjön, kell az előző cikkben leírt motor.h kód is! (LINK))
/* WILL-I V2.0 vezerlo program
Nyomogomb: PD2-es labra kotve
Sharp IR szenzor: PC0-as (ADC0) labra kotve
Szervo: PB3-as labra kotve
*/
//---------------------------------------------------------------------
#define F_CPU 1000000UL /* 1 MHz-es CPU orajel megadasa*/
#define jobbra 16 // Szervo jobb szelso pozicio
#define kozepre 23 // Szervo kozepso (Neutral) pozicio
#define balra 31 // Szervo bal szelso pozicio
#include <util/delay.h> // idozito, keslelteto rutinokat tart. fajl
#include <avr/io.h> //AVR konstansokat, beallitasokat tart. fájl
#include <util/motor.h> // motorvezerlo utasitasokat tart. fajl
volatile int SharpIR = 0;
volatile int SharpIRjobb = 0;
volatile int SharpIRbal = 0;
void Konfig10bitADC() // ADC konfiguralas (beallitas)
{
ADMUX |= (1<<REFS0); // Vcc mint referencia
ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0); // ADC engedelyezese, ADC eloosztas = 8 (125 KHz)
}
unsigned int Beolvas10bitADC(unsigned char csatorna)
{
ADMUX = (ADMUX & 0b11110000) | csatorna; //ADC csatorna kivalasztasa
ADCSRA |= (1<<ADSC); // elso ADC konverzio elinditasa
while (ADCSRA & (1<<ADSC)); // varas az atalakitasra
ADCSRA |= (1<<ADSC); // masodik ADC konverzió elindítás
while (ADCSRA & (1<<ADSC)); // varas az atalakitasra
return (ADCL | (ADCH<<8)); // ADC ertek kiolvasasa
}
void KonfigSzervo() // Szervo konfiguralas (beallitas)
{
DDRB = _BV(DDB3); // PORTB 3. lab kimenet (Szervo PWM)
TCCR2 = _BV(WGM20) // Timer2 8bites gyors PWM mod
| _BV(WGM21) // Timer2 8bites gyors PWM mod
| _BV(COM21) // nem-invertalt PWM
| _BV(CS22); // Timer2 eloosztas: 1/64 (61 Hz-es PWM impulzus frekvencia)
OCR2 = 23; // 1.5ms-os kezdeti PWM impulzus (Szervo kozepso (Neutral) pozicio)
}
void Szervo(unsigned char pozicio) // Szervo pozicionalo utasitas
{
OCR2 = pozicio;
}
int main (void)
{
PORTD |= (1<<PD2); // PD2-es lab bemenet, pull-up bekapcs (nyomogomb)
DDRC &= ~(1<<PC0); // PC0-as lab bemenet (Sharp IR szenzor)
PORTC = 0x00; // PORTC osszes laban a felhuzoellenallasok kikapcsolva
KonfigSzervo(); // Szervo beallitas lefuttatasa
Konfig10bitADC(); // ADC beallitas lefuttatasa
while (PIND & (1<<PD2)); // várakozó cilkus amíg PD2 erteke nem 0 (amig a gomb nincs lenyomva)
while (1)
{
elore(100); // teljes gozzel elore!
SharpIR = Beolvas10bitADC(0);
if ( SharpIR > 200 )
{
motor_stop(mind);
Szervo(balra);
_delay_ms(500); // 0.5 s kesleltetes
SharpIRbal = Beolvas10bitADC(0);
_delay_ms(500); // 0.5 s kesleltetes
Szervo(jobbra);
_delay_ms(500); // 0.5 s kesleltetes
SharpIRjobb = Beolvas10bitADC(0);
_delay_ms(500); // 0.5 s kesleltetes
Szervo(kozepre);
if ( SharpIRbal > SharpIRjobb)
{
fordul_jobb(100); // fordulas balra 0.5 s
_delay_ms(500);
}
else
{
fordul_bal(100); //fordulas jobbra 0.5 s
_delay_ms(500);
}
}
else
{
_delay_ms(100); // 0.1 s kesleltetes
}
}
}
A cikk még nem ért véget, lapozz!
Értékeléshez bejelentkezés szükséges!