Lezione 14 di 18

RI Lezione 14

🤖 Robotica Industriale · IV Anno · Lezione 14 — Progetto

Cella Pick & Sort 🏭

Progetto integrato di fine IV anno · Presa · Sorting a 3 vie · Loop di produzione · Contatori · Gestione errori

⏱️ Durata stimata: 90–120 min 📊 Livello: Intermedio (fine IV anno) 🔧 Strumenti: Simulatore web · Editor TP · Pannello I/O · Pannello DATA 📋 Prerequisiti: Lezioni 06–13 — tutte

🏭 Questo è il progetto finale del IV anno. Non ci sono nuove istruzioni da imparare — si tratta di mettere insieme tutto: movimenti J/L, teaching dei punti, I/O digitali, registri, strutture di controllo e loop. Il risultato è un programma TP completo e funzionante per una cella di sorting industriale a 3 destinazioni. È il tipo di programma che si scrive davvero in un impianto reale.

🎯 Obiettivi del progetto

  • Progettare autonomamente la struttura di un programma TP complesso a partire dalle specifiche di cella
  • Integrare movimenti, I/O, registri e strutture di controllo in un unico programma coerente
  • Insegnare tutti i punti P[ ] necessari con la strategia approccio/lavoro sviluppata nelle Lezioni 07–08
  • Verificare il programma con la procedura STEP → RUN 10% → RUN 100%
  • Documentare il programma con commenti e nomenclatura standard

📐 1. Specifiche della cella

1.1 — Layout fisico

Zona di presa — Nastro di ingresso
Posizione TCP: X=300, Y=0, Z=50
Approccio: Z+150 sopra il punto di presa
Orientamento pinza: W=180°, P=0°, R=0°
Sensore pezzo presente: DI[2]
Config robot: N U T, 0, 0, 0
Pinza pneumatica
DO[1] = ON → chiudi pinza
DO[1] = OFF → apri pinza
DI[1] = ON → conferma chiusura (reed)
DI[1] = OFF → conferma apertura (reed)
Tempo corsa atteso: ≤ 0,5 s
Zona A — Pezzi tipo 1 (R[5]=1)
Posizione TCP: X=–200, Y=150, Z=50
Approccio: Z+150
Config robot: N U T, 0, 0, 0
Zona B — Pezzi tipo 2 (R[5]=2)
Posizione TCP: X=0, Y=250, Z=50
Approccio: Z+150
Config robot: N U T, 0, 0, 0
Zona C — Pezzi tipo 3 (R[5]=3)
Posizione TCP: X=200, Y=150, Z=50
Approccio: Z+150
Config robot: N U T, 0, 0, 0
Zona S — Scarti (R[5]=0 o valore non valido)
Posizione TCP: X=–200, Y=–150, Z=50
Approccio: Z+150
Config robot: N U T, 0, 0, 0

1.2 — Segnali I/O completi

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]Reset allarme (dopo errore)
Uscite DO
DOFunzione
DO[1]Pinza (ON=chiusa)
DO[2]Robot pronto / in attesa start
DO[3]Ciclo attivo
DO[4]Fine lotto
DO[5]Allarme generico
DO[6]Allarme pinza (timeout)

1.3 — Registri R[ ] del programma

Registro Nome Funzione Valore iniziale
R[1]CONT_TOTPezzi totali processati0
R[2]CONT_APezzi depositati in zona A0
R[3]CONT_BPezzi depositati in zona B0
R[4]CONT_CPezzi depositati in zona C0
R[5]TIPO_PEZZOTipo pezzo da sistema visione (1/2/3/0)—
R[6]CONT_SCPezzi andati in zona scarti0
R[10]LIMITEDimensione lotto30

1.4 — Punti P[ ] da insegnare

P[ ] Nome Descrizione Derivazione
P[1]HOMEPosizione di home sicuraTeach diretto
P[10]APPR_PICKApproccio presa (Z+150 su PICK)Derivato da P[11]
P[11]PICKPunto di presa sul nastroTeach diretto
P[20]APPR_AApproccio zona A (Z+150 su PLACE_A)Derivato da P[21]
P[21]PLACE_APunto deposito zona ATeach diretto
P[30]APPR_BApproccio zona BDerivato da P[31]
P[31]PLACE_BPunto deposito zona BTeach diretto
P[40]APPR_CApproccio zona CDerivato da P[41]
P[41]PLACE_CPunto deposito zona CTeach diretto
P[50]APPR_SCApproccio zona scartiDerivato da P[51]
P[51]PLACE_SCPunto deposito scartiTeach diretto
I punti “Derivati” si ottengono copiando il punto di lavoro e modificando solo Z: Z_approccio = Z_lavoro + 150.

🗂️ 2. Struttura del programma

Prima di scrivere una sola riga di codice, si pianifica su carta la struttura del programma: sezioni, flusso principale, condizioni di uscita. Questo è il passo che distingue il programmatore esperto dall’inesperto.

Mappa delle sezioni del programma PICK_SORT_3
Sezione 0
INIT — azzera registri, apri pinza, vai in home, spegni tutti i DO
↓
LBL[5]
ATTESA START — DO[2]=ON (pronto), WAIT DI[3]=ON, DO[2]=OFF, DO[3]=ON
↓
LBL[1]
INIZIO CICLO — check stop DI[4], check fine lotto R[1]≥R[10]
↓
Fase 1
PRESA — aspetta DI[2], J a APPR_PICK, L a PICK, DO[1]=ON, WAIT DI[1]=ON
↓
Fase 2
SMISTAMENTO — SELECT R[5] → JMP LBL[10/20/30/40]
↓ (4 rami paralleli)
Fase 3
DEPOSITO (×4 rami) — J a APPR_x, L a PLACE_x, DO[1]=OFF, WAIT DI[1]=OFF, R[n]++, JMP LBL[49]
↓
LBL[49]
CONVERGENZA — R[1]++, JMP LBL[1]
↓ (uscite)
LBL[9]
STOP PULITO — home, DO[3]=OFF, JMP LBL[5] (torna ad aspettare start)
LBL[8]
FINE LOTTO — home, DO[3]=OFF, DO[4]=ON (fine lotto), JMP LBL[5]
LBL[99]
ERRORE — home, DO[1]=OFF, DO[5]=ON o DO[6]=ON — non ritorna automaticamente

💾 3. Programma completo: PICK_SORT_3

! ============================================================
! PROG: PICK_SORT_3
! Cella pick & sort a 3 destinazioni + zona scarti
! R[5]=tipo pezzo (1/2/3/0=scarto) da sistema visione
! ============================================================

! === SEZIONE 0: INIZIALIZZAZIONE ===
1: R[1:CONT_TOT]=0
2: R[2:CONT_A ]=0
3: R[3:CONT_B ]=0
4: R[4:CONT_C ]=0
5: R[6:CONT_SC ]=0
6: R[10:LIMITE ]=30
7: DO[1]=OFF                                        ! pinza aperta
8: DO[3]=OFF
9: DO[4]=OFF
10: DO[5]=OFF
11: DO[6]=OFF
12: J P[1:HOME] 100% FINE

! === SEZIONE 1: ATTESA START ===
13: LBL[5]
14: DO[2]=ON                                        ! robot pronto
15: WAIT DI[3]=ON                                  ! aspetta start da PLC
16: DO[2]=OFF
17: DO[3]=ON                                        ! ciclo attivo

! === SEZIONE 2: INIZIO CICLO ===
18: LBL[1]
19: IF DI[4]=ON, JMP LBL[9]                        ! stop richiesto?
20: IF R[1]>=R[10], JMP LBL[8]                      ! lotto completato?

! === SEZIONE 3: PRESA ===
21: WAIT DI[2]=ON                                  ! aspetta pezzo sul nastro
22: J P[10:APPR_PICK] 80% CNT100
23: L P[11:PICK ] 150mm/sec FINE
24: DO[1]=ON                                        ! chiudi pinza
25: WAIT DI[1]=ON TIMEOUT, LBL[99]                  ! conferma chiusura
26: L P[10:APPR_PICK] 150mm/sec FINE                ! risalita

! === SEZIONE 4: SMISTAMENTO ===
27: SELECT R[5]=1, JMP LBL[10]                      ! tipo 1 → zona A
         =2, JMP LBL[20]                                ! tipo 2 → zona B
         =3, JMP LBL[30]                                ! tipo 3 → zona C
         ELSE, JMP LBL[40]                                ! tipo 0/altro → scarti

! === SEZIONE 5A: DEPOSITO ZONA A ===
28: LBL[10]
29: J P[20:APPR_A ] 80% CNT100
30: L P[21:PLACE_A ] 150mm/sec FINE
31: DO[1]=OFF
32: WAIT DI[1]=OFF TIMEOUT, LBL[98]
33: L P[20:APPR_A ] 150mm/sec FINE
34: R[2:CONT_A]=R[2]+1
35: JMP LBL[49]

! === SEZIONE 5B: DEPOSITO ZONA B ===
36: LBL[20]
37: J P[30:APPR_B ] 80% CNT100
38: L P[31:PLACE_B ] 150mm/sec FINE
39: DO[1]=OFF
40: WAIT DI[1]=OFF TIMEOUT, LBL[98]
41: L P[30:APPR_B ] 150mm/sec FINE
42: R[3:CONT_B]=R[3]+1
43: JMP LBL[49]

! === SEZIONE 5C: DEPOSITO ZONA C ===
44: LBL[30]
45: J P[40:APPR_C ] 80% CNT100
46: L P[41:PLACE_C ] 150mm/sec FINE
47: DO[1]=OFF
48: WAIT DI[1]=OFF TIMEOUT, LBL[98]
49: L P[40:APPR_C ] 150mm/sec FINE
50: R[4:CONT_C]=R[4]+1
51: JMP LBL[49]

! === SEZIONE 5S: DEPOSITO SCARTI ===
52: LBL[40]
53: J P[50:APPR_SC ] 80% CNT100
54: L P[51:PLACE_SC] 150mm/sec FINE
55: DO[1]=OFF
56: WAIT DI[1]=OFF TIMEOUT, LBL[98]
57: L P[50:APPR_SC ] 150mm/sec FINE
58: R[6:CONT_SC]=R[6]+1
59: JMP LBL[49]

! === SEZIONE 6: CONVERGENZA E LOOP ===
60: LBL[49]
61: R[1:CONT_TOT]=R[1]+1                          ! conta pezzo processato
62: JMP LBL[1]

! === SEZIONE 7: STOP PULITO (DI[4]=ON) ===
63: LBL[9]
64: J P[1:HOME] 100% FINE
65: DO[3]=OFF
66: JMP LBL[5]                                      ! torna ad aspettare start

! === SEZIONE 8: FINE LOTTO ===
67: LBL[8]
68: J P[1:HOME] 100% FINE
69: DO[3]=OFF
70: DO[4]=ON                                        ! segnala fine lotto al PLC
71: JMP LBL[5]                                      ! attende nuovo start

! === SEZIONE 9: ERRORE TIMEOUT PINZA (apertura) ===
72: LBL[98]
73: J P[1:HOME] 100% FINE
74: DO[3]=OFF
75: DO[6]=ON                                        ! allarme pinza
! [non torna automaticamente — attende intervento tecnico]

! === SEZIONE 10: ERRORE TIMEOUT PINZA (chiusura) ===
76: LBL[99]
77: DO[1]=OFF                                        ! apri pinza per sicurezza
78: J P[1:HOME] 100% FINE
79: DO[3]=OFF
80: DO[5]=ON                                        ! allarme generico
! [non torna automaticamente]

🔬 4. Procedura di collaudo nel simulatore

Un programma non verificato non va in produzione. La procedura di collaudo va seguita in quest’ordine — saltare fasi aumenta il rischio di danni al robot o all’impianto.

FASE 1
Teach
Insegna tutti i punti P[ ] nel simulatore
Insegna prima P[11] (PICK), poi deriva P[10] copiando P[11] e modificando Z+150. Ripeti per tutte le zone. Verifica che tutti i punti abbiano la stessa configurazione N U T, 0, 0, 0 e W=180°.
FASE 2
STEP 10%
Esegui riga per riga con velocità ridotta al 10%
Imposta override velocità al 10%. Usa STEP per avanzare una riga alla volta. Controlla che i movimenti vadano nella direzione giusta, che non ci siano collisioni, che gli I/O si attivino nel momento corretto. Forza i DI manualmente dal pannello I/O per sbloccare i WAIT.
FASE 3
RUN 30%
Esegui ciclo completo a velocità ridotta
Esegui 3 cicli completi impostando R[5]=1, poi R[5]=2, poi R[5]=3 dal pannello DATA prima di ogni ciclo. Verifica che il robot vada nella zona corretta. Verifica i contatori R[2], R[3], R[4] nel pannello DATA.
FASE 4
Test I/O
Verifica tutti i percorsi alternativi
Test A: imposta R[5]=0 → il robot deve andare agli scarti e R[6] deve incrementarsi. Test B: forza DI[4]=ON a metà ciclo → il robot deve completare il pezzo e fermarsi a LBL[9]. Test C: non rispondere al WAIT DI[1]=ON → il programma deve saltare a LBL[99] e accendere DO[5].
FASE 5
RUN 100%
Ciclo completo a velocità nominale
Esegui un lotto completo di almeno 10 cicli a velocità nominale con mix di R[5]=1/2/3. Verifica che R[1]=10 e che R[2]+R[3]+R[4]+R[6]=R[1] al termine. Verifica che DO[4] si accenda al completamento del lotto di 30 pezzi.

🏋️ 5. Varianti e modifiche guidate

Dopo aver verificato il programma base, prova a implementare queste varianti — ciascuna aggiunge una funzione reale che si trova nei programmi industriali.

🟢 Variante 1 — Lotto configurabile da pannello (Facile)

Attualmente R[10]=30 è hardcoded nell’inizializzazione. Modifica il programma in modo che R[10] non venga azzerato all’avvio se ha già un valore diverso da 0 — così l’operatore può impostarlo dal pannello DATA prima di avviare il ciclo e il programma lo rispetta. Usa un IF: IF R[10]<>0, JMP LBL[4] per saltare la riga di inizializzazione di R[10]. Cosa succede alla prima accensione quando R[10]=0?

🔵 Variante 2 — Contatore scarti con allarme soglia (Medio)

Aggiungi una logica che blocca il ciclo se il numero di scarti supera il 20% dei pezzi totali processati. Dopo ogni deposito in zona scarti, calcola: R[7]=R[6]*100 / R[1] (percentuale scarti). Se R[7]>20: esegui stop di qualità (LBL[97]): vai in home, DO[3]=OFF, accendi DO[7]=ON (allarme qualità), attendi DI[5]=ON (reset), poi azzera R[7] e torna a LBL[5].

Attenzione alla divisione per zero alla prima iterazione (R[1]=0). Come la gestisci?

🟠 Variante 3 — Multi-lotto con statistiche cumulative (Medio)

Attualmente ogni nuovo start (LBL[5] → DI[3]=ON) riparte con i registri azzerati dall’init. Modifica la struttura in modo che:

  • L’init (azzeramento R[1..6]) avvenga solo alla prima accensione (R[8:INIZIALIZZATO]=0)
  • Ad ogni start successivo i contatori continuano dall’ultimo valore — le statistiche sono cumulative per turno
  • L’azzeramento avviene solo quando DI[6]=ON (reset turno esplicito) prima del start
  • R[8] viene impostato a 1 dopo il primo init e non viene mai più toccato dall’init

🟣 Variante 4 — Progettazione da zero: cella a zona doppia (Difficile)

Progetta da zero (specifiche → struttura → codice → collaudo) una variante della cella in cui ogni zona A, B, C può ricevere al massimo 10 pezzi. Quando una zona è piena (R[2]=10, R[3]=10 o R[4]=10), i pezzi di quel tipo vengono reindirizzati automaticamente alla zona scarti finché l’operatore non svuota la zona e preme il pulsante di reset zona (DI[7]=reset A, DI[8]=reset B, DI[9]=reset C).

Requsiti: gestione dei flag zona_piena (R[11], R[12], R[13]), logica di reindirizzamento nel SELECT, reset zone dal pannello I/O, contatore separato per “scarti da zona piena” vs “scarti da tipo 0”.


❓ 6. Quiz di verifica

1. Perché R[1] (contatore totale) viene incrementato in LBL[49] (convergenza) e non dentro ciascun ramo di deposito?

Mostra risposta

Perché il contatore totale deve incrementarsi per ogni pezzo processato indipendentemente dalla destinazione — zona A, B, C o scarti. Se fosse in ogni ramo, andrebbe scritto 4 volte (duplicazione) e basterebbe dimenticarlo in uno dei rami per avere un conteggio errato. Posizionarlo nel punto di convergenza LBL[49] garantisce che venga eseguito esattamente una volta per ogni ciclo, qualunque sia stato il percorso. I contatori parziali (R[2], R[3], R[4], R[6]) invece devono stare dentro i rami specifici perché contano solo la destinazione di quel ramo.


2. Il programma ha due etichette di errore: LBL[98] e LBL[99]. Qual è la differenza tra i due casi e perché LBL[99] include DO[1]=OFF mentre LBL[98] no?

Mostra risposta

LBL[99] è il timeout sulla chiusura della pinza (WAIT DI[1]=ON): la pinza ha ricevuto il comando di chiudersi (DO[1]=ON) ma il sensore reed non conferma la chiusura. La pinza potrebbe essere parzialmente chiusa, inceppata, o il pezzo non correttamente agganciato. In questo stato DO[1] è ancora ON — bisogna spegnerlo (DO[1]=OFF) per mettere in sicurezza l’elettrovalvola e tentare l’apertura. LBL[98] invece è il timeout sulla apertura della pinza (WAIT DI[1]=OFF): DO[1] è già OFF (il comando di apertura è già stato dato), il problema è che il sensore non conferma l’apertura completa. DO[1]=OFF è già stato eseguito alla riga precedente al WAIT, quindi non serve ripeterlo.


3. A fine lotto (LBL[8]), il programma esegue JMP LBL[5] senza azzerare R[1]. È un bug o un comportamento voluto?

Mostra risposta

È un comportamento voluto. Se il programma tornasse a LBL[5] con R[1] ancora uguale a R[10] (30), al prossimo start la riga 20 (IF R[1]>=R[10]) sarebbe vera fin dal primo ciclo e il programma salterebbe immediatamente a LBL[8] senza processare nessun pezzo — loop istantaneo fine lotto. Per evitarlo, all’inizio di ogni nuovo lotto R[1] deve essere azzerato. Questo azzeramento avviene all’inizio dell’init (riga 1), che viene eseguito solo all’avvio del programma. In questo progetto base l’operatore deve ri-avviare il programma tra un lotto e l’altro. Nelle varianti più avanzate (Variante 3) si gestisce questo con flag di stato e reset selettivo.


4. Perché la verifica del lotto (IF R[1]>=R[10]) usa >= invece di =?

Mostra risposta

Perché con = il check funziona solo se R[1] raggiunge esattamente R[10]. Se per qualunque motivo R[1] saltasse direttamente da 29 a 31 (scenario improbabile ma possibile in caso di doppio incremento per bug), la condizione R[1]=30 non sarebbe mai vera e il ciclo non si fermerebbe mai. Con >= il lotto si completa correttamente anche se R[1] supera R[10]. È una pratica difensiva generale: usare >= o <= per le condizioni di uscita dai loop invece di = puntuale, a meno che l'uguaglianza esatta non sia semanticamente necessaria.


5. Se il sistema di visione non scrive R[5] prima che il robot arrivi alla riga SELECT, cosa succede al programma?

Mostra risposta

Il SELECT legge il valore attuale di R[5] — che è quello dell’ultima scrittura del sistema di visione, o il valore residuo della precedente iterazione se la visione non ha aggiornato. Se il valore di R[5] è 1, 2 o 3, il pezzo andrà nella zona sbagliata (quella del ciclo precedente). Se R[5] ha un valore non previsto (es. 0 o ancora non inizializzato), la clausola ELSE manda il pezzo agli scarti. In un impianto reale questo problema si risolve sincronizzando la lettura: il robot si posiziona sul nastro, poi WAIT che la visione abbia finito (DI di “visione OK”), poi legge R[5]. Senza quella sincronizzazione il programma può processare pezzi con il tipo del ciclo precedente.


📚 Riepilogo del progetto

Competenze consolidate con questo progetto:
  • Lettura di specifiche → progettazione su carta → codice → collaudo: metodo completo
  • Movimenti J/L con approccio/lavoro e gestione configurazione robot
  • I/O integrati: DO comando, DI conferma, WAIT TIMEOUT, etichette errore separate per tipo
  • Registri: contatori multipli per destinazione, limite lotto, flag stato
  • Strutture di controllo: SELECT a 4 vie con ELSE, IF di uscita loop, convergenza LBL[49]
  • Loop: ciclo infinito con start/stop da PLC, uscita fine lotto, statistiche R[2]+R[3]+R[4]+R[6]=R[1]
  • Invariante di controllo: R[2]+R[3]+R[4]+R[6] deve sempre uguale R[1] — se non è così c’è un bug

🎓 Fine IV Anno — Inizio V Anno

Hai completato il percorso di IV anno: sai programmare una cella robotizzata completa con movimenti, I/O, logica e ciclo produttivo. Nel V anno si passa al livello avanzato: posizionamento dinamico con PR[ ] e offset, ottimizzazione delle traiettorie, programmazione multi-programma con CALL, fino al progetto finale di cella robotizzata completa.

Lezione 15 — Registri di posizione PR[ ] e offset
Spostare il TCP in modo dinamico durante il ciclo.
Applicazione pratica: deposito su griglia senza insegnare ogni singolo punto.

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