Skip to main content

ROS2 en ReSpeaker XVF3800

Introducción

pir

Este proyecto demuestra la integración del ReSpeaker XVF3800 con ROS2 para aplicaciones robóticas, enfocándose en la detección de voz y estimación de Dirección de Llegada (DOA). Usando el nodo Turtlesim, simulamos el control robótico basado en entrada de voz, habilitando movimiento preciso a través de control PID. El tutorial cubre la configuración del entorno ROS2, configuración del ReSpeaker XVF3800, y cómo aplicar comandos de voz para controlar un robot. Al final, los usuarios entenderán cómo conectar interfaces de voz con robótica y usar algoritmos de control básicos para navegación.

pir

Cómo Instalar ROS 2 en la Computadora Host

Para este proyecto, usamos ROS 2 Humble como middleware. Si estás instalando ROS 2 por primera vez, por favor sigue la guía de instalación oficial para pasos detallados:

Guía de Instalación de ROS 2 Humble (Ubuntu)

Configurar el Array de Micrófono USB ReSpeaker

Si estás usando el Array de Micrófono USB ReSpeaker para tu robot o aplicaciones de voz, sigue estos pasos para configurarlo en tu sistema Ubuntu.

Encontrar los IDs de Proveedor y Producto de tu Dispositivo

Para encontrar los IDs de tu dispositivo, ejecuta:

lsusb

Busca el dispositivo ReSpeaker (ej., vendor 0x2886, product 0x001A).

Crear una Regla udev para el Dispositivo

Crea una nueva regla udev para asegurar permisos apropiados para el Array de Micrófono ReSpeaker:

sudo nano /etc/udev/rules.d/50-respeaker.rules

Agrega las siguientes líneas al archivo:

# ReSpeaker USB Mic Array
SUBSYSTEM=="usb", ATTR{idVendor}=="2886", ATTR{idProduct}=="0018", MODE="0666", GROUP="plugdev"
SUBSYSTEM=="usb", ATTR{idVendor}=="2886", ATTR{idProduct}=="001a", MODE="0666", GROUP="plugdev"

Recargar Reglas udev y Reiniciar el Servicio

Recarga las reglas udev y reinicia el servicio para que los cambios tomen efecto:

sudo udevadm control --reload-rules
sudo udevadm trigger
sudo service udev restart

Desconecta y vuelve a conectar tu Array de Micrófono USB ReSpeaker para aplicar las nuevas reglas.

Configurando el Espacio de Trabajo ROS2 y Controlando tu Robot con ROS2

Esta guía te lleva a través del proceso de configurar un espacio de trabajo ROS2, crear un paquete ROS2 personalizado, controlar un robot usando Python, y configurar el Array de Micrófono USB ReSpeaker para uso en tu proyecto ROS2.

Instalar Dependencias Requeridas

Instalar Extensión Python Colcon

Primero, asegúrate de que las extensiones Python necesarias para construir paquetes ROS2 estén instaladas:

sudo apt install python3-colcon-common-extensions

Configurar Auto-completado de Colcon (Opcional)

Si necesitas configurar auto-completado para colcon:

cd /usr/share/colcon_argcomplete/hook/
ls
gedit ~/.bashrc
clear

Luego, agrega source ~/.bashrc para recargar el entorno:

source ~/.bashrc

Crear un Espacio de Trabajo ROS2

Crea un nuevo espacio de trabajo ROS2 y prepara el entorno:

mkdir ros2_ws
cd ros2_ws/
mkdir src
colcon build

Cargar el Espacio de Trabajo ROS2

Después de construir el espacio de trabajo, cárgalo para configurar las variables de entorno:

source ~/ros2_ws/install/setup.bash

Puedes agregar esta línea a tu ~/.bashrc para cargar el espacio de trabajo automáticamente cada vez que abras una nueva terminal:

echo "source ~/ros2_ws/install/setup.bash" >> ~/.bashrc
source ~/.bashrc

Crear un Nuevo Paquete ROS2

Ahora, vamos a crear un nuevo paquete ROS2 para tu controlador de robot. Este paquete usará ament_python para construcción y rclpy como dependencia:

cd ~/ros2_ws/src
ros2 pkg create my_robot_controller --build-type ament_python --dependencies rclpy

Agregar Script Python para Control de Robot

Navega dentro de tu paquete recién creado, y crea un script Python (ej., rotate_doa.py) para controlar el robot:

cd my_robot_controller/
touch rotate_doa.py
chmod +x rotate_doa.py

Edita el script con tu lógica de control deseada (ej., usando un editor como VS Code):

cd ..
code .
rotate_doa.py
import sys
import struct
import usb.core
import usb.util
import time
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose
from std_msgs.msg import Float32
import math

# name, resid, cmdid, length, type
PARAMETERS = {
"VERSION": (48, 0, 4, "ro", "uint8"),
"AEC_AZIMUTH_VALUES": (33, 75, 16 + 1, "ro", "radians"),
"DOA_VALUE": (20, 18, 4 + 1, "ro", "uint16"),
}

class ReSpeaker:
TIMEOUT = 100000

def __init__(self, dev):
self.dev = dev

def write(self, name, value):
pass

def read(self, name):
try:
data = PARAMETERS[name]
except KeyError:
return

resid = data[0]
cmdid = 0x80 | data[1]
length = data[2]

response = self.dev.ctrl_transfer(
usb.util.CTRL_IN | usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE,
0, cmdid, resid, length, self.TIMEOUT)

if data[4] == 'uint8':
result = response.tolist()
elif data[4] == 'radians':
byte_data = response.tobytes()
float1, float2, float3, float4 = struct.unpack('<ffff', byte_data[1:17])
result = [float1 * 180 / 3.1415926,
float2 * 180 / 3.1415926,
float3 * 180 / 3.1415926,
float4 * 180 / 3.1415926]
elif data[4] == 'uint16':
result = response.tolist()

return result

def close(self):
"""
close the interface
"""
usb.util.dispose_resources(self.dev)


def find(vid=0x2886, pid=0x001A):
dev = usb.core.find(idVendor=vid, idProduct=pid)
if not dev:
return None
return ReSpeaker(dev)


class AngleController(Node):
def __init__(self):
super().__init__('angle_controller')
self.target_angle = 0.0
self.pose_sub = self.create_subscription(Pose, '/turtle1/pose', self.pose_callback, 10)
self.cmd_pub = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)

# PID params
self.kp = 1.5
self.ki = 0.0
self.kd = 0.2

self.integral = 0.0
self.prev_error = 0.0
self.prev_time = self.get_clock().now()

def publish_angle(self, angle_deg: float):
self.target_angle = math.radians(angle_deg) # Convert to radians
self.get_logger().info(f"New target angle: {angle_deg:.2f}°")

def pose_callback(self, msg: Pose):
current_yaw = msg.theta
error = self.normalize_angle(self.target_angle - current_yaw)

now = self.get_clock().now()
dt = (now - self.prev_time).nanoseconds / 1e9
self.prev_time = now

self.integral += error * dt
derivative = (error - self.prev_error) / dt if dt > 0 else 0.0

control = self.kp * error + self.ki * self.integral + self.kd * derivative

twist = Twist()
twist.angular.z = control
self.cmd_pub.publish(twist)

self.prev_error = error

@staticmethod
def normalize_angle(angle):
while angle > math.pi:
angle -= 2.0 * math.pi
while angle < -math.pi:
angle += 2.0 * math.pi
return angle


def main(args=None):
rclpy.init(args=args)
node = AngleController()

# Setup ReSpeaker device
dev = find()
if not dev:
node.get_logger().error("No ReSpeaker device found")
return

# Function to handle publishing DOA angle when speech is detected
def publish_doa_angle():
result = dev.read("DOA_VALUE")
if result:
speech_detected = result[3]
doa_angle = result[1]
if speech_detected: # If speech is detected
node.publish_angle(doa_angle)
node.get_logger().info(f"Speech detected, DOA angle: {doa_angle}°")

# Timer to check for DOA updates every 1 second
timer = node.create_timer(1.0, publish_doa_angle)

rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
dev.close()


if __name__ == '__main__':
main()

Agrega estas dependencias a package.xml

  <depend>rclpy</depend>
<depend>geometry_msgs</depend>
<depend>turtlesim</depend>
<depend>std_msgs</depend>

Agrega este endpoint a package.xml


entry_points={
'console_scripts': [
'rotate_doa = my_controller.rotate_doa:main',
],
},

Construir y Ejecutar el Paquete

Después de editar tu script Python, construye el paquete:

colcon build
source ~/ros2_ws/install/setup.bash

Finalmente, ejecuta el paquete con:

ros2 run my_robot_controller rotate_doa

En otra terminal, también puedes ejecutar un nodo ROS2 básico (ej., turtlesim para pruebas):

ros2 run turtlesim turtlesim_node

Soporte Técnico y Discusión del Producto

¡Gracias por elegir nuestros productos! Estamos aquí para proporcionarte diferentes tipos de soporte para asegurar que tu experiencia con nuestros productos sea lo más fluida posible. Ofrecemos varios canales de comunicación para atender diferentes preferencias y necesidades.

Loading Comments...