Spaces:
Running
Running
File size: 5,271 Bytes
a8d792f |
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 |
import type { RobotConnection, LiveCalibrationData, WebCalibrationResults, TeleoperationState } from "@/types/robot"
const MOCK_MOTOR_NAMES = ["shoulder_pan", "shoulder_lift", "elbow_flex", "wrist_flex", "wrist_roll", "gripper"]
export const lerobot = {
isWebSerialSupported: () => true,
findPort: async ({
robotConfigs,
onMessage,
}: { robotConfigs?: any[]; onMessage?: (msg: string) => void }): Promise<{ result: Promise<RobotConnection[]> }> => {
onMessage?.("Searching for robots...")
const resultPromise = new Promise<RobotConnection[]>((resolve) => {
setTimeout(() => {
if (robotConfigs && robotConfigs.length > 0) {
onMessage?.(`Found ${robotConfigs.length} saved robots.`)
const reconnectedRobots = robotConfigs.map((config) => ({
...config,
isConnected: Math.random() > 0.3,
port: {},
}))
resolve(reconnectedRobots)
} else {
onMessage?.("Simulating successful device connection.")
const newRobot: RobotConnection = {
port: {},
name: "Cyber-Arm 7",
isConnected: true,
robotType: "so100_follower",
robotId: `robot-${Date.now()}`,
serialNumber: `SN-${Math.floor(Math.random() * 1000000)}`,
}
resolve([newRobot])
}
}, 1500)
})
return { result: resultPromise }
},
calibrate: async (
robot: RobotConnection,
{
onLiveUpdate,
onProgress,
}: { onLiveUpdate: (data: LiveCalibrationData) => void; onProgress: (msg: string) => void },
): Promise<{ result: Promise<WebCalibrationResults>; stop: () => void }> => {
onProgress("MOCK: Starting calibration... Move all joints to their limits.")
let intervalId: NodeJS.Timeout
const liveData: LiveCalibrationData = MOCK_MOTOR_NAMES.reduce((acc, name) => {
acc[name] = { current: 2048, min: 4095, max: 0, range: 0 }
return acc
}, {} as LiveCalibrationData)
intervalId = setInterval(() => {
MOCK_MOTOR_NAMES.forEach((name) => {
const motor = liveData[name]
motor.current = 2048 + Math.floor((Math.random() - 0.5) * 4000)
motor.min = Math.min(motor.min, motor.current)
motor.max = Math.max(motor.max, motor.current)
motor.range = motor.max - motor.min
})
onLiveUpdate({ ...liveData })
}, 100)
const resultPromise = new Promise<WebCalibrationResults>((resolve) => {
;(stop as any)._resolver = resolve
})
const stop = () => {
clearInterval(intervalId)
onProgress("MOCK: Calibration recording finished.")
const finalResults = MOCK_MOTOR_NAMES.reduce((acc, name) => {
acc[name] = { min: liveData[name].min, max: liveData[name].max }
return acc
}, {} as WebCalibrationResults)
;(stop as any)._resolver(finalResults)
}
return { result: resultPromise, stop }
},
teleoperate: async (
robot: RobotConnection,
{
calibrationData,
onStateUpdate,
}: { calibrationData: WebCalibrationResults; onStateUpdate: (state: TeleoperationState) => void },
): Promise<{
start: () => void
stop: () => void
updateKeyState: (key: string, pressed: boolean) => void
moveMotor: (motorName: string, position: number) => void
}> => {
let isActive = false
let intervalId: NodeJS.Timeout
const keyStates: { [key: string]: { pressed: boolean } } = {}
const motorConfigs = MOCK_MOTOR_NAMES.map((name) => ({
name,
currentPosition: calibrationData[name] ? (calibrationData[name].min + calibrationData[name].max) / 2 : 2048,
minPosition: calibrationData[name]?.min ?? 0,
maxPosition: calibrationData[name]?.max ?? 4095,
}))
const teleopState: TeleoperationState = { isActive, motorConfigs, keyStates }
const updateLoop = () => onStateUpdate({ ...teleopState, motorConfigs: [...motorConfigs] })
return {
start: () => {
if (isActive) return
isActive = true
teleopState.isActive = true
intervalId = setInterval(updateLoop, 100)
onStateUpdate({ ...teleopState })
},
stop: () => {
if (!isActive) return
isActive = false
teleopState.isActive = false
clearInterval(intervalId)
onStateUpdate({ ...teleopState })
},
updateKeyState: (key: string, pressed: boolean) => {
keyStates[key] = { pressed }
teleopState.keyStates = { ...keyStates }
onStateUpdate({ ...teleopState })
},
moveMotor: (motorName: string, position: number) => {
const motor = motorConfigs.find((m) => m.name === motorName)
if (motor) {
motor.currentPosition = position
onStateUpdate({ ...teleopState, motorConfigs: [...motorConfigs] })
}
},
}
},
SO100_KEYBOARD_CONTROLS: {
shoulder_pan: { positive: "ArrowRight", negative: "ArrowLeft" },
shoulder_lift: { positive: "ArrowUp", negative: "ArrowDown" },
elbow_flex: { positive: "w", negative: "s" },
wrist_flex: { positive: "a", negative: "d" },
wrist_roll: { positive: "q", negative: "e" },
gripper: { positive: "o", negative: "c" },
stop: "Escape",
},
MOCK_MOTOR_NAMES,
}
|