Spaces:
Running
Running
docs: update conventions
Browse files- docs/conventions.md +243 -13
docs/conventions.md
CHANGED
@@ -15,6 +15,9 @@
|
|
15 |
- **Serial API Separation**: Always use `serialport` package for Node.js and Web Serial API for browsers - never mix or bridge these incompatible APIs
|
16 |
- **Minimal Console Output**: Only show essential information - reduce cognitive load for users
|
17 |
- **Hardware-First Testing**: Always validate with real hardware, not just simulation
|
|
|
|
|
|
|
18 |
|
19 |
## Project Goals
|
20 |
|
@@ -285,22 +288,29 @@ lerobot/
|
|
285 |
##### 5. UI Framework Integration
|
286 |
|
287 |
- **Node.js**: CLI-based interaction (inquirer, chalk)
|
288 |
-
- **Web**: React components with
|
289 |
-
- **Critical
|
290 |
-
- **
|
291 |
-
- **
|
292 |
- **Real-time Updates**: Hardware callbacks β React state updates
|
293 |
- **Professional UI**: shadcn Dialog, Card, Button components for robotics interfaces
|
294 |
- **Architecture Pattern**:
|
|
|
295 |
```typescript
|
296 |
-
//
|
297 |
-
function
|
298 |
-
const [
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
304 |
}
|
305 |
```
|
306 |
|
@@ -634,4 +644,224 @@ const STS3215_REGISTERS = {
|
|
634 |
- **Loop Time**: < 5ms (when not reading positions)
|
635 |
- **User Experience**: "Buttery smooth", "fucking working and super perfect"
|
636 |
|
637 |
-
**Golden Rule**: When you achieve smooth control, NEVER change the step size, delays, or update patterns without extensive testing. These values were optimized through real hardware testing
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
- **Serial API Separation**: Always use `serialport` package for Node.js and Web Serial API for browsers - never mix or bridge these incompatible APIs
|
16 |
- **Minimal Console Output**: Only show essential information - reduce cognitive load for users
|
17 |
- **Hardware-First Testing**: Always validate with real hardware, not just simulation
|
18 |
+
- **Library/Demo Separation**: Standard library handles hardware communication, demos handle storage/UI concerns - never mix these responsibilities
|
19 |
+
- **No Code Duplication**: Use shared utils, never reimplement the same functionality across files
|
20 |
+
- **Direct Library Usage**: End users call library functions directly (e.g., `calibrate()`, `teleoperate()`) - avoid unnecessary abstraction layers
|
21 |
|
22 |
## Project Goals
|
23 |
|
|
|
288 |
##### 5. UI Framework Integration
|
289 |
|
290 |
- **Node.js**: CLI-based interaction (inquirer, chalk)
|
291 |
+
- **Web**: React components with direct library function usage
|
292 |
+
- **Critical Patterns**:
|
293 |
+
- **Direct Library Usage**: Call `calibrate()`, `teleoperate()` directly in components
|
294 |
+
- **Controlled Hardware Access**: Single controlled serial operation via refs
|
295 |
- **Real-time Updates**: Hardware callbacks β React state updates
|
296 |
- **Professional UI**: shadcn Dialog, Card, Button components for robotics interfaces
|
297 |
- **Architecture Pattern**:
|
298 |
+
|
299 |
```typescript
|
300 |
+
// Direct library usage in components (NO custom hooks)
|
301 |
+
function CalibrationPanel({ robot }) {
|
302 |
+
const [calibrationState, setCalibrationState] = useState();
|
303 |
+
const calibrationProcessRef = useRef(null);
|
304 |
+
|
305 |
+
useEffect(() => {
|
306 |
+
const initCalibration = async () => {
|
307 |
+
const process = await calibrate(robot, {
|
308 |
+
onLiveUpdate: setCalibrationState,
|
309 |
+
});
|
310 |
+
calibrationProcessRef.current = process;
|
311 |
+
};
|
312 |
+
initCalibration();
|
313 |
+
}, [robot]);
|
314 |
}
|
315 |
```
|
316 |
|
|
|
644 |
- **Loop Time**: < 5ms (when not reading positions)
|
645 |
- **User Experience**: "Buttery smooth", "fucking working and super perfect"
|
646 |
|
647 |
+
**Golden Rule**: When you achieve smooth control, NEVER change the step size, delays, or update patterns without extensive testing. These values were optimized through real hardware testing.
|
648 |
+
|
649 |
+
## Clean Library Architecture (Critical Lessons)
|
650 |
+
|
651 |
+
### Standard Library Design Principles
|
652 |
+
|
653 |
+
**End users should be able to use the library with minimal effort and excellent UX:**
|
654 |
+
|
655 |
+
```typescript
|
656 |
+
// β
PERFECT: Clean, self-contained library functions
|
657 |
+
const calibrationProcess = await calibrate(robotConnection, options);
|
658 |
+
const result = await calibrationProcess.result;
|
659 |
+
|
660 |
+
const teleoperationProcess = await teleoperate(robotConnection, options);
|
661 |
+
teleoperationProcess.start();
|
662 |
+
```
|
663 |
+
|
664 |
+
**β WRONG: Custom hooks and abstraction layers**
|
665 |
+
|
666 |
+
```typescript
|
667 |
+
// Never create hooks like useTeleoperation, useCalibration
|
668 |
+
// End users shouldn't need React to use robotics functions
|
669 |
+
```
|
670 |
+
|
671 |
+
### Library vs Demo Separation (CRITICAL)
|
672 |
+
|
673 |
+
**Library Responsibilities:**
|
674 |
+
|
675 |
+
- Hardware communication protocols
|
676 |
+
- Robot control logic
|
677 |
+
- Calibration algorithms
|
678 |
+
- Motor communication utilities
|
679 |
+
- Device-agnostic interfaces
|
680 |
+
|
681 |
+
**Demo Responsibilities:**
|
682 |
+
|
683 |
+
- localStorage/storage management
|
684 |
+
- UI state management
|
685 |
+
- JSON file export/import
|
686 |
+
- User interface components
|
687 |
+
- Application-specific workflows
|
688 |
+
|
689 |
+
**β WRONG: Mixing concerns**
|
690 |
+
|
691 |
+
```typescript
|
692 |
+
// NEVER put localStorage in standard library
|
693 |
+
export function teleoperate(robot, options) {
|
694 |
+
const calibration = localStorage.getItem("calibration"); // β Demo concern in library
|
695 |
+
}
|
696 |
+
```
|
697 |
+
|
698 |
+
**β
CORRECT: Clean separation**
|
699 |
+
|
700 |
+
```typescript
|
701 |
+
// Library: Pure hardware function
|
702 |
+
export function teleoperate(robot, options) {
|
703 |
+
// options.calibrationData passed from demo
|
704 |
+
}
|
705 |
+
|
706 |
+
// Demo: Handles storage
|
707 |
+
const calibrationData = getUnifiedRobotData(robot.serialNumber)?.calibration;
|
708 |
+
const process = await teleoperate(robot, { calibrationData });
|
709 |
+
```
|
710 |
+
|
711 |
+
### Utils Structure (No Code Duplication)
|
712 |
+
|
713 |
+
**Proper utils organization prevents reimplementation:**
|
714 |
+
|
715 |
+
```
|
716 |
+
src/lerobot/web/utils/
|
717 |
+
βββ sts3215-protocol.ts # Protocol constants
|
718 |
+
βββ sign-magnitude.ts # Encoding/decoding
|
719 |
+
βββ serial-port-wrapper.ts # Web Serial wrapper
|
720 |
+
βββ motor-communication.ts # Core motor operations
|
721 |
+
βββ motor-calibration.ts # Calibration functions
|
722 |
+
```
|
723 |
+
|
724 |
+
**β WRONG: Duplicate implementations**
|
725 |
+
|
726 |
+
- Multiple files with same motor communication code
|
727 |
+
- Calibration logic copied across files
|
728 |
+
- Protocol constants scattered everywhere
|
729 |
+
|
730 |
+
**β
CORRECT: Single source of truth**
|
731 |
+
|
732 |
+
- Shared utilities with clear responsibilities
|
733 |
+
- Import from utils, never reimplement
|
734 |
+
- Kebab-case naming for consistency
|
735 |
+
|
736 |
+
### Types Organization
|
737 |
+
|
738 |
+
**Types belong in dedicated directories, not mixed with business logic:**
|
739 |
+
|
740 |
+
```
|
741 |
+
src/lerobot/web/types/
|
742 |
+
βββ robot-connection.ts # Core connection types
|
743 |
+
βββ robot-config.ts # Hardware configuration types
|
744 |
+
```
|
745 |
+
|
746 |
+
**β WRONG: Types in business logic files**
|
747 |
+
|
748 |
+
```typescript
|
749 |
+
// Never export types from find_port.ts, calibrate.ts, etc.
|
750 |
+
import type { RobotConnection } from "./find_port.js"; // β Bad architecture
|
751 |
+
```
|
752 |
+
|
753 |
+
**β
CORRECT: Proper type imports**
|
754 |
+
|
755 |
+
```typescript
|
756 |
+
import type { RobotConnection } from "./types/robot-connection.js"; // β
Clean
|
757 |
+
```
|
758 |
+
|
759 |
+
### Device-Agnostic Architecture
|
760 |
+
|
761 |
+
**Standard library must support multiple robot types without hardcoding:**
|
762 |
+
|
763 |
+
**β
CORRECT: Configuration-driven**
|
764 |
+
|
765 |
+
```typescript
|
766 |
+
// Generic library function
|
767 |
+
export async function teleoperate(robotConnection, options) {
|
768 |
+
const config = createRobotConfig(robotConnection.robotType); // Device-specific
|
769 |
+
// ... generic logic using config
|
770 |
+
}
|
771 |
+
|
772 |
+
// Device-specific configuration
|
773 |
+
export function createSO100Config(type) {
|
774 |
+
return {
|
775 |
+
motorIds: [1, 2, 3, 4, 5, 6],
|
776 |
+
keyboardControls: SO100_KEYBOARD_CONTROLS,
|
777 |
+
// ... other device specifics
|
778 |
+
};
|
779 |
+
}
|
780 |
+
```
|
781 |
+
|
782 |
+
**β WRONG: Hardcoded device values**
|
783 |
+
|
784 |
+
```typescript
|
785 |
+
// Never hardcode in generic library
|
786 |
+
const KEYBOARD_CONTROLS = { w: "elbow_flex" }; // β SO-100 specific in generic code
|
787 |
+
```
|
788 |
+
|
789 |
+
### Browser Keyboard Timing (Critical for Teleoperation)
|
790 |
+
|
791 |
+
**Browser keyboard repeat pattern:**
|
792 |
+
|
793 |
+
1. Initial keydown β immediate
|
794 |
+
2. ~500ms delay (browser default)
|
795 |
+
3. Rapid repeating
|
796 |
+
|
797 |
+
**β
CORRECT: Account for browser delays**
|
798 |
+
|
799 |
+
```typescript
|
800 |
+
private readonly KEY_TIMEOUT = 600; // ms - longer than browser repeat delay
|
801 |
+
```
|
802 |
+
|
803 |
+
**β WRONG: Too short timeout**
|
804 |
+
|
805 |
+
```typescript
|
806 |
+
private readonly KEY_TIMEOUT = 100; // β Causes pause during browser repeat delay
|
807 |
+
```
|
808 |
+
|
809 |
+
### State Update Callbacks (UI Responsiveness)
|
810 |
+
|
811 |
+
**Library must notify UI of state changes, especially when stopping:**
|
812 |
+
|
813 |
+
**β
CORRECT: Always notify on state changes**
|
814 |
+
|
815 |
+
```typescript
|
816 |
+
stop(): void {
|
817 |
+
this.isActive = false;
|
818 |
+
// ... cleanup ...
|
819 |
+
|
820 |
+
// CRITICAL: Notify UI immediately
|
821 |
+
if (this.onStateUpdate) {
|
822 |
+
this.onStateUpdate(this.getState());
|
823 |
+
}
|
824 |
+
}
|
825 |
+
```
|
826 |
+
|
827 |
+
### Component Architecture (No Custom Hardware Hooks)
|
828 |
+
|
829 |
+
**Use library functions directly in components, just like calibration:**
|
830 |
+
|
831 |
+
**β
CORRECT: Direct library usage**
|
832 |
+
|
833 |
+
```typescript
|
834 |
+
// In component
|
835 |
+
const [teleoperationState, setTeleoperationState] =
|
836 |
+
useState<TeleoperationState>();
|
837 |
+
const teleoperationProcessRef = useRef<TeleoperationProcess | null>(null);
|
838 |
+
|
839 |
+
useEffect(() => {
|
840 |
+
const initTeleoperation = async () => {
|
841 |
+
const process = await teleoperate(robot, {
|
842 |
+
onStateUpdate: setTeleoperationState,
|
843 |
+
});
|
844 |
+
teleoperationProcessRef.current = process;
|
845 |
+
};
|
846 |
+
initTeleoperation();
|
847 |
+
}, [robot]);
|
848 |
+
```
|
849 |
+
|
850 |
+
**β WRONG: Custom hooks**
|
851 |
+
|
852 |
+
```typescript
|
853 |
+
// Never create these - adds unnecessary complexity
|
854 |
+
const { start, stop, motorConfigs } = useTeleoperation(robot);
|
855 |
+
const { startCalibration, isActive } = useCalibration(robot);
|
856 |
+
```
|
857 |
+
|
858 |
+
### Architecture Success Metrics
|
859 |
+
|
860 |
+
**When architecture is correct:**
|
861 |
+
|
862 |
+
- β
End users can use library functions directly without React
|
863 |
+
- β
Adding new robot types requires zero changes to existing code
|
864 |
+
- β
Demo and library have zero shared dependencies
|
865 |
+
- β
No code duplication across files
|
866 |
+
- β
Types are properly organized and importable
|
867 |
+
- β
UI updates immediately reflect hardware state changes
|