Adaptació de les pràctiques¶
Simulació de dispositius¶
Suposem que heu d’utilitzar un sensor de distància via ultrasons. Per exemple, el HCSR04. Explorant per internet podeu trobar exemples d’ús, com per exemple aquest enllaç, o aquest altre enllaç. El codi present en aquests portals per gestionar el dispositiu HCSR04 es pot reestructurar dissenyant una classe com la que segueix:
import RPi.GPIO as GPIO
import time
class HCSR04:
def __init__(self, pinTrigger, pinEcho):
#GPIO Mode (BOARD / BCM)
GPIO.setmode(GPIO.BCM)
self.GPIO_TRIGGER = pinTrigger
self.GPIO_ECHO = pinEcho
#set GPIO direction (IN / OUT)
GPIO.setup(self.GPIO_TRIGGER, GPIO.OUT)
GPIO.setup(self.GPIO_ECHO, GPIO.IN)
def __iter__(self):
while True:
yield self.llegeix()
def llegeix(self):
GPIO.output(self.GPIO_TRIGGER, True)
# set Trigger after 0.01ms to LOW
time.sleep(0.00001)
GPIO.output(self.GPIO_TRIGGER, False)
StartTime = time.time()
StopTime = time.time()
# save StartTime
while GPIO.input(self.GPIO_ECHO) == 0:
StartTime = time.time()
# save time of arrival
while GPIO.input(self.GPIO_ECHO) == 1:
StopTime = time.time()
# time difference between start and arrival
TimeElapsed = StopTime - StartTime
# multiply with the sonic speed (34300 cm/s)
# and divide by 2, because there and back
distance = (TimeElapsed * 34300) / 2
return distance
def tanca(self):
GPIO.cleanup()
El podeu descarregar a hcsr04.py. En raspberrypi.org trobareu informació sobre el mòdul GPIO i el connector d’entrades/sortides de la raspberrypi.
Bàsicament tots els sensors o dispositius tenen el següent esquema d’interacció:
class Dispositiu:
def __init__(self, dadesInicials):
pass
def __iter__(self):
while True:
yield self.llegir()
def fixa(self, paràmetre):
pass
def llegir(self):
pass
def escriure(self, dada):
pass
def tancar(self):
pass
L’especificació genèrica és:
- class dispositiu.Dispositiu(dadesInicials)¶
Torna un objecte que a partir de dadesInicials, inicialitza i configura el dispositiu que representa, i estarà apunt per gestionar-lo amb els seus mètodes,
Mètodes
- dispositiu.fixa(paràmetres)¶
A partir dels paràmetres condiciona el comportament futur del sensor
- dispositiu.llegeix()¶
Retorna la dada obtinguda pel dispositiu
- dispositiu.escriu(dades)¶
Envia dades al dispositiu per que aquest ho projecti cap a fora.
- dispositiu.tanca()¶
Desactiva el dispositiu
A més, la classe suporta la funció iter que retorna un iterador que lliura les dades que el dispositiu va obtenint.
A partir d’aquest esquema, aconseguim encapsular i concentrar tota la gestió i interacció del dispositiu o sensor en un objecte de la classe. D’aquesta manera, les particularitats del dispositiu no es dispersen en el codi del programa general que gestioni el dispositiu. Si més endavant canviem de dispositiu, només caldrà canviar la implementació de la classe, i no caldrà canviar pas la resta del programa.
Donades les circumstàncies, molts no tindreu el hardware necessari, i probablement no teniu ni una raspberry al vostre abast. Ho podem fer virtualment instal·lant raspbian en una VirtualBox. Per completar el projecte el que cal es substituir el codi de les classes de sensors per un codi que ho simula sense canviar la resta del programa. Per exemple, en el cas de l’exemple, generem dades de distància i les guardem en un fitxer per després llegir-les com si el sensor les obtingués. El codi que substituiria l’original quedaria:
import time
class HCSR04:
def __init__(self, pinTrigger, pinEcho):
self.f = open('hcsr04.dat','r')
def __iter__(self):
for dada in self.f:
yield float(dada)
def llegeix(self):
return float(self.f.readline())
def tanca(self):
self.f.close()
I un programa de prova que serviria pels dos dissenys del sensor HCSR04,
from hcsr04s import HCSR04
import time
def programa():
sensorDistància = HCSR04(22, 27)
try:
for dist in sensorDistància:
print ("Distància = {:.1f} cm".format(dist))
time.sleep(1)
# aturada per control + 'C'
except KeyboardInterrupt:
pass
print("Fi mesures")
sensorDistància.tanca()
if __name__ == '__main__':
programa()
Us ho podeu descarregar a hcsr04s.py i programa2.py. Si ho voleu provar, editeu un fitxer hcsr04.dat amb dades de distància, una per línia. Un exemple d’ús el teniu aquí (test-hcsr04.txt):
>>> from programa2 import programa
>>> dades = open('hcsr04.dat','w')
>>> bytes = dades.write("""3
... 5
... 7
... 2
... 1
... 2
... 3
... """)
>>> dades.close()
>>> programa()
Distància = 3.0 cm
Distància = 5.0 cm
Distància = 7.0 cm
Distància = 2.0 cm
Distància = 1.0 cm
Distància = 2.0 cm
Distància = 3.0 cm
Fi mesures
No cal que sigui un fitxer de dades les dades a adquirir, També les podem generar de forma aleatòria com per exemple (hcsr04s2.py) ,
import random
class HCSR04:
def __init__(self, pinTrigger, pinEcho):
random.seed(255) # per poder repetir la seqüència aleatòria
def __iter__(self):
while True:
yield self.llegeix()
def llegeix(self):
return random.uniform(1,30) # genera un número entre 1 i 30 cm
def tanca(self):
pass
O usant una funció (hcsr04s3.py) ,
import math
class HCSR04:
def __init__(self, pinTrigger, pinEcho):
self.f = lambda x: 1.0 + 30 * (math.sin(x))**2
self.x = 0.0
def __iter__(self):
while True:
yield self.llegeix()
def llegeix(self):
r = self.f(self.x)
self.x = self.x + 0.5
return r
def tanca(self):
pass
Actualitzant la importació en el programa de prova, podreu veure com funciona aquestes darreres adaptacions. Recordeu d’aturar el flux prement les tecles control i C simultàniament.
L’adaptació exposada és extensiva per la resta de dispositius. Pel cas d’una càmera és el mateix (En picamera, capture correspon al mètode llegeix, start_preview és el mètode fixa, etc). Ho podem simular fent una gravació de vídeo, i després fer veure que aquesta gravació és el que veu la càmera.
Pel cas de leds (per exemple, veure aquí) el mètode escriu tindria un paràmetre que seria ON o OFF depenent de si volem encendre’l o apagar-lo. Podríem posar en classe un atribut que controli si està encès o apagat.
Pel cas de motors, llegeix seria per obtenir posició del motor, i escriu seria per fixar la posició del motor.
Els leds i els motors es podrien simular usant el paquet pygame. Teniu una introducció de pygame a aquesta pàgina. La documentació completa la trobareu al lloc oficial de pygame.
Cada dispositiu hauria de ser un sprite (i per tant, la classe haurà de heretar de la classe Sprite. Cada cop que modifiquem l’estat del dispositiu, actualitzem la pantalla.
Ús del control de versions¶
Recordeu que tots esteu donats d’alta al portal http://jocs.lsi.upc.edu:5000/. Amb les circumstàncies actuals de confinament, que res millor que fer-ne un ús intensiu del gestor de dipòsits per provar si és una eina adequada per seguir treballant en equip.
En aquesta altra pàgina trobareu indicacions de com treballar en la plataforma kalithea de gestió de dipòsits.
En aquesta pàgina trobareu un exemple de com treballar amb Mercurial.
En la documentació final dels projectes us demanaré que dediqueu mitja pàgina dedicada a descriure com ha estat la vostra experiència de col·laboració amb mercurial/kallithea (avantatges, crítica, dificultats, etc).