Navigazione Intelligente

Applicabile a robot con Basic Stamp)

Stufo di veder gironzolare a caso, il robot per la casa, ho cercato di definire prima, e programmare poi, quella che chiamo navigazione Intelligente.
Ho lasciato perdere i vari concetti per eseguire una mappatura dell’ambiente, questa richiede troppo tempo per definirla e costringe il robot a navigare solo in ambienti noti.

Mi sono posto questi obbiettivo:
Definite le coordinate iniziali x,y, il robot deve essere in grado di raggiungere le coordinate di un obbiettivo x1,y1, nel modo più diretto, ovviamente aggirando tutti gli ostacoli che incontra sul percorso diretto.

Per ottenere questo risultato il robot deve:
    1)
       Dopo ogni movimento eseguito, deve essere in grado di conoscere le coordinate di dove si trova.
    2)
       Calcolare continuamente la direzione ottimale e la distanza dall’obbiettivo.
    3)
       Il tutto realizzato via software. (In seguito si potrà implementare con eventuale bussola).

Definizione delle grandezze in gioco:
Ruote con servo motori.
1 impulso servo corrisponde a 2,8mm percorsi.
Lunghezza asse delle ruote = 148 mm.
Centro di riferimento della posizione: punto centrale dell’asse delle ruote.
5 byte per contenere le coordinate di posizionamento:
    1 word per l’asse  X (può contenere sino a + -  32768 inpulsi, corrispondenti a  97,750 metri.)
    1 word per l’asse Y (stessi valori di X).
    1 byte per i gradi.
Altri 5 byte per le coordinate da raggiungere.

Apro subito una parentesi per i gradi: Il PBasic utilizza un solo byte per contenere i gradi, quindi l’angolo giro è di 256 gradi non di 360. Questo non porta alcuna difficoltà pratica, ma nei calcoli successivi, bisogna ricordare la conversione: 1 angolo Pbasic = angolo/360*256. (un angolo standard di 90 corrisponde ad un angolo PBasic di 64).

Abbiamo definito quindi che il ‘mondo’ esplorabile intelligentemente dal nostro Robot è un’area di 97,750+97,750 = 183,5 metri con le coordinate iniziali 0,0 poste nel punto iniziale di accensione del robot. La precisione all’interno di questa area sarà di  2,8 mm..

Prerequisiti:
Per utilizzare praticamente queste idee ed adattarle al vostro programma dovete:

  1. Disporre di due microprocessori: io utilizzo un BS2 (sleave) per la lettura degli encoders ed il calcolo della posizione, ed un BS2E (master) per il calcolo della direzione e la gestione vere e propria dei motori delle ruote.

  2. Lo spazio di programma necessario per le righe di istruzione per i calcoli di posizionamento e determinazione della posizione da raggiungere occupano più di uno slot di 2k, per cui è impossibile utilizzare un semplice BS2, senza considerare anche i problemi di tempificazione per il continuo monitoraggio degli encoders.

  3. Le variabili da utilizzare sono molte e lo spazio del solo Bs2 non sono sufficienti.

  4. Dovete avere già perfettamente funzionante un buon programma di navigazione, cioè un Robot che non urta, ma evita tutti gli ostacoli, frontali, laterali e posteriori. Non utilizzando una bussola, il posizionamento è calcolato solo via software e l’urto contro ostacoli, produrrebbe un errore sulle coordinate di posizionamento, dovuto allo slittamento delle ruote.

Calcolo Posizione (sul BS2 sleave)
Esistono tre diversi tipi di movimento, per ognuno dei quali si devono usare calcoli diversi:

Lineare, (direzione fissa senza alcuna rotazione)
Svolta, (una ruota gira e l’altra è ferma).
Rotazione (una ruota avanti e l’altra indietro).    

Per ciascun movimento descrivo il calcolo teorico e come applicarlo alla programmazione in PBasic.

Movimento Lineare.
Le coordinate di partenza sono 0,0 ed a gradi 0. (la direzione di gradi 0 è verso le ore 12 dell’orologio).
Il movimento lineare è il più semplice, le coordinate al termine di un movimento sono date da:

Asse Y = Asse Y iniziale + distanza * cos(gradi)
Asse X = Asse X iniziale + distanza * sen(gradi)

Dove distanza è = il numero di impulsi mandati al servo * 2,8 mm., se il movimento ruote è all’indietro la distanza è negativa. Gradi sono i gradi di posizionamento rispetto alle ore 12.

Traduzione in PBasic.
Le considerazioni da tenere sempre presenti sono molte:

  1. Il PBasic non gestisce i decimali

  2. Bisogna sempre tenere conto della massima capacità della variabile che si usano.

  3. Eseguire moltiplicazioni e divisioni con i numeri assoluti ed aggiungere il segno alla fine.

  4. L’angolo giro è di 256 gradi.

  5. I valori di seno e coseno sono espressi in numeri da -127 a +127 e non da -1 a +1.

 Le due semplici espressioni per il movimento lineare vengono, purtroppo così tradotte in PBasic:

sign = DeltaAsseY.BIT15
dist = DeltaAsseX

DeltaAsseY = COS(gradi)
sign = DeltaAsseY.BIT15^dist.BIT15
DeltaAsseY = ABS dist*DeltaAsseY
DeltaAsseY = ABS DeltaAsseY/127 '*28/10
IF sign=1 THEN DeltaAsseY=-DeltaAsseY

DeltaAsseX = SIN(gradi)
sign = DeltaAsseX.BIT15^dist.BIT15
DeltaAsseX = ABS dist*DeltaAsseX
DeltaAsseX = ABS DeltaAsseX/127 '*28/10
IF sign=1 THEN DeltaAsseX=-DeltaAsseX

AsseX = AsseX + DeltaAsseX
AsseY = AsseY + DeltaAsseY

 

Svolta (una ruota gira e l’altra è ferma)
E’ come percorrere un arco di cerchio avente il raggio uguale all’asse tra le due ruote.
Questi i calcoli da considerare:
Impulsi = numero di impulsi al servo.
Circonferenza = 2 * PI * raggio = 2* 6,28* 148 = 930
Arco = Impulsi * 2,8
formule complete:
DeltaGradi=256*dist/circ
Gradi=Gradi+DeltaGradi
DeltaAsseX = rag/2 * sen(Gradi-DeltaGradi) - rag/2 * cos(Gradi) 'svolta Sx
DeltaAsseY = rag/2 * sen(Gradi-DeltaGradi) - rag/2 * sen(Gradi)
DeltaAsseX e DeltaAsseY invertiti per svolta Dx (dx centro)
AsseX = AsseX + DeltaAsseX
AsseY = AsseY + DeltaAsseY

Traduzione in PBasic:

DirSvoltaSx:
    dist = DeltaAsseX
    GOTO DirSvolta

DirSvoltaDx:
    dist = DeltaAsseY
    GOTO DirSvolta

DirSvolta:
    sign = dist.BIT15
    DeltaGradi = ABS dist*/$E4 '256/circ = 256/228 = 0,8888 = $00e4
    IF sign=1 THEN DeltaGradi=256-DeltaGradi
    IF moto=MAvantiSx OR moto=MIndietroSx THEN DeltaGradi=256-DeltaGradi 'inverte gradiV per avanti Sx
    Gradi = Gradi+DeltaGradi

    DeltaAsseX = (hraggio*COS(Gradi-DeltaGradi)) - (hraggio*COS(gradi))
    sign=DeltaAsseX.BIT15: DeltaAsseX=ABS DeltaAsseX/127 :IF sign=1 THEN DeltaAsseX=-DeltaAsseX

    DeltaAsseY = (hraggio*SIN(gradi)) - (hraggio*SIN(Gradi-DeltaGradi))
    sign=DeltaAsseY.BIT15: DeltaAsseY=ABS DeltaAsseY/127: IF sign=1 THEN DeltaAsseY=-DeltaAsseY

    IF moto=MAvantiSx THEN DeltaAsseX=-DeltaAsseX: DeltaAsseY=-DeltaAsseY
    AsseX = AsseX + DeltaAsseX
    AsseY = AsseY + DeltaAsseY
 

Rotazione (una ruota avanti e l’altra indietro)

formule complete:
dist=(DeltaAsseY-DeltaAsseX)/2
DeltaGradi=256*dist/gcirc
Gradi=Gradi+DeltaGradi
 

Traduzione in PBasic:

dist = ABS (DeltaAsseY-DeltaAsseX)/2
DeltaGradi = ABS dist*/$01c7     '256/gcirc = 256/144 = 1,7777 = $01c7
IF moto = MGiraSx THEN DeltaGradi=-DeltaGradi
Gradi = Gradi+DeltaGradi
 

Calcolo Direzione ottimale da prendere (calcolo eseguito sul BS2E master)

trova distanza & gradi
formula ideale: distanza=SQR(asseX^2+asseY^2)
formula ridotta: distanza = maggiore differenza tra assi
formula ideale: gradi=ASIN(assY/distanza)

Traduzione in PBasic:

 'Trova differenza X
    GET MemAsseXLow, AsseX.LOWBYTE
    GET MemAsseXHigh, AsseX.HIGHBYTE
    GET MemPuntoXLow, work4.LOWBYTE
    GET MemPuntoXHigh, work4.HIGHBYTE
    asseX = work4-AsseX

    'Trova differenza Y
    GET MemAsseYLow, AsseY.LOWBYTE
    GET MemAsseYHigh, AsseY.HIGHBYTE
    GET MemPuntoYLow, work4.LOWBYTE
    GET MemPuntoYHigh, work4.HIGHBYTE
    asseY = work4-AsseY
    IF Flag.BIT0 = 1 THEN 'Trace on (debug)
        DEBUG "Delta Ob. x,y: ", SDEC asseX, ",", SDEC asseY
    ENDIF

CalcoloDirezione:
    IF ABS asseX<ABS asseY THEN
        Distanza = ABS AsseY
        DeltaGradi=ABS asseX*32/ABS asseY
    ELSE
        Distanza = ABS asseX
        DeltaGradi=ABS asseY*32/ABS asseX
        DeltaGradi=64-DeltaGradi
    ENDIF

    'trova quadrante (1/4)
    stato=0
    stato.BIT0 = asseY.BIT15
    stato.BIT1 = asseX.BIT15
    LOOKUP stato, [1,2,4,3], quadrante
    SELECT quadrante
    CASE 2
        DeltaGradi=96
    CASE 3
        DeltaGradi=160
    CASE 4
        IF ABS asseX<ABS asseY THEN
            DeltaGradi=ABS asseX*32/ABS asseY
            DeltaGradi=256-DeltaGradi
        ELSE
            DeltaGradi=ABS asseY*32/ABS asseX
            DeltaGradi=196+DeltaGradi
        ENDIF
    ENDSELECT
    DeltaGradi = DeltaGradi-gradi
    PUT MemDistanzaLow,Distanza.BYTE0: PUT MemDistanzaHigh,Distanza.BYTE1
    PUT MemPuntoGradi, DeltaGradi
    IF Flag.BIT0 = 1 THEN     'debug
        DEBUG " Dist. Ob.: ", DEC Distanza,", gradiOb: ", DEC DeltaGradi, " quad: ", DEC quadrante,CR,CR
    ENDIF
    IF Distanza < 500 THEN CntSt = 10
    'Verifica se l'obbiettivo è stato raggiunto
    IF Distanza > 150 THEN DirCkGradi
        DEBUG "Obbiettivo raggiunto a: ",DEC Distanza,CR
    FlagObbiettivi=FlagObbiettivi-1
    'GOSUB suonoBip: GOSUB suonoBip:
    defmoto = MFerma
    PUT MemRtnGo, $46 :RUN 4 'suona roulette

DirCkGradi:
    IF DeltaGradi < 65 AND DeltaGradi > 20 THEN defmoto=MAvantiDx: RETURN
    IF DeltaGradi < 129 AND DeltaGradi > 64 THEN defmoto=MgiraDx: RETURN
    IF DeltaGradi < 236 AND DeltaGradi > 191 THEN defmoto=MAvantiSx: RETURN
    IF DeltaGradi < 192 AND DeltaGradi > 128 THEN defmoto=MgiraSx: RETURN
    'debug "Direz gradi OK: ",dec gradiV,cr
    defmoto=MAvanti
    RETURN

 

Chiaro ?  ....

     (comunque il progetto è finito e funziona!!!)