# backend/robot_detector.py import serial.tools.list_ports import gradio as gr import json import os # Define the path to save/load configurations CONFIG_FILE = "config/default_config.json" def list_serial_ports(): """ Lista todos los puertos seriales disponibles en el sistema. """ ports = serial.tools.list_ports.comports() port_list = [] if not ports: return ["No se encontraron puertos seriales disponibles."] for port in ports: port_info = f"{port.device} - {port.description}" if port.hwid and "VID:PID" in port.hwid: port_info += f" ({port.hwid})" port_list.append(port_info) return port_list def save_config(follower_port: str, follower_id: str, leader_port: str, leader_id: str): """ Guarda la configuración del robot y teleoperador en un archivo JSON. """ config = { "robot_follower": { "port": follower_port.split(' ')[0] if follower_port else "", # Extract only device path "id": follower_id }, "teleop_leader": { "port": leader_port.split(' ')[0] if leader_port else "", # Extract only device path "id": leader_id } } try: with open(CONFIG_FILE, 'w') as f: json.dump(config, f, indent=4) return f"Configuración guardada en {CONFIG_FILE} correctamente." except Exception as e: return f"Error al guardar la configuración: {e}" def load_config(): """ Carga la configuración del robot y teleoperador desde un archivo JSON. """ if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, 'r') as f: config = json.load(f) return ( config.get("robot_follower", {}).get("port", ""), config.get("robot_follower", {}).get("id", ""), config.get("teleop_leader", {}).get("port", ""), config.get("teleop_leader", {}).get("id", "") ) except Exception as e: gr.Warning(f"Error al cargar la configuración existente: {e}. Se usarán valores por defecto.") return "", "", "", "" return "", "", "", "" # --- Gradio Interface --- # Load initial config values for Gradio components initial_follower_port, initial_follower_id, initial_leader_port, initial_leader_id = load_config() with gr.Blocks(title="Detector y Configuración de Robots LeRobot") as demo: gr.Markdown("#
Detector y Configuración de Robots LeRobot
") gr.Markdown( "Esta herramienta te ayuda a identificar los puertos seriales de tus robots " "SO101 (follower y leader) y asignarles un ID consistente." ) with gr.Tab("1. Detectar Puertos Seriales"): gr.Markdown("## Puertos Seriales Detectados") gr.Markdown( "Conecta tus robots SO101 (follower y leader) a tu computadora y haz clic en 'Refrescar Puertos'. " "Observa la lista y compara con los nombres de tus dispositivos si es posible para identificarlos." ) refresh_btn = gr.Button("🔄 Refrescar Puertos") port_list_output = gr.Textbox( label="Puertos Seriales Disponibles", interactive=False, lines=10, value="\n".join(list_serial_ports()), # Initial list on startup show_copy_button=True ) refresh_btn.click( fn=lambda: "\n".join(list_serial_ports()), inputs=None, outputs=port_list_output ) with gr.Tab("2. Asignar Puertos e IDs"): gr.Markdown("## Asignación de Robots y Teleoperadores") gr.Markdown( "Selecciona el puerto correcto para cada dispositivo y asigna un ID único. " "**¡Es crucial usar el mismo ID en todos los scripts (grabación, entrenamiento, evaluación)!**" ) gr.Markdown("### Robot Follower (Brazo del Robot SO101)") follower_port_dropdown = gr.Dropdown( label="Puerto Serial del Robot Follower", choices=[], # Will be populated dynamically value=initial_follower_port, info="Selecciona el puerto donde está conectado el brazo del robot (SO101)." ) follower_id_input = gr.Textbox( label="ID del Robot Follower", value=initial_follower_id if initial_follower_id else "my_robot_follower_arm", placeholder="ej. my_robot_follower_arm", info="Un identificador único para este robot. Usado para archivos de calibración." ) gr.Markdown("### Teleoperador Leader (Brazo de Control SO101)") leader_port_dropdown = gr.Dropdown( label="Puerto Serial del Teleoperador Leader", choices=[], # Will be populated dynamically value=initial_leader_port, info="Selecciona el puerto donde está conectado el dispositivo de control (SO101)." ) leader_id_input = gr.Textbox( label="ID del Teleoperador Leader", value=initial_leader_id if initial_leader_id else "my_teleop_leader_arm", placeholder="ej. my_teleop_leader_arm", info="Un identificador único para el dispositivo de teleoperación." ) save_btn = gr.Button("💾 Guardar Configuración", variant="primary") save_status_output = gr.Textbox( label="Estado de Guardado", interactive=False, visible=False ) save_btn.click( fn=save_config, inputs=[follower_port_dropdown, follower_id_input, leader_port_dropdown, leader_id_input], outputs=save_status_output ) # Function to update dropdown choices based on current ports def update_port_choices(): ports = list_serial_ports() # Extract just the device path for dropdown value (e.g., /dev/tty.usbmodemXXX) device_paths = [p.split(' ')[0] for p in ports if "No se encontraron" not in p] return gr.update(choices=device_paths), gr.update(choices=device_paths) # Update dropdowns on initial load and when refresh button is clicked demo.load( fn=update_port_choices, inputs=None, outputs=[follower_port_dropdown, leader_port_dropdown] ) refresh_btn.click( # Re-bind refresh button to update dropdowns too fn=update_port_choices, inputs=None, outputs=[follower_port_dropdown, leader_port_dropdown] ) gr.Markdown("---") gr.Markdown(f"**La configuración guardada se almacenará en:** `{os.path.abspath(CONFIG_FILE)}`") gr.Markdown( "**Instrucciones para otros scripts:**\n" "1. Abre `robot_config.json`.\n" "2. Copia los valores de `port` y `id` a tus scripts de `record_controller.py` y `train_robot.py` (para evaluación)." ) gr.Markdown("Hecho con ❤️ para RobotCleanPupusas503") if __name__ == "__main__": try: import serial # Test if pyserial is installed except ImportError: print("Error: La librería 'pyserial' no está instalada.") print("Por favor, instala 'pyserial' ejecutando: pip install pyserial") exit(1) demo.launch(share=False)