You dont have javascript enabled! Please enable it!

Caso práctico: parada antiobstáculos UGV con Raspberry Pi

Caso práctico: parada antiobstáculos UGV con Raspberry Pi — hero

Objetivo y caso de uso

Lo que construirás: Un prototipo de vehículo guiado automáticamente (AGV) de tracción en 2 ruedas (2WD) que avanza continuamente y ejecuta una parada de emergencia de baja latencia cuando se detecta un obstáculo a menos de 15 centímetros.

Por qué es importante / Casos de uso

  • Logística de almacenes: Esencial para carros automatizados que requieren una prevención de colisiones robusta para proteger el inventario y las estanterías estáticas.
  • Abstracción de hardware: Desacopla la lógica de navegación de alto nivel de la manipulación GPIO de bajo nivel, permitiendo probar el código en computadoras portátiles estándar antes de la implementación física.
  • Integración de sensores: Demuestra la interconexión segura de sensores analógicos/digitales de 5V (HC-SR04) con microprocesadores de 3.3V utilizando divisores de voltaje.
  • Control de motores: Aplica conceptos de puente H para el control de velocidad PWM y la gestión del estado de espera (standby) utilizando el controlador TB6612FNG.

Resultado esperado

  • Una arquitectura de software en Python 3.11 que utiliza el patrón Strategy para alternar sin problemas entre interfaces de hardware físicas y simuladas (mock).
  • Sondeo continuo de distancia ultrasónica ejecutándose a aproximadamente 10Hz.
  • Desactivación inmediata del motor (parada) activada en el momento en que la distancia medida cae por debajo del umbral de 15.0 cm.

Audiencia: Desarrolladores de Python e ingenieros en robótica; Nivel: Intermedio

Arquitectura/flujo: Capa de abstracción de hardware en Python sondeando un sensor HC-SR04 a 10Hz, evaluando umbrales de distancia y controlando un controlador de motor TB6612FNG a través de PWM GPIO con anulaciones de parada automáticas.

Nota educativa de validación

Antes de publicar este caso, el contenido pasó la puerta automática de validación de Prometeo con estado PASS. El validador comprobó los bloques de código, la estructura del artículo, los comandos copiables y la coherencia con el catálogo de dispositivos soportados.

Evidencia de validación publicada

  • Resultado automático: PASS.
  • Estructura parseada: 3 apartados, 2 tablas y 2 bloques de código detectados antes de publicar.
  • Código comprobado: 2 Python/py_compile.
  • Catálogo soportado: el texto se contrastó contra los perfiles de dispositivo validables de Prometeo y los stacks no soportados bloquean la publicación.
  • Hallazgos del informe: sin hallazgos bloqueantes.

Esta validación confirma compatibilidad sintáctica y de herramientas para el material publicado, pero no sustituye la prueba física sobre tu hardware, cableado y entorno exactos.

Nota educativa de seguridad

Este proyecto es un prototipo educativo, no un producto certificado. Antes de encender el montaje, verifica el esquema de pines de la revisión exacta de tu placa ULX3S, mantén las señales de E/S de la FPGA a 3.3 V, nunca conectes 5 V directamente a los pines de E/S, desconecta la alimentación antes de cambiar el cableado y utiliza fuentes de alimentación externas adecuadas para cargas, motores o servos, compartiendo la tierra (ground) solo cuando el cableado lo requiera.

Diagrama de bloques conceptual

Vista de alto nivel: qué entra, qué procesa cada bloque y qué sale del sistema.

Arquitectura funcional

Botones ULX3S

Sincronizador/antirrebote

Selector de modo

Generador periodo 20 ms

Comparador ancho de pulso

Salida PWM 50 Hz

Servo SG90

Flujo conceptual de control: entrada de botones, selección de modo, temporización PWM y movimiento del servo.

Ruta de validación

Verilog fuente

Verilator lint/testbench

Yosys síntesis

nextpnr-ecp5

ecppack bitstream

ULX3S programada

La validación automática comprueba sintaxis, simulación/lint y compatibilidad con la toolchain ULX3S/ECP5.

Requisitos previos

  • Software: Una computadora o portátil para pruebas en vacío (dry-run) (Windows, macOS o Linux) con Python 3.11 instalado. Para la implementación física, una Raspberry Pi ejecutando Raspberry Pi OS Bookworm (64 bits) con Python 3.11 y la biblioteca gpiozero instalada.
  • Habilidades: Familiaridad básica con operaciones de línea de comandos en Linux, programación fundamental en Python (clases, herencia, manejo de excepciones) y uso básico de protoboard (comprensión de tierra común y divisores de voltaje).

Materiales

Para completar este caso práctico, debes usar EXACTAMENTE esta configuración de modelos de dispositivos:
* Microcomputadora: Raspberry Pi 4 Model B (cualquier variante de RAM).
* Controlador de motores: Placa breakout de controlador de motor dual TB6612FNG.
* Sensor de distancia: Sensor ultrasónico HC-SR04.
* Plataforma del robot: Chasis UGV 2WD (incluye dos motores de engranajes de CC y ruedas).
* Fuente de alimentación: Un banco de energía (power bank) USB-C de 5V para la Raspberry Pi, y un paquete de baterías separado (ej. 4x AA que proporcionan 6V) para los motores de CC.
* Componentes pasivos: Una resistencia de 1kΩ y una resistencia de 2kΩ (requeridas para el divisor de voltaje del HC-SR04).
* Cableado: Protoboard y cables puente (jumper) variados macho-hembra y macho-macho.

Configuración/Conexión

El cableado adecuado es fundamental para evitar daños a la Raspberry Pi. Los pines GPIO de la Raspberry Pi operan con lógica de 3.3V. El HC-SR04 requiere 5V para funcionar y emite una señal de 5V en su pin ECHO. Conectar un pin ECHO de 5V directamente a un pin GPIO de 3.3V dañará la Raspberry Pi. Debemos usar un divisor de voltaje (resistencias de 1kΩ y 2kΩ) para reducir la señal de 5V a aproximadamente 3.3V.

Además, los motores deben ser alimentados por un paquete de baterías externo, no por los pines de 5V o 3.3V de la Raspberry Pi. Los motores de CC consumen una corriente significativa y crean picos de voltaje que pueden causar que la Raspberry Pi sufra caídas de tensión (brown-out) o se reinicie.

Cableado del controlador de motor dual TB6612FNG

Pin TB6612FNGConexión / GPIO de Raspberry PiPropósito
VCCPi 3.3V (Pin 1)Alimentación lógica para el CI del controlador de motor.
VMOTPositivo del paquete de baterías (ej. 6V)Alimentación de alta corriente para los motores de CC.
GNDPi GND (Pin 6) + GND de bateríaReferencia de tierra común. Crucial.
PWMAPi GPIO 12 (Pin 32)Señal PWM para la velocidad del Motor A (Izquierdo).
AIN1Pi GPIO 5 (Pin 29)Control de dirección 1 para el Motor A.
AIN2Pi GPIO 6 (Pin 31)Control de dirección 2 para el Motor A.
STBYPi GPIO 17 (Pin 11)Pin de espera (standby). Debe estar en ALTO (HIGH) para habilitar los motores.
PWMBPi GPIO 13 (Pin 33)Señal PWM para la velocidad del Motor B (Derecho).
BIN1Pi GPIO 16 (Pin 36)Control de dirección 1 para el Motor B.
BIN2Pi GPIO 26 (Pin 37)Control de dirección 2 para el Motor B.
AO1 / AO2Terminales del motor izquierdoSalida de potencia al motor de CC izquierdo.
BO1 / BO2Terminales del motor derechoSalida de potencia al motor de CC derecho.

Cableado del sensor ultrasónico HC-SR04

Pin HC-SR04ConexiónPropósito
VCCPi 5V (Pin 2)Fuente de alimentación para el sensor ultrasónico.
GNDPi GND (Pin 39)Referencia de tierra común.
TRIGPi GPIO 23 (Pin 16)Recibe el pulso de activación (trigger) de 3.3V desde la Pi.
ECHODivisor de voltaje -> Pi GPIO 24 (Pin 18)Emite 5V. El divisor lo reduce a 3.3V para la Pi.

Construcción del divisor de voltaje para ECHO:
1. Conecta el pin ECHO del HC-SR04 a un extremo de una resistencia de 1kΩ.
2. Conecta el otro extremo de la resistencia de 1kΩ al GPIO 24 de la Raspberry Pi.
3. Desde el GPIO 24 de la Raspberry Pi, conecta una resistencia de 2kΩ a tierra (GND).
Esto forma un circuito donde V_out = V_in * (2k / (1k + 2k)) = 5V * (2/3) = 3.33V.

Método de validación y evidencia esperada

Para validar las afirmaciones de rendimiento (sondeo a 10Hz y umbral de parada absoluto de 15.0 cm):
1. Verificación en vacío (Dry-Run): Ejecuta la aplicación principal con la bandera --dry-run. Observa la salida de la consola. Deberías ver 10 lecturas de distancia por segundo (10Hz). La distancia simulada disminuirá en 2 cm con cada tick. Exactamente cuando la lectura de distancia caiga por debajo de 15.0 cm, la consola debe mostrar OBSTACLE DETECTED! Executing emergency halt. y salir inmediatamente.
2. Verificación física: Coloca el robot terminado sobre una superficie plana frente a una pared exactamente a 30 cm de distancia. Ejecuta el script físico. El robot debe avanzar y detenerse. Usa una cinta métrica para medir la distancia entre la parte delantera del sensor HC-SR04 y la pared. La evidencia esperada es una distancia de reposo de aproximadamente 14.0 cm a 14.9 cm (teniendo en cuenta la inercia física después de la activación a los 15.0 cm).

Código de la capa de abstracción de hardware

Guarda el siguiente código como ugv_hardware.py. Este archivo maneja la manipulación directa de GPIO o la salida simulada (mock) dependiendo de cómo sea invocado por el script principal.

"""
ugv_hardware.py
Hardware Abstraction Layer for the UGV.
Provides physical and mock implementations for the TB6612FNG and HC-SR04.
"""

try:
    from gpiozero import PWMOutputDevice, DigitalOutputDevice, DistanceSensor
    HAS_GPIO = True
except ImportError:
    HAS_GPIO = False

class BaseMotorDriver:
    """Abstract base class for a dual motor driver."""
    def forward(self, speed_percent: float) -> None:
        pass

    def stop(self) -> None:
        pass

class BaseUltrasonic:
    """Abstract base class for an ultrasonic distance sensor."""
    def get_distance_cm(self) -> float:
        return 0.0

class MockMotorDriver(BaseMotorDriver):
    """Simulated motor driver for dry-run testing."""
    def forward(self, speed_percent: float) -> None:
        print(f"[MOCK MOTOR] Moving FORWARD at {speed_percent}% speed.")

    def stop(self) -> None:
        print("[MOCK MOTOR] HALTED.")

class MockUltrasonic(BaseUltrasonic):
    """Simulated ultrasonic sensor that gradually approaches an obstacle."""
    def __init__(self) -> None:
        self.tick_count = 0
        self.starting_distance = 35.0  # Start at 35 cm

    def get_distance_cm(self) -> float:
        self.tick_count += 1
        # Simulate moving 2 cm closer each tick
        current_distance = self.starting_distance - (self.tick_count * 2.0)
        if current_distance < 5.0:
            current_distance = 5.0
        return current_distance

class PhysicalMotorDriver(BaseMotorDriver):
    """Physical motor driver using gpiozero for TB6612FNG."""
    def __init__(self) -> None:
        if not HAS_GPIO:
            raise RuntimeError("gpiozero library not found. Cannot use physical hardware.")
        # Left motor
        self.pwma = PWMOutputDevice(12)
        self.ain1 = DigitalOutputDevice(5)
        self.ain2 = DigitalOutputDevice(6)
        # Right motor
        self.pwmb = PWMOutputDevice(13)
        self.bin1 = DigitalOutputDevice(16)
        self.bin2 = DigitalOutputDevice(26)
        # Standby
        self.stby = DigitalOutputDevice(17)
        self.stby.on()  # Enable driver

    def forward(self, speed_percent: float) -> None:
        speed = max(0.0, min(1.0, speed_percent / 100.0))

        self.ain1.on()
        self.ain2.off()
        self.pwma.value = speed

        self.bin1.on()
        self.bin2.off()
        self.pwmb.value = speed

    def stop(self) -> None:
        self.pwma.value = 0.0
        self.pwmb.value = 0.0
        self.ain1.off()
        self.ain2.off()
        self.bin1.off()
        self.bin2.off()

class PhysicalUltrasonic(BaseUltrasonic):
    """Physical ultrasonic sensor using gpiozero for HC-SR04."""
    def __init__(self) -> None:
        if not HAS_GPIO:
            raise RuntimeError("gpiozero library not found. Cannot use physical hardware.")
        # max_distance=2.0 meters provides enough range for a 15cm threshold
        self.sensor = DistanceSensor(echo=24, trigger=23, max_distance=2.0)

    def get_distance_cm(self) -> float:
        # DistanceSensor returns distance in meters, convert to cm
        return self.sensor.distance * 100.0

Código de la lógica de control principal

Guarda el siguiente código como ugv_main.py en el mismo directorio. Este script contiene la lógica de sondeo a 10Hz y el umbral para evitar obstáculos.

"""
ugv_main.py
Main control logic for the UGV obstacle avoidance.
"""

import time
import argparse
import sys
from ugv_hardware import (
    MockMotorDriver, MockUltrasonic,
    PhysicalMotorDriver, PhysicalUltrasonic, HAS_GPIO
)

def main() -> None:
    parser = argparse.ArgumentParser(description="UGV Obstacle Avoidance Control")
    parser.add_argument("--dry-run", action="store_true", help="Run with mock hardware")
    args = parser.parse_args()

    if args.dry_run:
        print("Initializing MOCK hardware...")
        motor = MockMotorDriver()
        sensor = MockUltrasonic()
    else:
        if not HAS_GPIO:
            print("Error: gpiozero not installed. Run with --dry-run or install gpiozero.")
            sys.exit(1)
        print("Initializing PHYSICAL hardware...")
        motor = PhysicalMotorDriver()
        sensor = PhysicalUltrasonic()

    threshold_cm = 15.0
    polling_rate_hz = 10.0
    sleep_interval = 1.0 / polling_rate_hz

    print("Starting UGV autonomous navigation...")
    try:
        while True:
            distance = sensor.get_distance_cm()
            print(f"Distance: {distance:.1f} cm")

            if distance < threshold_cm:
                print("OBSTACLE DETECTED! Executing emergency halt.")
                motor.stop()

                if args.dry_run:
                    # Break out of loop for automated dry-run validation
                    print("Dry-run test complete.")
                    break
            else:
                motor.forward(50.0)  # Drive forward at 50% speed

            time.sleep(sleep_interval)

    except KeyboardInterrupt:
        print("\nManual override triggered. Shutting down.")
    finally:
        motor.stop()
        print("UGV safely halted.")

if __name__ == "__main__":
    main()

Encuentra este producto y/o libros sobre este tema en Amazon

Ir a Amazon

Como afiliado de Amazon, gano con las compras que cumplan los requisitos. Si compras a través de este enlace, ayudas a mantener este proyecto.

Quiz rápido

Pregunta 1: ¿Qué tipo de vehículo se construye en el prototipo descrito?




Pregunta 2: ¿A qué distancia debe detectar un obstáculo el vehículo para ejecutar una parada de emergencia?




Pregunta 3: ¿Cuál es uno de los casos de uso principales de este prototipo en la logística de almacenes?




Pregunta 4: ¿Qué beneficio principal proporciona la abstracción de hardware en este proyecto?




Pregunta 5: ¿Qué sensor específico se menciona para la integración y detección en el prototipo?




Pregunta 6: ¿Qué método se utiliza para interconectar de forma segura el sensor de 5V con el microprocesador de 3.3V?




Pregunta 7: ¿Qué controlador se utiliza para la gestión del estado de espera y control de motores?




Pregunta 8: ¿Qué concepto se aplica para el control de velocidad de los motores en este proyecto?




Pregunta 9: ¿Qué lenguaje de programación y versión se espera utilizar para la arquitectura de software?




Pregunta 10: ¿Qué patrón de diseño de software se menciona para alternar la lógica en la arquitectura del proyecto?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Ingeniero Superior en Electrónica de Telecomunicaciones e Ingeniero en Informática (titulaciones oficiales en España).

Sígueme:
Scroll al inicio