NERDDISCO commited on
Commit
659ebc7
Β·
1 Parent(s): 6a83670

chore: not needed anymore

Browse files
Files changed (1) hide show
  1. docs/planning/005_same_api.md +0 -447
docs/planning/005_same_api.md DELETED
@@ -1,447 +0,0 @@
1
- # User Story 005: Building Block API - Clean, Composable, Unified
2
-
3
- ## 🎯 Goal
4
-
5
- Create a clean building block API across Python lerobot, Node.js lerobot.js, and Web lerobot.js where users manage state and workflow while we abstract away robotics complexity. Users learn core concepts once and apply them everywhere.
6
-
7
- ## 🚨 Problem Statement
8
-
9
- Currently, the APIs have fundamentally different approaches:
10
-
11
- ### Current Inconsistencies
12
-
13
- 1. **State Management**:
14
- - Python/Node.js: Functions handle connection lifecycle internally
15
- - Web: User must manually manage controllers and connections
16
- 2. **Complexity Exposure**:
17
- - Python/Node.js: `calibrate(config)` - single call does everything
18
- - Web: Multi-step `controller.performHomingStep()`, `controller.moveToPosition()`, etc.
19
- 3. **Resource Management**:
20
- - Python/Node.js: Each function connects/disconnects automatically
21
- - Web: User manages port lifecycle manually
22
- 4. **Device Identification**:
23
- - Node.js: Port discovery finds system ports by name (COM4, /dev/ttyUSB0)
24
- - Web: No device identification - WebSerial can't distinguish between identical devices
25
-
26
- ### User Pain Points
27
-
28
- - **Different Mental Models**: Same operations require different approaches per platform
29
- - **Web Complexity Exposure**: Users must understand servo protocols and multi-step procedures
30
- - **Inconsistent Resource Management**: Some platforms auto-manage, others don't
31
- - **Device Identification Issues**: Web can't identify which physical robot is connected
32
- - **Hidden State Problems**: Users can't control when connections happen or track device status
33
-
34
- ## 🎯 Success Criteria
35
-
36
- ### Primary Goal: Building Block Consistency
37
-
38
- - [ ] **Same Operation Patterns**: All platforms use identical patterns for core operations
39
- - [ ] **Clean Responsibility Split**: Users manage state/UI, library handles robotics
40
- - [ ] **Self-Contained Operations**: Each function manages its own connection lifecycle
41
- - [ ] **Unified Control Model**: Long-running operations use same session-based control
42
-
43
- ### Secondary Goal: Platform-Appropriate Implementation
44
-
45
- - [ ] **Smart Device Pairing**: Web uses WebUSB + WebSerial for device identification + communication
46
- - [ ] **Hidden Robotics Complexity**: Baud rates, protocols, servo commands abstracted away
47
- - [ ] **Graceful Resource Management**: Automatic connection cleanup with user control
48
- - [ ] **Consistent Error Handling**: Platform-specific errors mapped to common patterns
49
-
50
- ## πŸ“‹ Ground Truth Decisions
51
-
52
- ### 1. Building Block Philosophy
53
-
54
- **DECIDED: User manages state, we provide clean tools**
55
-
56
- βœ… **User Responsibilities:**
57
-
58
- - Device pairing and storage (when to pair, how to persist)
59
- - UI workflow (when to show dialogs, how to display progress)
60
- - State management (which devices they have, tracking connections)
61
- - Error handling UI (how to display errors to end users)
62
-
63
- βœ… **Library Responsibilities:**
64
-
65
- - WebUSB + WebSerial pairing dialogs (native browser interactions)
66
- - Baud rate configuration (1000000 for SO-100)
67
- - STS3215 servo protocol implementation
68
- - Calibration and teleoperation procedures
69
- - Resource conflict prevention with clear error messages
70
-
71
- ### 2. Device Pairing vs Connection Pattern
72
-
73
- **DECIDED: Separate pairing (once) from connection (per operation)**
74
-
75
- ```javascript
76
- // PAIRING: One-time device identification (stores device reference)
77
- const deviceInfo = await lerobot.pairDevice();
78
- // User stores this: localStorage, state management, etc.
79
-
80
- // OPERATIONS: Each function connects internally using device reference
81
- const session = await lerobot.calibrate({ robot: { device: deviceInfo } });
82
- await session.stop(); // Graceful disconnect
83
-
84
- const session2 = await lerobot.teleoperate({ robot: { device: deviceInfo } });
85
- await session2.stop(); // Independent connection lifecycle
86
- ```
87
-
88
- **Web Implementation Details:**
89
-
90
- - `pairDevice()` shows WebUSB dialog β†’ gets device info β†’ shows WebSerial dialog β†’ extracts serial ID
91
- - Each operation connects to paired device internally (no singleton state)
92
- - Users never see servo protocols, baud rates, or connection management
93
-
94
- ### 3. Connection Lifecycle Pattern
95
-
96
- **DECIDED: Each operation is self-contained (like Python/Node.js)**
97
-
98
- ```javascript
99
- // PYTHON/NODE.JS CURRENT PATTERN (keep this):
100
- await calibrate(config); // Internally: connect β†’ calibrate β†’ disconnect
101
- await teleoperate(config); // Internally: connect β†’ teleoperate β†’ disconnect
102
-
103
- // WEB NEW PATTERN (match Python/Node.js):
104
- const cal = await calibrate({ robot: { device: pairedDevice } }); // connect β†’ start calibration
105
- await cal.stop(); // disconnect
106
-
107
- const tel = await teleoperate({ robot: { device: pairedDevice } }); // connect β†’ start teleoperation
108
- await tel.stop(); // disconnect
109
- ```
110
-
111
- **Key Principle:** No shared connections, no singletons, each operation manages its own lifecycle.
112
-
113
- ### 4. Long-Running Operation Control
114
-
115
- **DECIDED: Both calibrate and teleoperate are long-running until user stops**
116
-
117
- ```javascript
118
- // UNIFIED SESSION PATTERN for both operations:
119
- interface RobotSession {
120
- stop(): Promise<void>; // Graceful shutdown + disconnect
121
- getState(): SessionState; // Current operation state
122
- }
123
-
124
- // Both operations return controllable sessions:
125
- const calibration = await lerobot.calibrate({
126
- robot: { device: myDevice },
127
- onStateChange: (state) => updateUI(state),
128
- });
129
-
130
- const teleoperation = await lerobot.teleoperate({
131
- robot: { device: myDevice },
132
- onStateChange: (state) => updateUI(state),
133
- });
134
-
135
- // Same control pattern for both:
136
- await calibration.stop();
137
- await teleoperation.stop();
138
- ```
139
-
140
- **Calibrate Reality Check:**
141
-
142
- - Starts calibration procedure
143
- - Waits for user to move robot through ranges
144
- - Records positions until user decides to stop
145
- - Can run indefinitely like teleoperate
146
-
147
- ### 5. Web Device Identification Solution
148
-
149
- **DECIDED: WebUSB + WebSerial both required**
150
-
151
- ```javascript
152
- // Why both APIs are needed:
153
- // 1. WebUSB: Get device serial number β†’ "This is robot arm #ABC123"
154
- // 2. WebSerial: Actual communication β†’ Send servo commands
155
-
156
- async function pairDevice(): Promise<DeviceInfo> {
157
- // Step 1: WebUSB for device identification
158
- const usbDevice = await navigator.usb.requestDevice({...});
159
- const serialId = extractSerialNumber(usbDevice);
160
-
161
- // Step 2: WebSerial for communication
162
- const serialPort = await navigator.serial.requestPort();
163
- await serialPort.open({ baudRate: 1000000 }); // We handle baud rate
164
-
165
- return {
166
- serialId,
167
- usbDevice,
168
- serialPort,
169
- type: detectRobotType(usbDevice) // We detect SO-100 vs other robots
170
- };
171
- }
172
- ```
173
-
174
- ## πŸ› οΈ Implementation Architecture
175
-
176
- ### Core Building Blocks
177
-
178
- ```javascript
179
- // DEVICE PAIRING (Web-specific, one-time)
180
- export async function pairDevice(): Promise<DeviceInfo>
181
-
182
- // UNIFIED OPERATIONS (Same across all platforms)
183
- export async function calibrate(config: CalibrationConfig): Promise<CalibrationSession>
184
- export async function teleoperate(config: TeleoperationConfig): Promise<TeleoperationSession>
185
-
186
- // UNIFIED SESSION CONTROL
187
- interface RobotSession {
188
- stop(): Promise<void>
189
- getState(): SessionState
190
- }
191
- ```
192
-
193
- ### Platform-Specific Implementation
194
-
195
- **Node.js (No changes needed - already correct pattern):**
196
-
197
- ```javascript
198
- // Each function connects/disconnects internally - keep as-is
199
- export async function calibrate(config) {
200
- const device = createDevice(config.robot);
201
- await device.connect();
202
- // ... calibration work
203
- await device.disconnect();
204
- }
205
- ```
206
-
207
- **Web (New unified wrapper):**
208
-
209
- ```javascript
210
- // Hide controller complexity behind unified interface
211
- export async function calibrate(config) {
212
- let connection = null;
213
- try {
214
- connection = await connectToDevice(config.robot.device); // We handle WebSerial setup
215
- const session = new CalibrationSession(connection, config);
216
- return session; // User controls session lifecycle
217
- } catch (error) {
218
- if (connection) await connection.disconnect();
219
- throw error;
220
- }
221
- }
222
- ```
223
-
224
- ## πŸ“‹ Detailed API Specification
225
-
226
- ### 1. Device Pairing API (Web Only)
227
-
228
- ```javascript
229
- // Web-specific device pairing
230
- interface DeviceInfo {
231
- serialId: string; // From WebUSB device descriptor
232
- robotType: 'so100_follower' | 'so100_leader';
233
- usbVendorId: number;
234
- usbProductId: number;
235
- // Internal connection details (user doesn't need to know)
236
- }
237
-
238
- export async function pairDevice(): Promise<DeviceInfo>
239
- ```
240
-
241
- **User Workflow:**
242
-
243
- 1. Call `pairDevice()` β†’ Native browser dialogs appear
244
- 2. Store `DeviceInfo` in their state management
245
- 3. Use stored device for all subsequent operations
246
-
247
- ### 2. Unified Calibration API
248
-
249
- ```javascript
250
- interface CalibrationConfig {
251
- robot: {
252
- device: DeviceInfo; // Web: paired device, Node.js: { type, port }
253
- };
254
- onStateChange?: (state: 'connecting' | 'connected' | 'active' | 'stopping' | 'stopped') => void;
255
- onProgress?: (progress: CalibrationProgress) => void;
256
- }
257
-
258
- interface CalibrationSession extends RobotSession {
259
- stop(): Promise<CalibrationResults>; // Returns calibration data
260
- }
261
-
262
- // SAME across all platforms:
263
- export async function calibrate(config: CalibrationConfig): Promise<CalibrationSession>
264
- ```
265
-
266
- ### 3. Unified Teleoperation API
267
-
268
- ```javascript
269
- interface TeleoperationConfig {
270
- robot: {
271
- device: DeviceInfo; // Web: paired device, Node.js: { type, port }
272
- };
273
- controls?: {
274
- [key: string]: ControlMapping;
275
- };
276
- onStateChange?: (state: SessionState) => void;
277
- }
278
-
279
- interface TeleoperationSession extends RobotSession {
280
- updateKeyState(key: string, pressed: boolean): void; // For keyboard control
281
- getCurrentPositions(): Promise<Record<string, number>>;
282
- }
283
-
284
- // SAME across all platforms:
285
- export async function teleoperate(config: TeleoperationConfig): Promise<TeleoperationSession>
286
- ```
287
-
288
- ### 4. Error Handling
289
-
290
- ```javascript
291
- // Standard error types across platforms
292
- class DeviceNotPairedError extends Error {}
293
- class DeviceConnectionError extends Error {}
294
- class DeviceAlreadyConnectedError extends Error {}
295
- class CalibrationError extends Error {}
296
- class TeleoperationError extends Error {}
297
-
298
- // Platform-specific errors mapped to standard types
299
- // Web: WebSerial/WebUSB errors β†’ DeviceConnectionError
300
- // Node.js: SerialPort errors β†’ DeviceConnectionError
301
- ```
302
-
303
- ## πŸ”„ User Experience Flows
304
-
305
- ### First-Time Web User
306
-
307
- ```javascript
308
- // 1. Pair device once (shows native browser dialogs)
309
- const myRobot = await lerobot.pairDevice();
310
- localStorage.setItem("myRobot", JSON.stringify(myRobot));
311
-
312
- // 2. Use paired device for operations
313
- const calibration = await lerobot.calibrate({
314
- robot: { device: myRobot },
315
- onStateChange: (state) => updateStatus(state),
316
- });
317
-
318
- // 3. User controls session
319
- await calibration.stop();
320
- ```
321
-
322
- ### Returning Web User
323
-
324
- ```javascript
325
- // 1. Load stored device (no browser dialogs)
326
- const myRobot = JSON.parse(localStorage.getItem("myRobot"));
327
-
328
- // 2. Operations work immediately (connect internally)
329
- const teleoperation = await lerobot.teleoperate({
330
- robot: { device: myRobot },
331
- onStateChange: (state) => updateStatus(state),
332
- });
333
- ```
334
-
335
- ### Node.js User (Unchanged)
336
-
337
- ```javascript
338
- // Same as current - no changes needed
339
- const calibration = await lerobot.calibrate({
340
- robot: { type: "so100_follower", port: "COM4" },
341
- });
342
- await calibration.stop();
343
- ```
344
-
345
- ## πŸ§ͺ Implementation Phases
346
-
347
- ### Phase 1: Core Building Blocks
348
-
349
- - [ ] Define unified interfaces (`RobotSession`, `DeviceInfo`)
350
- - [ ] Implement Web `pairDevice()` with WebUSB + WebSerial
351
- - [ ] Create session-based wrappers for Web calibration/teleoperation
352
- - [ ] Standardize error types and mapping
353
-
354
- ### Phase 2: API Unification
355
-
356
- - [ ] Ensure Node.js returns session objects (minimal changes)
357
- - [ ] Web operations return sessions that hide controller complexity
358
- - [ ] Unified state change callbacks and progress reporting
359
- - [ ] Cross-platform testing with real hardware
360
-
361
- ### Phase 3: Developer Experience
362
-
363
- - [ ] Update examples to use building block pattern
364
- - [ ] Create migration guide from current APIs
365
- - [ ] Unified documentation with platform-specific notes
366
- - [ ] Performance optimization and bundle size analysis
367
-
368
- ## πŸ“Š Success Metrics
369
-
370
- ### Developer Experience
371
-
372
- - [ ] **Single Learning Curve**: Core concepts (pairing, sessions, operations) work everywhere
373
- - [ ] **User Control**: Developers control state, UI, and workflow completely
374
- - [ ] **Platform Abstraction**: Robotics complexity hidden, platform differences minimal
375
-
376
- ### Technical Quality
377
-
378
- - [ ] **Resource Management**: No connection leaks, graceful cleanup
379
- - [ ] **Error Handling**: Clear, actionable errors with consistent types
380
- - [ ] **Performance**: No regressions, efficient resource usage
381
-
382
- ### API Consistency
383
-
384
- - [ ] **Same Patterns**: Operations, sessions, and control work identically
385
- - [ ] **Platform Appropriate**: Each platform uses native capabilities optimally
386
- - [ ] **Migration Path**: Existing code can adopt new patterns incrementally
387
-
388
- ## πŸ” Edge Cases & Considerations
389
-
390
- ### Multiple Device Management
391
-
392
- ```javascript
393
- // User manages multiple devices
394
- const robot1 = await lerobot.pairDevice(); // First robot
395
- const robot2 = await lerobot.pairDevice(); // Second robot
396
-
397
- // Independent sessions
398
- const cal1 = await lerobot.calibrate({ robot: { device: robot1 } });
399
- const tel2 = await lerobot.teleoperate({ robot: { device: robot2 } });
400
- ```
401
-
402
- ### Resource Conflicts
403
-
404
- ```javascript
405
- // Library prevents conflicts with clear errors
406
- const session1 = await lerobot.calibrate({ robot: { device: myRobot } });
407
-
408
- try {
409
- const session2 = await lerobot.teleoperate({ robot: { device: myRobot } });
410
- } catch (error) {
411
- if (error instanceof DeviceAlreadyConnectedError) {
412
- // User gets clear message: "Device already in use by calibration. Stop calibration first."
413
- await session1.stop();
414
- const session2 = await lerobot.teleoperate({ robot: { device: myRobot } });
415
- }
416
- }
417
- ```
418
-
419
- ### Browser Tab Management
420
-
421
- ```javascript
422
- // Sessions automatically cleanup on page unload
423
- window.addEventListener("beforeunload", async () => {
424
- if (currentSession) {
425
- await currentSession.stop(); // Graceful disconnect
426
- }
427
- });
428
- ```
429
-
430
- ## πŸ“ Definition of Done
431
-
432
- - [ ] **Web Pairing**: `pairDevice()` uses WebUSB + WebSerial to identify and store device info
433
- - [ ] **Session Control**: Both `calibrate()` and `teleoperate()` return controllable sessions
434
- - [ ] **Self-Contained Operations**: Each operation connects/disconnects internally, no shared state
435
- - [ ] **Unified Error Handling**: Platform-specific errors mapped to consistent error types
436
- - [ ] **Resource Management**: Clear error messages for conflicts, automatic cleanup
437
- - [ ] **Documentation**: Single API reference with platform-specific implementation notes
438
- - [ ] **Migration Path**: Existing users can adopt new patterns without breaking changes
439
- - [ ] **Hardware Validation**: Tested with real SO-100 hardware on all platforms
440
-
441
- ---
442
-
443
- **Priority**: πŸ”΄ Critical - Blocks adoption due to API inconsistency
444
- **Effort**: πŸ”₯πŸ”₯πŸ”₯ High - Significant Web refactoring, but Node.js mostly unchanged
445
- **Impact**: πŸš€πŸš€πŸš€ Transformative - Enables true cross-platform development
446
-
447
- **Impact**: πŸš€πŸš€πŸš€ Very High - Transforms user experience and enables widespread adoption