File size: 6,576 Bytes
bdc1ac8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**
 * Teleoperation Demo
 *
 * Demonstrates different ways to control robot motors
 */

import { findPort, connectPort, teleoperate } from "@lerobot/node";
import type { RobotConnection, DiscoveredPort } from "@lerobot/node";
import { createInterface } from "readline";

function askUser(question: string): Promise<string> {
  const rl = createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  return new Promise((resolve) => {
    rl.question(question, (answer) => {
      rl.close();
      resolve(answer.trim());
    });
  });
}

async function demoTeleoperate() {
  console.log("๐ŸŽฎ Teleoperation Demo");
  console.log("=====================\n");

  try {
    // Step 1: Find available robot ports
    console.log("๐Ÿ“ก Looking for connected robots...");
    const findProcess = await findPort();
    const discoveredPorts = await findProcess.result;

    if (discoveredPorts.length === 0) {
      throw new Error("No robots found. Please connect your robot first.");
    }

    console.log(`โœ… Found robot on ${discoveredPorts[0].path}`);

    // Step 2: Connect to robot
    console.log("๐Ÿ”Œ Connecting to robot...");
    const robot = await connectPort(
      discoveredPorts[0].path,
      "so100_follower",
      "teleop_demo"
    );
    console.log(`โœ… Connected: ${robot.robotType} (ID: ${robot.robotId})\n`);

    // Step 3: Choose teleoperation mode
    console.log("๐ŸŽฏ Choose teleoperation mode:");
    console.log("1. Keyboard Control (interactive)");
    console.log("2. Direct Control (programmatic)");

    const mode = await askUser("Enter choice (1 or 2): ");

    if (mode === "1") {
      // Keyboard teleoperation demo
      await demoKeyboardControl(robot);
    } else if (mode === "2") {
      // Direct control demo
      await demoDirectControl(robot);
    } else {
      console.log("Invalid choice. Defaulting to keyboard control...");
      await demoKeyboardControl(robot);
    }
  } catch (error) {
    console.error("\nโŒ Teleoperation failed:", error.message);
    console.log("\n๐Ÿ”ง Troubleshooting:");
    console.log("- Ensure robot is calibrated first");
    console.log("- Check that robot is connected and responsive");
    console.log("- Verify calibration data exists");
    console.log("- Try smaller step sizes if movements are too large");
    process.exit(1);
  }
}

async function demoKeyboardControl(robot: RobotConnection) {
  console.log("\nโŒจ๏ธ  Keyboard Control Demo");
  console.log("=========================");
  console.log("\n๐ŸŽฎ Robot Controls:");
  console.log("  Arrow Keys: Shoulder pan/lift");
  console.log("  W/S: Elbow flex/extend");
  console.log("  A/D: Wrist down/up");
  console.log("  Q/E: Wrist roll left/right");
  console.log("  O/C: Gripper open/close");
  console.log("  ESC: Emergency stop");
  console.log("  Ctrl+C: Exit demo\n");

  const teleop = await teleoperate({
    robot,
    teleop: {
      type: "keyboard",
      // Using optimized defaults for smooth control
    },
    onStateUpdate: (state) => {
      if (state.isActive) {
        // Show live motor positions
        const motorInfo = state.motorConfigs
          .map((motor) => {
            const pos = Math.round(motor.currentPosition);
            const percent = (
              ((pos - motor.minPosition) /
                (motor.maxPosition - motor.minPosition)) *
              100
            ).toFixed(0);
            return `${motor.name}:${pos}(${percent}%)`;
          })
          .join(" ");
        process.stdout.write(`\r๐Ÿค– ${motorInfo}`);
      }
    },
  });

  // Start keyboard control
  teleop.start();
  console.log("โœ… Keyboard control active!");
  console.log("๐Ÿ’ก Move robot with keyboard, press Ctrl+C to exit");

  // Handle graceful shutdown
  process.on("SIGINT", async () => {
    console.log("\n๐Ÿ›‘ Stopping keyboard control...");
    teleop.stop();
    await teleop.disconnect();
    console.log("โœ… Keyboard control demo completed!");
    process.exit(0);
  });

  // Keep demo running
  await new Promise(() => {}); // Keep alive
}

async function demoDirectControl(robot: RobotConnection) {
  console.log("\n๐ŸŽฏ Direct Control Demo");
  console.log("======================");
  console.log("This demonstrates programmatic robot control\n");

  const teleop = await teleoperate({
    robot,
    teleop: { type: "direct" },
    onStateUpdate: (state) => {
      const motorInfo = state.motorConfigs
        .map((motor) => `${motor.name}:${Math.round(motor.currentPosition)}`)
        .join(" ");
      console.log(`๐Ÿค– ${motorInfo}`);
    },
  });

  teleop.start();

  // Get direct control interface
  const directController = teleop.teleoperator as any;

  console.log("๐ŸŽฌ Running automated movement sequence...\n");

  try {
    // Demo sequence: move different motors
    const movements = [
      {
        motor: "shoulder_pan",
        position: 2048,
        description: "Center shoulder pan",
      },
      { motor: "shoulder_lift", position: 1500, description: "Lift shoulder" },
      { motor: "elbow_flex", position: 2500, description: "Flex elbow" },
      { motor: "wrist_flex", position: 2000, description: "Adjust wrist" },
      { motor: "wrist_roll", position: 2048, description: "Center wrist roll" },
      { motor: "gripper", position: 1800, description: "Adjust gripper" },
    ];

    for (const movement of movements) {
      console.log(`๐ŸŽฏ ${movement.description}...`);
      await directController.moveMotor(movement.motor, movement.position);
      await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
    }

    console.log("\n๐ŸŽ‰ Movement sequence completed!");

    // Demo multi-motor movement
    console.log("\n๐ŸŽญ Demonstrating simultaneous multi-motor movement...");
    const results = await directController.moveMotors({
      shoulder_pan: 2048,
      shoulder_lift: 2048,
      elbow_flex: 2048,
      wrist_flex: 2048,
    });

    console.log("๐Ÿ“Š Movement results:");
    Object.entries(results).forEach(([motor, success]) => {
      console.log(`   ${motor}: ${success ? "โœ…" : "โŒ"}`);
    });

    // Show current positions
    const positions = directController.getCurrentPositions();
    console.log("\n๐Ÿ“ Final positions:");
    Object.entries(positions).forEach(([motor, position]) => {
      console.log(`   ${motor}: ${Math.round(position as number)}`);
    });
  } finally {
    console.log("\n๐Ÿ›‘ Stopping direct control...");
    teleop.stop();
    await teleop.disconnect();
    console.log("โœ… Direct control demo completed!");
  }
}

demoTeleoperate();