Aula
Desafio: Criar um robô que parte da base, segue uma linha e para diante de um obstáculo com segurança e repetibilidade.
Objetivos de aprendizagem
- Integrar DriveBase, ColorSensor e UltrasonicSensor num mesmo programa.
- Aplicar controle proporcional (P) para seguir linha.
- Implementar parada por distância com margem de segurança.
- Organizar o código em funções reutilizáveis (equivalentes a MyBlocks com entrada/saída).
- (Opcional) Usar giroscópio para manter rumo em trechos de linha “desbotada”.
Requisitos do cenário
- Uma linha preta sobre base clara (ou o inverso) partindo da base.
- Um obstáculo posicionado sobre a linha (caixinha/parede), a ~200–300 mm do ponto de parada desejado.
- Altura do ultrassônico alinhada e sensor de cor a 2–3 mm do piso.
Arquitetura do código
Dividiremos em funções (como MyBlocks):
configurar_robot()– Inicializa hub, motores, sensores e DriveBase.seguir_linha_P(alvo, kp, v)– Mantém o robô sobre a borda da linha usando controle P (entrada: alvo/kp/v; sem retorno).distancia_media(n)– Retorna média das leituras do ultrassônico (entrada: janela n; saída: distância em mm).ir_da_base_ate_obstaculo(...)– Comanda o fluxo: parte da base, segue linha e para ao atingir a distância alvo.
Parâmetros de calibração
- alvo_reflexao: média entre claro e escuro (meça no seu tapete).
- Kp_linha: ganho do controlador P do segue-linha (comece entre 3.0 e 4.5).
- vel_mm_s: velocidade linear alvo (150–220 mm/s para precisão).
- dist_parada_mm: distância de parada do obstáculo (ex.: 200 mm).
Código completo do projeto
Compatível com SPIKE Prime + PyBricks. Ajuste portas e dimensões do robô.
from pybricks.hubs import PrimeHub from pybricks.pupdevices import Motor, ColorSensor, UltrasonicSensor from pybricks.robotics import DriveBase from pybricks.parameters import Port, Color, Stop from pybricks.tools import wait # --------------------------- # Configurações (ajuste ao seu robô) # --------------------------- WHEEL_DIAMETER = 56 # mm AXLE_TRACK = 120 # mm (distância entre rodas, centro a centro) ALVO_REFLEXAO = 50 # média entre claro/escuro KP_LINHA = 4.0 # ganho do P para seguir linha VEL_MM_S = 180 # velocidade linear DIST_PARADA_MM = 200 # para a ~200 mm do obstáculo JANELA_MEDIA = 4 # tamanho da média móvel para ultrassônico USAR_GIROSCOPIO = True # opcional: correção de rumo KP_RUMO = 2.0 # ganho do P para rumo (heading) # --------------------------- # Inicialização # --------------------------- hub = PrimeHub() hub.imu.reset_heading(0) mE = Motor(Port.A) # motor esquerdo mD = Motor(Port.B) # motor direito sensor_cor = ColorSensor(Port.C) us = UltrasonicSensor(Port.D) robot = DriveBase(mE, mD, WHEEL_DIAMETER, AXLE_TRACK) robot.settings(straight_speed=VEL_MM_S, turn_rate=90) # --------------------------- # Funções utilitárias (MyBlocks em Python) # --------------------------- def distancia_media(n=JANELA_MEDIA): global _buffer try: _buffer except NameError: _buffer = [us.distance()] * n _buffer.pop(0) _buffer.append(us.distance()) return sum(_buffer) / len(_buffer) def seguir_linha_P(alvo=ALVO_REFLEXAO, kp=KP_LINHA, v=VEL_MM_S, usar_imu=USAR_GIROSCOPIO, kp_rumo=KP_RUMO): ref = sensor_cor.reflection() erro_linha = alvo - ref turn = kp * erro_linha # correção de curva (°/s) if usar_imu: rumo_erro = 0 - hub.imu.heading() # manter heading ~ 0 turn += kp_rumo * rumo_erro # mistura leve de correção por rumo robot.drive(v, turn) def ir_da_base_ate_obstaculo(dist_parada=DIST_PARADA_MM): while True: d = distancia_media(JANELA_MEDIA) if d <= dist_parada: robot.stop(Stop.HOLD) hub.light.on(Color.YELLOW) break seguir_linha_P() wait(15) # --------------------------- # Fluxo principal # --------------------------- def main(): hub.light.on(Color.BLUE) # pronto para sair da base wait(300) hub.light.off() ir_da_base_ate_obstaculo(DIST_PARADA_MM) hub.light.on(Color.GREEN) # objetivo cumprido wait(500) main()
Explicando entradas (inputs) e saídas (outputs)
seguir_linha_P(alvo, kp, v, usar_imu, kp_rumo)– entradas: alvo de reflexão, ganho P, velocidade e opções de correção por IMU; saída: controla o robô (sem retorno).distancia_media(n)– entrada: tamanho da janela; saída: distância filtrada em mm (valor retornado porreturn).ir_da_base_ate_obstaculo(dist_parada)– entrada: distância alvo de parada; saída: nenhuma (efeito é parar o robô).
Equivalência com MyBlocks: cada função acima se comporta como um MyBlock:
• Parâmetros = entradas do bloco •
• Parâmetros = entradas do bloco •
return (quando existe) = saída do bloco.Calibração passo a passo
- Reflexão: meça claro e escuro com
sensor_cor.reflection(); definaALVO_REFLEXAOcomo a média. - Kp linha: comece com 3.0. Se o robô “sai” da borda, aumente; se oscila, diminua.
- Velocidade: inicie em 160–200 mm/s para estabilidade; aumente só após ficar consistente.
- Ultrassônico: valide a leitura em mm e ajuste
DIST_PARADA_MMconforme sua mesa/obstáculo. - IMU (opcional): use
hub.imu.reset_heading(0)antes de iniciar e ajusteKP_RUMO(1.5–2.5).
Rubrica de avaliação (FLL)
| Critério | Básico | Bom | Excelente |
|---|---|---|---|
| Repetibilidade | Completa 1x | 3/5 execuções | 5/5 execuções |
| Precisão da parada | ±40 mm | ±20 mm | ±10 mm |
| Organização do código | Sem funções | Funções básicas | Funções modulares e parâmetros claros |
| Calibração e testes | Poucos ajustes | Ajustes por sessão | Registro de medições e evolução de Kp/vel |
Checklist rápido
- Leituras de reflexão calibradas e estáveis (altura do sensor).
- Ultrassônico sem interferências (suporte firme, sem peças à frente).
- Rodas bem fixas e mesma bitola; cabos sem puxar o chassi.
- Heading resetado no início; piso limpo e nível.
- Velocidade adequada à precisão desejada.
Erros comuns e correções
- Oscilação na linha: reduza
KP_LINHAouVEL_MM_S; ajusteALVO_REFLEXAO. - Parada irregular: use
distancia_media()(média móvel) e garanta obstáculo plano. - Puxa para um lado: verifique montagem, diâmetro de roda e axle_track; considere leve correção por IMU.
- “Perde” a linha: aproxime o sensor do chão (2–3 mm), use borda mais marcada e diminua a velocidade.
Extensões (para ir além)
- Waypoints de cor: mude parâmetros (vel/Kp) ao detectar marcadores coloridos.
- Retomada de rota: se perder a linha, execute varredura em arco curto até reencontrar.
- Estacionamento: após parar no obstáculo, recuar 50 mm e alinhar por giro preciso (IMU).
- Modularização: separar funções em arquivos: linha.py, distancia.py, missoes.py.
Parabéns! Com este projeto, sua equipe domina o essencial para unir percepção (sensores) e ação (DriveBase), criando trajetórias confiáveis na FLL.