Humainoid-robotics / backend /src /devices /ros_interface.py
Awais
Restore original HF deployment with RAG search fallback fix
04dc214
Raw
History Blame Contribute Delete
13.2 kB
"""
ROS Interface Module
Provides standardized interfaces for communicating with ROS-based physical AI devices
"""
import asyncio
import json
from typing import Dict, Any, Optional, List
from datetime import datetime
from enum import Enum
class ROSMessageType(Enum):
"""Enumeration of ROS message types supported"""
REQUEST = "request"
RESPONSE = "response"
PUBLISH = "publish"
SUBSCRIBE = "subscribe"
SERVICE_CALL = "service_call"
class ROSBridgeClient:
"""
Client to interface with ROS Bridge for communication with ROS-based devices
"""
def __init__(self, ros_bridge_url: str = "ws://localhost:9090"):
self.ros_bridge_url = ros_bridge_url
self.connected = False
self.ros_client = None # Will hold the actual ROS bridge client
self.topic_subscriptions = {}
self.service_clients = {}
async def connect(self) -> bool:
"""
Connect to the ROS Bridge
"""
try:
# In a real implementation, this would connect to the actual ROS Bridge
# using a library like roslibpy or similar WebSocket client
# For now, we'll simulate the connection
print(f"Connecting to ROS Bridge at {self.ros_bridge_url}")
# Simulate connection establishment
await asyncio.sleep(0.1) # Simulate network delay
self.connected = True
print("Connected to ROS Bridge successfully")
return True
except Exception as e:
print(f"Failed to connect to ROS Bridge: {str(e)}")
return False
async def disconnect(self) -> bool:
"""
Disconnect from the ROS Bridge
"""
try:
if self.connected:
# In a real implementation, this would close the actual connection
print("Disconnecting from ROS Bridge")
# Cancel all subscriptions
for topic_name in list(self.topic_subscriptions.keys()):
await self.unsubscribe_from_topic(topic_name)
self.connected = False
print("Disconnected from ROS Bridge")
return True
except Exception as e:
print(f"Error disconnecting from ROS Bridge: {str(e)}")
return False
async def publish_to_topic(self, topic_name: str, message: Dict[str, Any]) -> bool:
"""
Publish a message to a ROS topic
"""
if not self.connected:
raise RuntimeError("Not connected to ROS Bridge")
try:
# In a real implementation, this would publish via ROS Bridge
print(f"Publishing to topic '{topic_name}': {message}")
# Simulate publishing
# In real implementation: self.ros_client.publish(topic_name, message)
return True
except Exception as e:
print(f"Error publishing to topic '{topic_name}': {str(e)}")
return False
async def subscribe_to_topic(self, topic_name: str, callback_func=None) -> bool:
"""
Subscribe to a ROS topic
"""
if not self.connected:
raise RuntimeError("Not connected to ROS Bridge")
try:
# In a real implementation, this would subscribe via ROS Bridge
print(f"Subscribing to topic '{topic_name}'")
# Simulate subscription
self.topic_subscriptions[topic_name] = {
'callback': callback_func,
'subscribed_at': datetime.utcnow().isoformat()
}
# In real implementation: self.ros_client.subscribe(topic_name, callback_func)
return True
except Exception as e:
print(f"Error subscribing to topic '{topic_name}': {str(e)}")
return False
async def unsubscribe_from_topic(self, topic_name: str) -> bool:
"""
Unsubscribe from a ROS topic
"""
if not self.connected:
raise RuntimeError("Not connected to ROS Bridge")
try:
if topic_name in self.topic_subscriptions:
print(f"Unsubscribing from topic '{topic_name}'")
# In real implementation: self.ros_client.unsubscribe(topic_name)
del self.topic_subscriptions[topic_name]
return True
else:
print(f"Not subscribed to topic '{topic_name}'")
return False
except Exception as e:
print(f"Error unsubscribing from topic '{topic_name}': {str(e)}")
return False
async def call_service(self, service_name: str, request_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""
Call a ROS service
"""
if not self.connected:
raise RuntimeError("Not connected to ROS Bridge")
try:
print(f"Calling service '{service_name}' with request: {request_data}")
# In a real implementation: return await self.ros_client.call_service(service_name, request_data)
# Simulate service response
response = {
'result': 'success',
'service': service_name,
'timestamp': datetime.utcnow().isoformat(),
'original_request': request_data
}
return response
except Exception as e:
print(f"Error calling service '{service_name}': {str(e)}")
return None
class ROSDeviceInterface:
"""
Standardized interface for interacting with ROS-based physical AI devices
"""
def __init__(self, device_id: str, ros_bridge_url: str = "ws://localhost:9090"):
self.device_id = device_id
self.ros_client = ROSBridgeClient(ros_bridge_url)
self.device_namespace = f"/{device_id}"
self.motion_control_topic = f"{self.device_namespace}/cmd_vel"
self.sensor_data_topic = f"{self.device_namespace}/sensor_data"
self.status_topic = f"{self.device_namespace}/status"
self.emergency_stop_service = f"{self.device_namespace}/emergency_stop"
async def connect_device(self) -> bool:
"""
Connect to the ROS-based device
"""
return await self.ros_client.connect()
async def disconnect_device(self) -> bool:
"""
Disconnect from the ROS-based device
"""
return await self.ros_client.disconnect()
async def send_motion_command(self, linear_velocity: float, angular_velocity: float) -> bool:
"""
Send motion command to the device (linear and angular velocities)
"""
command = {
'linear': {
'x': linear_velocity,
'y': 0.0,
'z': 0.0
},
'angular': {
'x': 0.0,
'y': 0.0,
'z': angular_velocity
}
}
return await self.ros_client.publish_to_topic(self.motion_control_topic, command)
async def get_sensor_data(self) -> Optional[Dict[str, Any]]:
"""
Request current sensor data from the device
"""
# In a real implementation, this would subscribe to sensor data
# For now, we'll return mock data
return {
'device_id': self.device_id,
'timestamp': datetime.utcnow().isoformat(),
'sensor_data': {
'position': {'x': 0.0, 'y': 0.0, 'z': 0.0},
'orientation': {'x': 0.0, 'y': 0.0, 'z': 0.0, 'w': 1.0},
'battery_level': 95.5,
'temperature': 23.4,
'proximity_sensors': [1.2, 0.8, 1.5, 1.0],
'gripper_status': 'open'
}
}
async def subscribe_to_sensor_data(self, callback_func) -> bool:
"""
Subscribe to continuous sensor data updates
"""
return await self.ros_client.subscribe_to_topic(self.sensor_data_topic, callback_func)
async def subscribe_to_status_updates(self, callback_func) -> bool:
"""
Subscribe to device status updates
"""
return await self.ros_client.subscribe_to_topic(self.status_topic, callback_func)
async def trigger_emergency_stop(self) -> Optional[Dict[str, Any]]:
"""
Trigger emergency stop service on the device
"""
request_data = {
'device_id': self.device_id,
'trigger_time': datetime.utcnow().isoformat(),
'reason': 'emergency_stop_triggered'
}
return await self.ros_client.call_service(self.emergency_stop_service, request_data)
async def get_device_status(self) -> Optional[Dict[str, Any]]:
"""
Get current status of the device
"""
# In a real implementation, this might call a status service
# For now, we'll return mock status data
return {
'device_id': self.device_id,
'timestamp': datetime.utcnow().isoformat(),
'status': 'active',
'mode': 'manual_control',
'battery_level': 95.5,
'error_codes': [],
'safety_status': 'nominal',
'last_communication': datetime.utcnow().isoformat()
}
async def send_custom_command(self, command_type: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""
Send a custom command to the device
"""
command_topic = f"{self.device_namespace}/commands"
command_message = {
'command_type': command_type,
'params': params,
'timestamp': datetime.utcnow().isoformat(),
'device_id': self.device_id
}
success = await self.ros_client.publish_to_topic(command_topic, command_message)
if success:
return {
'status': 'command_sent',
'command_id': command_type,
'timestamp': datetime.utcnow().isoformat()
}
return None
class ROSDeviceManager:
"""
Manager for handling multiple ROS-based devices
"""
def __init__(self):
self.devices: Dict[str, ROSDeviceInterface] = {}
self.ros_bridge_url = "ws://localhost:9090" # Default URL
async def register_device(self, device_id: str, ros_bridge_url: str = None) -> bool:
"""
Register a new ROS-based device
"""
if device_id in self.devices:
print(f"Device {device_id} already registered")
return False
# Use provided URL or default
url = ros_bridge_url or self.ros_bridge_url
device_interface = ROSDeviceInterface(device_id, url)
self.devices[device_id] = device_interface
print(f"Registered ROS device: {device_id}")
return True
async def connect_device(self, device_id: str) -> bool:
"""
Connect to a specific ROS device
"""
if device_id not in self.devices:
print(f"Device {device_id} not registered")
return False
return await self.devices[device_id].connect_device()
async def disconnect_device(self, device_id: str) -> bool:
"""
Disconnect from a specific ROS device
"""
if device_id not in self.devices:
print(f"Device {device_id} not registered")
return False
return await self.devices[device_id].disconnect_device()
async def send_command_to_device(self, device_id: str, command_type: str, params: Dict[str, Any]) -> Optional[Dict[str, Any]]:
"""
Send a command to a specific device
"""
if device_id not in self.devices:
print(f"Device {device_id} not registered")
return None
return await self.devices[device_id].send_custom_command(command_type, params)
async def get_device_sensor_data(self, device_id: str) -> Optional[Dict[str, Any]]:
"""
Get sensor data from a specific device
"""
if device_id not in self.devices:
print(f"Device {device_id} not registered")
return None
return await self.devices[device_id].get_sensor_data()
async def trigger_emergency_stop_for_device(self, device_id: str) -> Optional[Dict[str, Any]]:
"""
Trigger emergency stop for a specific device
"""
if device_id not in self.devices:
print(f"Device {device_id} not registered")
return None
return await self.devices[device_id].trigger_emergency_stop()
async def get_all_device_statuses(self) -> Dict[str, Any]:
"""
Get statuses for all registered devices
"""
statuses = {}
for device_id, device_interface in self.devices.items():
status = await device_interface.get_device_status()
statuses[device_id] = status
return statuses
async def list_registered_devices(self) -> List[str]:
"""
List all registered device IDs
"""
return list(self.devices.keys())
# Global instance of the ROS device manager
ros_device_manager = ROSDeviceManager()