Lezione 18 di 18

RI Lezione 18

🤖 Robotica Industriale · V Anno · Lezione 18 — Progetto Finale

Cella Robotizzata Completa 🏆

Integrazione di tutto il percorso · Architettura modulare · Griglia con offset · Ottimizzazione traiettorie · Collaudo formale

⏱️ Durata stimata: 180–240 min (2–3 sessioni) 📊 Livello: Avanzato — progetto integrativo 🔧 Strumenti: Simulatore web · Editor TP · tutti gli strumenti del corso 📋 Prerequisiti: Lezioni 01–17 — tutto il percorso

🏆 Questo è il progetto finale del percorso triennale. Non ci sono nuove istruzioni da imparare — si tratta di integrare tutto: cinematica, sistemi di riferimento, movimenti J/L, I/O, registri, strutture di controllo, loop, offset su griglia, ottimizzazione traiettorie e architettura modulare. La cella progettata in questa lezione è un impianto industriale realistico che potrebbe girare sul robot fisico in laboratorio.

🎯 Obiettivi del progetto finale

  • Leggere e interpretare le specifiche di una cella industriale complessa
  • Progettare l’architettura modulare completa prima di scrivere una riga di codice
  • Implementare tutti i sotto-programmi con interfacce documentate
  • Applicare la metodologia di ottimizzazione traiettorie (CNT, velocità, ACC)
  • Eseguire il collaudo formale a 5 fasi e documentarne i risultati
  • Discutere le scelte progettuali in un colloquio tecnico simulato

📐 1. Specifiche della cella: “CELL_FINAL”

1.1 — Descrizione impianto

La cella gestisce il fine-linea di un processo di assemblaggio. Un nastro trasportatore porta pezzi già assemblati in uscita dalla linea. Un sistema di visione (già integrato, scrive R[5]) classifica ogni pezzo come conforme tipo 1, conforme tipo 2 o non conforme. Il robot FANUC ER-4iA deve prelevare ogni pezzo, depositare i conformi su due vassoi (tipo 1 → vassoio A 3×3, tipo 2 → vassoio B 3×3) e inviare i non conformi al bin di scarto. Quando un vassoio è pieno (9 pezzi), il robot lo segnala al PLC e attende il cambio vassoio prima di continuare.

1.2 — Layout e coordinate TCP

Zona TCP riferimento (0,0) Griglia Passo X Passo Y Config
Nastro presa X=320, Y=0, Z=55 — — — NUT,0,0,0
Vassoio A (tipo 1) X=–180, Y=160, Z=45 3×3 55 mm 55 mm NUT,0,0,0
Vassoio B (tipo 2) X=180, Y=160, Z=45 3×3 55 mm 55 mm NUT,0,0,0
Bin scarti X=0, Y=–220, Z=60 — — — NUT,0,0,0
Home J1=0°, J2=0°, J3=0°, J4=0°, J5=–90°, J6=0° Posizione asse — teach e salva
Altezza approccio: Z+130 mm su tutti i punti di lavoro. Orientamento TCP: W=180°, P=0°, R=0° per tutti i punti.

1.3 — Segnali I/O

Ingressi DI
DIFunzione
DI[1]Conferma stato pinza (reed)
DI[2]Pezzo presente sul nastro
DI[3]Start ciclo da PLC
DI[4]Stop ciclo da PLC
DI[5]Vassoio A sostituito (ok per continuare)
DI[6]Vassoio B sostituito
DI[7]Reset allarme generico
Uscite DO
DOFunzione
DO[1]Pinza (ON=chiusa)
DO[2]Robot pronto / in attesa start
DO[3]Ciclo attivo
DO[4]Vassoio A pieno — richiesta cambio
DO[5]Vassoio B pieno — richiesta cambio
DO[6]Allarme pinza (timeout)
DO[7]Allarme generico

1.4 — Registri R[ ] del progetto

Registro Nome Funzione Init
R[1]CONT_TOTPezzi totali processati0
R[2]CONT_APezzi sul vassoio A (corrente)0
R[3]CONT_BPezzi sul vassoio B (corrente)0
R[4]CONT_SCScarti totali0
R[5]TIPO_PEZZOTipo da sistema visione (1/2/0)—
R[10]CAP_VASSCapacità vassoio (fisso: 9)9
R[20]RIG_AIndice riga vassoio A (0–2)0
R[21]COL_AIndice colonna vassoio A (0–2)0
R[22]RIG_BIndice riga vassoio B (0–2)0
R[23]COL_BIndice colonna vassoio B (0–2)0
R[50]PARAM_DESTParametro destinazione per CALL DEPOSITA—
R[56]FLAG_ERRFlag errore sotto-programmi (0=ok)—
R[90]LAST_ERRCodice ultimo errore (diagnostica)0

1.5 — Punti P[ ] e PR[ ] da insegnare

P[ ] Nome Descrizione Come si ottiene
P[1]HOMEPosizione di home (joint)Teach diretto
P[10]APPR_PICKApproccio nastro (Z+130)Derivato da P[11]
P[11]PICKPunto presa sul nastroTeach diretto
P[20]APPR_A0Approccio riferimento vassoio A cella(0,0)Derivato da P[21]
P[21]PLACE_A0Riferimento vassoio A cella(0,0)Teach diretto
P[30]APPR_B0Approccio riferimento vassoio B cella(0,0)Derivato da P[31]
P[31]PLACE_B0Riferimento vassoio B cella(0,0)Teach diretto
P[40]APPR_SCApproccio bin scartiDerivato da P[41]
P[41]PLACE_SCPunto deposito scartiTeach diretto
PR[1] = offset cella vassoio A (calcolato a runtime) · PR[2] = offset cella vassoio B · PR[99] = buffer LPOS per debug.

🗂️ 2. Architettura del progetto

Struttura modulare CELL_FINAL — 6 programmi
CELL_MAIN
Orchestrazione: init → attesa start → loop ciclo → SELECT tipo → CALL deposita → aggiorna contatori → check vassoio pieno → stop/fine
↓ CALL
PRESA
Approccio → pick → risalita
OUT: R[56] flag errore
DEPOSITA
IN: R[50]=dest
Offset PR + place + risalita
OUT: R[56] flag errore
INIT_CELLA
Azzera R[ ], apri pinza
Spegni tutti DO
Vai in HOME
CAMBIA_VASSOIO
IN: R[50]=1 (vass.A) o 2 (vass.B)
Accende DO[4]/DO[5], aspetta DI[5]/DI[6]
Azzera contatore e indici griglia
STOP_PULITO
Home, DO[3]=OFF
Spegni segnali attivi
Torna a LBL[5] (attesa start)
Profondità massima di annidamento: 2 livelli (CELL_MAIN → sotto-programma). Nessun sotto-programma chiama altri sotto-programmi — architettura piatta e leggibile.

💾 3. Codice completo

CELL_MAIN

! ============================================================
! CELL_MAIN — Orchestrazione cella fine-linea
! ============================================================

1: CALL INIT_CELLA

! — Attesa start —
2: LBL[5]
3: DO[2]=ON
4: WAIT DI[3]=ON
5: DO[2]=OFF · DO[3]=ON

! — Inizio ciclo —
6: LBL[1]
7: IF DI[4]=ON, JMP LBL[9]                    ! stop richiesto?

! — Presa —
8: WAIT DI[2]=ON                                ! pezzo presente
9: CALL PRESA
10: IF R[56]=1, JMP LBL[99]                    ! check errore pinza

! — Routing per tipo —
11: SELECT R[5]=1, JMP LBL[10]                  ! tipo 1 → vassoio A
         =2, JMP LBL[20]                                  ! tipo 2 → vassoio B
         ELSE, JMP LBL[30]                                  ! non conforme → scarti

! — Ramo A —
12: LBL[10]
13: R[50]=1
14: CALL DEPOSITA
15: IF R[56]=1, JMP LBL[99]
16: R[2]=R[2]+1
17: IF R[2]>=R[10], CALL CAMBIA_VASSOIO        ! vassoio A pieno?
18: JMP LBL[49]

! — Ramo B —
19: LBL[20]
20: R[50]=2
21: CALL DEPOSITA
22: IF R[56]=1, JMP LBL[99]
23: R[3]=R[3]+1
24: IF R[3]>=R[10], CALL CAMBIA_VASSOIO        ! vassoio B pieno?
25: JMP LBL[49]

! — Ramo scarti —
26: LBL[30]
27: R[50]=0
28: CALL DEPOSITA
29: IF R[56]=1, JMP LBL[99]
30: R[4]=R[4]+1

! — Convergenza —
31: LBL[49]
32: R[1]=R[1]+1
33: JMP LBL[1]

! — Stop pulito —
34: LBL[9]
35: CALL STOP_PULITO
36: JMP LBL[5]

! — Errore —
37: LBL[99]
38: DO[1]=OFF
39: J P[1:HOME] 100% FINE
40: DO[3]=OFF · DO[6]=ON
41: R[90]=R[56]                                    ! salva codice errore
! [attende intervento tecnico — non ritorna]

PRESA

! PRESA — preleva pezzo dal nastro
! IN: nessun parametro
! OUT: R[56]=0 ok · R[56]=1 timeout chiusura pinza

1: R[56]=0
2: ACC 100
3: J P[10:APPR_PICK] 80% CNT50
4: L P[11:PICK ] 120mm/sec FINE
5: DO[1]=ON
6: WAIT DI[1]=ON TIMEOUT, LBL[99]
7: ACC 40                                        ! dolce con pezzo
8: L P[10:APPR_PICK] 250mm/sec CNT80
! fine — ritorno ok

9: LBL[99]                                        ! timeout pinza
10: R[56]=1
! fine sotto-programma — chiamante gestisce errore

DEPOSITA

! DEPOSITA — deposita pezzo nella destinazione indicata
! IN: R[50]=1 vassoio A · R[50]=2 vassoio B · R[50]=0 scarti
! OUT: R[56]=0 ok · R[56]=1 timeout apertura pinza
! Aggiorna PR[1] o PR[2] con offset cella corrente

1: R[56]=0
2: SELECT R[50]=1, JMP LBL[10]
         =2, JMP LBL[20]
         ELSE, JMP LBL[30]

! — Vassoio A —
3: LBL[10]
4: PR[1,1]=0 · PR[1,2]=0 · PR[1,3]=0
5: PR[1,4]=0 · PR[1,5]=0 · PR[1,6]=0
6: PR[1,1]=R[21]*55                            ! offset X = colonna × 55mm
7: PR[1,2]=R[20]*55                            ! offset Y = riga × 55mm
8: ACC 100
9: J P[20:APPR_A0] 80% CNT50 OFFSET, PR[1]
10: L P[21:PLACE_A0] 120mm/sec FINE OFFSET, PR[1]
11: DO[1]=OFF
12: WAIT DI[1]=OFF TIMEOUT, LBL[98]
13: ACC 100
14: L P[20:APPR_A0] 300mm/sec CNT80 OFFSET, PR[1]
! avanza indice cella vassoio A
15: R[21]=R[21]+1
16: IF R[21]<3, JMP LBL[49]                      ! ancora colonne in questa riga
17: R[21]=0                                        ! reset colonna, avanza riga
18: R[20]=R[20]+1
19: JMP LBL[49]

! — Vassoio B (identico ad A con PR[2], R[22], R[23], P[30]/P[31]) —
20: LBL[20]
21: PR[2,1]=0 · PR[2,2]=0 · PR[2,3]=0
22: PR[2,4]=0 · PR[2,5]=0 · PR[2,6]=0
23: PR[2,1]=R[23]*55
24: PR[2,2]=R[22]*55
25: J P[30:APPR_B0] 80% CNT50 OFFSET, PR[2]
26: L P[31:PLACE_B0] 120mm/sec FINE OFFSET, PR[2]
27: DO[1]=OFF
28: WAIT DI[1]=OFF TIMEOUT, LBL[98]
29: L P[30:APPR_B0] 300mm/sec CNT80 OFFSET, PR[2]
30: R[23]=R[23]+1
31: IF R[23]<3, JMP LBL[49]
32: R[23]=0 · R[22]=R[22]+1
33: JMP LBL[49]

! — Scarti —
34: LBL[30]
35: J P[40:APPR_SC] 80% CNT80
36: L P[41:PLACE_SC] 120mm/sec FINE
37: DO[1]=OFF
38: WAIT DI[1]=OFF TIMEOUT, LBL[98]
39: L P[40:APPR_SC] 300mm/sec CNT100

! — Convergenza —
40: LBL[49]
41: R[56]=0
! fine sotto-programma — ritorno ok

42: LBL[98]                                        ! timeout apertura pinza
43: R[56]=1
! fine sotto-programma — chiamante gestisce errore

CAMBIA_VASSOIO

! CAMBIA_VASSOIO — segnala vassoio pieno, attende sostituzione
! IN: R[50]=1 vassoio A · R[50]=2 vassoio B
! OUT: contatore vassoio azzerato · indici griglia azzerati

1: J P[1:HOME] 100% FINE                      ! in home per sicurezza operatore
2: SELECT R[50]=1, JMP LBL[10]
         ELSE, JMP LBL[20]

3: LBL[10]
4: DO[4]=ON                                        ! segnala vassoio A pieno
5: WAIT DI[5]=ON                                  ! attende conferma sostituzione
6: DO[4]=OFF
7: R[2]=0 · R[20]=0 · R[21]=0                   ! azzera contatore e indici A
8: JMP LBL[49]

9: LBL[20]
10: DO[5]=ON
11: WAIT DI[6]=ON
12: DO[5]=OFF
13: R[3]=0 · R[22]=0 · R[23]=0

14: LBL[49]
! fine — ritorno al ciclo

INIT_CELLA e STOP_PULITO

! INIT_CELLA — inizializzazione completa cella
1: R[1]=0 · R[2]=0 · R[3]=0 · R[4]=0
2: R[10]=9 · R[20]=0 · R[21]=0 · R[22]=0 · R[23]=0
3: R[90]=0
4: DO[1]=OFF · DO[3]=OFF · DO[4]=OFF · DO[5]=OFF · DO[6]=OFF · DO[7]=OFF
5: J P[1:HOME] 100% FINE

! STOP_PULITO — fine ciclo ordinato
1: J P[1:HOME] 100% FINE
2: DO[3]=OFF
! fine — CELL_MAIN torna a LBL[5]

🔬 4. Procedura di collaudo formale

T1 — Teach
& Review
Insegna tutti i punti P[ ] nel simulatore. Prima del primo RUN: verifica nel pannello DATA che nessun P[ ] sia “uninitialized”. Leggi ogni punto con LPOS e confronta con le coordinate delle specifiche. Tolleranza accettabile: ±5 mm (simulatore).
T2 — STEP
10%
Esegui CELL_MAIN riga per riga con override 10%. Forza manualmente DI[3]=ON (start), DI[2]=ON (pezzo), DI[1]=ON/OFF (pinza). Verifica che ad ogni CALL il controller entri nel sotto-programma corretto. Testa il ramo R[5]=1 per intero prima di procedere.
T3 — RUN
3 rami
Esegui 3 cicli completi a 30%: R[5]=1 → verifica R[2]=1 e cella (0,0) di vassoio A occupata. R[5]=2 → verifica R[3]=1. R[5]=0 → verifica R[4]=1. Verifica invariante: R[2]+R[3]+R[4]=R[1] dopo ogni ciclo.
T4 — Test
griglia 9
Esegui 9 cicli consecutivi tutti con R[5]=1 a 50%. Verifica che le 9 celle del vassoio A vengano visitate nell’ordine corretto: (0,0)→(0,1)→(0,2)→(1,0)→…→(2,2). All’ultimo pezzo: DO[4] deve accendersi e il programma deve aspettare DI[5]=ON. Forza DI[5]=ON: R[2] deve azzerarsi e gli indici R[20], R[21] tornare a 0.
T5 — Test
errori
Test A: forza DI[4]=ON durante un ciclo → il robot deve completare il pezzo e fermarsi in home con DO[3]=OFF. Test B: simula timeout pinza (non rispondere al WAIT DI[1]=ON) → R[56]=1 → CELL_MAIN deve saltare a LBL[99], andare in home, accendere DO[6]. Verifica che R[90] contenga il codice errore corretto.

🎓 5. Colloquio tecnico — domande di discussione

Il progetto finale si conclude con un colloquio tecnico simulato. Per ciascuna delle domande seguenti, prepara una risposta che faccia riferimento a scelte specifiche del progetto CELL_FINAL.

D1. Perché DEPOSITA aggiorna gli indici di griglia (R[20], R[21]) invece di farlo nel CELL_MAIN?

Mostra risposta

Perché l’indice di cella è strettamente accoppiato all’operazione di deposito: si avanza alla cella successiva esattamente quando un pezzo è stato depositato con successo. Se l’aggiornamento fosse in CELL_MAIN dopo il ritorno di DEPOSITA, si creerebbe un’asimmetria: DEPOSITA potrebbe tornare con R[56]=1 (timeout apertura pinza) dopo aver fisicamente rilasciato il pezzo, e CELL_MAIN salterebbe a LBL[99] senza avanzare l’indice — la prossima volta che il vassoio viene usato, il robot tornerebbe sulla cella già occupata. Tenere l’aggiornamento dentro DEPOSITA garantisce coerenza: indice e deposito fisico avanzano sempre insieme, nello stesso contesto di esecuzione.


D2. In CAMBIA_VASSOIO il robot torna a HOME prima di segnalare DO[4]/DO[5]. Perché è obbligatorio e non opzionale?

Mostra risposta

Perché il cambio vassoio richiede l’accesso fisico dell’operatore alla cella. Se il robot rimanesse sul vassoio con il pezzo ancora afferrato in attesa di DI[5], l’operatore si troverebbe il braccio del robot sopra il vassoio che deve rimuovere — rischio di collisione tra l’operatore e il TCP durante la rimozione del vassoio pieno o l’inserimento del vassoio vuoto. Portare il robot in HOME prima di segnalare DO[4] garantisce che il braccio sia nella posizione di sicurezza certificata, fuori dalla zona di lavoro, prima che l’operatore entri. Questo è un requisito di sicurezza funzionale — non una scelta di comodo.


D3. CELL_MAIN usa IF R[2]>=R[10], CALL CAMBIA_VASSOIO sulla riga 17, dopo aver già incrementato R[2]. Perché non si usa IF R[2]>R[10]?

Mostra risposta

Perché R[2] viene incrementato prima del check: dopo il deposito del 9° pezzo, R[2] diventa 9, e R[10]=9. La condizione R[2]>=R[10] è vera (9>=9) → cambio vassoio corretto. Con R[2]>R[10] sarebbe falsa (9>9 è falso) e il cambio non verrebbe chiamato. Il robot tornerebbe alla riga 6 (LBL[1]) e alla prossima iterazione tenterà di depositare il 10° pezzo su una griglia con indici R[21] che, dopo l’avanzamento in DEPOSITA, valgono R[21]=0 e R[20]=3 — riga 3 di una griglia 0-indexed 3×3, che non esiste. Causa posizioni errate o collisioni. Il >= è la condizione corretta per un confronto su valore già incrementato rispetto a un limite superiore inclusivo.


D4. Il programma non ha un limite di cicli totali — gira indefinitamente. In un impianto reale, come si aggiunge un meccanismo di manutenzione preventiva basato sul conteggio?

Mostra risposta

Si aggiunge un registro R[80:MAINT_CNT] che conta i cicli dall’ultima manutenzione (persistente tra spegnimenti) e un limite R[81:MAINT_LIM] impostato a es. 50000 cicli. In LBL[49] (convergenza), dopo R[1]++, si aggiunge: R[80]=R[80]+1, poi IF R[80]>=R[81], JMP LBL[95]. LBL[95] è la sequenza di manutenzione: HOME, DO[3]=OFF, DO[8]=ON (segnale manutenzione al PLC), WAIT DI[8]=ON (conferma manutenzione eseguita), DO[8]=OFF, R[80]=0, JMP LBL[5]. Il tecnico esegue la manutenzione (ingrassaggio, controllo riduttori, verifica calibrazione) e preme il pulsante di reset manutenzione. Il robot riparte con R[80]=0. Questo pattern — contatore di cicli con soglia configurabile — è standard negli impianti di produzione seria.


D5. Descrivi in 5 frasi cosa hai imparato in questo percorso triennale che non sapevi prima.

Mostra risposta

Risposta libera — è la domanda più importante del colloquio. Una risposta modello potrebbe essere: “Ho imparato che un robot industriale non è una macchina misteriosa ma un sistema cinematico preciso con sei gradi di libertà, che obbedisce a regole matematiche precise. Ho capito che scrivere codice che funziona è solo l’inizio — scrivere codice sicuro, ottimizzato e manutenibile richiede metodo, non solo tecnica. Ho visto che la scelta di una terminazione (FINE vs CNT) può valere un 40% di produttività. Ho capito che la modularità non è un lusso ma l’unico modo per scrivere programmi che si possono mantenere nel tempo. E ho capito che il robot è uno strumento al servizio di un processo produttivo — programmare bene significa conoscere quel processo, non solo conoscere le istruzioni TP.”


📚 Riepilogo del progetto finale

In questo progetto hai integrato:
  • Cinematica e sistemi di riferimento (Lez. 02–03) — configurazione NUT, W=180°, approccio verticale
  • Jog e teach dei punti (Lez. 04–08) — P[ ] derivati, 9 punti per una cella completa
  • Movimenti J/L con FINE e CNT (Lez. 07, 16) — ACC dolce con pezzo, aggressivo a vuoto
  • I/O digitali con TIMEOUT (Lez. 10) — DI conferma pinza, DO segnali PLC, LBL[98/99] distinti
  • Registri R[ ] come contatori, limiti, parametri (Lez. 11) — R[1..4], R[10], R[50], R[56], R[90]
  • Strutture di controllo e loop (Lez. 12–13) — SELECT a 3 vie, IF errore, loop infinito con start/stop
  • Offset su griglia con PR[ ] (Lez. 15) — 2 punti insegnati per 9 celle, indici R[20–23]
  • Architettura modulare con CALL (Lez. 17) — 6 programmi, interfaccia R[50]/R[56], max 2 livelli

🎓 Fine del percorso — Robotica Industriale FANUC ER-4iA

Hai completato 18 lezioni di programmazione robotica industriale. Sai leggere le specifiche di una cella, progettare l’architettura, scrivere il codice TP, ottimizzare le traiettorie e collaudare il programma con metodo. Il robot fisico in laboratorio usa lo stesso controller R-30iB Mate e lo stesso linguaggio TP che hai usato nel simulatore.

18
lezioni completate
3
anni di percorso
1
cella industriale reale

© Prof. D. Bertolino · Robotica Industriale FANUC ER-4iA · Lezione 18 di 18