# LeRobot Arena - Robot Control Architecture v2.0 > **Master-Slave Pattern for Scalable Robot Control** > A revolutionary architecture that separates command generation (Masters) from execution (Slaves), enabling sophisticated robot control scenarios from simple manual operation to complex multi-robot coordination. ## ๐Ÿ—๏ธ Architecture Overview The architecture follows a **Master-Slave Pattern** with complete separation of concerns: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Web Frontend โ”‚ โ”‚ RobotManager โ”‚ โ”‚ Masters โ”‚ โ”‚ โ”‚โ—„โ”€โ”€โ–บโ”‚ โ”‚โ—„โ”€โ”€โ–บโ”‚ โ”‚ โ”‚ โ€ข 3D Visualization โ”‚ โ€ข Robot Creation โ”‚ โ”‚ โ€ข USB Master โ”‚ โ”‚ โ€ข Manual Controlโ”‚ โ”‚ โ€ข Master/Slave โ”‚ โ”‚ โ€ข Remote Server โ”‚ โ”‚ โ€ข Monitoring โ”‚ โ”‚ Orchestration โ”‚ โ”‚ โ€ข Mock Sequence โ”‚ โ”‚ (disabled when โ”‚ โ”‚ โ€ข State Sync โ”‚ โ”‚ (1 per robot) โ”‚ โ”‚ master active) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Robot Class โ”‚โ—„โ”€โ”€โ–บโ”‚ Slaves โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Joint States โ”‚ โ”‚ โ€ข USB Robot โ”‚ โ”‚ โ€ข URDF Model โ”‚ โ”‚ โ€ข Remote Robot โ”‚ โ”‚ โ€ข Command Queue โ”‚ โ”‚ โ€ข WebSocket โ”‚ โ”‚ โ€ข Calibration โ”‚ โ”‚ (N per robot) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ–ผ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Python Backend โ”‚ โ”‚ โ”‚ โ”‚ โ€ข WebSocket API โ”‚ โ”‚ โ€ข Connection Mgr โ”‚ โ”‚ โ€ข Robot Manager โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ### Control Flow States ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” Master Connected โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Manual Mode โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บ โ”‚ Master Mode โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โœ… Panel Active โ”‚ โ”‚ โŒ Panel Locked โ”‚ โ”‚ โœ… Direct Controlโ”‚ โ”‚ โœ… Master Commandsโ”‚ โ”‚ โŒ No Master โ”‚ โ”‚ โœ… All Slaves Execโ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Master Disconnected ``` ## ๐ŸŽฏ Core Concepts ### Masters (Command Sources) **Purpose**: Generate and provide robot control commands | Type | Description | Connection | Use Case | |------|-------------|------------|----------| | **USB Master** | Physical robot as command source | USB/Serial | Teleoperation, motion teaching | | **Remote Server** | WebSocket/HTTP command reception | Network | External control systems | | **Mock Sequence** | Predefined movement patterns | Internal | Testing, demonstrations | **Key Rules**: - ๐Ÿ”’ **Exclusive Control**: Only 1 master per robot - ๐Ÿšซ **Panel Lock**: Manual control disabled when master active - ๐Ÿ”„ **Seamless Switch**: Masters can be swapped dynamically ### Slaves (Execution Targets) **Purpose**: Execute commands on physical or virtual robots | Type | Description | Connection | Use Case | |------|-------------|------------|----------| | **USB Slave** | Physical robot control | USB/Serial | Hardware execution | | **Remote Server Slave** | Network robot control | WebSocket | Distributed robots | | **WebSocket Slave** | Real-time WebSocket execution | WebSocket | Cloud robots | **Key Rules**: - ๐Ÿ”ข **Multiple Allowed**: N slaves per robot - ๐ŸŽฏ **Parallel Execution**: All slaves execute same commands - ๐Ÿ”„ **Independent Operation**: Slaves can fail independently ### Architecture Comparison | Aspect | v1.0 (Single Driver) | v2.0 (Master-Slave) | |--------|---------------------|---------------------| | **Connection Model** | 1 Driver โ†” 1 Robot | 1 Master + N Slaves โ†” 1 Robot | | **Command Source** | Always UI Panel | Master OR UI Panel | | **Execution Targets** | Single Connection | Multiple Parallel | | **Control Hierarchy** | Flat | Hierarchical | | **Scalability** | Limited | Unlimited | ## ๐Ÿ“ Project Structure ### Frontend Architecture (TypeScript + Svelte) ``` src/lib/robot/ โ”œโ”€โ”€ Robot.svelte.ts # Individual robot master-slave coordination โ”œโ”€โ”€ RobotManager.svelte.ts # Global robot orchestration โ””โ”€โ”€ drivers/ โ”œโ”€โ”€ USBMaster.ts # Physical robot as command source โ”œโ”€โ”€ RemoteServerMaster.ts # WebSocket command reception โ”œโ”€โ”€ USBSlave.ts # Physical robot execution โ”œโ”€โ”€ RemoteServerSlave.ts # Network robot execution โ””โ”€โ”€ WebSocketSlave.ts # Real-time WebSocket execution src/lib/types/ โ”œโ”€โ”€ robotDriver.ts # Master/Slave interfaces โ””โ”€โ”€ robot.ts # Robot state management ``` ### Backend Architecture (Python + FastAPI) ``` src-python/src/ โ”œโ”€โ”€ main.py # FastAPI server + WebSocket endpoints โ”œโ”€โ”€ robot_manager.py # Server-side robot lifecycle โ”œโ”€โ”€ connection_manager.py # WebSocket connection handling โ””โ”€โ”€ models.py # Pydantic data models ``` ## ๐ŸŽฎ Usage Examples ### Basic Robot Setup ```typescript import { robotManager } from "$lib/robot/RobotManager.svelte"; // Create robot from URDF const robot = await robotManager.createRobot("demo-arm", { urdfPath: "/robots/so-arm100/robot.urdf", jointNameIdMap: { "Rotation": 1, "Pitch": 2, "Elbow": 3 }, restPosition: { "Rotation": 0, "Pitch": 0, "Elbow": 0 } }); // Add execution targets (slaves) await robotManager.connectUSBSlave("demo-arm"); // Real hardware await robotManager.connectRemoteServerSlave("demo-arm"); // Network robot // Connect command source (master) - panel becomes locked await robotManager.connectUSBMaster("demo-arm"); // Result: USB master controls both USB and Remote slaves ``` ### Master Switching Workflow ```typescript const robot = robotManager.getRobot("my-robot"); // Start with manual control console.log(robot.manualControlEnabled); // โœ… true // Switch to USB master (robot becomes command source) await robotManager.connectUSBMaster("my-robot"); console.log(robot.manualControlEnabled); // โŒ false (panel locked) // Switch to remote control await robotManager.disconnectMaster("my-robot"); await robotManager.connectMaster("my-robot", { type: "remote-server", url: "ws://robot-controller:8080/ws" }); // Restore manual control await robotManager.disconnectMaster("my-robot"); console.log(robot.manualControlEnabled); // โœ… true (panel restored) ``` ## ๐Ÿ”Œ Driver Implementations ### USB Master Driver **Physical robot as command source for teleoperation** ```typescript // USBMaster.ts - Core implementation export class USBMaster implements MasterDriver { readonly type = "master" as const; private feetechDriver: FeetechSerialDriver; private pollIntervalId?: number; async connect(): Promise { // Initialize feetech.js serial connection this.feetechDriver = new FeetechSerialDriver({ port: this.config.port || await this.detectPort(), baudRate: this.config.baudRate || 115200 }); await this.feetechDriver.connect(); this.startPolling(); } private startPolling(): void { this.pollIntervalId = setInterval(async () => { try { // Read current joint positions from hardware const jointStates = await this.readAllJoints(); // Convert to robot commands const commands = this.convertToCommands(jointStates); // Emit commands to slaves this.notifyCommand(commands); } catch (error) { console.error('USB Master polling error:', error); } }, this.config.pollInterval || 100); } private async readAllJoints(): Promise { const states: DriverJointState[] = []; for (const [jointName, servoId] of Object.entries(this.jointMap)) { const position = await this.feetechDriver.readPosition(servoId); states.push({ name: jointName, servoId, type: "revolute", virtualValue: position, realValue: position }); } return states; } } ``` **Usage Pattern:** - Connect USB robot as master - Physical robot becomes the command source - Move robot manually โ†’ slaves follow the movement - Ideal for: Teleoperation, motion teaching, demonstration recording ### USB Slave Driver **Physical robot as execution target** ```typescript // USBSlave.ts - Core implementation export class USBSlave implements SlaveDriver { readonly type = "slave" as const; private feetechDriver: FeetechSerialDriver; private calibrationOffsets: Map = new Map(); async executeCommand(command: RobotCommand): Promise { for (const joint of command.joints) { const servoId = this.getServoId(joint.name); if (!servoId) continue; // Apply calibration offset const offset = this.calibrationOffsets.get(joint.name) || 0; const adjustedValue = joint.value + offset; // Send to hardware via feetech.js await this.feetechDriver.writePosition(servoId, adjustedValue, { speed: joint.speed || 100, acceleration: 50 }); } } async readJointStates(): Promise { const states: DriverJointState[] = []; for (const joint of this.jointStates) { const position = await this.feetechDriver.readPosition(joint.servoId); const offset = this.calibrationOffsets.get(joint.name) || 0; states.push({ ...joint, realValue: position - offset // Remove offset for accurate state }); } return states; } async calibrate(): Promise { console.log('Calibrating USB robot...'); for (const joint of this.jointStates) { // Read current hardware position const currentPos = await this.feetechDriver.readPosition(joint.servoId); // Calculate offset: desired_rest - actual_position const offset = joint.restPosition - currentPos; this.calibrationOffsets.set(joint.name, offset); console.log(`Joint ${joint.name}: offset=${offset.toFixed(1)}ยฐ`); } } } ``` **Features:** - Direct hardware control via feetech.js - Real position feedback - Calibration offset support - Smooth motion interpolation ### Remote Server Master **Network command reception via WebSocket** ```typescript // RemoteServerMaster.ts - Core implementation export class RemoteServerMaster implements MasterDriver { readonly type = "master" as const; private websocket?: WebSocket; private reconnectAttempts = 0; async connect(): Promise { const wsUrl = `${this.config.url}/ws/master/${this.robotId}`; this.websocket = new WebSocket(wsUrl); this.websocket.onopen = () => { console.log(`Remote master connected: ${wsUrl}`); this.reconnectAttempts = 0; this.updateStatus({ isConnected: true }); }; this.websocket.onmessage = (event) => { try { const message = JSON.parse(event.data); this.handleServerMessage(message); } catch (error) { console.error('Failed to parse server message:', error); } }; this.websocket.onclose = () => { this.updateStatus({ isConnected: false }); this.attemptReconnect(); }; } private handleServerMessage(message: any): void { switch (message.type) { case 'command': // Convert server message to robot command const command: RobotCommand = { timestamp: Date.now(), joints: message.data.joints.map((j: any) => ({ name: j.name, value: j.value, speed: j.speed })) }; this.notifyCommand([command]); break; case 'sequence': // Handle command sequence const sequence: CommandSequence = message.data; this.notifySequence(sequence); break; } } async sendSlaveStatus(slaveStates: DriverJointState[]): Promise { if (!this.websocket) return; const statusMessage = { type: 'slave_status', timestamp: new Date().toISOString(), robot_id: this.robotId, data: { joints: slaveStates.map(state => ({ name: state.name, virtual_value: state.virtualValue, real_value: state.realValue })) } }; this.websocket.send(JSON.stringify(statusMessage)); } } ``` **Protocol:** ```json // Command from server to robot { "type": "command", "timestamp": "2024-01-15T10:30:00Z", "data": { "joints": [ { "name": "Rotation", "value": 45, "speed": 100 }, { "name": "Elbow", "value": -30, "speed": 80 } ] } } // Status from robot to server { "type": "slave_status", "timestamp": "2024-01-15T10:30:01Z", "robot_id": "robot-1", "data": { "joints": [ { "name": "Rotation", "virtual_value": 45, "real_value": 44.8 }, { "name": "Elbow", "virtual_value": -30, "real_value": -29.9 } ] } } ``` ### Remote Server Slave **Network robot execution via WebSocket** ```typescript // RemoteServerSlave.ts - Core implementation export class RemoteServerSlave implements SlaveDriver { readonly type = "slave" as const; private websocket?: WebSocket; async executeCommand(command: RobotCommand): Promise { if (!this.websocket) throw new Error('Not connected'); const message = { type: 'command', timestamp: new Date().toISOString(), robot_id: this.config.robotId, data: { joints: command.joints.map(j => ({ name: j.name, value: j.value, speed: j.speed })) } }; this.websocket.send(JSON.stringify(message)); // Wait for acknowledgment return new Promise((resolve, reject) => { const timeout = setTimeout(() => reject(new Error('Command timeout')), 5000); const messageHandler = (event: MessageEvent) => { const response = JSON.parse(event.data); if (response.type === 'command_ack') { clearTimeout(timeout); this.websocket?.removeEventListener('message', messageHandler); resolve(); } }; this.websocket.addEventListener('message', messageHandler); }); } async readJointStates(): Promise { if (!this.websocket) throw new Error('Not connected'); const message = { type: 'status_request', timestamp: new Date().toISOString(), robot_id: this.config.robotId }; this.websocket.send(JSON.stringify(message)); return new Promise((resolve, reject) => { const timeout = setTimeout(() => reject(new Error('Status timeout')), 3000); const messageHandler = (event: MessageEvent) => { const response = JSON.parse(event.data); if (response.type === 'joint_states') { clearTimeout(timeout); this.websocket?.removeEventListener('message', messageHandler); const states = response.data.joints.map((j: any) => ({ name: j.name, servoId: j.servo_id, type: j.type, virtualValue: j.virtual_value, realValue: j.real_value })); resolve(states); } }; this.websocket.addEventListener('message', messageHandler); }); } } ``` ## ๐Ÿ”„ Command Flow Architecture ### Command Structure ```typescript interface RobotCommand { timestamp: number; joints: { name: string; value: number; // degrees for revolute, speed for continuous speed?: number; // optional movement speed }[]; duration?: number; // optional execution time metadata?: Record; } ``` ### Control Flow 1. **Master Generation**: Masters generate commands from various sources 2. **Robot Routing**: Robot class routes commands to all connected slaves 3. **Parallel Execution**: All slaves execute commands simultaneously 4. **State Feedback**: Slaves report back real joint positions 5. **Synchronization**: Robot maintains synchronized state across all slaves ### State Management ```typescript // Robot.svelte.ts - Core state management export interface ManagedJointState { name: string; urdfJoint: IUrdfJoint; servoId?: number; // State values virtualValue: number; // What the UI shows realValue?: number; // What hardware reports commandedValue: number; // Last commanded value // Calibration calibrationOffset: number; // Hardware compensation restPosition: number; // Safe default position // Synchronization lastVirtualUpdate: Date; lastRealUpdate?: Date; lastCommandUpdate?: Date; } ``` ## ๐Ÿ“Š Benefits Summary | Benefit | Description | Impact | |---------|-------------|---------| | **๐Ÿ”’ Clear Control Hierarchy** | Masters provide commands exclusively, slaves execute in parallel | No command conflicts, predictable behavior | | **๐Ÿ”„ Flexible Command Sources** | Easy switching between manual, automated, and remote control | Supports development, testing, and production | | **๐Ÿ“ก Multiple Execution Targets** | Same commands executed on multiple robots simultaneously | Real hardware + simulation testing | | **๐ŸŽ›๏ธ Automatic Panel Management** | UI automatically adapts to master presence | Intuitive user experience | | **๐Ÿš€ Development Workflow** | Clear separation enables independent development | Faster iteration cycles | --- **This architecture provides unprecedented flexibility for robot control, from simple manual operation to sophisticated multi-robot coordination, all with a clean, extensible, and production-ready design.**