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, centrándose en la detección de voz y la estimación de la Dirección de Llegada (DOA). Usando el nodo Turtlesim, simulamos el control robótico basado en la entrada de voz, permitiendo un movimiento preciso mediante control PID. El tutorial cubre la configuración del entorno ROS2, la configuración del ReSpeaker XVF3800 y cómo aplicar comandos de voz para controlar un robot. Al final, los usuarios comprenderán cómo conectar interfaces de voz con robótica y utilizar algoritmos de control básicos para la navegación.

pir

Cómo instalar ROS 2 en el ordenador host

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

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

Configurar ReSpeaker USB Mic Array

Si estás utilizando el ReSpeaker USB Mic Array 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 (por ejemplo, vendor 0x2886, product 0x001A).

Crear una regla udev para el dispositivo

Crea una nueva regla udev para garantizar los permisos adecuados para el ReSpeaker Mic Array:

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

Añade 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 las reglas udev y reiniciar el servicio

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

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

Desconecta y vuelve a conectar tu ReSpeaker USB Mic Array para aplicar las nuevas reglas.

Configuración del espacio de trabajo ROS2 y control de tu robot con ROS2

Esta guía te acompaña en el proceso de configurar un espacio de trabajo ROS2, crear un paquete ROS2 personalizado, controlar un robot usando Python y configurar el ReSpeaker USB Mic Array para usarlo en tu proyecto ROS2.

Instalar las dependencias necesarias

Instalar la extensión Colcon para Python

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

sudo apt install python3-colcon-common-extensions

Configurar la autocompletación de Colcon (opcional)

Si necesitas configurar la autocompletación para colcon:

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

Luego, añade 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 compilar el espacio de trabajo, cárgalo para configurar las variables de entorno:

source ~/ros2_ws/install/setup.bash

Puedes añadir 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 utilizará ament_python para la compilación y rclpy como dependencia:

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

Añadir un script de Python para el control del robot

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

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

Edita el script con la lógica de control que desees (por ejemplo, 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()

Añade estas dependencias a package.xml

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

Añade este endpoint a package.xml


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

Compilar y ejecutar el paquete

Después de editar tu script de Python, compila 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 (por ejemplo, turtlesim para pruebas):

ros2 run turtlesim turtlesim_node

Soporte técnico y debate sobre el producto

Gracias por elegir nuestros productos. Estamos aquí para ofrecerte diferentes tipos de soporte y garantizar que tu experiencia con nuestros productos sea lo más fluida posible. Ofrecemos varios canales de comunicación para adaptarnos a diferentes preferencias y necesidades.

Loading Comments...