RI Lezione 12
🤖 Robotica Industriale · IV Anno · Lezione 12
Strutture di controllo 🔀
IF · JMP · LBL · SELECT · Logica condizionale su DI, R[ ] e stati del sistema
🔀 Il programma decide. Fino ad ora il robot eseguiva sempre le stesse istruzioni nello stesso ordine. Con le strutture di controllo il programma può scegliere percorsi diversi in base a condizioni reali — lo stato di un sensore, il valore di un registro, la presenza o assenza di un pezzo. È la differenza tra un robot che ripete e un robot che ragiona.
🎯 Obiettivi della lezione
- Usare LBL[ ] e JMP LBL[ ] per creare salti incondizionati nel programma
- Scrivere istruzioni IF complete con condizioni su DI[ ], R[ ] e DO[ ]
- Costruire strutture IF/ELSE con doppio LBL per gestire due percorsi alternativi
- Usare SELECT per smistare il flusso su più di due percorsi in base al valore di un registro
- Combinare strutture di controllo con I/O e R[ ] in un programma di sorting realistico
📖 1. Teoria
1.1 — LBL[ ] e JMP LBL[ ]: salto incondizionato
Un’etichetta LBL[ ] è un marcatore nel programma — non esegue nulla, serve solo come destinazione di un salto. Il comando JMP LBL[ ] trasferisce l’esecuzione all’etichetta indicata, da qualsiasi punto del programma.
1: J P[1] 100% FINE
2: JMP LBL[10] ! salta a riga con LBL[10]
3: J P[2] 100% FINE ! questa riga NON viene eseguita
4: LBL[10] ! destinazione del salto
5: J P[3] 100% FINE
! Salto indietro (loop)
10: LBL[1]
11: J P[1] 100% FINE
12: J P[2] 100% FINE
13: JMP LBL[1] ! torna a LBL[1] → loop infinito
📌 Convenzione numerazione LBL: usa range dedicati per evitare conflitti: LBL[1]–LBL[9] per loop principali · LBL[10]–LBL[49] per diramazioni IF/ELSE · LBL[50]–LBL[89] per sottosezioni · LBL[90]–LBL[99] per gestione errori. Non riusare lo stesso numero in punti diversi dello stesso programma — il controller salta sempre alla prima occorrenza.
1.2 — IF: condizione semplice con salto
L’istruzione IF valuta una condizione e, se vera, esegue un’azione — tipicamente un JMP. Se la condizione è falsa, l’esecuzione prosegue normalmente alla riga successiva.
| Tipo condizione | Sintassi | Significato |
|---|---|---|
| Ingresso digitale ON | IF DI[1]=ON, JMP LBL[10] | Salta se DI[1] è attivo |
| Ingresso digitale OFF | IF DI[2]=OFF, JMP LBL[20] | Salta se DI[2] non è attivo |
| Registro uguale | IF R[1]=0, JMP LBL[30] | Salta se R[1] è zero |
| Registro minore | IF R[1]<R[2], JMP LBL[1] | Salta se R[1] è minore di R[2] |
| Registro diverso | IF R[3]<>0, JMP LBL[40] | Salta se R[3] non è zero |
| Uscita digitale ON | IF DO[1]=ON, JMP LBL[50] | Salta se DO[1] è attivo (lettura stato) |
1.3 — Struttura IF/ELSE con doppio LBL
TP non ha una parola chiave ELSE nativa, ma la struttura IF/ELSE si realizza con due etichette: una per il ramo “vero” e una per ricongiungersi dopo entrambi i rami.
! Condizione: DI[3]=ON → zona OK · DI[3]=OFF → zona NG
1: IF DI[3]=ON, JMP LBL[10] ! se OK → salta al ramo OK
! — Ramo ELSE (DI[3]=OFF, pezzo NG) —
2: J P[30:APPR_NG ] 80% CNT100
3: L P[31:PLACE_NG] 150mm/sec FINE
4: DO[1]=OFF
5: L P[30:APPR_NG ] 150mm/sec FINE
6: JMP LBL[19] ! salta alla fine IF/ELSE
! — Ramo IF (DI[3]=ON, pezzo OK) —
7: LBL[10]
8: J P[20:APPR_OK ] 80% CNT100
9: L P[21:PLACE_OK] 150mm/sec FINE
10: DO[1]=OFF
11: L P[20:APPR_OK ] 150mm/sec FINE
! — Fine IF/ELSE —
12: LBL[19] ! entrambi i rami convergono qui
13: J P[1:HOME] 100% FINE
→ Salta a riga 7 (LBL[10])
→ Esegue righe 8–11 (ramo OK)
→ Riga 12 (LBL[19]) → continua
→ Continua con riga 2 (ramo NG)
→ Esegue righe 2–5
→ Riga 6: JMP LBL[19] → salta a riga 12
1.4 — SELECT: smistamento su più percorsi
Quando i percorsi sono più di due, usare una catena di IF diventa difficile da leggere. L’istruzione SELECT valuta il valore di un registro e salta all’etichetta corrispondente — è l’equivalente TP di un costrutto switch/case.
SELECT R[5]=1, JMP LBL[10] ! se R[5]=1 → zona A
=2, JMP LBL[20] ! se R[5]=2 → zona B
=3, JMP LBL[30] ! se R[5]=3 → zona C
ELSE, JMP LBL[99] ! nessun valore atteso → errore
LBL[10] ! ramo zona A
J P[10] 80% CNT100
L P[11] 150mm/sec FINE
JMP LBL[49] ! fine ramo A → convergenza
LBL[20] ! ramo zona B
J P[20] 80% CNT100
L P[21] 150mm/sec FINE
JMP LBL[49]
LBL[30] ! ramo zona C
J P[30] 80% CNT100
L P[31] 150mm/sec FINE
JMP LBL[49]
LBL[49] ! convergenza — tutti i rami arrivano qui
J P[1:HOME] 100% FINE
✅ ELSE nel SELECT: aggiungere sempre la clausola ELSE come ultima riga del SELECT — reindirizza a LBL[99] (gestione errore) se il registro ha un valore inatteso. Senza ELSE, se R[5]=4 il SELECT non salta da nessuna parte e prosegue con la riga successiva — comportamento imprevedibile.
1.5 — Pattern completo: ciclo con logica di sorting
Combinando tutto — loop, IF, contatori R[ ] e I/O — si costruisce il ciclo di sorting più comune nell’industria: presa dal nastro, lettura esito controllo qualità, deposito nella zona corretta, conteggio separato OK/NG.
! PROG: SORT_OK_NG
! Sorting pezzi OK→zona A, NG→zona B
! DI[2]=pezzo presente DI[3]=OK/NG DO[1]=pinza
! R[1]=cont.tot R[2]=cont.OK R[3]=cont.NG R[10]=limite
! ============================================
! — INIT —
1: R[1]=0
2: R[2]=0
3: R[3]=0
4: R[10]=20 ! lotto 20 pezzi
5: DO[1]=OFF
6: J P[1:HOME] 100% FINE
! — INIZIO CICLO —
7: LBL[1]
8: WAIT DI[2]=ON ! aspetta pezzo
! — PRESA —
9: J P[10:APPR_PICK] 80% CNT100
10: L P[11:PICK ] 150mm/sec FINE
11: DO[1]=ON
12: WAIT DI[1]=ON TIMEOUT, LBL[99]
13: L P[10:APPR_PICK] 150mm/sec FINE
! — LETTURA ESITO CONTROLLO QUALITÀ —
14: IF DI[3]=ON, JMP LBL[10] ! pezzo OK → zona A
! — RAMO NG —
15: J P[30:APPR_NG ] 80% CNT100
16: L P[31:PLACE_NG] 150mm/sec FINE
17: DO[1]=OFF
18: WAIT DI[1]=OFF TIMEOUT, LBL[99]
19: L P[30:APPR_NG ] 150mm/sec FINE
20: R[3]=R[3]+1 ! conta NG
21: JMP LBL[19]
! — RAMO OK —
22: LBL[10]
23: J P[20:APPR_OK ] 80% CNT100
24: L P[21:PLACE_OK] 150mm/sec FINE
25: DO[1]=OFF
26: WAIT DI[1]=OFF TIMEOUT, LBL[99]
27: L P[20:APPR_OK ] 150mm/sec FINE
28: R[2]=R[2]+1 ! conta OK
! — FINE IF/ELSE, AGGIORNA TOTALE E VERIFICA LOTTO —
29: LBL[19]
30: R[1]=R[1]+1 ! conta totale
31: IF R[1]<R[10], JMP LBL[1] ! lotto non finito → continua
! — FINE LOTTO —
32: J P[1:HOME] 100% FINE
33: DO[5]=ON ! segnala fine lotto
! — ERRORE TIMEOUT —
LBL[99]
34: J P[1:HOME] 100% FINE
35: DO[6]=ON ! segnala allarme
💻 2. Esempi guidati nel simulatore
Esempio A — IF su DI: routing manuale
- Insegna 3 punti: P[1]=home · P[2]=posizione sinistra (J1=–45°) · P[3]=posizione destra (J1=+45°).
- Scrivi il programma:
1: J P[1] 100% FINE
2: IF DI[1]=ON, JMP LBL[10]
3: J P[2] 50% FINE ! ramo DI[1]=OFF → sinistra
4: JMP LBL[19]
5: LBL[10]
6: J P[3] 50% FINE ! ramo DI[1]=ON → destra
7: LBL[19]
8: J P[1] 100% FINE - Esegui con DI[1]=OFF (non forzato). Il robot va a sinistra (P[2]).
- Forza DI[1]=ON nel pannello I/O. Riesegui. Il robot va a destra (P[3]).
- Esegui con STEP osservando quale riga viene saltata nei due casi.
Esempio B — SELECT su R[ ]: 3 destinazioni
- Insegna P[1]=home e 3 punti aggiuntivi a J1=–60°, J1=0°, J1=+60°.
- Scrivi il programma con SELECT su R[5]:
1: J P[1] 100% FINE
2: SELECT R[5]=1, JMP LBL[10]
=2, JMP LBL[20]
=3, JMP LBL[30]
ELSE, JMP LBL[99]
3: LBL[10]
4: J P[2] 50% FINE
5: JMP LBL[49]
6: LBL[20]
7: J P[3] 50% FINE
8: JMP LBL[49]
9: LBL[30]
10: J P[4] 50% FINE
11: JMP LBL[49]
12: LBL[49]
13: J P[1] 100% FINE
14: LBL[99]
15: DO[6]=ON - Prima di eseguire, imposta R[5]=1 dal pannello DATA. Esegui → va a P[2].
- Imposta R[5]=2. Esegui → va a P[3]. Imposta R[5]=3 → va a P[4].
- Imposta R[5]=7 (valore non previsto). Esegui → va a LBL[99], DO[6]=ON.
Esempio C — Sorting semplificato: pezzi OK/NG senza movimenti reali
- Scrivi il programma:
1: R[1]=0
2: R[2]=0
3: R[3]=0
4: R[10]=6
5: LBL[1]
6: WAIT 0.30(sec)
7: IF DI[3]=ON, JMP LBL[10]
8: R[3]=R[3]+1
9: JMP LBL[19]
10: LBL[10]
11: R[2]=R[2]+1
12: LBL[19]
13: R[1]=R[1]+1
14: IF R[1]<R[10], JMP LBL[1]
15: DO[5]=ON - Esegui con DI[3] sempre OFF: tutti i pezzi vanno in NG. Verifica R[1]=6, R[2]=0, R[3]=6.
- Azzera i registri. Esegui alternando DI[3] ON/OFF ogni ciclo: R[1]=6, R[2]=3, R[3]=3.
- Ora hai verificato la logica. Aggiungere i movimenti P[ ] diventa semplice sostituzione delle WAIT.
🏋️ 3. Esercizi
🟢 Esercizio 1 — Traccia di flusso (Facile)
Per ognuno dei seguenti casi, indica quali righe vengono eseguite e qual è il valore finale di R[1], dato il programma:
2: IF DI[1]=ON, JMP LBL[10]
3: R[1]=R[1]+5
4: JMP LBL[19]
5: LBL[10]
6: R[1]=R[1]+10
7: LBL[19]
8: R[1]=R[1]+1
Caso A: DI[1]=OFF. Righe eseguite: ?, valore finale R[1]: ?
Caso B: DI[1]=ON. Righe eseguite: ?, valore finale R[1]: ?
Verifica nel simulatore forzando DI[1] nei due stati.
🔵 Esercizio 2 — Sorting fisico a 2 vie nel simulatore (Medio)
Implementa una cella di sorting fisico a 2 destinazioni:
— P[1] = home · P[10]/P[11] = approccio/presa nastro
— P[20]/P[21] = approccio/deposito zona OK
— P[30]/P[31] = approccio/deposito zona NG
— DI[3]=ON → pezzo OK → zona OK · DI[3]=OFF → pezzo NG → zona NG
— Contatori R[1]=totale, R[2]=OK, R[3]=NG, R[10]=limite 8 pezzi
Insegna tutti i punti nel simulatore. Implementa il programma con la struttura IF/ELSE e il loop. Esegui 8 cicli alternando DI[3] per avere mix OK e NG. Verifica i contatori nel pannello DATA al termine.
🟠 Esercizio 3 — Smistamento a 3 vie con SELECT (Medio)
Un sistema di visione legge il colore di ogni pezzo e scrive il risultato in R[5]: 1=rosso, 2=verde, 3=blu, 0=errore lettura. Scrivi un programma che:
— Legge R[5] con SELECT e smista verso 3 zone deposito
— Se R[5]=0 (errore): porta il pezzo in una quarta zona “scarti” (P[40]/P[41]) e incrementa R[4:ERRORI]
— Conta separatamente R[1]=rossi, R[2]=verdi, R[3]=blu
— Ferma il ciclo dopo R[10]=12 pezzi totali (R[1]+R[2]+R[3]+R[4])
Implementa la sola logica (senza movimenti fisici — usa WAIT 0.3s al posto dei pick & place) e verifica i 4 contatori eseguendo con vari valori di R[5].
🟣 Esercizio 4 — Arresto produzione per NG consecutivi (Difficile)
Aggiungi al programma SORT_OK_NG di questa lezione una logica di sicurezza: se il robot rileva 3 pezzi NG consecutivi, deve fermare la produzione (non tornare a LBL[1]), segnalare DO[7]=ON e attendere un reset manuale (DI[5]=ON) prima di riprendere.
Implementazione suggerita: usa R[4:NG_CONSEC] che si incrementa a ogni NG e si azzera a ogni OK. Quando R[4]=3 esegui la procedura di arresto.
Scrivi il programma completo con questa logica integrata. Verifica nel simulatore: forza 3 NG consecutivi e osserva che il programma si ferma correttamente. Poi forza DI[5]=ON e verifica che il ciclo riprenda con R[4] azzerato.
❓ 4. Quiz di verifica
1. In TP non esiste la parola chiave ELSE. Come si realizza comunque una struttura IF/ELSE?
Mostra risposta
Con il pattern IF + doppio LBL: (1) IF condizione, JMP LBL[A] — se vera salta al ramo “then”; (2) righe del ramo “else” (eseguite se la condizione è falsa); (3) JMP LBL[Z] — salta alla fine, bypassando il ramo “then”; (4) LBL[A] — inizio ramo “then”; (5) righe del ramo “then”; (6) LBL[Z] — punto di convergenza di entrambi i rami. Il ramo “else” è quello immediatamente sotto l’IF (eseguito quando la condizione è falsa), il ramo “then” è raggiunto tramite salto (eseguito quando la condizione è vera).
2. Qual è la differenza tra IF R[1]=0, JMP LBL[10] e IF R[1]<>0, JMP LBL[10]?
Mostra risposta
IF R[1]=0 salta a LBL[10] solo quando R[1] è esattamente zero — è un test di uguaglianza. IF R[1]<>0 salta a LBL[10] quando R[1] è qualsiasi valore diverso da zero — è un test di disuguaglianza. Sono condizioni complementari: se una è vera l’altra è falsa. Nella pratica: IF R[1]=0 si usa per rilevare che un contatore è esaurito o che un flag è azzerato; IF R[1]<>0 si usa per rilevare che qualcosa è accaduto (il contatore ha contato almeno un evento, il flag è stato impostato).
3. Perché è importante aggiungere ELSE nel SELECT invece di lasciare solo i rami espliciti?
Mostra risposta
Perché senza ELSE, se il registro ha un valore non previsto (es. R[5]=0 in un SELECT che gestisce solo 1, 2, 3) il SELECT non salta da nessuna parte e il programma prosegue con la riga immediatamente successiva al blocco SELECT — che è tipicamente l’inizio del primo ramo. Il robot esegue quindi un ramo casuale senza che la condizione sia verificata. Con ELSE, JMP LBL[99] reindirizza il flusso alla gestione errore: il problema viene rilevato, segnalato e gestito in modo controllato invece di procedere con un’azione non prevista.
4. Nel programma SORT_OK_NG, la riga 30 (R[1]=R[1]+1) si trova dopo LBL[19] (convergenza IF/ELSE). Cosa succederebbe se fosse collocata dentro solo il ramo OK (riga 28)?
Mostra risposta
R[1] verrebbe incrementato solo quando il pezzo è OK — i pezzi NG non verrebbero contati nel totale. Il confronto IF R[1]<R[10] della riga 31 verificherebbe solo i pezzi OK: se il lotto fosse 20 e tutti i pezzi fossero NG, il ciclo non finirebbe mai (R[1] resterebbe a 0). Posizionare R[1]=R[1]+1 nella zona di convergenza (dopo LBL[19]) garantisce che il totale venga incrementato per ogni pezzo processato indipendentemente dal risultato OK/NG — comportamento corretto per un contatore di produzione.
5. Un programma ha LBL[10] in due punti diversi. Cosa fa il controller quando incontra JMP LBL[10]?
Mostra risposta
Il controller FANUC salta alla prima occorrenza di LBL[10] nel programma — quella con il numero di riga più basso. La seconda occorrenza è ignorata ai fini del salto. Questo comportamento è deterministico ma pericoloso: se il programmatore credeva di saltare alla seconda LBL[10] (più avanti nel codice) il programma si comporta in modo completamente diverso dall’intenzione. Per questo la numerazione delle etichette deve essere unica nell’intero programma — non riusare mai lo stesso numero LBL. Se si ha bisogno di molte etichette, usare i range dedicati per funzione (1–9 per loop, 10–49 per IF/ELSE, ecc.) aiuta a evitare collisioni.
📚 Riepilogo della lezione
LBL[n]= etichetta (marcatore, non esegue nulla) ·JMP LBL[n]= salto incondizionatoIF condizione, JMP LBL[n]— salta se vera, continua se falsa · condizioni su DI, DO, R[ ]- IF/ELSE in TP = IF + ramo else + JMP LBL[fine] + LBL[then] + ramo then + LBL[fine]
SELECT R[n]=val, JMP LBL[x]— smistamento multiplo · sempre aggiungereELSE, JMP LBL[99]- Convenzione LBL: 1–9 loop · 10–49 IF/ELSE · 50–89 sezioni · 90–99 errori — numeri unici nel programma
- Pattern sorting: presa → IF/SELECT su DI/R[ ] → rami deposito → convergenza → contatori → loop
Prossima lezione →
Lezione 13 — Cicli e loop
FOR implicito con LBL/JMP, loop infiniti controllati, uscita anticipata dal loop.
Integrazione con contatori R[ ] e condizioni di uscita multiple.
© Prof. D. Bertolino · Robotica Industriale FANUC ER-4iA · Lezione 12 di 18