RI Lezione 11
🤖 Robotica Industriale · IV Anno · Lezione 11
Registri R[ ] 🔢
Variabili numeriche · Operazioni aritmetiche · Contatori · Offset dinamici su posizioni
📦 Il programma inizia a ragionare. Finora il robot eseguiva sequenze fisse: sempre gli stessi punti, sempre la stessa logica. Con i registri R[ ] il programma può ricordare valori numerici, fare calcoli, contare pezzi, e modificare dinamicamente le posizioni durante il ciclo. È il primo passo verso la programmazione flessibile.
🎯 Obiettivi della lezione
- Capire cosa sono i registri R[ ] e come si differenziano dai registri di posizione P[ ]
- Assegnare valori e eseguire operazioni aritmetiche sui registri
- Usare R[ ] come contatori di ciclo e pezzi prodotti
- Leggere e scrivere R[ ] in relazione agli I/O e alle condizioni IF
- Applicare R[ ] come offset sulle coordinate di posizione (cenni a PR[ ])
📖 1. Teoria
1.1 — Cosa sono i registri R[ ]
I registri R[ ] sono variabili numeriche del controller FANUC. Ogni R[ ] può contenere un numero intero o in virgola mobile, conservato in memoria anche dopo lo spegnimento del controller. Sono disponibili da R[1] a R[200] (o più, a seconda della configurazione).
| Caratteristica | R[ ] — Registro numerico | P[ ] — Registro posizione |
|---|---|---|
| Contenuto | Un numero (intero o reale) | X, Y, Z, W, P, R + Config |
| Uso tipico | Contatori, flag, offset, calcoli | Posizioni TCP del robot |
| Range indici | R[1] … R[200] | P[1] … P[999] |
| Persistenza | Conservato dopo spegnimento | Conservato dopo spegnimento |
| Operazioni | +, –, ×, ÷, confronto, assegnazione | Solo teach / modifica coordinate |
Come per i P[ ], è buona pratica assegnare un commento descrittivo a ogni R[ ] usato nel programma (max 16 caratteri nell’editor DATA del controller):
R[ 2:PEZZI_OK ] = 0
R[ 3:OFFSET_Y ] = 0.000
R[ 4:FLAG_ERRORE ] = 0
1.2 — Assegnazione e operazioni aritmetiche
L’istruzione di assegnazione ha la forma R[n] = espressione. L’espressione può essere un valore costante, un altro registro, o un’operazione aritmetica:
R[1] = 0 ! azzera R[1]
R[1] = 100 ! assegna 100
R[2] = R[1] ! copia R[1] in R[2]
! Operazioni aritmetiche
R[1] = R[1] + 1 ! incrementa di 1 (contatore)
R[1] = R[1] – 1 ! decrementa di 1
R[3] = R[1] * 25.4 ! moltiplica (es. pollici → mm)
R[4] = R[2] / 2 ! divide per 2
R[5] = R[1] + R[2] ! somma di due registri
| Operatore | Operazione | Esempio | Risultato |
|---|---|---|---|
| + | Addizione | R[1]=3, R[2]=R[1]+7 | R[2] = 10 |
| – | Sottrazione | R[1]=10, R[2]=R[1]-4 | R[2] = 6 |
| * | Moltiplicazione | R[1]=5, R[2]=R[1]*3 | R[2] = 15 |
| / | Divisione | R[1]=10, R[2]=R[1]/4 | R[2] = 2.5 |
| MOD | Modulo (resto) | R[1]=10, R[2]=R[1] MOD 3 | R[2] = 1 |
| DIV | Divisione intera | R[1]=10, R[2]=R[1] DIV 3 | R[2] = 3 |
1.3 — R[ ] come contatori
Il caso d’uso più frequente di R[ ] è il contatore di ciclo: un registro che viene incrementato a ogni pezzo prodotto e confrontato con un limite per decidere quando fermarsi o segnalare.
! Init: azzera contatore all’avvio
1: R[1:CONTATORE]=0
2: R[2:LIMITE ]=10 ! lotto di 10 pezzi
! Corpo ciclo
3: LBL[1]
4: J P[1:HOME] 100% FINE
! … ciclo pick & place …
5: J P[1:HOME] 100% FINE
! Incrementa e verifica
6: R[1]=R[1]+1 ! pezzo completato
7: IF R[1]<R[2], JMP LBL[1] ! se contatore < limite → continua
! Lotto completato
8: DO[5]=ON ! segnala fine lotto al PLC
Il confronto tra registri in un’istruzione IF usa gli operatori standard: =, <>, <, >, <=, >=. La struttura completa di IF/JMP/LBL viene approfondita nella Lezione 12 — qui vediamo solo il pattern del contatore.
1.4 — R[ ] come offset di posizione
Un uso avanzato dei registri è la modifica dinamica delle posizioni durante il ciclo. Supponiamo di avere un vassoio con 5 colonne di pezzi distanziate di 50 mm in Y: invece di insegnare 5 punti di presa separati, insegniamo uno solo e spostiamo Y di un offset calcolato dal registro.
Il meccanismo TP per applicare un offset a una posizione usa i Registri di Posizione PR[ ] — che verranno approfonditi nella Lezione 15. Per ora vediamo il concetto di base: come si calcola l’offset con R[ ] in preparazione a quel passo.
! 5 colonne, passo 50 mm in Y, prima colonna a Y=0
1: R[1:COL ]=0 ! indice colonna corrente (0..4)
2: R[2:PASSO ]=50 ! passo colonne in mm
3: LBL[1]
! … presa colonna R[1] (offset = R[1]*R[2]) …
4: R[3:OFFSET_Y]=R[1]*R[2] ! offset Y per la colonna corrente
! … usa R[3] per modificare P[11].Y prima del movimento …
! (implementazione completa con PR[] in Lezione 15)
5: R[1]=R[1]+1 ! passa alla colonna successiva
6: IF R[1]<5, JMP LBL[1] ! se non finito → continua
1.5 — Convenzioni di nomenclatura per R[ ]
Come per i P[ ], organizzare i registri R[ ] per blocchi funzionali evita confusione in programmi complessi:
| Range R[ ] | Funzione consigliata | Esempi |
|---|---|---|
| R[1]–R[9] | Contatori principali | CONTATORE, PEZZI_OK, PEZZI_NG |
| R[10]–R[19] | Limiti e soglie | LIMITE_LOTTO, MAX_ERRORI, SOGLIA |
| R[20]–R[29] | Offset geometrici | OFFSET_X, OFFSET_Y, OFFSET_Z, PASSO |
| R[30]–R[39] | Flag e stati | FLAG_ERRORE, STATO_PINZA, TIPO_PEZZO |
| R[90]–R[99] | Variabili temporanee | TEMP1, TEMP2 — usati e liberati nel ciclo |
📌 Attenzione alla persistenza: i registri R[ ] conservano il loro valore dopo lo spegnimento e tra un ciclo e l’altro. Se R[1] è un contatore di pezzi e il programma riparte senza azzerarlo, il contatore partirà dal valore dell’ultima esecuzione. Buona pratica: azzerare esplicitamente tutti i registri all’inizio del programma, oppure usare un flag di “prima esecuzione” per decidere se azzerare o recuperare il valore precedente.
💻 2. Esempi guidati nel simulatore
Esempio A — Calcoli di base e verifica nel pannello DATA
- Scrivi il programma:
1: R[1]=0
2: R[2]=0
3: R[1]=10
4: R[2]=R[1]+5
5: R[3]=R[1]*R[2]
6: R[4]=R[3] DIV R[1]
7: R[5]=R[3] MOD R[1]
8: R[6]=R[1]-R[2] - Esegui con RUN. Prima di eseguire, calcola mentalmente i valori attesi di R[1]…R[6].
- Apri il pannello DATA → Registers nel simulatore. Verifica che i valori corrispondano alle tue previsioni.
- Risposte attese: R[1]=10, R[2]=15, R[3]=150, R[4]=15, R[5]=0, R[6]=–5.
- Modifica R[1]=7 prima dell’esecuzione (dal pannello DATA, non dal programma) e riesegui. Quali valori cambiano?
Esempio B — Contatore con uscita DO al completamento lotto
- Scrivi il seguente programma che simula un contatore lotto senza i movimenti del robot (solo logica):
! Contatore lotto — 5 cicli poi segnale fine lotto
1: R[1:CONT ]=0
2: R[2:LIMITE]=5
3: DO[4]=OFF
4: LBL[1]
5: WAIT 0.50(sec)
! (qui andrebbe il ciclo pick&place)
6: R[1]=R[1]+1
7: IF R[1]<R[2], JMP LBL[1]
8: DO[4]=ON
! fine lotto: DO[4] segnala al PLC - Esegui. Il programma cicla 5 volte (con pausa 0.5s simulata) poi accende DO[4].
- Verifica nel pannello DATA che R[1] valga 5 al termine.
- Cambia R[2]=3 (dal pannello DATA prima di eseguire) e ri-esegui: ora il lotto si completa dopo 3 cicli.
- Prova a cambiare la riga 7 con
IF R[1]<=R[2]: quanti cicli esegue adesso? Perché?
Esempio C — Calcolo offset Y per vassoio a 4 colonne
- Scrivi il programma:
! Calcolo offset Y per 4 colonne, passo 60mm
1: R[20:PASSO ]=60
2: R[21:COL ]=0
3: LBL[1]
4: R[22:OFF_Y ]=R[21]*R[20]
! R[22] contiene ora l’offset Y della colonna R[21]
5: WAIT 0.30(sec)
6: R[21]=R[21]+1
7: IF R[21]<4, JMP LBL[1] - Esegui con STEP. Dopo ogni iterazione osserva nel pannello DATA il valore di R[21] e R[22].
- I valori di R[22] nelle 4 iterazioni devono essere: 0, 60, 120, 180 — gli offset Y delle 4 colonne.
- Modifica R[20]=40 e riesegui: gli offset diventano 0, 40, 80, 120.
🏋️ 3. Esercizi
🟢 Esercizio 1 — Traccia manuale di un programma (Facile)
Senza eseguire nel simulatore, calcola manualmente i valori finali di tutti i registri dopo l’esecuzione di questo programma:
2: R[2]=3
3: R[3]=R[1]*R[2]
4: R[4]=R[3] DIV R[2]
5: R[5]=R[3] MOD R[2]
6: R[1]=R[1]+R[2]
7: R[2]=R[1]-R[4]
8: R[6]=R[2]*R[5]
Compila la tabella: R[1]=?, R[2]=?, R[3]=?, R[4]=?, R[5]=?, R[6]=?
Poi verifica nel simulatore. Se il tuo calcolo differisce, identifica a quale riga hai sbagliato.
🔵 Esercizio 2 — Contatore pezzi OK / NG con I/O (Medio)
Scrivi un programma che simula una stazione di controllo qualità:
— R[1:PEZZI_TOT]: conta tutti i pezzi processati
— R[2:PEZZI_OK]: conta i pezzi buoni (DI[3]=ON → pezzo OK)
— R[3:PEZZI_NG]: conta i pezzi difettosi (DI[3]=OFF → pezzo NG)
— R[10:LIMITE]: 8 pezzi totali poi fine ciclo
Per ogni “pezzo” (simulato con WAIT 0.5s): incrementa R[1], leggi DI[3] e incrementa R[2] o R[3] di conseguenza. Alla fine del lotto: segnala DO[5]=ON se R[3]=0 (lotto perfetto) oppure DO[6]=ON se R[3]>0 (lotto con difetti).
Usa la forma IF DI[3]=ON, JMP LBL[n] per separare i due percorsi. Esegui forzando DI[3] a ON e OFF alternativamente dal pannello I/O.
🟠 Esercizio 3 — Integra il contatore nel pick & place (Medio)
Prendi il programma PICK_PLACE_IO della Lezione 10 e aggiungi la gestione del contatore pezzi:
— All’inizio: R[1:CONT]=0, R[2:LIMITE]=10
— Dopo ogni deposito completato (dopo la risalita da P[20]): R[1]=R[1]+1
— Se R[1] < R[2]: torna all’inizio del ciclo (JMP LBL[1]) senza tornare in home
— Se R[1] = R[2]: torna in home, accendi DO[5] (fine lotto), fermati
Struttura il programma così che LBL[1] sia posizionato dopo la riga di verifica DI[2]=ON (ogni ciclo ricomincia aspettando il pezzo). Esegui nel simulatore forzando DI[2]=ON e DI[1] ad ogni ciclo.
🟣 Esercizio 4 — Piano di distribuzione su griglia 3×3 (Difficile)
Un vassoio di deposito è organizzato come una griglia 3×3 (9 celle). La cella (0,0) è all’angolo in alto a sinistra, le righe avanzano in Y (+40 mm) e le colonne avanzano in X (+40 mm). Il punto di riferimento (0,0) è X=200, Y=0, Z=30.
Scrivi il programma di calcolo (solo R[ ], nessun movimento fisico) che:
— Usa R[30] come indice riga (0..2) e R[31] come indice colonna (0..2)
— Calcola R[32]=X effettivo e R[33]=Y effettivo della cella corrente
— Scorre tutte le 9 celle in ordine (riga per riga) usando due loop annidati con LBL
— Visualizza (con WAIT 0.2s tra una cella e l’altra) i valori X,Y calcolati nel pannello DATA
Valori attesi: (200,0), (240,0), (280,0), (200,40), (240,40), (280,40), (200,80), (240,80), (280,80).
❓ 4. Quiz di verifica
1. Qual è la differenza tra R[1]=R[1] DIV 3 e R[1]=R[1] / 3 quando R[1]=10?
Mostra risposta
Con R[1]=10: R[1]=R[1]/3 dà R[1]=3.333… (divisione reale con decimali); R[1]=R[1] DIV 3 dà R[1]=3 (divisione intera, scarta il resto). L’operatore DIV è utile quando si deve lavorare con indici interi — ad esempio per calcolare il numero di riga in una griglia dato un indice lineare: riga = indice DIV numero_colonne. L’operatore MOD è il complemento: fornisce il resto, utile per calcolare il numero di colonna: colonna = indice MOD numero_colonne.
2. Un robot lavora su turni di 8 ore. R[1] è il contatore pezzi prodotti. Cosa succede al valore di R[1] quando si spegne e si riaccende il controller?
Mostra risposta
R[1] conserva il suo valore: i registri R[ ] sono non volatili — salvati in memoria SRAM con batteria tampone (o flash). Se il turno precedente ha prodotto 847 pezzi e R[1]=847, alla riaccensione R[1] sarà ancora 847. Questo è un vantaggio (si possono recuperare statistiche di produzione) ma anche un rischio: se il programma non azzera R[1] all’inizio del turno, il contatore ripartirà da 847 invece che da 0, falsando il conteggio del nuovo turno. La buona pratica è avere un’istruzione R[1]=0 esplicita all’inizio del ciclo di turno, oppure salvare R[1] in un registro di archivio prima di azzerarlo.
3. Nel pattern del contatore (sezione 1.3), perché il limite è memorizzato in R[2] invece di scriverlo direttamente nell’IF come IF R[1]<10?
Mostra risposta
Perché usando R[2] come limite, il valore può essere modificato senza cambiare il codice del programma: un operatore o un sistema di supervisione può scrivere un nuovo valore in R[2] dal pannello DATA del controller (o via rete/PLC) per cambiare la dimensione del lotto senza accedere all’editor TP. Con IF R[1]<10 il numero 10 è un letterale nel codice — per cambiarlo bisogna aprire il programma, trovare la riga giusta e modificarla. In produzione, i parametri di lotto cambiano frequentemente (10 pezzi oggi, 50 domani) e poterli modificare senza toccare il codice è una pratica essenziale di flessibilità industriale.
4. Dato R[1]=7 e R[2]=3, calcola: R[3]=R[1] MOD R[2], R[4]=R[1] DIV R[2], R[5]=R[4]*R[2]+R[3]. Cosa osservi in R[5]?
Mostra risposta
R[3] = 7 MOD 3 = 1 (resto della divisione 7÷3). R[4] = 7 DIV 3 = 2 (quoziente intero). R[5] = 2×3+1 = 7. R[5] è uguale a R[1]: questa è la proprietà fondamentale della divisione intera — dividendo = quoziente × divisore + resto. In robotica si usa per convertire un indice lineare (0..N-1) nelle coordinate riga/colonna di una griglia: riga=indice DIV num_col, colonna=indice MOD num_col. La verifica R[4]*R[2]+R[3]=R[1] conferma che il calcolo è corretto.
5. Perché conviene memorizzare il limite del lotto in R[2] già all’inizio del programma piuttosto che a metà ciclo?
Mostra risposta
Perché in un ciclo continuo il programma torna su LBL[1] e ri-esegue le righe di inizializzazione solo se le si colloca prima di LBL[1]. Se R[2] viene assegnato dopo LBL[1] (all’interno del loop), viene riscritto a ogni ciclo — il che è inutile ma non pericoloso se il valore è costante. Se invece si vuole che l’operatore possa modificare R[2] durante il ciclo (cambio lotto a metà produzione), conviene che R[2] non venga riscritto a ogni giro dal programma: va assegnato una sola volta prima del loop, e poi lasciato libero per eventuali modifiche esterne. La struttura corretta è quindi: init (R[1]=0, R[2]=valore) → LBL[1] → ciclo → incremento/verifica → JMP LBL[1].
📚 Riepilogo della lezione
- R[ ] sono variabili numeriche persistenti (R[1]…R[200]) — sopravvivono allo spegnimento
- Operatori:
+ – * / DIV MOD— DIV e MOD per aritmetica intera su indici e griglie - Pattern contatore: R[n]=0 → LBL[1] → … → R[n]=R[n]+1 → IF R[n]<limite, JMP LBL[1]
- I limiti vanno in registri (R[2]=10) non hardcoded nell’IF — permette modifica senza toccare il codice
- R[ ] come preparatori di offset geometrici per griglie e vassoi — applicazione pratica in Lezione 15 con PR[ ]
- Convenzione: blocchi numerici per funzione — contatori 1–9, limiti 10–19, offset 20–29, flag 30–39
Prossima lezione →
Lezione 12 — Strutture di controllo
IF/ELSE, JMP, LBL, SELECT: logica condizionale completa.
Decisioni basate su DI, R[ ] e stati del robot.
© Prof. D. Bertolino · Robotica Industriale FANUC ER-4iA · Lezione 11 di 18