Sunteți pe pagina 1din 41

Lucrarea 1

Simulator Open Source (GPL) cu ajutorul căruia pot fi


urmărite mişcările roboţilor

1.1. Prezentare generală


Programul “robot-sandbox” este util pentru realizarea unor animaţii privind mişcările
roboţilor. Acest program este un simulator Open Source (GPL) cu ajutorul căruia pot fi rulate o
parte din programele realizate în V+. Simulatorul implementează un subset al limbajului V+.
Programele demonstrative disponibile împreună cu acest program sunt următoarele:
 hanoi.v2: problema turnurilor din Hanoi (se muta un turn format din 3 segmente
descrescătoare suprapuse, mutând cate o piesa odată, dar întotdeauna o piesa
mai mica sa fie peste o piesa mai mare)
 drawing.v2: exemplu în care robotul desenează o curbă parametrică
 mistake.v2: simularea efectului unor comenzi eronate care duc la lovirea pieselor
dintr-o stivă
 singular.v2: ilustrează cele 4 tipuri de singularităţi ale robotului articulat vertical
Adept Viper
O altă categorie este cea prin care robotul execută mişcări de poziţionare, pick&place:
 stiva.vert.v2: manipularea pieselor aflate într-o stivă verticală
 pal.ideal.v2: paletizare 3D în cazul ideal (paleta aliniată perfect cu robotul)
 pal.rot.v2: paletizare 3D cu paleta rotită cu un unghi cunoscut
 pal.inc.v2: paletizare 3D pe plan înclinat
 spirala.v2: problema rezolvată “Spirala”
 casuta.v2: problema rezolvată “Căsuţa”
Există și alte aplicații diferite care cuprind algoritmi procedurali cum ar fi programe care
simulează aplicaţia de desenare cu ajutorul robotului:
 cerc(): desenarea unui cerc descris parametric
 picasso(): desenarea pe baza unei traiectorii citite dintr-un fişier, obţinută prin
recunoaşterea conturului unei imagini
Signals:
 blink.v2: program care comută starea unui semnal de ieşire la care este
conectată o lampă
 plc.v2: program care simulează comunicaţia între un robot şi un automat
programabil
Belt: programe de interacţiune cu banda conveioare (banda transportoare)
 belt1.v2: un program simplu de interacţiune cu banda conveioare
 belt2.v2: un program similar cu cel anterior

1
Lansarea simulatorului se face prin lansarea comenzii “robot-sandbox.cmd” din
directorul “robot-sandbox”. Pe ecran vor apărea două ferestre, prima cu consola programului
(unde se pot da comenzi), fig. 1, iar a doua cu reprezentarea grafică a mişcărilor robotului, fig.
2. Sintaxa pentru lansarea programelor demonstrative este: “demo nume_program.v2”. Se pot
folosi comenzi pentru listarea fisierelor din directorul curent, “ls”, iar pentru ieşirea din
program se foloseşte comanda “exit()”. Alte comenzi se pot afla prin comanda “mc”.

Fig. 1. Consola simulatorului

Fig. 2. Exemplu de imagine generată de simulator

2
Pentru o parte dintre programe se poate interacţiona direct cu robotul din fereastra
OpenGL, folosind mouse-ul.
Pentru programele, altele decât cele demo, procedura de lansare în execuţie este
următoarea:
- cu ajutorul comenzi “cd” se accesează directorul care conţine programul de interes;
- în consola de comenzi se scriu succesiv comenzile: „env nume_fisier.env” pentru
încărcarea variabilelor (dacă este cazul, unele programe nu au variabile descrise în afara
programului); „load nume_fisier.v2” pentru încărcarea în memorie a programului; „exec
nume_fisier.v2” pentru lansarea în execuţie a programului.
În fereastra grafică OpenGL se află un buton în colţul dreapta-sus. De la acesta, prin
apăsări succesive se pot comuta modurile de control manual al robotului, similar cu cele de pe
modurile de comandă manuală (MCP). Astfel avem următoarele situaţii:
- COMP; doar în acest mod se pot rula programe robot şi instrucțiuni de mişcare.
- WORLD; efectorul terminal se deplasează pe axele World, indicate în fereastra de
simulare prin culorile roşu (X), verde (y) şi albastru (z).
- TOOL; efectorul terminal se deplasează pe axele proprii (pe sistemul TOOL).
- JOINT; robotul este controlat pe fiecare articulaţie.
În modurile manual (WORLD, TOOL şi JOINT), viteza se setează din slider-ul “MCP
speed”. În modul COMP, viteza de lucru este dată de viteza monitor, stabilită fie prin comanda
monitor “speed”, fie prin sliderul “Monitor speed”. “MCP speed” şi “Monitor speed” sunt în
partea de jos pe fereastra grafică OpenGL. Comanda “speed” se poate da în oricare din
modurile ferestrei OpenGL.

1.2. Desfăşurarea lucrării


Se parcurg următoarele etape, pentru valorificarea majorităţii posibilităţilor programului
de simulare şi de dezvoltare de programe pentru roboţi, “robot-sandbox”.

Etapa I. Se încarcă de către fiecare student un program demonstativ din directorul


demos. Atenţie! dacă fereastra OpenGL nu este în mod “COMP”, încărcarea programului
eşuează. Este posibil ca în funcţie de comenzile date anterior, să apară erori la execuţia
programului. Se recomandă o reiniţializare a mediului de programare sau execuţia
următoarelor comenzi la promptul („.”) consolei:
. zero
. env nume_fisier
. load nume_fisier
. exec nume_fisier
Se urmăreşte şi ca directorul curent să conţină programul dorit. Acest aspect se
stabilește cu ajutorul comenzilor “ls” şi “cd”.
Ieşirea din simulare se face în fereastra de comenzi, prin tasta „ENTER” (apăsând
"ENTER” se revine la prompt-ul “.”).

Etapa II. Se folosesc diferite comenzi pentru familiarizarea cu utilizarea simulatorului,


sinteza acestora fiind în anexa I. Recunoaşterea comenzilor se verifică prin listarea acestora, cu
comanda “mc”.

3
Etapa III.
Se vor acţiona butoanele existente în fereastra grafică OpenGL, urmărind acţiunea
fiecăruia. Se vor folosi toate modurile: COMP, WORLD, TOOL şi JOINT. Ce se poate spune despre
funcţionarea butonului „ALIGN”?

Etapa IV.
Se realizeaza primul program. Se recomanda folosirea editorului “notepad2”, activat cu
comanda “see” ( se poate introduce ca parametru numele programului). Alte editoare text pot
introduce coduri de formatare care pot genera erori.

.PROGRAM hello()
TYPE "Hello, World!"; determina apariția unui text pe ecran
.END ; este pus automat in cazul editorului implicit “Notepad2”

Se pot observa: antetul .PROGRAM nume.program(lista parametri), corpul programului


(secvenţa de instrucţiuni, aici comanda de scriere pe ecran), comentariile, precum şi sfârşitul
programului marcat prin .END . Pentru inserarea de comentarii se folosește “;”.
Observaţii:
- Editorul SEE introduce automat .END
- Se poate da comenzi “see” cu parametrul numele programului (atenție la extensie, de
preferat se pune extensia v2, chiar daca simulatorul recunoaște orice lista de
instrucțiuni compatibila indiferent de nume sau extensie)
- Dacă nu se folosește comanda „see” cu parametrul numele programului, acesta trebuie
salvat cu un nume pentru a putea fi încărcat si lansat in execuție. Numele programului,
in cazul în care nu a fost creat cu “see”, trebuie sa fie la fel cu numele fișierului.
Terminatorul .END trebuie însă introdus în editoarele externe (AdeptWindows, Offline
Editor, Notepad etc).
Verificarea programului realizat se face prin comenzile (lipsește comanda “env” pentru
ca nu avem variabile/configurări prin fișier):

.load x.v2 ; atenție, se poate scrie numele complet, cu sau fără extensie
.exec x; se scrie numele fișierului fără extensie

4
ANEXA I
Comenzi (neverificate toate!) ce se pot executa în fereastra monitor

Comanda Explicaţii
abort Opreşte forţat un program robot
calibrate Nu are efect, dar pe anumite controlere (Adept) execută o calibrare
electromecanică a robotului
cd Schimbă directorul curent
directory Listează programele încărcate în memorie. Se poate folosi şi %dir
disable Dezactivează un switch. Se pote prescurta dis
do Execută o instrucţiune program la consolă
enable Activează un switch. Se poate prescurta en
execute Execută un program robot. Se poate prescurta exec. Acceptă parametrii
simpli (nu avansaţi)
fcopy Copiază un fişier. In Windows, copy, în Linux, cp
fdelete Sterge un fişier. În Windows, del, în Linux rm
fdirectory Afisează lista fişierelor din directorul curent. Se poate folosi ls
fdirectory /c Creează un director. Se poate folosi mkdir
fdirectory/d Şterge un director. Folositi rmdir
flist Afişează un director la consolă. Pentru Linux se foloseşte cat
here Învaţă un punct robot. Variabilele create sunt globale
listl Listează variabilele de tip transformare
listr Listează variabile de tip scalar (reale, întregi)
lists Listează variabile de tip şir de caractere
load Încarcă un program V+. din fişiere .v2, nu şi din variabile
ls Afişează lista fişierelor din directorul curent
parameter Setează valoarea unui parametru V+
reset Dezactivează toate semnalele de la ieşire. Pe simulare, semnalele de intrare
sunt de asemenea dezactivate şi scoase de pe interfaţa grafică
see Porneşte editorul de programe V+
signal Activează sau dezactivează unul sau mai multe semnale de ieşire sau din
program (signal 50 activează, iar signal -50 dezactivează ieşirea 50)
speed Ajustează viteza monitor între 0 şi 100
status Afişează informaţii despre programele în curs de execuţie
tool Stabileşte transformarea TOOL
zero Şterge toate programele şi variabilele din memorie

5
Lucrarea 2
Mediul de programare V+
1. Sistemul de operare V+
Pe controlerele existente pe roboţi, pe lângă alte programe, rulează sistemul de operare V+ .
Utilizatorul poate controla sistemul folosind o serie de aplicaţii. Dintre acestea se pot aminti:
• AdeptWindows PC
• AdeptDesktop
AdeptWindows PC oferă acces la consola sistemului, o interfaţă în mod text similară cu cea folosită de
Linux. În mediul V+ , consola se numeşte monitor. Utilizatorul poate interacţiona cu sistemul de operare
utilizând comenzi monitor. Informațiile prezentate sunt pentru un sistem de operare real care actionează
asupra unor roboți reali. Pentru simulator sunt valabile aceleași informații cu precizarea că sunt câteva mici
diferențe.
AdeptDesktop este un mediu de dezvoltare a programelor robot de tip IDE (Integrated Development
Environment). Lucrul cu V+ în AdeptDesktop se poate realiza fie prin interfaţa grafică, fie prin introducerea de
comenzi la consola monitor. Comenzile monitor nu sunt case-sensitive şi pot fi prescurtate.
În configuraţia standard, sistemul V+ poate rula până la 7 task-uri utilizator, numerotate de la 0 la 6.
La un moment dat, un robot poate fi controlat de un singur task. În mod implicit, task-ul 0 este programul care
controlează robotul (pe care se pot executa instrucţiuni de mişcare).
Sistemul V+ are o structură de fişiere organizate în directoare, similară cu cea folosită în sistemele de
operare uzuale. Programele V+ pot fi salvate în fişiere cu extensia .v2 sau .pg.

1.2. Editarea programelor


Pentru editarea programelor avem câteva variante:
• AdeptWindows Offline Editor
• Editorul din AdeptDesktop
• Editorul SEE din consola monitor V+
• Orice alt editor ASCII (Notepad)
Variantele recomandate sunt AdeptDesktop (dacă este disponibil) sau editorul SEE.

Dacă se doreşte lucrul offline, se poate utiliza un editor ASCII, sau editorul offline AdeptWindows
(nerecomandat).
Editorul SEE se activează folosind comanda monitor see:
.see numeprogram
Editorul SEE porneşte în modul “Command”; pentru a scrie cod, trebuie trecut în modul “Insert”.
Aşadar, apăsaţi tasta INSERT. Pentru simulator apare direct ferestra pentru programul existent sau nou.
Editorul suportă Copy/Paste la nivel de linii. Mai exact, o linie întreagă este copiată cu tasta F9.
Operaţia Paste se realizează cu F10. Pentru a copia mai multe linii în memoria tampon (copy buffer ), apăsaţi
de mai multe ori F9 si de mai multe ori F10.
Nu se poate realiza Copy/Paste dintr-un program în altul. Mai mult, dacă există linii în copy buffer, nu
puteţi închide editorul. Acesta va afişa mesajul “Cannot exit while lines attached”.
Pentru a ieşi va trebui să apăsaţi CTRL-K de mai multe ori, până se goleşte buffer-ul.
Pentru a ieşi din editorul SEE se va folosi tasta F4. Modificările vor fi salvate automat în memoria
RAM. Pentru salvarea programului pe disc folosiţi comenzile store*.
1.3. Gestionarea programelor
Programele V+ pot fi organizate în module. Comenzi monitor pentru gestionarea programelor:
.dir ; lista programelor încărcate în RAM (nu de pe disc)
.rename newprog = oldprog ; redenumire în memoria RAM
.copy newprog = oldprog ; copiere în memoria RAM
.deletep prog ; şterge programul prog din memorie
.testp prog
.status ; afişează starea task-urilor

1.4. Salvarea şi încărcarea programelor


Un program aflat în memorie poate fi salvat pe disc cu una din comenzile:
.store fisier ; salvează tot (programe+variabile) în fisier.v2
.store fisier = prog ; salvează programul prog împreună cu subrutinele şi variabilele utilizate de acesta,
în fisier.v2
.store fisier = p1, p2
.storep fisier.v2 ; salvează toate programele, fără variabile, înfisier.v2 (extensia implicită este .pg)
.storep fis.v2 = p ; salvează doar programul p împreună cu subrutinele apelate de acesta, dar fără
variabile
Variabilele pot fi salvate în fişiere separate, cu comenzile storel (variabile de tip locaţie şi puncte de
precizie), storer (numere reale), stores (variabile de tip string).
.storel fisier ; salvează variabilele locaţie în fisier.lc
.storel fis = prog ; salvează în fis.lc variabilele locaţie utilizate de programul prog
Încărcarea programelor şi a variabilelor de pe disc se face cu comanda
.load numefisier . Programele care există deja în memorie nu sunt suprascrise, însă variabilele
existente sunt suprascrise.
1.5. Gestionarea variabilelor
.listl
.listr
.lists
.deleter realvar
.deletes $stringvar
.deletel loc.pick
.deletel #loc.safe
.deletel locations[]
.deleter timestamps[]

1.6. Execuţia şi depanarea programelor


Debugging: Removing the needles from the haystack.
.exec prog .bpt
.exec prog(pick,place) .proceed
.abort 0 .retry
.kill 0 .sstep
.debug prog .xstep
.watch .prime
1.7. Comenzi diverse
.zero
Informaţii detaliate pot fi găsite în manualele V + Operating System User’s Guide şi V+ Operating
System Reference Guide.
Indicaţie: se va consulta modul de utilizare a comenzii deletep în manualul V+ Operating System
Reference Guide.
1.8. Variabile
V+ operează cu următoarele tipuri de date:
• Numere reale şi întregi
• Şiruri de caractere (string)
• Variabile locaţie (transformări şi puncte de precizie)
• Vectori (arrays)
Variabilele pot fi:
• GLOBAL: persistente şi vizibile în toate programele
• LOCAL: persistente şi vizibile în programul curent; toate instanţele programului curent folosesc în
comun variabilele de tip LOCAL.
• AUTO: vizibile doar în programul curent; fiecare instanţă a programului are o copie proprie a
variabilei de tip AUTO. Una din diferenţele majore dintre LOCAL şi AUTO apare atunci când un
program are mai multe instanţe (fie rulează acelaşi program pe 2 task-uri în paralel, fie programul
este recursiv). Atunci când un program modifică o variabilă LOCALă, toate celelalte instanţe ale
programului vor vedea modificarea. Modificările asupra unei variabile AUTO nu sunt vizibile în
celelalte instanţe ale programului. Cu alte cuvinte, echivalentul unei variabile locale din C/C++ este
AUTO. Variabilele persistente (GLOBAL şi LOCAL) sunt menţinute şi după terminarea execuţiei
programului, şi îşi păstrează valoarea la următoarea execuţie (dacă nu sunt reiniţializate). Variabilele
nedeclarate sunt implicit globale. Se recomandă declararea variabilelor folosite ca AUTO sau LOCAL,
cu excepţia cazului în care acestea sunt folosite pentru comunicaţia între task-uri, sau reprezintă
puncte robot învăţate de la consolă. De asemenea, se recomandă declararea explicită a variabilelor
GLOBALe.

Variabile scalare (numerice)


Variabilele de tip numeric pot fi întregi sau reale. Numerele reale pot avea precizie simplă (implicit)
sau dublă. V+ decide singur dacă o variabilă este număr întreg (pe 32 biţi) sau număr real în precizie
simplă. Variabilele în precizie dublă se declară explicit.

AUTO n
AUTO e
AUTO DOUBLE ang
n=5
e = 2.718
ang = ATAN2(5, 100)

Caractere şi string-uri
Variabilele de tip string sunt prefixate cu caracterul ”$”. Variabilele character sunt tratate ca variabile
întregi, iar atribuirea se face cu ajutorul caracterului apostrof.

AUTO $msg
AUTO chr
$msg = "Variabila de tip string"
chr = ’X

Funcţii utile pentru prelucrare variabile


• LEN($msg) - lungimea unui string
• POS(haystack, needle, start) - caută un subşir
• VAL($x) - conversie string → numeric
• $MID($str, first.char, num.chars) - extragere subşir
• ASC($str, index) - extragere caracter
• $CHR(cod.ascii) - conversie caracter - string
• $ENCODE("X = ", x) - concatenare
Variabile locaţie
Variabilele de tip locaţie sunt de 2 tipuri:
• Puncte de precizie: descriu poziţia robotului prin valorile individuale ale articulaţiilor. Sunt prefixate
cu caracterul ”#”, iar atribuirea se face cu SET.
• Transformări: descriu locaţia în spaţiu a unui corp solid (poziţie + orientare). Nu au prefix; atribuirea
acestora se face de asemenea cu SET. Conţin 6 elemente: X, Y, Z, Yaw, Pitch, Roll.
AUTO loc.test
AUTO #loc.safe
SET loc.test = TRANS(100,100,50,0,180,45)
SET #loc.safe = #PPOINT(0,-90,90,0,0,0)

Vectori (arrays)
Variabilele de tip vector pot conţine numere, locaţii sau stringuri. Vectorii GLOBAL şi LOCAL sunt
alocaţi dinamic (se extind automat cât e nevoie). Vectorii LOCAL au dimensiune fixă.
AUTO joints[6]
AUTO joints[]
LOCAL timestamps[]
LOCAL timestamps[1000]
GLOBAL path.to.follow[]
GLOBAL $user.messages[]
1.9. Structuri de control

Buclele se pot întrerupe forţat cu instrucţiunea EXIT (a nu se confunda cu BREAK, care este
instrucţiune de mişcare).

1.10. Subrutine
Orice program poate fi considerat o subrutină.
CALL subrutina()
CALL subrutina(param1, param2)
În mod implicit, parametrii sunt transmişi prin referinţă!. Din acest motiv, modificările efectuate în
subrutină asupra parametrilor sunt vizibile în programul care a apelat subrutina.
Avantaj: parametrii sunt atât de intrare, cât şi de ieşire.
Dezavantaj: programul poate deveni dificil de înţeles si depanat.
Transmiterea parametrilor prin valoare se realizează folosind un truc, mai exact, se construiesc
artificial expresii care nu modifică valoarea variabilelor:
CALL subrutina((a), loc:NULL, $msg+"")
Aici, a este o variabilă reală, loc este o variabilă de tip transformare, iar $msg este un şir de caractere.
Nu se pot defini funcţii utilizator (care întorc o valoare). Se pot folosi doar subrutine cu parametri de
ieşire.
1.11. Lucrul cu consola
• TYPE "Au fost recunoscute ", x, " piese."
→ afis,are mesaj la consol˘a
• PROMPT "Introduceti numele piesei: ", $nume.piesa
→ citirea unui sir de caractere
• PROMPT "Cate piese sunt? ", nr.piese
→ citirea unei variabile de tip numeric

1.12. Instrucţiuni de mişcare


MOVE loc
MOVES loc
MOVET loc, hand.opening
MOVEST loc, hand.opening
BREAK
APPRO loc, d
APPROS loc, d
DEPART d
DEPARTS d
Instrucţiunile APPRO/S poziţionează efectorul terminal la distanţa d ”deasupra” punctului loc. Instrucţ
iunile DEPART/S se deplasează cu distanţa d ”ˆınapoi” sau ”deasupra” poziţiei curente. Deplasarea se
face în sensul negativ al axei Z din sistemul de coordonate ataşat efectorului terminal (Tool).
Instrucţiunile cu sufixul S realizează o mişcare liniară în spaţiul operat, ional (cartezian), iar
instrucţiunile fără acest sufix realizează o mişcare interpolată liniar în spaţiul articulaţiilor. În timpul
unei mişcări liniare în spaţiul operat, ional, configuraţia robotului (LEFTY/RIGHTY, ABOVE/BELOW,
FLIP/NOFLIP) nu poate fi schimbată.
Instrucţiunea BREAK aşteaptă până cînd mişcarea curentă este terminată.
Controlul vitezei şi acceleraţiei:
SPEED 30 ; doar pentru următoarea mişcare
SPEED 50 ALWAYS ; valabil pentru toate instrucţiunile de mişcare ce urmează, cu excepţia celor
precedate de SPEED spd
ACCEL 50, 10 ; controlul acceleraţiei şi deceleraţiei

1.13. Specificarea configuraţiei robotului


Cobra si Viper: Numai Viper:
LEFTY ABOVE
RIGHTY BELOW
SINGLE [ALWAYS] FLIP
MULTIPLE [ALWAYS] NOFLIP

1.14. Controlul gripper-ului


PARAMETER hand.time = 0.5
OPENI
CLOSEI
OPEN
CLOSE
MOVET loc, TRUE
Switch-uri şi parametri
ENABLE POWER
DISABLE UPPER
SWITCH DRY.RUN = FALSE
PARAMETER HAND.TIME = 0.5
Switch-uri de interes:
• POWER - activează High Power (alimentarea braţului robot)
• DRY.RUN - pentru testarea programelor fără a mişca robotul
• TRACE - se afişează fiecare linie de program executată (pentru depanare)
• CP - traiectorie continuă (prin interpolare similară cu B-Spline)
• UPPER - dacă este dezactivat, comparaţia între string-uri este case sensitive
Parametri de interes:
• HAND.TIME - timpul de aşteptare la închiderea/deschiderea gripper-ului
• VTIMEOUT - pentru operaţiile de vedere AdeptSight
1.15. Semnale
• Semnale de intrare: de la 1001 la 1012, de la 1033 la 1512.
• Semnale de ieşire: de la 1 la 8, de la 33 la 512.
• Semnale software interne (intrare/ieşire): de la 2001 la 2512.
SIGNAL 5 ; activare semnal
SIGNAL -5 ; dezactivare semnal
SIGNAL -4,5,2010 ; activarea semnalelor 5 şi 2010 şi dezactivarea semnalului 4
SIG(1001) ; testare (citire) semnal 1001
SIG(-1002) ; citire semnal 1002 în logică negativă
WAIT SIG(1001) ; se aşteaptă până când semnalul 1001 devine activ

1.16. Temporizare
Aşteptare de 5 secunde:
WAIT.EVENT , 5
Aşteptare până la îndeplinirea unei condiţii:
WAIT SIG(1001) OR TIMER(1) > 5
Multitasking
EXECUTE task.num prog.name(param1, param2)
TAS ; test and set
STATUS("prog.name")
ABORT task.num
CYCLE.END task.num
KILL task.num
Informaţii detaliate pot fi găsite în manualele V+ Language User’s Guide şi V+ Language Reference
Guide.
De reţinut
• Toate variabilele se declară la începutul programului!
• Parametrii subrutinelor sunt transmişi prin referinţă!
• Nu se foloseşte instrucţiunea GOTO!

2. Programe V+
2.1. Un program Hello World în V+ arată astfel:
.PROGRAM hello()
TYPE "Hello, World!"
.END
2.2. Un program puţin mai complex:
.PROGRAM numara(n) ; program cu parametru
LOCAL i
TYPE "Numar până la ", n ; afisare mesaj la consola
FOR i = 1 TO n
TYPE i
END
.END
2.3. Un program interactiv cu mediul grafic (fata de programele anterioare rezultatul se observa in
fereastra OpenGL):
.PROGRAM blink()
; Test pentru semnale şi timere
AUTO i, delay
FOR i = 1 TO 10
delay = 3/i
SIGNAL 49
TIMER 1 = 0
WAIT TIMER(1) > delay
SIGNAL -49
TIMER 1 = 0
WAIT TIMER(1) > delay
END
.END

Se pot observa: antetul .PROGRAM nume.program(lista parametri), declaraţiile de variabile (LOCAL i),
corpul programului (secvenţa de instrucţiuni), comentariile, precum şi sfârşitul programului marcat
prin .END .
Observaţie: Editorul SEE introduce automat .END, dar nu îl afişează explicit.
Terminatorul .END trebuie însă introdus în editoarele externe (AdeptWindows, Offline Editor,
Notepad etc).

3. Desfășurarea lucrării (Pick and Place)


Operația de Pick and Place presupune deplasarea unei piese din poziția
A (pick) în poziția B (place) cu ajutorul robotului.

MOVE #safe

#safe APPRO pick, z.pick APPRO place, z.place

DEPARTS z.pick DEPARTS z.place


MOVES pick MOVES place

pick place
CLOSEI OPENI
Figura 2.1: Secvența Pick and Place

Mișcarea robotului va începe dintr-o locație numita safe, în care robotul


este retras astfel încât mișcarea până în punctele pick și place sa poată fi
efectuată în siguranța, fără a lovi obiectele din jur.
Pașii necesari pentru învățarea operației de Pick and Place:
 Inițializarea sistemului
 Învățarea locației safe
 Învățarea locațiilor pick și place
 Scrierea programului pick.place
 Testarea programului

3.1. Învățarea unei locații


Locațiile pot fi transformări sau puncte de precizie.
Un punct de precizie descrie complet poziția brațului robot prin definirea
poziției individuale a fiecărei articulații. Pentru roboții Cobra, un punct de
precizie are 4 componente (RRPR - rotație, rotație, translație, rotație).
Pentru roboții Viper, un punct de precizie are 6 componente, toate de
rotație. Punctele de precizie sunt marcate prin prefixul "#".
O transformare specifica locația (poziția și orientarea în spațiu) a
efectorului terminal, și are 6 componente: x, y, z, yaw, pitch, roll.
Robotul Cobra poate atinge aceeași locație în doua moduri: fie în
configurația LEFTY, fie în RIGHTY. Exista locații care pot atinse cu
ambele configurații, precum și locații care pot atinse doar în LEFTY sau
doar în RIGHTY.
Cu alte cuvinte, unui punct de precizie îi corespunde întotdeauna o
singura transformare (o singura locație a efectorului terminal), însa unei
transformări (locații) îi pot corespunde mai multe puncte de precizie.
Calculul transformării asociate unui punct de precizie poartă numele de
cinematică directa, și are întotdeauna o soluție unica, iar calculul
punctelor de precizie corespunzătoare unei transformări poartă numele
de cinematică inversa, și de obicei are mai multe soluții (Fig. 2.2). In
majoritatea cazurilor, pentru cinematica inversa se poate alege o soluție
unica prin specificarea configurației robotului.
Cinematică directă

Transformare Punct de precizie


var.loc #var.loc

Cinematică inversă

Figura 2.2. Modelul cinematic

Punctul de precizie este un vector în spațiul articulațiilor, iar


transformarea este un vector în spațiul operațional (de obicei cartezian).

Transformările permit efectuarea de operații în spațiul cartezian


(translații, rotații, compuneri de transformări etc). Punctele de precizie nu
permit acest lucru.

Poziția safe se recomandă a fi învățată ca punct de precizie, deoarece


este unic definită. Pozițiile pick și place se recomand a învățate ca
transformări, unul din motive fiind faptul ca instrucțiunea APPRO nu
acceptă puncte de precizie ca argument.
Poziția se învață astfel:

 Se deplasează robotul în poziția dorita folosind MCP-ul (in ferestra


grafică)
+
 Se introduce de la consola monitor V comanda:

.HERE nume.locatie

 Pentru cele 3 locații, comenzile sunt (comanda se scrie cu litere


mici, aici au folst folosite litere mari pentru a fi mai ușor observate):

.HERE #safe

.HERE pick

.HERE place

 Se confirma învățarea locației cu ENTER (de doua ori dacă este


cazul).

Pozițiile învățate astfel sunt variabile globale și pot fi folosite în program.

3.2. Controlul mișcarii


Instrucțiuni de mișcare
MOVE dst și MOVES dst

- deplasare spre locația destinație dst


APPRO dst z și APPROS dst z

- deplasare deasupra (în sensul negativ al axei ZTOOL) locației dst la o distanța
egală cu z mm.

DEPART d și DEPARTS d
- deplasare deasupra locației curente pe o distanța egală cu d milimetri

Instrucțiunile cu sufixul S (MOVES, APPROS, DEPARTS) executa o


mișcare liniară în spațiul operațional (cartezian), efectorul terminal
deplasându-se pe un segment de dreaptă. Instrucțiunile fără sufixul S
(MOVE, APPRO, DEPART) executa o mișcare liniară în spațiul
articulațiilor, care corespunde unei traiectorii curbilinii în spațiul
operațional.

În timpul unei mișcări liniare în spațiul cartezian, configurația robotului (LEFTY /


RIGHTY, ABOVE / BELOW, FLIP / NOFLIP) nu poate schimbat . Este posibilă
schimbarea configurației doar prin mișcări în spațiul articulațiilor.
Sistemul V+ executa mișcarile robotului în paralel cu execuția
programului. Astfel, o instrucțiune de mișcare (MOVE, MOVES,
APPROS, DEPARTS etc) va returna controlul imediat ce mișcarea
robotului a început. Instrucțiunile urmatoare se vor executa în paralel cu
mișcarea robotului. Pentru a aștepta terminarea mișcarii curente se poate
folosi instrucțiunea BREAK.

3.3. Setarea vitezei


Viteza de lucru a robotului se poate seta în procente fața de viteza
maxima, sau în milimetri pe secunda, cu ajutorul instrucțiunii SPEED:

SPEED 50 ALWAYS - setează viteza generală de lucru la 50% din valoarea


maximă (valabil pentru întregul program)
SPEED 20 - setează viteza generală de lucru la 20% din valoarea maximă
(valabil doar pentru instrucțiunea de mișcare următoare)
SPEED 30 MMPS – stabilește viteza liniar a efectorului terminal la 30
milimetri pe secunda (valabil doar pentru instrucțiunea de mișcare
următoare)

Operațiile de apropiere și depărtare se vor executa cu viteza redusa, așa


cum se face și pentru un robot real, deoarece este necesar poziționarea
precisa a robotului. Deplasarea între punctele pick și place poate făcută
cu viteza mare.
Se poate seta de asemenea viteza monitor, care limitează viteza globală
de lucru. Astfel, o viteza de 20% monitor specificată la consola V +,
combinată cu o viteza de 50 ALWAYS specificată în program, va avea ca
rezultat o viteza efectiva de lucru de 10% din valoarea maximă.
Viteza monitor se specifica cu comanda monitor .speed, care este diferita de
instrucțiunea program SPEED. Astfel, comanda monitor (dată în consolă)
.speed 10 stabilește viteza monitor la 10%, și este echivalent cu .do speed
10 monitor. In program, instrucțiunea SPEED 10 va seta viteza de 10%
pentru următoarea mișcare.

.speed 10 este echivalentă cu: .do speed 10 monitor


î
Viteza monitor (scrisă în consolă) este utila pentru testarea programelor.
In cazul în care programul conține o eroare care duce la coliziune,
rularea cu viteza monitor redusa (de exemplu 5%) permite operatorului a
evita accidentele (virtuale). Dupa ce utilizatorul este sigur ca programul
robot funcționează fară erori, poate crește viteza monitor gradual,
asigurându-se ca mărirea vitezei nu influențează negativ funcționarea
programului.

3.4. Controlul gripper-ului

Gripper-ul este efectorul terminal care permite manipularea pieselor. Pot


exista grippere acționate de un motor electric (mai lente), grippere cu
vacuum (foarte rapide), electromagnetice, electrostatice, pneumatice,
etc.
Gripper-ul nu acționeaz instantaneu (are nevoie de un timp pentru a se
închide sau se deschide). Sistemul V + permite setarea acestui timp
folosind parametrul HAND.TIME. Pentru gripper-ul de pe roboți tip
Cobra, timpul de închidere/deschidere a gripper-ului este de 0.5
secunde. In cazul celorlalți roboți, acest parametru este 0.2 secunde.
Un gripper se poate comanda prin comenzile OPEN / OPENI, CLOSE /
CLOSEI, precum și prin MOVET / MOVEST. Semnificația acestora este:

OPENI / CLOSEI deschide sau închide gripper-ul imediat după terminarea


mișcării curente, și efectuează o temporizare egală cu valoarea parametrului
HAND.TIME.

OPEN / CLOSE deschide sau închide gripper-ul în timpul mișcarii următoare,


fără temporizare.

MOVET dst TRUE / MOVET dst FALSE, precum și MOVEST cu parametri


identici: deschide/închide gripper-ul în timpul mișcarii spre dst. Echivalent cu
OPEN / CLOSE urmat de MOVE / MOVES.

3.5. Testarea programului


+
Testarea unui program V cuprinde următoarele etape:
 Rularea în modul Dry Run, fară a mișca efectiv robotul;
 Rularea cu viteza redusa, de 5%;
 Creșterea gradual a vitezei de lucru: 20, 50, 70 si 100%.

Înainte de rularea efectiva a programului pe robot acesta trebuie testat în


modul Dry Run, fară a mișca robotul. Acest pas permite depistarea erorilor de
logica din program, precum si existenta variabilelor neinițializate sau tastate
greșit.
Acest pas, de testare, se impune în cazul roboților reali. Pentru simulator,
acest pas este opțional deoarece nu se face decât o verificare a programului,
efectul unui program incorect neducând la acțiuni periculoase sau distructive
ale robotului (fiind doar o simulare pe calculator).

Pașii necesari pentru testarea unui program robot:

 Se selectează din MCP un mod de lucru diferit de “Comp”.


Acest lucru previne orice posibilitate de a mișca robotul
accidental din program sau din linia de comand . Pentru mai
multa siguranța, viteza monitor se stabilește la 5%:

.speed 5

 Se activează switch-ul DRY.RUN:

.enable dry.run

 Se executa programul:

.exec numeprogram

 Se verifica mesajele afișate la consola; în cazul în care


exista erori, acestea se corectează. Este utilă afișarea
variabilelor folosite în program cu instrucțiunea TYPE, sau
activarea switch-ului TRACE pentru afișarea liniilor de
program executate.
 După ce programul rulează corect în modul Dry Run, acesta
se rulează cu viteza redusa:

.disable dry.run
.speed 5
.exec numeprogram

 Dacă programul a rulat corect cu viteza redusa, aceasta poate


crescută gradual, de exemplu la 20, 50, 70 si 100%. În timpul
rulării programului, utilizatorul urmărește traseul robotului
dacă este cel dorit.
3.6. Programul Pick and Place

1 .PROGRAM pick.place()
2 ; Laboratorul 2 - Pick and Place & Nume Student & Grupa
3
4 GLOBAL #safe, pick, place ; locatii robot
5 AUTO z.pick
6 AUTO z.place ; variabile locale
7 z.pick = 80
8 z.place = 100
9
10 PARAMETER HAND.TIME = 0.5
11 SPEED 100 ALWAYS
12
13 OPEN
14 MOVE #safe ; incepe miscarea din #safe
15 BREAK ; cu gripper-ul deschis
16
17 APPRO pick, z.pick ; deplasare deasupra
18 BREAK ; punctului ’pick’
19
20 SPEED 50
21 MOVES pick
22 CLOSEI ; prindere piesa
23
24 SPEED 30
25 DEPARTS z.pick
26 BREAK
27
28 APPRO place, z.place ; deplasare deasupra
29 BREAK ; punctului ’place’
30
31 SPEED 20
32 MOVES place
33 OPENI ; asezare piesa
34
35 SPEED 50
36 DEPARTS z.place
37 BREAK
38
39 MOVE #safe ; intoarcere in #safe
40 .END
3.7. Teme

Tema 1

+
Identificați instrucțiunile de mișcare ale limbajului V și grupați-le pe
categorii:
 Instrucțiuni care operează cu transformări
 Instrucțiuni care operează cu puncte de precizie
 Instrucțiuni care operează atât cu transformări, cât și cu puncte de
precizie

Tema 2

Se presupune ca punctul pick poate fi atins doar în configurația LEFTY, iar


punctul place poate atins doar în RIGHTY. In cazul în care un punct nu
poate atins cu configurația curenta, V + va afișa un mesaj de eroare.
Modificați programul pick.place astfel încât sa poată funcționa corect în
situația descrisa mai sus.

Tema 3

 Ce se întâmplă dacă programatorul uita sa seteze parametrul


HAND.TIME? Ce urmări poate avea setarea incorecta a acestui
parametru?

 Ce se întâmplă dacă în loc de OPENI/CLOSEI se folosește


OPEN/CLOSE pe liniile 22 și 33 în programul pick.place?
Modificați programul astfel încât sa funcționeze corect cu
OPEN/CLOSE.

 Ce se întâmplă dacă se folosește OPENI în loc de OPEN pe


linia 13 din programul pick.place?

13
ANEXA 1
Sursa programului Pick & Place

.PROGRAM pp()
; Laboratorul 2 - Pick and Place

GLOBAL #safe, pick, place ; locatii robot


AUTO z.pick
AUTO z.place ; variabile locale
z.pick = 80
z.place = 100

PARAMETER HAND.TIME = 0.5


SPEED 100 ALWAYS

OPEN
MOVE #safe ; incepe miscarea din #safe
BREAK ; cu gripper-ul deschis

APPRO pick, z.pick ; deplasare deasupra


BREAK ; punctului 'pick'

SPEED 50
MOVES pick
CLOSEI ; prindere piesa

SPEED 30
DEPARTS z.pick
BREAK

APPRO place, z.place ; deplasare deasupra


BREAK ; punctului 'place'

SPEED 20
MOVES place
OPENI ; asezare piesa

SPEED 50
DEPARTS z.place
BREAK

MOVE #safe ; intoarcere in #safe

.END

14
Lucrarea 3 Robotica
(stabilirea variabilelor de lucru prin fisierul .env)

Obiectivul lucrari este familiarizarea folosirii fisierului cu extensia .env, pentru stabilirea
variabilelor/mediului de lucru.

1. Pentru inceput se reia programul din lucrarea anterioara (2) si se creeaza fisierul .env.
Comanda folosita este:
.see nume_fisier.env

Pentru definirea variabilelor de miscare, care vor fi accesibile in programele robot, se pot
folosi:
 TRANS – creeaza o variabila de tip transformare
 PPOINT – creeaza un punct de precizie

In editorul notepad2 care apare, se scrie ceva asemanator:

#safe = PPOINT( 106, -86.4, 176, -90, 110, 110)


pick = TRANS(-27.8, 211, 351, -24.2, 78.4, -4.17)
place = TRANS( 672, -289, 351, -24.2, 78.4, -4.17)

Observatie: Deoarece numele de variabile in Python nu poate contine anumite caractere


accesptate de V+. siin V+ toate varibilele se scriu cu litere mici, caracterele speciale sunt
inlocuite dupa cum urmeaza:
. -> O # -> pP_ $ -> sT_

Deci secventa anterioara se scrie (doar pentru simulator nu si pentru comanda reala a unui
robot):
pP_safe = PPOINT( 106, -86.4, 176, -90, 110, 110)
pick = TRANS(-27.8, 211, 351, -24.2, 78.4, -4.17)
place = TRANS( 672, -289, 351, -24.2, 78.4, -4.17)

Daca apar erori ( de exemplu la #safe) se tine cont de cele scrise mai sus.
Pentru coordonatele bratului de robot, se preiau informatiile ce apar in consola (“copy” ->
“paste”), pozitionand bratul de robot pentru cele trei pozitii si folosind comanda <.here>.

• Se deplasează robotul în poziția dorita folosind MCP-ul (in fereastra grafică)


• Se introduce de la consola monitor V + comanda:

.here nume.locatie

Nu este nevoie de alte comenzi in fisierul nume_fisier.env, pentru inceput. Fisierul se


salveaza. Programul asociat este din lucrarea 2 si este prezentat in Anexa I.
Se recomandă o reiniţializare a mediului de programare sau execuţia comenzilor la promptul
(„.”) consolei:
. zero

1
. env nume_fisier.env
. load nume_fisier.v2
. exec nume_fisier
Se urmăreşte şi ca directorul curent să conţină programul dorit. Acest aspect se
stabileste cu ajutorul comenzilor “ls” şi “cd”.
Ieşirea din simulare se face în ferestra de comenzi, prin tasta „ENTER”.
Se urmaresc mesajele din consola si miscarile robotului din fereastra MCP (OpenGL).
Se poate observa executia programului pas cu pas activand switch-ul TRACE.
.enable trace
.exec nume_program

Dupa depanare se executa “.disable trace”.

Daca nu sunt erori se trece la etapa urmatoare.

2. Programul urmator este mai complex. Scopul este de a prezenta multitudinea de


variabile cu care se poate lucra. Un aspect interesant este ca in program se pot folosi
obiecte grafice create in exterior (de exemplu cu extensia .stl) si care pot fi introduse
in mediul de lucru al robotului.
Pentru inceput se creeaza cele doua fisiere , .env si .v2, cu continutul prezentat mai jos
(programul determina ca bratul de robot sa culeaga o “frunza” si sa o arunce; se poate folosi
orige imagine grafica rezonabila dimensional):

Fisierul .env:

#def setup_environment():

#setup_environment()

Frunza = Load("frunza.stl")[0]
Frunza.name = "Frunza"

Frunza.pos = (0.4, 0, 0.01)


#Frunza.scale = (2,2,2)
Frunza.setMaterial(matBlueBox)

Frunza.box = Box(pos=Frunza.pos + (0,-10e-3,2e-3), lx=20e-3, ly=80e-3, lz=4e-3,


mass=0.01, material=matBlueBox)
Frunza.box2 = Box(pos=Frunza.pos + (29.5e-3,0,2e-3), lx=10e-3, ly=30e-3, lz=4e-3,
mass=0.01)
Frunza.box3 = Box(pos=Frunza.pos + (-29.5e-3,0,2e-3), lx=10e-3, ly=30e-3, lz=4e-3,
mass=0.01)

Frunza.fixed_joint = ODEFixedJoint(name="SuperGlue", body1=Frunza.box,


body2=Frunza.box2)
Frunza.fixed_joint2 = ODEFixedJoint(name="SuperGlue2", body1=Frunza.box,
body2=Frunza.box3)

odeSim.add([Frunza.box, Frunza.box2, Frunza.box3, Frunza.fixed_joint,


Frunza.fixed_joint2])

2
link(Frunza, Frunza.box)

Frunza.box.manip.odebody.setAutoDisableFlag(0)

pick = TRANS(400,-45,95,0,180,0)

Frunza.box.visible = False
Frunza.box2.visible = False
Frunza.box3.visible = False

safe = PPOINT(0,-90,180,0,0,0)

Programul principal este :

.PROGRAM throw()

PARAMETER HAND.TIME = 0.1

APPRO pick, 100


MOVES pick
CLOSEI
DEPARTS 100
BREAK

SPEED 50
MOVES SHIFT(HERE BY 0,-300,0)
BREAK

SPEED 100, "MONITOR"


SPEED 100
MOVES SHIFT(HERE BY 0,200,400):RX(-90)
WAIT DISTANCE(HERE, DEST) < 100
OPEN

.END

In aceasta etapa se urmareste functionarea programului si se explica cat mai multe din
comenzile prezente in script.

Referatul laboratorului cuprinde capturi ale celor doua programe (inclusiv fisierele cu
variabilele folosite), explicatii la cateva din comenzile prezente in program si in fisierul de
configurare .env, dar si raspunsurile la intrebari.

Intrebari
1. Cu ce fisiere poate lucra simulatorul (.stl,, .jpg, .png, etc)?
2. Cu ce comanda se schimba culoarea obiectului “aruncat” de simulator?
3. Care este diferenta dintre comanda “MOVES” si “MOVES SHIFT”? Dar diferenta
intre “MOVE” si “MOVES”?

3
4
ANEXA I

1 .PROGRAM pick.place()
2 ; Laboratorul 2 - Pick and Place & Nume Student & Grupa
3
4 GLOBAL #safe, pick, place ; locatii robot
5 AUTO z.pick
6 AUTO z.place ; variabile locale
7 z.pick = 80
8 z.place = 100
9
10 PARAMETER HAND.TIME = 0.5
11 SPEED 100 ALWAYS
12
13 OPEN
14 MOVE #safe ; incepe miscarea din #safe
15 BREAK ; cu gripper-ul deschis
16
17 APPRO pick, z.pick ; deplasare deasupra
18 BREAK ; punctului ’pick’
19
20 SPEED 50
21 MOVES pick
22 CLOSEI ; prindere piesa
23
24 SPEED 30
25 DEPARTS z.pick
26 BREAK
27
28 APPRO place, z.place ; deplasare deasupra
29 BREAK ; punctului ’place’
30
31 SPEED 20
32 MOVES place
33 OPENI; asezare piesa
34
35 SPEED 50
36 DEPARTS z.place
37 BREAK
38
39 MOVE #safe ; intoarcere in #safe
40 .END

5
Laborator X. Semnale de I/O și temporizări
1. Noțiuni generale semnale
Semnalele digitale de intrare/ieșire reprezintă metoda cea mai simplă de comunicație
între robot și echipamentele externe (de exemplu alți roboți, mașini de prelucrare sau
automate programabile), dar și cea mai puțin robustă, deoarece nu se realizează nici o
verificare a datelor transmise.
Un semnal digital transmite un semnal binar, de tip ON sau OFF (TRUE sau FALSE).
Un semnal este FALSE dacă are valoare logică “0” și TRUE dacă are valoarea logică “-1” sau
orice altă valoare.
În V+, semnalele sunt grupate astfel:
 Semnale de intrare: de la 1001 la 1012, de la 1033 la 1512.
 Semnale de ieșire: de la 1 la 8, de la 33 la 512.
 Semnale software interne (intrare/ieșire): de la 2001 la 2512.
Semnalele de ieșire transmit un mesaj de la controller-ul robotului spre un echipament
extern. Prin semnalele de intrare se obțin informații de la echipamentul extern, de aceea
valoarea lor poate fi doar citită. Semnalele software sunt interne (vizibile doar în programele
care rulează pe același controler) și servesc la comunicația dintre task-uri, fiind echivalente ca
funcționalitate cu variabilele globale booleene.
Înainte de utilizare se recomandă definirea adreselor semnalelor folosite, pentru a face
programul mai lizibil și mai ușor de modificat. De exemplu:

out.door_open = 5
out.door_close = 6
in.machine_busy = 1001

Semnalele de ieșire sau cele interne software pot fi setate cu instrucțiunea SIGNAL.
Pentru resetarea unui semnal se poate nega adresa acestuia.

SIGNAL out.door_open
SIGNAL -out.door_close

Se pot seta mai multe semnale simultan, cu o singură instrucțiune:

SIGNAL out.door_open, -out.door_close

SIGNAL acceptă ca argument doar semnale de ieșire sau semnale software. Semnalele
de intrare nu pot fi scrise! SIGNAL este atât comandă monitor (în consolă) cât și instrucțiune
program.
Starea unui semnal se poate citi cu funcția SIG. Citirea unui semnal digital se poate
face cu funcția SIG, având ca argument adresa semnalului citit:

SIG(adresa_semnal)

SIG este o funcție, deci poate fi folosită doar în expresii (de obicei boolene):

TYPE SIG(in.machine_busy) ; afișează 0 (False) sau -1 (True)


sau
IF SIG(in.machine_busy) THEN
………………………………
END
sau
WAIT SIG(in.machine_busy)

Starea semnalului este utilă în expresii condiționale, precum:

IF SIG(in.machine_busy) THEN
TYPE “Machine is busy”
ELSE
TYPE “Machine is ready”
END

Testarea unui semnal în logică negativă se poate face fie cu SIG(-addr), fie cu NOT
SIG(addr), cele două expresii fiind echivalente.
Semnalul de intrare poate fi doar citit!
Se mai poate testa funcția în consolă:

.do type sig(1001)

Funcția SIG poate primi mai multe adrese de semnale ca argumente; în acest caz va
returna operația AND între valorile logice ale intrărilor testate:

SIG(semnal_1, …., semnal_n)

Este echivalent cu

SIG(semnal_1) AND ……AND SIG(semnal_n)

SIG acceptă ca argument orice semnal (intrare, ieșire, software). SIG este funcție și
poate fi folosită doar în expresii.

2. Noțiuni generale temporizatoare


Temporizarea este folosită pentru introducerea unor întârzieri în program. Pentru
temporizare, cea mai simplă implementare folosește WAIT.EVENT în care primul parametru
este omis, iar al doilea reprezintă durata exprimată în secunde:

WAIT.EVENT , 5 ; se așteaptă 5 secunde

În V+ sunt implementate 15 timere globale, numerotate de la 1 la 15, care pot fi scrise


și citite în programele utilizator folosind cuvântul cheie TIMER, care poate fi instrucțiune
program (pentru scriere) sau funcție (pentru citire). Există și timere cu ID-ul negativ,
accesibile doar read-only. Timere-le pornesc odată cu sistemul și nu pot fi oprite. Singurele
operați care se pot face pe timere sunt citirea (pentru orice timer) si scrierea (timere cu id-ul
pozitiv).
Scrierea uni timer sau atribuirea unei anumite valori ( in secunde) pentru un timer
pozitiv se face :

TIMER id = valoare
TIMER 1 = 5.5
Citirea unui timer (in secunde) se obține folosind funcția TIMER:

TIMER(id)
Funcția se poate utiliza doar în cadrul unei expresii V+:

IF TIMER(1) > 10 THEN


……………..
END

Temporizarea egală cu 5 secunde se poate scrie:

TIMER 1 = 0
WAIT TIMER(1) > 5

Se menționează că instrucțiunea WAIT folosește tehnica busy-waiting ce ține


procesorul ocupat ceea ce poate crea probleme în programele multitasking.
Există și instrucțiunea DELAY care efectuează o temporizare pe partea de mișcare (se
execută în paralel cu instrucțiunile de calcul V+) și este implementat sub forma unei
instrucțiuni de tip move-to-here.

DELAY 5

Este echivalent cu

DURATION 5
MOVE DEST ; echivalent cu MOVE HERE doar dacă robotul nu se
mișcă

Se poate genera o așteptare până la îndeplinirea unei condiții care depinde de starea
unui echipament extern. De exemplu:

WAIT SIG(-machine_busy) OR TIMER(1) > 5

Secvența de cod anterioară poate fi implementată pentru un mecanism de timeout.


Dacă pentru deschiderea unei uși sunt necesare maxim 10 secunde și considerăm comanda
ușii prin semnalul 5, iar ușa confirmă deschiderea prin semnalul 1001, codul este:

out.door_open = 5 ; semnal de deschidere robot -> usa


in.door_opened = 1001 ; confirmare usa deschisa
TIMER(1) = 0
SIGNAL out.door_open
WAIT SIG(in.door_opened) OR TIMER(1) > 10
IF SIG(in.door_opened) THEN
…………………………….; usa a fost deschisa
ELSE
…………………………….; eroare
END

Pentru cronometrarea timpului necesar execuției unei seri de operații/instrucțiuni se


poate folosi codul următor:
TIMER 1 = 0
;……………..
TYPE “Timpul de efectuare a fost “, TIMER(1), “ secunde”

Pentru o precizie de o milisecunda, dar pentru un singur task (necesită o încărcare


mare a procesorului) codul poate fi:
TIMER 1 = 0
WAIT TIMER(1) > delay
Sau

TIMER 1 = -delay
WAIT TIMER(1) > 0

Pentru o precizie redusă (16 milisecunde) dar o încărcare redusă a procesorului se


poate folosi:
TIMER 1 = 0
WHILE TIMER(1) < delay DO
WAIT
END
Sau mai simplu folosind WAIT.EVENT

WAIT.EVENT , delay

3. Breviar/Exemple
Semnale
 Semnale de intrare: de la 1001 la 1012, de la 1033 la 1512.
 Semnale de ieșire: de la 1 la 8, de la 33 la 512.
 Semnale software interne (intrare/ieșire): de la 2001 la 2512.
SIGNAL 5 ; activare semnal
SIGNAL -5 ; dezactivare semnal
SIGNAL -4,5,2010 ; activarea semnalelor 5 si 2010 si dezactivarea semnalului 4
SIG(1001) ; testare (citire) semnal 1001
SIG(-1002) ; citire semnal 1002 în logică negativă
WAIT SIG(1001) ; se așteaptă până când semnalul 1001 devine activ

Temporizare
Așteptare de 5 secunde:
WAIT.EVENT , 5
Așteptare până la îndeplinirea unei condiții:
WAIT SIG(1001) OR TIMER(1) > 5

Instrucțiunea WAIT. Sintaxa este


WAIT condiție
Sau
WAIT

Pentru “WAIT condiție”, aceasta pune programul în așteptare până când condiția devine
adevărată. Așteptarea este de tip busy waiting și poate bloca celelalte task-uri. Instrucțiunea
este echivalentă cu:
WHILE NOT (condiție) DO
END

Experimental se poate observa ca editorul rulează mai încet dacă avem activ un task care
execută o instrucțiune de tip “WAIT condiție”.

Pentru “WAIT”, aceasta va suspenda programul circa 16 milisecunde. Nu solicită procesorul.

WHILE ….. DO
WAIT
END

Rezultatul este faptul că evaluarea condiiei de la WHILE se face din 16 în 16 milisecunde.


Așteptarea este tot de tip busy waiting.
Pentru folosirea impreună cu semnale de intrare secvențele de cod sunt echivalente:

WAIT SIG(1001) ; simplu


---------------------------------------------------------------------
WHILE SIG(-1001) DO ; solicită puțin procesorul
WAIT
END
---------------------------------------------------------------------
;sau cu timere:
---------------------------------------------------------------------
WAIT TIMER(1) > 10 ; simplu
---------------------------------------------------------------------
WHILE TIMER(1) < 10 DO ; solicită puțin procesorul
WAIT
END
---------------------------------------------------------------------
; așteptare folosind o condiție mai complexă
; secvența așteaptă până se activează semnalul de intrare 1001
; sau până când timer-ul 1 depășește 10 secunde:
---------------------------------------------------------------------
WAIT SIG(1001) OR TIMER(1) > 10

Informații detaliate pot fi găsite în manualele V+ Language User's Guide si V+ Language


Reference Guide.

Utilizarea MCP (interfața grafică)


MCP-ul (Manual Control Pendant) permite realizarea următoarelor funcții:
 Controlul robotului prin activarea/dezactivarea alimentarii; comanda manuala a
robotului;
 Învățarea locațiilor (punctelor) pentru robot;
 Afișarea poziției curente a robotului (World sau Joint), a punctelor învățate,
vizualizarea semnalelor de I/O si a mesajelor sistem;
 Pornirea si oprirea aplicațiilor;
 Afișarea si editarea variabilelor.
Comanda manuală a robotului

Moduri de lucru:
 Comp: robotul este controlat de programul utilizator
 World: Efectorul terminal al robotului este deplasat în spațiul cartezian World (din
baza robotului) (X/Y /Z) sau rotit (RX/RY /RZ)
 Tool : Deplasarea se realizează în sistemul de coordonate atașat efectorului terminal
(Tool)
 Joint: Se controlează poziția individuala a fiecărei articulații

4. Desfășurarea lucrării
4.1.Pentru început se realizează un program care realizează aprinderea si stingerea unui
semnal de ieșire. Secvența de cod poate să conțină:

delay = 1
SIGNAL 49
TIMER 1 = 0
WAIT TIMER(1) > delay
SIGNAL -49

4.2.Al doilea program este derivat din primul dar cu temporizare ce ocupa mai puțin
procesorul si se urmărește ocuparea procesorului. Cele doua programe se lansează,
separat, si se urmărește daca simulatorul funcționează mai greu (de exemplu se
lansează editorul SEE).

5. Intrebări și exerciții
1. Este corectă instrucțiunea TIMER(1) = 0?
2. Să se realizeze un program care să genereze un joc de lumini de tip deplasare aprins
unu din 3. Cum se poate schimba sensul?

3
ANEXA 1
Exemplu de utilizare semnale și temporizatoare

.PROGRAM blink()

; Test pentru semnale şi timere

AUTO i, delay

FOR i = 1 TO 10

delay = 3/i

SIGNAL 49
TIMER 1 = 0
WAIT TIMER(1) > delay

SIGNAL -49
TIMER 1 = 0
WAIT TIMER(1) > delay

END

.END
Lucrarea 5. Stive verticale
(stive si paletizare)

1. Stive și paletizare
Operația “Pick and place” poate fi aplicată pentru a aranja mai multe piese identice
pe o stivă, o paletă, etc.
O abordare posibilă, dar ineficientă, ar fi învățarea fiecărui punct accesat de robot.
Este posibila învățarea unui singur punct (originea), si cunoscând dimensiunile si orientarea
depozitului, celelalte puncte pot fi calculate.
Pentru aceasta se poate folosi funcția V+ SHIFT, care modifică o locație în Spațiul
cartezian World, doar în translație (orientarea se păstrează).

SET loc.new = SHIFT(loc.old BY dx, dy, dz)

Exemplu: Se dorește preluarea unor piese identice dintr-o stivă verticală pentru care
s-a învățat un singur punct numit baza stivei. Se cunoaște grosimea pieselor h.piesa si
numărul de piese din stiva nr.piese.
Poziția de prindere pentru piesa din vârful stivei este:

SET pick = SHIFT (baza BY 0, 0, (nr.piese-1) * h.piesa)

Se da o stiva cu n piese de înălțime h, pentru care se cunoaște punctul de la baza


acesteia, st.a. Se dorește mutarea tuturor pieselor în stiva a doua, pentru care se cunoaște
punctul robot st.b .
Aplicația începe si se încheie cu robotul în poziția #safe, cu gripperul deschis.

Fig. 1. Stive verticale, unidimensionale


Un exemplu de implementare este:
.PROGRAM stiva.vert()

GLOBAL #safe, st.a, st.b ; locaţii robot


AUTO n, h, i, r
AUTO pick, place

n = 5;
h = 15;

SPEED 100 ALWAYS


MOVET #safe, TRUE ; mişcarea începe din #safe
BREAK ; cu gripper-ul deschis

FOR i = 1 TO n ; r = nr. pieselor rămase în stiva st.a

1
r=n-i+1
SET pick = SHIFT(st.a BY 0, 0, (r - 1) * h);
SET place = SHIFT(st.b BY 0, 0, (i - 1) * h);

CALL pick.place(pick, place)


END

MOVE #safe ; întoarcere în \#safe


.END

2. Desfășurarea lucrării
2.1. Execuția unui program complet
Se rulează programul plc.v2, după ce se încarcă variabilele de mediu din plc.env
(sunt prezentate in anexele 1 si 2). Pentru pornirea programului trebuie apăsat butonul 1033.
Înainte de aceasta se stabilește din care stiva se preiau cărămizile (roșii sau galbene, funcție de
apăsarea sau nu a butonului 1034) si numărul acestora (in binar din combinația butoanelor
1035, 1036, 1037).
Dacă se execută repetat programul, se recomandă inițializarea variabilelor de lucru
de fiecare dată (prin comanda “env plc.env”). După execuție programul comandă ieșirile 49-
52. Se parcurge programul și se analizează fiecare comandă.

Fig. 1. Programul, după execuție (aici nu a fost selectat butonul 1034 si au fost luate piesele roșii)

2.2.Spirala
Sa se realizeze un program care sa formeze o spirala folosind 5 sau 7 piese
paralelipipedice (vezi anexa 3 pentru detalii).
2.3. Construcția unei case
Folosind sursa programului anterior, să se realizeze un program care sa aranjeze in
diferite moduri piesele paralelipipedice sub diferite forme: intâi de o culoare plasate sub
forma unui patrat și continuând cu cele de cealaltă culoare (se poate folosi anexa 4 pentru
inspiratie).
Un exemplu de sursa de program care construieste o casa (de fapt aranjeaza “caramizi”
după un program) este urmatorul (ATENȚIE, trebuie scrisa si procedura, deja clasică
pick.place() :

2
def setup_environment():
createBoxStack(7, pos=(0.3, 0.3, 0.02), material=matRedBox)
createBoxStack(7, pos=(0.3, -0.3, 0.02), material=matYellowBox)
createBoxStack(1, pos=(500E-3, 0, 1.5E-3), size=(100 * 1E-3,200 * 1E-3,3E-3),
material=matPallet, kinematic=True)

setup_environment()

h = 15
a = TRANS(300, 300, 190, 0, 180, 90)
b = TRANS(300,-300, 190, 0, 180, 90)
pP_safe = PPOINT(0,-90,180,0,0,0)
c = TRANS(500, 0, 120, 0, 180, 0)

.PROGRAM casuta_original()
GLOBAL a, b, c, #safe, h
AUTO sursa[4], desti[4]
AUTO i, j, d
d = 15

SPEED 100 ALWAYS


PARAMETER HAND.TIME = 0.2

MOVET #safe, TRUE


BREAK
LEFTY
ABOVE
NOFLIP
FOR i = 0 TO 2
FOR j = 0 TO 1
SET sursa[j] = SHIFT(a BY 0, 0, - h * (2*i + j))
SET desti[j] = SHIFT(c BY 0, d * SIGN(j-1)*2, h*(i*2)):RZ(90)
END
FOR j = 0 TO 1
SET sursa[j+2] = SHIFT (b BY 0, 0, - h * (2*i + j))
SET desti[j+2] = SHIFT (c BY d * SIGN(j-1)*2, 0, h*(i*2+1))
END
FOR j = 0 TO 3
CALL pick.place(sursa[j], desti[j])
END
END
MOVET #safe, TRUE
.END

3
ANEXA 1
Sursa variabile de lucru plc.v2 (fisierul plc.env)
def setup_environment():
createBoxStack(7, pos=(0.3, 0.3, 0.02), material=matRedBox)
createBoxStack(7, pos=(0.3, -0.3, 0.02), material=matYellowBox)
createBoxStack(1, pos=(500E-3, 0, 1.5E-3), size=(100 * 1E-3,200 * 1E-3,3E-3), material=matPallet,
kinematic=True)

SIG(1033,1034,1035,1036, 1037)
SIGNAL(-49,-50,-51,-52)

setup_environment()

pP_safe = PPOINT(0,-90,180,0,0,0)

h = 15
l_storage = TRANS(300, 300, 100, 0, 180, 90)
nl_storage = TRANS(300,-300, 100, 0, 180, 90)
pal = TRANS(500, 0, 103, 0, 180, 0)

print
print """

Fiecare stiva contine cate 7 piese.


Grosimea unei piese: h = 15

Puncte robot:
=============
l_storage, nl_storage: la baza celor doua stive
pal: baza stivei de pe paleta

Semnale:
=======
PLC -> Robot:
* 1033 - comanda inceperea sarcinii pentru robot
- confirma validitatea semnalelor 1034 ... 1036
* 1034 - determina tipul de piese dorit (galbene sau rosii)
* 1035, 1036 - numaul de piese dorit, codificat pe 2 biti (1036 = LSB)

Robot -> PLC:


* 49 - anunta incheierea sarcinii robotului
- confirm validitatea semnalelor 50 ... 52
* 50, 51, 52 - numaul de secunde necesar executarii sarcinii (0...7),
reprezentat ca intreg codificat pe 3 biti (52 = LSB)
"""

4
ANEXA 2
Programul plc.v2 ( include si subprogramul pick&place)
.PROGRAM plc()
; Comunicaţie cu automatul programabil cu ajutorul semnalelor
GLOBAL l_storage, nl_storage, pal, #safe
AUTO pick, place, storage
AUTO p, r, h, d, n, nr
h = 15
nr = 7

in.start_valid = 1033
in.part_type = 1034
in.nr_parts_b0 = 1035
in.nr_parts_b1 = 1036
in.nr_parts_b2 = 1037

out.done_valid = 49
out.time_b2 = 50
out.time_b1 = 51
out.time_b0 = 52

PARAMETER HAND.TIME = 0.5


SPEED 100 ALWAYS
MOVET #safe, TRUE
RIGHTY

SIGNAL (-out.done_valid)
SIGNAL (-out.time_b2)
SIGNAL (-out.time_b1)
SIGNAL (-out.time_b0)

WHILE SIG(-in.start_valid) DO
WAIT
END

; citesc numărul de piese dorit de pe cei trei biti 1035, 1036, 1037:
n=0
IF SIG(in.nr_parts_b0) THEN
n = n+1
END
IF SIG(in.nr_parts_b1) THEN
n = n+2
END
IF SIG(in.nr_parts_b2) THEN
n = n+4
END

; alternativă: deoarece SIG(...) returnează 0 sau -1,


; n = ABS(2*SIG(in.nr_parts_b1) + SIG(in.nr_parts_b0))

; citesc tipul piesei (ce culoare):


IF SIG(in.part_type) THEN
SET storage = nl_storage
ELSE
SET storage = l_storage
END

; execut operaţia şi cronometrez:


TIMER 1 = 0
FOR p = 1 TO n

5
r = nr - p + 1
SET pick = SHIFT(storage BY 0, 0, h * (r-1))
SET place = SHIFT(pal BY 0, 0, h * (p-1))
CALL pick.place(pick, place)
END

MOVE #safe
BREAK

d = TIMER(1)
TYPE "Timpul necesar: ", d, " secunde."

; dacă a durat mai mult de 7 secunde, trimit 7.


IF d > 7 THEN
d=7
END

; decodific valoarea lui d (pe 3 biţi):


d = INT(d)

IF d BAND 4 THEN ; BAND = AND pe biţi


SIGNAL (out.time_b2)
END

IF d BAND 2 THEN
SIGNAL (out.time_b1)
END

IF d BAND 1 THEN
SIGNAL (out.time_b0)
END

; semnalele care indică valoarea lui d sunt valide:


SIGNAL (out.done_valid)

.END

.PROGRAM pick.place(pick, place)


AUTO z.pick, z.place
z.pick = 80
z.place = 80
PARAMETER HAND.TIME = 0.2
OPEN
APPRO pick, z.pick
BREAK
SPEED 50
MOVES pick
CLOSEI
SPEED 30
DEPARTS z.pick
BREAK
APPRO place, z.place
BREAK
SPEED 20
MOVES place
OPENI
SPEED 50
DEPARTS z.place
BREAK
.END

6
ANEXA 3
Spirala (atat fisierul variabilelor de lucru cat si programul propriu zis)

createBoxStack(7, pos=(0.3, 0.3, 0.02), material=matRedBox)

pP_safe = PPOINT(0,-90,180,0,0,0)

h = 15
stOa = TRANS(300, 300, 190, 0, 180, 90)

a = stOa

c = TRANS(400,-200, 100, 0, 180, 180)


print "Stiva contine 7 piese."
print "Grosimea unei piese: h = 15 mm"

.PROGRAM spirala2()
GLOBAL a, b, c, #safe, h
AUTO p, i
total.ang = 360
n=7
SPEED 100 ALWAYS
PARAMETER HAND.TIME = 0.2
OPEN
MOVE #safe
BREAK
FOR p = 1 TO n
i = p-1
SET pick = SHIFT(a BY 0, 0, -h * INT(i))
SET place = SHIFT(c BY 0, 0, h*i):RZ(total.ang * i / n)
CALL pick.place(pick, place)
END
MOVE #safe
print "n=", n
.END

.PROGRAM pick.place(pick,place)
AUTO z.pick, z.place
z.pick = 100
z.place = 100
OPEN
APPRO pick, z.pick
BREAK
SPEED 30
MOVES pick
CLOSEI
SPEED 30
DEPARTS z.pick
BREAK
APPRO place, z.place
BREAK
SPEED 30
MOVES place
OPENI
SPEED 30
DEPARTS z.place
BREAK
.END

7
Anexa 4
(programe pentru realizarea unei casute sub diferite forme)

;Program original

.PROGRAM casuta_original()
GLOBAL a, b, c, #safe, h
AUTO sursa[4], desti[4]
AUTO i, j, d
d = 35

SPEED 100 ALWAYS


PARAMETER HAND.TIME = 0.2

MOVET #safe, TRUE


BREAK
LEFTY
ABOVE
NOFLIP
FOR i = 0 TO 2
FOR j = 0 TO 1
SET sursa[j] = SHIFT(a BY 0, 0, - h * (2*i + j))
SET desti[j] = SHIFT(c BY 0, d * SIGN(j-0.5), h*(i*2)):RZ(90)
END
FOR j = 0 TO 1
SET sursa[j+2] = SHIFT (b BY 0, 0, - h * (2*i + j))
SET desti[j+2] = SHIFT (c BY d*SIGN(j-0.5), 0, h*(i*2+1))
END
FOR j = 0 TO 3
CALL pick.place(sursa[j], desti[j])
END
END
MOVET #safe, TRUE
.END

;Sa schimbe modul in care se face o casuta: intai piesele de o culoare, apoi cele de cealalta culoare.

.PROGRAM casuta1()
GLOBAL a, b, c, #safe, h
AUTO sursa[6], desti[6]
AUTO i, j, d
d = 35

SPEED 100 ALWAYS


PARAMETER HAND.TIME = 0.2

MOVET #safe, TRUE


BREAK
LEFTY
ABOVE
NOFLIP
FOR i = 0 TO 1
IF i == 1 THEN
a=b
END
FOR j = 0 TO 1
SET sursa[j] = SHIFT(a BY 0, 0, - h * (j))
SET desti[j] = SHIFT(c BY i * d * SIGN(j-0.5), (1 - i) * d*SIGN(j-0.5), h*(i*3)):RZ(90 * (1 - i))
END
FOR j = 0 TO 1

8
SET sursa[j+2] = SHIFT (a BY 0, 0, - h * (j + 2))
SET desti[j+2] = SHIFT (c BY (1 - i) * d*SIGN(j-0.5), i * d * SIGN(j-0.5), h*(i*3+1)):RZ(90 * i)
END
FOR j = 0 TO 1
SET sursa[j+4] = SHIFT(a BY 0, 0, - h * (j +4))
SET desti[j+4] = SHIFT(c BY i * d * SIGN(j-0.5), (1 - i) * d*SIGN(j-0.5), h*(i*3+2)):RZ(90 * (1 - i))
END
FOR j = 0 TO 5
CALL pick.place(sursa[j], desti[j])
END

END
MOVET #safe, TRUE
.END

;Sa faca 2 casute in loc de 1: fiecare cu cate o culoare


.PROGRAM casuta2()
GLOBAL a, b, c, #safe, h
AUTO sursa[6], desti[6]
AUTO i, j, d
d = 35

SPEED 100 ALWAYS


PARAMETER HAND.TIME = 0.2

MOVET #safe, TRUE


BREAK
LEFTY
ABOVE
NOFLIP
FOR i = 0 TO 1
IF i == 1 THEN
a=b
END
FOR j = 0 TO 1
SET sursa[j] = SHIFT(a BY 0, 0, - h * (j))
SET desti[j] = SHIFT(c BY i * d * SIGN(j-0.5) + i * 100 - (1 - i) * 100, (1 - i) * d*SIGN(j-0.5), 0):RZ(90
* (1 - i))
END
FOR j = 0 TO 1
SET sursa[j+2] = SHIFT (a BY 0, 0, - h * (j + 2))
SET desti[j+2] = SHIFT (c BY (1 - i) * d*SIGN(j-0.5) + i * 100- (1 - i) * 100, i * d * SIGN(j-0.5),
h):RZ(90 * i)
END
FOR j = 0 TO 1
SET sursa[j+4] = SHIFT(a BY 0, 0, - h * (j +4))
SET desti[j+4] = SHIFT(c BY i * d * SIGN(j-0.5) + i * 100- (1 - i) * 100, (1 - i) * d*SIGN(j-0.5),
h*2):RZ(90 * (1 - i))
END
FOR j = 0 TO 5
CALL pick.place(sursa[j], desti[j])
END

END
MOVET #safe, TRUE
.END

;Se construiesc 2 casute in loc de 1: fiecare cu culori intercalate


.PROGRAM casuta3()
GLOBAL a, b, c, #safe, h

9
AUTO sursa[9], desti[9]
AUTO i, j, d, x, ok
d = 35
x=0
ok = 0

SPEED 100 ALWAYS


PARAMETER HAND.TIME = 0.2

MOVET #safe, TRUE


BREAK
LEFTY
ABOVE
NOFLIP
FOR i = 0 TO 2
FOR j = 0 TO 1
SET sursa[j] = SHIFT(a BY 0, 0, - h * (2*i + j))
SET desti[j] = SHIFT(c BY ok * 130 - (1 - ok) * 70-70, d * SIGN(j-0.5), h*(i*2+1 - ok * 2)):RZ(90)
END
x=x+1
IF x == 3 THEN
ok = 1
END
FOR j = 0 TO 1
SET sursa[j+2] = SHIFT (b BY 0, 0, - h * (2*i + j))
SET desti[j+2] = SHIFT (c BY d*SIGN(j-0.5) + ok * 130- (1 - ok) * 70-70, 0, h*(i*2+1 - ok * 2))
END
x=x+1
FOR j = 0 TO 3
print j, sursa[j], desti[j]
CALL pick.place(sursa[j], desti[j])
END
END
MOVET #safe, TRUE
.END

.PROGRAM pick.place(pick,place)
AUTO z.pick, z.place
z.pick = 100
z.place = 100
OPEN
APPRO pick, z.pick
BREAK
SPEED 30
MOVES pick
CLOSEI
SPEED 30
DEPARTS z.pick
BREAK
APPRO place, z.place
BREAK
SPEED 30
MOVES place
OPENI
SPEED 30
DEPARTS z.place
BREAK
.END

10

S-ar putea să vă placă și