Curs per a professors d’Introducció a LEGO® Mindstorms NXT i EV3

(versió 4.5)

 

UNITAT 7. Variables, rangs, bifurcacions múltiples, blocs d’usuari i càlcul numèric: el control del flux d’un programa.

 

En aquesta unitat didàctica utilitzarem variables per incrementar la potència dels càlculs, realitzar decisions més complexes i interaccionar entre les diferents parts o blocs del programa de forma més sofisticada. Veurem altres blocs com el bloc rang, per diferenciar un rang de les lectures d’un sensor, o el bloc display, per pintar en la finestra del maó intel·ligent, etc. També utilitzarem la bifurcació amb casos múltiples, el bloc “Random” o aleatori, per generar números a l’atzar, i veurem com canviar d’unitat els valors d’un bloc per tal de passar-los a un altre. Finalment, veurem com generar blocs d’usuari o subprogrames per encapsular programes i poder-los cridar en diferents parts d’un programa general, amb o sense arguments i/o variables de sortida.

 

Unitat 7 exercici 1: Bloc display, número a text, aleatori, variable i bifurcació múltiple.

 

Aquest petit exercici mostra com utilitzar i connectar diferents tipus de blocs (display, número a text, aleatori, variable i bifurcació múltiple) per realitzar operacions una mica més complexes i interactuar amb el display del maó intel·ligent, pintant imatges, números i text, en funció de l’estat del programa.

                                                   

Crear un nou programa, per exemple “u7ex1.rbt” i afegir un bloc display. Per defecte, el bloc display mostra una imatge (“Smile 01”). Si compilem i bolquem aquest programa al maó intel·ligent, veurem que no tenim temps per veure-la. Què hem de fer? Cal parar uns instants el programa per tenir temps de veure aquesta imatge. Com ho fem? Senzillament afegint un bloc “Wait” o esperar amb control de temps de 1 segon, per exemple. Podem fer proves amb text i, per exemple, escriure el nostre nom. Veurem que el bloc permet controlar la posició on surt el text.

 

        

 

Seguidament, afegim un bloc “Random” o aleatori a l’inici del programa. Aquest bloc genera un número aleatori entre un rang de valors mínim i màxim, que per defecte està entre 0 i 100. Ara es tracta de veure en la pantalla del maó intel·ligent el número aleatori que tria el bloc, cada cop que es crida el programa. Què hem de fer? Es tracta de connectar la sortida del bloc aleatori amb l’entrada del bloc display.

 

 

Al connectar els blocs el cable de dades apareix trencat. Això és així donat que la sortida del bloc aleatori és numèrica i l’entrada del bloc display és de text, i el programa no es pot compilar. Per tant, cal transformar el valor numèric en un text. Això es realitza mitjançant el bloc “número a text”.

 

 

Aquest programa permet visualitzar en pantalla els diferents valors aleatoris. Seguidament, modifiquem el rang del bloc aleatori per a que triï valors entre 1 i 3. Què hem d’afegir al programa per a que en comptes de veure els números puguem veure diferents cares o imatges (“Simile 01”, “Simile 02” o “Simile 03”) segons el número triat? Necessitem dues bifurcacions niades l’una dins l’altra, amb control de valor de tipus numèric, per discernir entre els tres valors. La primera bifurcació servirà per diferenciar el valor 1 de la resta de valors, per tant, la primera condició serà 1 i la segona pot ser 0 o 2, i la segona bifurcació diferenciarà entre el valor 2 i la resta de valors, que només pot ser 3. Quin problema ens trobem?

 

 

El problema és que no podem connectar la sortida del bloc aleatori amb l’entrada de la bifurcació. Si recordem, això només es pot fer si la bifurcació que hem de creuar amb el cable de dades es representa amb vista plana desactivada.

 

 

En segons quins casos, pot ser interessant visualitzar alhora imatges i texts en la pantalla del maó intel·ligent. En aquest exemple, com podem visualitzar la imatge de la cara i el valor aleatori? Per poder representar varies imatges i texts, el bloc display té l’opció “Clear” d’esborrar o no el display i de triar la posició del text que volem mostrar. Així, cada cop que arranquem el programa, aquest mostrarà la cara i el seu valor numèric corresponent.

 

 

Hi hauria alguna manera de fer el mateix, però mantenint la vista amb el desdoblament de la biga de seqüència? La solució és utilitzar una variable.

 

Una mica de teoria

 

En un programa informàtic, una variable és un símbol que representa un valor no especificat d’un conjunt de valors. Els valors poden estar definits dins d’un rang, per exemple “veritat” o “mentida”, un número sencer o real, un número de 0 a 100, un text, etc. El contrari d’una variable és una constant, que és un símbol o variable que representa un sol valor, per exemple el número PI. El programari permet definir variables i constants.

 

Per defecte, cada programa conté 3 variables predefinides, “Logic 1” pels valors “veritat” o “fals”, “Number 1” per a valors numèrics i “Text 1” per a valors o cadenes de text, i cap constant.

 

Si emmagatzemem el resultat del bloc aleatori en la variable “Number 1”, que hem de posar amb l’acció “Write” o escriure, aleshores podem inserir aquesta variable amb l’acció “Read” o llegir, just abans de la segona bifurcació i connectar-la a aquesta.

 

 

Seguidament veurem exemples més interessants, que permeten recuperar valors de sortida de blocs i passar-los a altres blocs en temps d’execució.

 

Finalment, és interessant mostrar que una bifurcació permet tenir més de dos casos. Per tant, podem refer aquest exemple utilitzant una sola bifurcació? Sí, hem d’utilitzar una bifurcació en vista plana amb control de valor numèric i afegir-li una tercera condició.

 

 

 

Podem observar que el concentrador de dades de la bifurcació sempre té un valor per defecte, que podem canviar, i que permet recollir tots aquells valors no especificats en la resta de condicions. Per tant, si fem que el bloc aleatori retorni més valors, aleshores, la resta de valors passaran per la condició 1, encara que siguin diferents de 1, donat que és la condició per defecte.

 

Ara podem refer l’exemple amb una mica més de complexitat, augmentant el rang del bloc aleatori de 1 a 6, i fer que si els valors són 1 i 2 es mostri la primera imatge, si els valors són 3 i 4 es mostri la segona, i si els valors són 5 i 6 es mostri la tercera imatge. Per això utilitzarem el bloc “Range” o rang i una variable, i intentarem no modificar la bifurcació. Si el número està dins del rang especificat, aleshores el valor de sortida del bloc serà “veritat” o “sí”. Com ho podem fer? La solució és comparar el valor de la variable amb diferents rangs i reescriure-la amb el rang original de 1 a 3. Per això necessitem només 2 blocs rang, donat que si no s’ha reescrit la variable, aleshores s’utilitzarà el valor per defecte de la bifurcació.

 

 

Primer rang de 1 a 2 amb l’operació “Inside Range” o dins del rang

 

Si el valor aleatori està dins el rang especificat, aleshores la bifurcació escriu el valor 1 a la variable “Number 1”. El segon bloc rang compara el valor amb el rang de 3 a 4, i si està dins aquest rang, escriu la variable amb el valor 2. Si el valor aleatori no està dins de cap d’aquests dos rangs, aleshores només pot ser 5 o 6. En aquest últim cas no es necessari escriure la variable a 3, donat que a l’inici del programa s’ha establert amb un valor 0, que és el que s’ha d’utilitzar com a valor per defecte de la bifurcació (també es pot utilitzar el valor 3) per cobrir la resta de casos.

 

 

Coneixements adquirits: L’alumne ha aprés a interactuar amb la pantalla o display del maó intel·ligent, la qual cosa permet un millor control del flux d’un programa, ja que podem utilitzar aquest recurs si no entenem què passa quan estem creant un programa i no funciona, el que s’anomena “debugging” en anglès. L’ús de rangs, variables i bifurcacions de casos múltiples permeten realitzar programes molt més complexes, precisos i adequats per resoldre situacions reals amb multiplicitat d’opcions.

 

Unitat 7 exercici 2: Combinació de blocs “Move” i “Motor” amb rangs, variables i bifurcacions.

 

El bloc rang també pot ser útil per diferenciar rangs de lectures d’un sensor. Per exemple, podem refer l’exercici 2 de la unitat didàctica 5, creant un nou programa, per exemple “u7ex2.rbt”, utilitzant un rang (entre 25 i 75) si la superfície té varis colors que es volen utilitzar per fer girar el robot. El rang tornarà un valor “veritat” o “fals” segons si la lectura del sensor està continguda o no dins del rang especificat. Finalment, només cal connectar aquesta sortida lògica del bloc rang a l’entrada de la bifurcació, que cal que sigui amb control de valor de tipus lògic. En l’exemple s’ha utilitzat un bloc “Move” per fer el gir sobre una roda (o punt) i el bucle s’ha associat al sensor de contacte.

 

 

Com podem fer que el robot vagi endavant i al trobar la línia negra, o xocar contra un obstacle, torni marxa enrere a la posició de sortida, però sense girar?

 

Una alternativa molt compacta és utilitzar una variable lògica i connectar-la a la presa de direcció del concentrador de dades del bloc “Move”. Si el sensor de contacte s’activa, aleshores cal canviar la variable mitjançant l’operació “Not” d’un bloc lògic, per tal de modificar la direcció del bloc “Move”, respecte al sentit de moviment anterior. Observar que, al començar el programa, s’estableix aquesta variable lògica a veritat o “True” per definir el sentit inicial de moviment del robot cap endavant.

 

 

Un altre exercici dinàmic, és construir un rastrejador que augmenti la velocitat en funció del nivell de so detectat pel sensor de so. És a dir, el robot anirà més ràpid quan el so sigui més alt i més lentament quan el so sigui més fluix. Podem partir del rastrejador de l’exercici 2 de la unitat didàctica 5 anterior i fer les modificacions per aconseguir el canvi dinàmic de velocitat segons la lectura del sensor de so, i a més, podem afegir un segon sensor, l’ultrasònic, per sortir del bucle. Podem demanar als alumnes com fer-ho. Els podem ajudar preguntant si creuen que necessitaran una variable, i si és així, on creuen que l’han de fer servir. Finalment, podem veure que per tal de que el robot sempre funcioni, cal afegir una velocitat mínima. Com ho podem fer? Senzillament, incrementant la lectura del sensor de so amb un valor constant, mitjançant un bloc matemàtic, en aquest exemple 20 unitats.

 

 

Recordem que si visualitzem la bifurcació en “vista plana” podem connectar directament la sortida del bloc matemàtic a l’entrada de cada bloc motor dins de cada pestanya, creuant la bifurcació.

 

 

Per finalitzar, podem afegir al programa la possibilitat de fer un gir sobre una roda, o sobre l’eix del robot, si aquest xoca contra un obstacle o un altre robot que també estigui rastrejant.

 

 

Coneixements adquirits: Els exemples anteriors mostrem com l’ús de variables permet modificar el comportament d’un programa de forma dinàmica o en “temps d’execució” del programa. És a dir, el robot pot prendre decisions dinàmiques en funció de les diferents lectures combinades dels seus sensors. Per això, les bifurcacions múltiples amb control de valor numèric o lògic controlades per variables obren un gran joc de possibilitats infinites.

 

Si pensem en un programa més llarg que els exemples anteriors, potser necessitarem utilitzar el rastrejador en varis llocs del programa. Aquest és el cas de l’exercici incremental 5 ó 6, on podem veure que la part del codi del rastrejador es va copiant literalment a mida que es necessita, és a dir, després de cada gir. Com podem evitar copiar el codi i fer-lo més compacte i estructurat? La solució està en definir subprogrames que es puguin cridar dins d’un programa principal, en aquest cas mitjançant els “blocs d’usuari”. La guia d’usuari explica en detall com crear els blocs d’usuari, i és un procés relativament simple.

 

Una mica de teoria

 

Els blocs d’usuari són subprogrames que permeten encapsular un conjunt d’ordres per ser executades en diferents llocs d’un mateix programa o en diferents programes. Els subprogrames permeten estructurar el codi i compactar-lo, de tal manera que ocupa menys espai, i al mateix temps el fa molt més intel·ligible. A més, els subprogrames poden tenir variables d’entrada i de sortida de dades, la qual cosa permet una gran potència de càlcul i interacció entre les parts d’un programa informàtic.

 

Hem vist la definició de variable i de constant, però una característica indispensable dels programes o subprogrames és la possibilitat d’incloure arguments. Un argument és una variable que rep un programa en el moment en que se’l crida i permet alterar el comportament del programa en el moment en que aquest s’executa o en “temps d’execució”. Les preses d’entrada del concentrador de dades d’un bloc s’anomenen arguments del bloc. Les preses de sortida representen les variables que retorna el bloc un cop s’ha executat.

 

Per continuar l’exercici anterior, podem crear un bloc d’usuari només amb la part del rastrejador, és a dir, amb el codi que serveix per seguir la línia. Per això seleccionarem el codi a partir del bloc matemàtic fins la primera bifurcació. Seguidament seleccionem “Make a New My Block” o crear un bloc nou, en el menú “Edit”. Aquí haurem d’introduir, com a mínim el nom, per exemple “rastrejador”, i podrem afegir una descripció i una icona, si fem clic al botó “Next” o “següent”.

 

 

Un cop fet això, veurem com en el programa, el codi del rastrejador queda substituït per un nou bloc anomenat “rastrejador”. És important observar que aquest bloc apareix connectat al nivell de so del bloc del sensor de so mitjançant un argument que anomena “Volume” o volum.

 

 

Concentrador de dades del bloc d’usuari creat

 

On ha anat a parar el subprograma amb el rastrejador? Si no es fan canvis en la instal·lació, el bloc d’usuari s’emmagatzema en la carpeta “My Blocks” de cada usuari. Si obrim el bloc d’usuari creat, aleshores podem veure el seu codi i l’argument creat anomenat “Volume” a l’inici del programa.

 

 

Suggeriment: Els blocs d’usuari poden tenir o no variables d’entrada o arguments i variables de sortida de dades. Si tenen variables, aleshores, és important que totes estiguin ben connectades amb els corresponents cables de dades, per tal de poder-los creuar al seleccionar els blocs que inclourem en el nou bloc d’usuari. Altrament no es crearan les variables. Si després de definir un bloc d’usuari volem afegir una nova variable, cal tornar a crear el bloc. Abans de tornar a crear el bloc, és recomanable esborrar la versió antiga per poder reutilitzar el mateix nom.

Per defecte els arguments o variables d’entrada del bloc utilitzen noms per defecte que podem modificar editant-los. Per veure aquests canvis, de vegades, caldrà entrar i sortir del programa.

Si bé els blocs d’usuari es creen en la carpeta “My Blocks”, els podem canviar de lloc. Per ubicar un bloc d’usuari en una carpeta diferent, cal canviar-lo de lloc abans d’obrir el programa principal. Al obrir el programa principal, el sistema intenta trobar el bloc d’usuari. Un cop trobat obre el programa principal. Aleshores, és convenient fer doble clic al bloc d’usuari per obrir-lo, salvar-lo amb la nova adreça, tancar-lo i finalment salvar el programa principal, que recordarà aquesta nova adreça del bloc d’usuari. Si el programa principal no troba el bloc d’usuari, el boc apareix marcat com a trencat en el programa principal, i cal trobar-lo o tornar-lo a crear.

 

Exercici incremental 7: Arribar al final de la gàbia rastrejant les línies negres i compactant el codi amb un bloc d’usuari.

 

Abans de començar, podem copiar l’exercici incremental 5 anterior i posar-li un nom diferent, per tal de mantenir una còpia.

 

Un exercici molt útil és fer que els alumnes discuteixin sobre com crear el bloc d’usuari, quins blocs incloure, si necessiten variables, etc. L’exercici es pot repetir amb el bucle controlat pel temps i el bucle controlat pel sensor de rotacions intern.

 

Respecte del rastrejador amb control de temps, hem de crear el bloc amb una variable per “passar” i emmagatzemar el temps de rastreig desitjat. Un cop fet això, cal esbrinar com podem utilitzar aquesta variable d’entrada o argument per tal d’interrompre el bucle. La solució és comparar el temps transcorregut, mitjançant el bloc “Timer” o temporitzador, amb el temps de rastreig de l’argument del bloc. Si el temps de rastreig és superior al de l’argument, aleshores cal sortir del bucle. Observar que pels blocs, la unitat de temps s’expressa en mil·lèsimes de segon, per tant, caldrà o bé definir l’argument de temps en aquesta unitat, o bé expressar-lo en segons i multiplicar l’argument per 1000, dins del bloc d’usuari, tal com es mostra en aquest exemple. També és important ressaltar que el bloc temporitzador sempre s’ha de reiniciar abans d’entrar dins del bucle. Què passarà si no ho fem? Cal deixar que els alumnes facin proves i entenguin correctament tot el procés de disseny del programa.

 

 

 

De forma similar, podem definir un bloc d’usuari pel gir sobre eix, on tindrem com a arguments la direcció de gir del robot cap a la dreta o cap a l’esquerra, que pels motors serà endavant o enrere (valor lògic), i els graus de gir (valor numèric). El procés per generar aquest nou bloc d’usuari és el següent:

 

Primer pas, definir les variables per poder generar els arguments del bloc

 

Segon pas, definir el bloc d’usuari que anomenarem “GirSobreEixBC”

(si es vol, abans podem acabar de definir el bloc)

 

Tercer pas, esborrar les variables, editar els arguments del bloc d’usuari inserit i substituir la resta de blocs, afegint els valors correctes dels dos arguments

 

Cal assumir, per exemple, que si la direcció de gir és la del motor B, aleshores quan el motor B va cap a endavant el robot gira cap a l’esquerra i quan va enrere gira cap a la dreta.

 

Concentradors de dades de les dues insercions del bloc d’usuari “rastrejador”

    

 

Finalment, el més important, definir el bloc d’usuari, que obrirem fent doble clic a la seva icona inserida en el programa principal.

 

Bloc d’usuari “GirSobreEixBC” definit segons les passes anteriors

 

Veiem que l’únic que cal fer és connectar l’argument de direcció al bloc del motor B, i cal invertir la direcció del motor C respecte de la del motor B. Com ho hem de fer? Senzillament afegint un bloc lògic amb l’operació “Not” abans de connectar l’argument de direcció a la presa del concentrador de dades del bloc del motor C.

 

Versió final del bloc d’usuari “GirSobreEixBC”

 

Suggeriment: Per evitar el creuament de cables de dades i obtenir un codi més “net”, un cop la presa d’entrada de dades d’un bloc està utilitzada, podem utilitzar la mateixa presa, però de sortida de dades del concentrador del bloc, per traspassar el seu valor cap a un altre bloc, com mostra l’exemple anterior amb el cable de dades lògic de color verd que connecta la presa de sortida de direcció del bloc del motor B amb la presa d’entrada A del bloc lògic.

 

Per acabar, podem realitzar les mateixes operacions, però amb el rastrejador controlat pel sensor de rotacions intern del motor.

 

Tal com hem fet anteriorment, abans de començar, podem copiar l’exercici incremental 6 anterior i posar-li un nom diferent, per tal de mantenir una còpia.

 

Quin és el repte d’aquest exercici? Com hem vist, segons cap a on ha de girar el robot, hem de controlar les rotacions del port B o les del port C. Per tant, l’opció més fàcil és crear 2 blocs d’usuari.

 

Bloc d’usuari inicial “RastrejadorRotacions”, on es controla el port B

 

És important observar que en el bloc d’usuari s’inclouen els 2 blocs finals per frenar el robot. Què passaria si no ho féssim? Si no es frenen els motors, el robot no frenarà una de les rodes al sortir del rastrejador. Per tant, si tampoc s’inclouen aquests blocs en el programa principal, el robot no tindrà prou precisió en els girs que realitza posteriorment.

 

Però, com podem generalitzar el programa per crear un sol bloc? Si volem utilitzar un sol bloc, aleshores necessitem un argument per informar del port que volem controlar cada cop que volem rastrejar. En una segona versió d’aquest bloc d’usuari podem incloure un argument pel port del bloc que volem controlar, i que podem anomenar “Port”. En el maó intel·ligent els ports es diferencien pel seu número, el port A = 1, B = 2 i C = 3. Per tant l’argument “Port” serà del tipus numèric. A més, donat que tenim 2 ports, hem d’utilitzar una variable numèrica, per exemple “Number 1”, per tal d’emmagatzemar el número del port del segon motor, de tal manera que si “Port” = 2 (port B), “Number 1” = 3 (port C) i si “Port” = 3 (port C), “Number 1” = 2 (port B).

 

Un cop hem afegit la primera variable al programa principal podem crear el bloc. Un cop hem creat el bloc, podem editar-lo per canviar el nom de l’argument per “Port” i modificar el programa per utilitzar aquest nou argument.

 

Primera versió del bloc d’usuari “RastrejadorRotacions”

amb l’argument “Port” i la variable “Number 1”

 

Podem observar que a l’inici del bloc s’estableix la nova variable “Number 1” que utilitzarem pel port del segon bloc “Motor”, en funció del valor de l’argument del port inicial, que utilitzarem com a primer port. Per realitzar el nou bloc, podem veure que només cal connectar tots els blocs que utilitzen el port B a l’argument “Port” i els que utilitzen el port C a la variable “Number 1”. La segona biga de seqüència d’aquest bloc es mostra més endavant.

 

En principi el programa està acabat, però si deixem el programa tal com s’ha mostrat anteriorment, tot i que el podrem compilar, veurem que al reobrir-lo tots els blocs d’usuari es mostraran trencats.

 

 

Suggeriment: Al crear blocs d’usuari que continguin bifurcacions i bucles, cal evitar cables de dades lògics que creuin alguna bifurcació i es connectin al bucle, per tal d’assegurar que el bloc d’usuari es compila correctament i que el programa obre correctament aquest bloc, sense mostrar-lo trencat. Aquest truc permet controlar un error del programari, que persisteix en la versió 2.

 

Per tant, per tal d’evitar que els blocs es mostrin trencats, caldrà utilitzar una segona variable lògica, “Logic 1”, que inicialitzarem com a “fals” i que ens permetrà controlar la sortida del bucle, quan el motor que volem controlar superi les rotacions establertes.

 

Versió final del bloc d’usuari “RastrejadorRotacions”

amb l’argument “Port” i les variables “Number 1” i “Logic 1”

 

 

Versió final del programa principal

 

Coneixements adquirits: Els blocs d’usuari, combinats amb tots els conceptes anteriorment estudiats, permeten il·lustrar la gran potència del concepte de subprograma, que permet entendre la programació com la combinació “intel·ligent” d’un conjunt infinit de funcions i programes bàsics, o programes definits pel propi usuari. Per tant, els programes es poden compartir i reutilitzar, tant per crear nous programes com per compactar el codi d’un programa principal, alhora que se’l fa més intel·ligible i més fàcil de depurar.

 

 

 

Creative Commons License

Curs d'introducció a LEGO® Mindstorms NXT i EV3 by Josep Maria Fargas is licensed under a Creative Commons Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España License.

Permissions beyond the scope of this license may be available at www.bogatech.org.