Comunicación por Servicios en ROS - Implementación Práctica con Python
ROS proporciona tres mecanismos principais de comunicación: comunicación por temas (topics), comunicación por servicios (services) y el servidor de parámetros (paramter server). Este artículo se centra específicamente en la implementación práctica del mecanismo de comunicación por servicios utilizando Python.
Requisitos Previos
Antes de implementar la comunicación por servicios, es necesario haber preparado el formato de datos srv personalizado. Para más información sobre cómo crear y configurar archivos srv personalizados, consulte la documentación sobre srv personalizados en ROS.
En la implementación del modelo de servicio, el ROS Master no requiere implementación explícita, ya que el establecimiento de conexiones está encapsulado. Los elementos fundamentales que debemos gestionar son:
- Proveedor de servicio (Server): Procesa las solicitudes y genera respuestas
- Solicitante de servicio (Client): Envía solicitudes y procesa respuestas
- Datos: Estructura definida por el archivo srv
Fases de Implementación
La implementación del mecanismo de comunicación por servicios en ROS sigue estos cinco pasos:
- Configuración del entorno de desarrollo
- Desarrollo del proveedor de servicio
- Desarrollo del solicitante de servicio
- Configuración de archivos de compilación
- Compilación y ejecución
Configuración del Entorno
Para mejorar la experiencia de desarrollo y obtener autocompletado de código, es necesario configurar el archivo settings.json de VSCode incluyendo la ruta donde se generan los archivos Python del paquete srv personalizado.
Implementación del Proveedor de Servicio
Flujo de Trabajo
El proveedor de servicio sigue este patrón de implementación:
- Importación de paquetes necesarios
- Inicialización del nodo ROS
- Creación del objeto servidor
- Definición de la lógica de procesamiento (función de callback)
- Ejecución del bucle principal con spin()
Código de Implementación
#! /usr/bin/env python3
import rospy
from mi_paquete_servicio.srv import OperacionMatematica, OperacionMatematicaRequest, OperacionMatematicaResponse
"""
Proveedor de servicio que procesa solicitudes y genera respuestas.
1. Importar paquetes necesarios
2. Inicializar el nodo ROS
3. Crear el objeto servidor
4. Definir lógica de procesamiento (callback)
5. Ejecutar spin()
"""
def procesar_solicitud(peticion):
"""Funcióncallback que procesa la solicitud del cliente"""
valor_a = peticion.valor_a
valor_b = peticion.valor_b
# Lógica de procesamiento: suma de dos números
resultado = valor_a + valor_b
# Crear y configurar la respuesta
respuesta = OperacionMatematicaResponse()
respuesta.resultado = resultado
rospy.loginfo("Servidor接受了请求: valor_a = %d, valor_b = %d, 结果 = %d",
valor_a, valor_b, resultado)
return respuesta
if __name__ == "__main__":
# Inicializar el nodo ROS con un nombre único
rospy.init_node("servidor_operaciones")
# Crear el objeto servidor
# Parámetro 1: nombre del servicio
# Parámetro 2: tipo de mensaje del servicio
# Parámetro 3: función callback
servidor = rospy.Service("operacion_matematica", OperacionMatematica, procesar_solicitud)
rospy.loginfo("El servidor de servicio ha iniciado correctamente")
# Mantener el servidor en ejecución
rospy.spin()
Configuración de Permisos de Ejecución
Es necesario otorgar permisos de ejecución al archivo del servidor. Desde el directorio scripts, ejecute en la terminal:
chmod +x demo_server.py
Configuración CMakeLists.txt
Modifique el archivo CMakeLists.txt del paquete para configurrar la instalación de scripts Python. Localice la sección catkin_install_python y configúrela según sea necesario.
Verificación del Servidor
Para verificar el funcionamiento correcto:
- Ejecute
roscoreen una terminal - En otra terminal, ejecute
source ./devel/setup.bashy luegorosrun mi_paquete_servicio demo_server.py - En una tercera terminal, pruebe el servicio con:
rosservice call operacion_matematica "valor_a: 5 valor_b: 3"
Implementación del Solicitante de Servicio
Flujo de Trabajo
El solicitante de servicio sigue este patrón:
- Importación de paquetes necesarios
- Inicialización del nodo ROS
- Creación del objeto cliente
- Preparación y envío de datos de solicitud
- Procesamiento de la respuesta
Código de Implementación
#! /usr/bin/env python3
import rospy
from mi_paquete_servicio.srv import OperacionMatematica, OperacionMatematicaRequest, OperacionMatematicaResponse
"""
Solicitante de servicio que envía solicitudes y procesa respuestas.
1. Importar paquetes necesarios
2. Inicializar el nodo ROS
3. Crear el objeto cliente
4. Preparar datos de solicitud y enviarlos
5. Procesar la respuesta
"""
if __name__ == "__main__":
# Inicializar el nodo ROS
rospy.init_node("cliente_operaciones")
# Crear el objeto cliente para llamar al servicio
# Parámetro 1: nombre del servicio
# Parámetro 2: tipo de mensaje del servicio
cliente = rospy.ServiceProxy("operacion_matematica", OperacionMatematica)
# Preparar datos de solicitud
numero_x = 25
numero_y = 17
# Enviar solicitud y obtener respuesta
respuesta = cliente.call(numero_x, numero_y)
# Procesar y mostrar la respuesta
rospy.loginfo("La respuesta recibida es: %d", respuesta.resultado)
Configuración de Permisos
Otorgue permisos de ejecución al archivo del cliente:
chmod +x demo_client.py
Compilación y Ejecución
Proceso de Compilación
Utilice el atajo de teclado Ctrl + Shift + B para compilar el proyecto.
Ejecución de los Componentes
Para ejecutar la aplicación completa:
- Paso 1: Inicie roscore en una terminal
- Paso 2: En una nueva terminal, ejecute el servidor:
rosrun mi_paquete_servicio demo_server.py - Paso 3: En otra terminal, ejecute el cliente:
rosrun mi_paquete_servicio demo_client.py
Mejoras y Optimizaciones
Parámetros Dinámicos
Para permitir la entrada dinámica de parámetros desde la línea de comandos, modifique el código del cliente:
#! /usr/bin/env python3
import rospy
from mi_paquete_servicio.srv import OperacionMatematica, OperacionMatematicaRequest, OperacionMatematicaResponse
import sys
"""
Cliente con soporte para parámetros dinámicos desde línea de comandos.
Formato: rosrun paquete cliente valor_a valor_b
"""
if __name__ == "__main__":
# Validar número de argumentos recibidos
if len(sys.argv) != 3:
rospy.loginfo("Número incorrecto de argumentos. Uso: valor_a valor_b")
sys.exit(1)
# Inicializar el nodo ROS
rospy.init_node("cliente_operaciones")
# Crear objeto cliente
cliente = rospy.ServiceProxy("operacion_matematica", OperacionMatematica)
# Parsear argumentos de línea de comandos
argumento_1 = int(sys.argv[1])
argumento_2 = int(sys.argv[2])
# Enviar solicitud con parámetros dinámicos
respuesta = cliente.call(argumento_1, argumento_2)
# Mostrar resultado
rospy.loginfo("El resultado de %d + %d es: %d",
argumento_1, argumento_2, respuesta.resultado)
Después de compilar, ejecute:
rosrun mi_paquete_servicio demo_client.py 10 20
Manejo de Inicio Anticipado del Cliente
Si el cliente se inicia antes que el servidor, se producirá una excepción. Para manejar esta situación, podemos implementar una espera activa utilizando las funciones integradas de ROS:
#! /usr/bin/env python3
import rospy
from mi_paquete_servicio.srv import OperacionMatematica, OperacionMatematicaRequest, OperacionMatematicaResponse
import sys
"""
Cliente con espera activa del servidor.
Si el servidor no está disponible, el cliente espera hasta que esté disponible.
"""
if __name__ == "__main__":
# Validar argumentos
if len(sys.argv) != 3:
rospy.loginfo("Argumentos inválidos. Uso: valor_a valor_b")
sys.exit(1)
# Inicializar nodo
rospy.init_node("cliente_operaciones")
# Crear cliente
cliente = rospy.ServiceProxy("operacion_matematica", OperacionMatematica)
rospy.loginfo("Cliente iniciado, esperando servidor...")
# Parsear argumentos
parametro_x = int(sys.argv[1])
parametro_y = int(sys.argv[2])
# Esperar a que el servicio esté disponible
# Método 1: cliente.wait_for_service()
# Método 2: rospy.wait_for_service("nombre_servicio")
rospy.wait_for_service("operacion_matematica")
# Enviar solicitud
respuesta = cliente.call(parametro_x, parametro_y)
# Procesar respuesta
rospy.loginfo("Resultado recibido: %d", respuesta.resultado)
Con esta implementación, el cliente permanecerá en estado de espera hasta que el servidor esté disponible, evitando así excepciones por conexión fallida.
Resumen
La comunicación por servicios en ROS proporciona un mecanismo de solicitud-respuesta síncrono ideal para operaciones que requieren confirmación inmediata. Las implementaciones mostradas demuestran cómo crear proveedores y solicitantes de servicio en Python, incluyendo características avanzadas como parámetros dinámicos y manejo de escenarios donde el cliente se ejecuta antes que el servidor.