Spaces:
Running
Running
File size: 12,769 Bytes
ec936d5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
# User Story 002: Robot Calibration
## Story
**As a** robotics developer setting up SO-100 robot arms
**I want** to calibrate my robot arms to establish proper motor positions and limits
**So that** my robot operates safely and accurately within its intended range of motion
## Background
Robot calibration is a critical setup step that establishes the zero positions, movement limits, and safety parameters for robotic arms. The Python lerobot provides a `calibrate.py` script that:
1. Connects to the specified robot (follower) or teleoperator (leader)
2. Runs the calibration procedure to set motor positions and limits
3. Saves calibration data for future robot operations
4. Ensures safe operation by establishing proper movement boundaries
The calibration process uses the USB ports identified by the `find_port` functionality from User Story 001, and supports both robot arms (followers) and teleoperators (leaders).
## Acceptance Criteria
### Core Functionality
- [x] **Robot Connection**: Connect to robot using discovered USB port from find_port
- [x] **Robot Types**: Support SO-100 follower robot type
- [x] **Teleoperator Support**: Support SO-100 leader teleoperator
- [x] **Calibration Process**: Run device-specific calibration procedures
- [x] **Configuration Management**: Handle robot-specific configuration parameters
- [x] **Cross-Platform**: Work on Windows, macOS, and Linux
- [x] **CLI Interface**: Provide `npx lerobot calibrate` command identical to Python version
### User Experience
- [x] **Clear Feedback**: Show calibration progress and status messages
- [x] **Error Handling**: Handle connection failures, calibration errors gracefully
- [x] **Safety Validation**: Confirm successful calibration before completion
- [x] **Results Display**: Show calibration completion status and saved configuration
### Technical Requirements
- [x] **Dual Platform**: Support both Node.js (CLI) and Web (browser) platforms
- [x] **Node.js Implementation**: Use serialport package for Node.js serial communication
- [x] **Web Implementation**: Use Web Serial API for browser serial communication
- [x] **TypeScript**: Fully typed implementation following project conventions
- [x] **CLI Tool**: Executable via `npx lerobot calibrate` (matching Python version)
- [x] **Configuration Storage**: Save/load calibration data to appropriate locations per platform
- [x] **Platform Abstraction**: Abstract robot/teleoperator interfaces work on both platforms
## Expected User Flow
### Node.js CLI Calibration (Traditional)
```bash
$ npx lerobot calibrate --robot.type=so100_follower --robot.port=COM4 --robot.id=my_follower_arm
Calibrating robot...
Robot type: so100_follower
Port: COM4
ID: my_follower_arm
Connecting to robot...
Connected successfully.
Starting calibration procedure...
Calibration completed successfully.
Configuration saved to: ~/.cache/huggingface/lerobot/calibration/robots/so100_follower/my_follower_arm.json
Disconnecting from robot...
```
### Web Browser Calibration (Interactive)
```typescript
// In a web application
import { calibrate } from "lerobot/web/calibrate";
// Must be triggered by user interaction (button click)
await calibrate({
robot: {
type: "so100_follower",
id: "my_follower_arm",
// port will be selected by user via browser dialog
},
});
// Browser shows port selection dialog
// User selects robot from available serial ports
// Calibration proceeds similar to CLI version
// Configuration saved to browser storage or downloaded as file
```
### Teleoperator Calibration
```bash
$ npx lerobot calibrate --teleop.type=so100_leader --teleop.port=COM3 --teleop.id=my_leader_arm
Calibrating teleoperator...
Teleoperator type: so100_leader
Port: COM3
ID: my_leader_arm
Connecting to teleoperator...
Connected successfully.
Starting calibration procedure...
Please follow the on-screen instructions to move the teleoperator through its range of motion...
Calibration completed successfully.
Configuration saved to: ~/.cache/huggingface/lerobot/calibration/teleoperators/so100_leader/my_leader_arm.json
Disconnecting from teleoperator...
```
### Error Handling
```bash
$ npx lerobot calibrate --robot.type=so100_follower --robot.port=COM99
Error: Could not connect to robot on port COM99
Please verify:
1. The robot is connected to the specified port
2. No other application is using the port
3. You have permission to access the port
Use 'npx lerobot find-port' to discover available ports.
```
## Implementation Details
### File Structure
```
src/lerobot/
βββ node/
β βββ calibrate.ts # Node.js calibration logic (uses serialport)
β βββ robots/
β β βββ config.ts # Shared robot configuration types
β β βββ robot.ts # Node.js Robot base class
β β βββ so100_follower.ts # Node.js SO-100 follower implementation
β βββ teleoperators/
β βββ config.ts # Shared teleoperator configuration types
β βββ teleoperator.ts # Node.js Teleoperator base class
β βββ so100_leader.ts # Node.js SO-100 leader implementation
βββ web/
βββ calibrate.ts # Web calibration logic (uses Web Serial API)
βββ robots/
β βββ robot.ts # Web Robot base class
β βββ so100_follower.ts # Web SO-100 follower implementation
βββ teleoperators/
βββ teleoperator.ts # Web Teleoperator base class
βββ so100_leader.ts # Web SO-100 leader implementation
src/cli/
βββ index.ts # CLI entry point (Node.js only)
```
### Key Dependencies
#### Node.js Platform
- **serialport**: For Node.js serial communication
- **commander**: For CLI argument parsing (matching Python argparse style)
- **fs/promises**: For configuration file management
- **os**: Node.js built-in for cross-platform home directory detection
- **path**: Node.js built-in for path manipulation
#### Web Platform
- **Web Serial API**: Built-in browser API (no external dependencies)
- **File System Access API**: For configuration file management (when available)
- **Streams API**: Built-in browser streams for data handling
### Platform API Differences
The Web Serial API and Node.js serialport APIs are **completely different** and require separate implementations:
#### Node.js Serial API (Traditional)
```typescript
// Node.js - Event-based, programmatic access
import { SerialPort } from "serialport";
// List ports programmatically
const ports = await SerialPort.list();
// Create port instance
const port = new SerialPort({
path: "COM4",
baudRate: 1000000, // Correct baudRate for Feetech motors (SO-100)
});
// Event-based data handling
port.on("data", (data) => {
console.log("Received:", data.toString());
});
// Direct write
port.write("command\r\n");
```
#### Web Serial API (Modern)
```typescript
// Web - Promise-based, user permission required
// Request port (requires user interaction)
const port = await navigator.serial.requestPort();
// Open with options
await port.open({ baudRate: 1000000 }); // Correct baudRate for Feetech motors (SO-100)
// Stream-based data handling
const reader = port.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log("Received:", new TextDecoder().decode(value));
}
// Stream-based write
const writer = port.writable.getWriter();
await writer.write(new TextEncoder().encode("command\r\n"));
writer.releaseLock();
```
### Core Functions to Implement
#### Shared Interface
```typescript
// calibrate.ts (matching Python naming and structure)
interface CalibrateConfig {
robot?: RobotConfig;
teleop?: TeleoperatorConfig;
}
async function calibrate(config: CalibrateConfig): Promise<void>;
// Robot/Teleoperator base classes (platform-agnostic)
abstract class Robot {
abstract connect(calibrate?: boolean): Promise<void>;
abstract calibrate(): Promise<void>;
abstract disconnect(): Promise<void>;
}
abstract class Teleoperator {
abstract connect(calibrate?: boolean): Promise<void>;
abstract calibrate(): Promise<void>;
abstract disconnect(): Promise<void>;
}
```
#### Platform-Specific Implementations
```typescript
// Node.js implementation
class NodeRobot extends Robot {
private port: SerialPort;
// Uses serialport package
}
// Web implementation
class WebRobot extends Robot {
private port: SerialPort; // Web Serial API SerialPort
// Uses navigator.serial API
}
```
### Configuration Types
```typescript
interface RobotConfig {
type: "so100_follower";
port: string;
id?: string;
calibration_dir?: string;
// SO-100 specific options
disable_torque_on_disconnect?: boolean;
max_relative_target?: number | null;
use_degrees?: boolean;
}
interface TeleoperatorConfig {
type: "so100_leader";
port: string;
id?: string;
calibration_dir?: string;
// SO-100 leader specific options
}
```
### Technical Considerations
#### Configuration Management
- **Storage Location**: `{HF_HOME}/lerobot/calibration/robots/{robot_name}/{robot_id}.json` (matching Python version)
- **HF_HOME Discovery**: Use Node.js equivalent of `huggingface_hub.constants.HF_HOME`
- Default: `~/.cache/huggingface` (Linux/macOS) or `%USERPROFILE%\.cache\huggingface` (Windows)
- Environment variable: `HF_HOME` can override the default
- Environment variable: `HF_LEROBOT_CALIBRATION` can override the calibration directory
- **File Format**: JSON for cross-platform compatibility
- **Directory Structure**: `calibration/robots/{robot_name}/` where robot_name matches the robot type
#### Safety Features
- **Movement Limits**: Enforce maximum relative target constraints
- **Torque Management**: Handle torque disable on disconnect
- **Error Recovery**: Graceful handling of calibration failures
#### Device Communication
- **Serial Protocol**: Match Python implementation's communication protocol
- **Timeout Handling**: Appropriate timeouts for device responses
- **Connection Validation**: Verify device is responding before calibration
#### Platform-Specific Challenges
**Node.js Platform:**
- **Port Access**: Direct system-level port access
- **Port Discovery**: Programmatic port listing via `SerialPort.list()`
- **Event Handling**: Traditional callback/event-based patterns
- **Error Handling**: System-level error codes and messages
**Web Platform:**
- **User Permission**: Requires user interaction for port selection
- **Limited Discovery**: Cannot programmatically list ports
- **Stream-Based**: Modern Promise/Stream-based patterns
- **Browser Security**: Limited to what browser security model allows
- **Configuration Storage**: Use browser storage APIs (localStorage/IndexedDB) or File System Access API
#### CLI Argument Parsing
- **Exact Matching**: Command line arguments must match Python version exactly
- **Validation**: Input validation for robot types, ports, and IDs
- **Help Text**: Identical help text and usage examples as Python version
#### Hugging Face Directory Discovery (Node.js)
```typescript
// Equivalent to Python's huggingface_hub.constants.HF_HOME
function getHfHome(): string {
if (process.env.HF_HOME) {
return process.env.HF_HOME;
}
const homeDir = os.homedir();
if (process.platform === "win32") {
return path.join(homeDir, ".cache", "huggingface");
} else {
return path.join(homeDir, ".cache", "huggingface");
}
}
// Equivalent to Python's HF_LEROBOT_CALIBRATION
function getCalibrationDir(): string {
if (process.env.HF_LEROBOT_CALIBRATION) {
return process.env.HF_LEROBOT_CALIBRATION;
}
return path.join(getHfHome(), "lerobot", "calibration");
}
```
## Definition of Done
- [x] **Functional**: Successfully calibrates SO-100 robots and teleoperators on both platforms
- [x] **CLI Compatible**: `npx lerobot calibrate` matches Python `python -m lerobot.calibrate`
- [x] **Web Compatible**: Browser-based calibration with Web Serial API
- [x] **Cross-Platform**: Node.js works on Windows, macOS, and Linux; Web works in Chromium browsers
- [x] **Tested**: Unit tests for core logic, integration tests with mock devices for both platforms
- [x] **Error Handling**: Platform-appropriate error handling and user-friendly messages
- [x] **Configuration**: Platform-appropriate configuration storage (filesystem vs browser storage)
- [x] **Type Safe**: Full TypeScript coverage with strict mode for both implementations
- [x] **Follows Conventions**: Matches Python lerobot UX/API exactly (CLI), provides intuitive web UX
- [x] **Integration**: Node.js works with ports discovered by User Story 001; Web uses browser port selection
|