LeRobot.js / examples /test-sequential-operations /test-sequential-operations.ts
NERDDISCO's picture
refactor: move examples into separate folders
4500b8e
raw
history blame
8.68 kB
/**
* Sequential Operations Test Logic
* Tests: findPort โ†’ calibrate โ†’ releaseMotors โ†’ teleoperate
*/
import { findPort, calibrate, releaseMotors, teleoperate } from "@lerobot/web";
let isRunning = false;
function log(message: string) {
const logElement = document.getElementById("log");
if (logElement) {
const timestamp = new Date().toLocaleTimeString();
logElement.textContent += `[${timestamp}] ${message}\n`;
logElement.scrollTop = logElement.scrollHeight;
}
}
function setButtonState(running: boolean) {
isRunning = running;
const button = document.getElementById("runTest") as HTMLButtonElement;
if (button) {
button.disabled = running;
button.textContent = running
? "โณ Running Test..."
: "๐Ÿš€ Run Sequential Operations Test";
}
}
declare global {
interface Window {
clearLog: () => void;
runSequentialTest: () => Promise<void>;
}
}
window.clearLog = function () {
const logElement = document.getElementById("log");
if (logElement) {
logElement.textContent = "Log cleared.\n";
}
};
window.runSequentialTest = async function () {
if (isRunning) return;
setButtonState(true);
log("๐Ÿš€ Starting sequential operations test...");
try {
// Step 1: Find port
log("\n1๏ธโƒฃ Finding robot port...");
const findProcess = await findPort();
const robots = await findProcess.result;
const robot = robots[0]; // Get first robot
if (!robot || !robot.isConnected) {
throw new Error("No robot found or robot not connected");
}
log(`โœ… Found robot: ${robot.name} (${robot.robotType})`);
log(` Serial: ${robot.serialNumber}`);
// Step 2: Release motors first for calibration setup
log("\n2๏ธโƒฃ Releasing motors for calibration setup...");
if (!robot.robotType) {
throw new Error("Robot type not configured");
}
log("๐Ÿ”“ Releasing all motors...");
await releaseMotors(robot);
log("โœ… Motors released - you can now move the arm freely!");
// Now start calibration
log("\n๐Ÿ“ Starting calibration with live position updates...");
log("๐Ÿ’ก Move the arm through its range of motion to see live updates!");
const useSimulatedCalibration = false; // Real calibration to see live updates
let calibrationResult: any;
if (useSimulatedCalibration) {
// Simulated calibration data (for testing without robot movement)
log("๐Ÿ“Š Using simulated calibration data for testing...");
await new Promise((resolve) => setTimeout(resolve, 2000)); // Simulate calibration time
calibrationResult = {
shoulder_pan: {
id: 1,
drive_mode: 0,
homing_offset: 34,
range_min: 994,
range_max: 3100,
},
shoulder_lift: {
id: 2,
drive_mode: 0,
homing_offset: 991,
range_min: 960,
range_max: 3233,
},
elbow_flex: {
id: 3,
drive_mode: 0,
homing_offset: -881,
range_min: 1029,
range_max: 3065,
},
wrist_flex: {
id: 4,
drive_mode: 0,
homing_offset: 128,
range_min: 710,
range_max: 3135,
},
wrist_roll: {
id: 5,
drive_mode: 0,
homing_offset: -15,
range_min: 0,
range_max: 4095,
},
gripper: {
id: 6,
drive_mode: 0,
homing_offset: -1151,
range_min: 2008,
range_max: 3606,
},
};
log("โœ… Calibration completed (simulated)");
} else {
// Real calibration
const calibrationProcess = await calibrate({
robot,
onProgress: (message) => log(`๐Ÿ“Š ${message}`),
onLiveUpdate: (data) => {
const motors = Object.keys(data);
if (motors.length > 0) {
const ranges = motors.map((m) => data[m].range).join(", ");
log(`๐Ÿ“ Motor ranges: [${ranges}]`);
}
},
});
// Auto-stop calibration after 8 seconds for testing
setTimeout(() => {
log("โฑ๏ธ Auto-stopping calibration for test...");
calibrationProcess.stop();
}, 8000);
await calibrationProcess.result;
log("โœ… Calibration completed (real)");
}
// Step 3: Use your provided calibration config for teleoperation
log("\n3๏ธโƒฃ Setting up teleoperation with your calibration config...");
// Use your provided calibration config instead of real calibration result
calibrationResult = {
shoulder_pan: {
id: 1,
drive_mode: 0,
homing_offset: 34,
range_min: 994,
range_max: 3100,
},
shoulder_lift: {
id: 2,
drive_mode: 0,
homing_offset: 991,
range_min: 960,
range_max: 3233,
},
elbow_flex: {
id: 3,
drive_mode: 0,
homing_offset: -881,
range_min: 1029,
range_max: 3065,
},
wrist_flex: {
id: 4,
drive_mode: 0,
homing_offset: 128,
range_min: 710,
range_max: 3135,
},
wrist_roll: {
id: 5,
drive_mode: 0,
homing_offset: -15,
range_min: 0,
range_max: 4095,
},
gripper: {
id: 6,
drive_mode: 0,
homing_offset: -1151,
range_min: 2008,
range_max: 3606,
},
};
log(
`โœ… Using your calibration config: ${Object.keys(calibrationResult).join(
", "
)}`
);
// Step 4: Teleoperate with auto key simulation
log("\n4๏ธโƒฃ Starting teleoperation...");
const teleoperationProcess = await teleoperate({
robot,
calibrationData: calibrationResult,
teleop: {
type: "keyboard",
stepSize: 25,
},
onStateUpdate: (state) => {
if (
state.isActive &&
state.keyStates &&
Object.keys(state.keyStates).length > 0
) {
const activeKeys = Object.keys(state.keyStates).filter(
(k) => state.keyStates![k].pressed
);
log(`๐ŸŽฎ Auto-simulated keys: ${activeKeys.join(", ")}`);
}
},
});
teleoperationProcess.start();
log("โœ… Teleoperation started");
log("๐Ÿค– Auto-simulating arrow key presses to move the arm...");
// Auto-simulate key presses (left/right arrows to move shoulder pan)
let keySimulationActive = true;
let currentDirection = "ArrowLeft";
const simulateKeys = () => {
if (!keySimulationActive) return;
// Press current key
teleoperationProcess.updateKeyState(currentDirection, true);
log(`๐Ÿ”„ Pressing ${currentDirection} (shoulder pan movement)`);
// Hold for 1 second, then release and switch direction
setTimeout(() => {
if (!keySimulationActive) return;
teleoperationProcess.updateKeyState(currentDirection, false);
// Switch direction
currentDirection =
currentDirection === "ArrowLeft" ? "ArrowRight" : "ArrowLeft";
// Wait 500ms then start next movement
setTimeout(() => {
if (keySimulationActive) simulateKeys();
}, 500);
}, 1000);
};
// Start key simulation after a brief delay
setTimeout(simulateKeys, 1000);
// Auto-stop teleoperation after 8 seconds for testing
setTimeout(() => {
keySimulationActive = false;
log("โฑ๏ธ Auto-stopping teleoperation for test...");
teleoperationProcess.stop();
log("\n๐ŸŽ‰ All sequential operations completed successfully!");
setButtonState(false);
}, 8000);
} catch (error: any) {
log(`โŒ Sequential operations failed: ${error.message}`);
// Check if it's a connection conflict
if (
error.message.includes("port") ||
error.message.includes("serial") ||
error.message.includes("connection")
) {
log("๐Ÿ” This might be a port connection conflict!");
log("๐Ÿ’ก Multiple WebSerialPortWrapper instances may be interfering");
}
setButtonState(false);
}
};
// Initialize on DOM load
document.addEventListener("DOMContentLoaded", () => {
// Check Web Serial support
if (!("serial" in navigator)) {
log("โŒ Web Serial API not supported in this browser");
log("๐Ÿ’ก Try Chrome/Edge with --enable-web-serial flag");
const button = document.getElementById("runTest") as HTMLButtonElement;
if (button) button.disabled = true;
} else {
log("โœ… Web Serial API supported");
log("Ready to test. Click 'Run Sequential Operations Test' to begin...");
}
});