Es tracta de mantenir l'objecte detectat en el centre de la pantalla.
La resolució de pantalla de HUSKYLENS és de 320x240 px.
L'objecte està en el centre de la pantalla si les coordenades del centre del rectangle que ocupa són (160, 120).
Quan la HuskyLens detecta un objecte, retorna un diccionari amb informació de l’objecte.
La instrucció bloc = husky.get_block() retorna:
bloc = {"x":120, "y":80, "w":60, "h":50, "id":1}
Per accedir a les dades:
x = bloc ["x"] # posició hoitzontal del centre del rectangle detectat
y = bloc ["y"] # posició vertical
w= bloc ["w"] # amplada del rectangle → serveix per saber si està a prop
h = bloc ["h"] # alçada
id = bloc ["id"]
Coordenada X de l'objecte detectat
from microbit import sleep
import husky
husky.knock() # convé començar comprovant que la càmera hi és
husky.set_algorithm(husky.OBJECT) # mode de visió
sleep(500)
while True:
bloc = husky.get_block (timeout=300) # espera màxim 300 ms per trobar un objecte
if bloc:
display.scroll (bloc["x"]) # bloc["x"] retorna la posició horitzontal de l'objecte
sleep(100)
Fem-m'ho més visual 👇
mostra x, w, h i una barra al display del micro:bit que indiqui la posició X de l’objecte
from microbit import *
import husky
# Inicialitzar càmera
husky.knock()
husky.set_algorithm(husky.OBJECT)
sleep(500)
while True:
bloc = husky.get_block(timeout=300)
if bloc:
x = bloc["x"] # posició horitzontal
# w = bloc["w"] # amplada
# h = bloc["h"] # alçada
# print("x:", x, " w:", w, " h:", h) # imprimir per consola
# --- barra visual al microbit ---
display.clear()
# convertim x (0..320) → columna 0..4
col = int(x / 64) # 320/5 ≈ 64
if col < 0:
col = 0
if col > 4:
col = 4
# dibuixa punt vertical
for y in range(5):
display.set_pixel(col, y, 9)
else:
# print("No veu objecte")
display.show(Image.NO)
sleep(200)
Comportament del robot
Ha de treballar amb 3 estats:
A) TRACK (veu l’objecte)
calcula error_x = x_obj - centre
gira proporcionalment a l’error (per centrar-lo)
avança si està prou centrat
para si està “massa a prop” (amplada excesiva)
B) LOST (objecte perdut)
deixa d’avançar
gira lentament cap a l’últim costat on l’havies vist (memòria)
si en X segons no el trobes → fes una cerca: gira 360° (o alterna esquerra/dreta)
C) FOUND (transició)
quan reapareix, torna a TRACK
diagrama de flux
test.py perquè el robot:
centri l’objecte amb girar()
avanci amb caminar()
si el perd, pari i busqui girant cap a l’últim costat on l’havia vist
quan el recupera, continua avançant
from microbit import *
import xgo
import husky
import radio
import music
# --- Paràmetres HuskyLens (ajustar si cal) ---
IMG_W = 320 # ample habitual HuskyLens (si el teu difereix, canvia-ho)
CENTER_X = IMG_W // 2
# --- Tuning (ajustar amb proves) ---
DEADBAND = 14 # pixels: zona "OK" sense corregir
KP = 0.25 # força de correcció (0.15 - 0.40 típic)
BASE_FWD = 0x92 # velocitat endavant (0x80 aturat; >0x80 endavant)
MIN_FWD = 0x86 # mínim endavant quan està descentrat
TURN_MIN = 10 # mínim gir (0..127 aprox, depèn implementació)
TURN_MAX = 55 # màxim gir
# "massa a prop": fem servir l'ample del bounding box (w)
NEAR_W = 120 # si w supera això, ja estàs molt a prop -> para (ajusta!)
# Cerca quan es perd
LOST_GRACE_MS = 450 # quant temps tolerem pèrdua abans de buscar de veritat
SEARCH_TURN = 0xA5 # valor per girar durant cerca (0x80 neutre; >/< depèn del teu xgo)
SEARCH_STEP_MS = 120 # pas de cerca
# Nota: en el teu xgo.py, girar(valor) posa 0x30=0x80 i escriu valor a 0x32.
# Habitualment 0x80 és neutral. Si el gir et surt invertit, canvia SIGN a -1.
SIGN = 1 # posa -1 si gira al revés
def clamp(v, lo, hi): # limita els valors dins d'un rang
return max(lo, min(v, hi))
def turn_command(turn_strength, direction):
"""
turn_strength: 0..TURN_MAX
direction: -1 esquerra, +1 dreta
Retorna un 'valor' per xgo.girar(valor)
"""
# Map: 0x80 neutral; +strength a un costat, -strength a l'altre
# Si el teu robot gira al revés, ajusta SIGN a -1.
delta = SIGN * direction * turn_strength
return clamp(0x80 + int(delta), 0x00, 0xFF)
def forward_command(err_abs):
"""
Redueix l'endavant si està molt descentrat.
"""
# Factor 1 quan està centrat, 0 quan err_abs és gran
factor = 1.0 - min(1.0, err_abs / CENTER_X)
v = int(MIN_FWD + (BASE_FWD - MIN_FWD) * factor)
return clamp(v, 0x80, 0xFF)
# --- Inicialització HuskyLens ---
ok = husky.knock()
sleep(100)
# Mode: OBJECT (aprenentatge d'objecte). Si vols TRACK, canvia-ho.
husky.set_algorithm(husky.OBJECT)
sleep(200)
# Opcional: si vols que la HuskyLens "aprengui" l'objecte ID 1 abans de començar
# (fes-ho només quan tinguis l'objecte al centre i a prop)
# husky.learn(1)
# sleep(300)
last_seen_dir = 1 # +1 dreta, -1 esquerra
last_seen_time = running_time()
while True:
b = husky.get_block(timeout=300)
if b is not None:
x = b["x"]
w = b["w"]
# Memòria de direcció i temps
err = x - CENTER_X
if err >= 0:
last_seen_dir = 1
else:
last_seen_dir = -1
last_seen_time = running_time()
# Si està massa a prop, para
if w >= NEAR_W:
xgo.stop()
sleep(60)
continue
err_abs = abs(err)
# Decideix gir i endavant
if err_abs <= DEADBAND:
# centrat: avança recte
xgo.caminar(forward_command(err_abs))
else:
# gira per centrar
# intensitat proporcional a l'error
strength = int(KP * err_abs)
strength = clamp(strength, TURN_MIN, TURN_MAX)
cmd = turn_command(strength, 1 if err > 0 else -1)
xgo.girar(cmd)
# i avança una mica (opcional) quan corregeix
# si vols que sigui més estable, comenta la línia següent i només gira
xgo.caminar(forward_command(err_abs))
sleep(60)
else:
# Objecte no detectat
now = running_time()
dt = now - last_seen_time
if dt < LOST_GRACE_MS:
# pèrdua breu: para un moment (evita oscil·lacions)
xgo.stop()
sleep(60)
else:
# cerca: gira cap a l’últim costat on el vam veure
xgo.stop()
sleep(30)
cmd = turn_command(35, last_seen_dir) # 35 força de cerca suau
xgo.girar(cmd)
sleep(SEARCH_STEP_MS)
Ajustos ràpids (els que sempre cal tocar)
Si gira al revés (quan l’objecte és a la dreta el robot gira a l’esquerra):
canvia SIGN = 1 a SIGN = -1.
Si no s’acosta prou:
puja BASE_FWD (p.ex. 0x96 o 0x9A)
baixa una mica DEADBAND
Si s’apropa massa i l’enxampa:
baixa BASE_FWD
baixa NEAR_W o posa-hi una aturada més aviat
Si “balla” esquerra-dreta:
baixa KP
puja DEADBAND
i/o fes que quan corregeix només giri (comentant el xgo.caminar() dins la correcció)
Si em dius quin valor de x et retorna quan l’objecte és al centre (aprox) i quin rang veus (0..319 o 0..239), t’ajusto IMG_W/CENTER_X i els guanys perquè vagi encara més fi. També: saps si la teva HuskyLens està en resolució 320×240 o una altra?
test.py simplificat:
si veu l’objecte: gira una mica cap on toca i avança
si no el veu: para i gira fins que el torni a trobar
from microbit import *
import xgo
import husky
# 1) Configura la HuskyLens
husky.knock()
husky.set_algorithm(husky.OBJECT) # OBJECT: seguiment d'un objecte
sleep(200)
# 2) Constants
CENTRE = 160 # si la imatge és de 320px d'ample (0..319)
MARGE = 20 # zona "gairebé centrat"
VELOCITAT = 0x92 # 0x80 = aturat, més = endavant
# Gir: 0x80 és neutre; menor / major gira (segons el teu robot pot estar invertit)
GIR_ESQ = 0x60
GIR_DRE = 0xA0
# Cerca quan el perd
CERCAR_ESQ = 0x68
CERCAR_DRE = 0x98
ultim_costat = 1 # 1 = dreta, -1 = esquerra
while True:
bloc = husky.get_block(timeout=300) # torna None si no veu res
if bloc: # si veu l'objecte
x = bloc["x"]
w = bloc["w"]
# Si és molt a prop -> parar
if w > 120: # ajusta 120 segons el teu objecte
xgo.stop()
sleep(100)
continue # deixa d’executar el que queda del bucle i torna a començar des de dalt
# Decideix a quin costat està l'objecte
if x < CENTRE - MARGE:
ultim_costat = -1
xgo.girar(GIR_ESQ) # gira a l'esquerra
elif x > CENTRE + MARGE:
ultim_costat = 1
xgo.girar(GIR_DRE) # gira a la dreta
else:
# centrat: no cal gir
xgo.girar(0x80)
# Sempre avança una mica mentre el veu
xgo.caminar(VELOCITAT)
else: # si NO veu l'objecte
xgo.stop()
# gira cap a l'últim costat on l'havia vist
if ultim_costat == -1:
xgo.girar(CERCAR_ESQ)
else:
xgo.girar(CERCAR_DRE)
sleep(80)
OK
def executar():
husky.knock()
husky.set_algorithm(husky.FACE)
CENTRE = 160
MARGE = 20
W_MAX = 100
xgo.posicio_inicial_estable() # robot a posició inicial
moving = False
girant = False
while True:
b = husky.get_block()
must_move = False
if b and b.get("id",0) == 1:
x = b["x"] #x = b.get ("x",0)
w = b["w"] #w = b.get ("w",0)
if w < W_MAX:
if CENTRE - MARGE < x < CENTRE + MARGE:
must_move = True
display.show(Image.HAPPY)
# 👈 MASSA A L'ESQUERRA → gira esquerra
elif x <= CENTRE - MARGE:
display.show(Image.ARROW_W)
xgo.girar (esquerra)
girant = True
# 👉 MASSA A LA DRETA → gira dreta
elif x >= CENTRE + MARGE:
display.show(Image.ARROW_E)
xgo.girar (dreta)
girant = True
else:
display.show(Image.SAD)
else:
display.show(Image.SURPRISED)
else:
display.show(Image.NO)
# caminar només si centrat
if must_move and not moving:
xgo.caminar(0xA0)
moving = True
elif not must_move and moving:
xgo.stop()
moving = False
# si estava caminant però ara estem girant → parar caminar
if girant and moving:
xgo.stop()
moving = False
sleep(100)