From 14319ee6083808e4a300a61e81f20522f743c752 Mon Sep 17 00:00:00 2001 From: pepijn kooijmans Date: Fri, 21 Nov 2025 11:48:52 +0100 Subject: [PATCH] add openarms mini --- examples/openarms/teleop_openarms_mini.py | 10 +- examples/openarms_web_interface/App.jsx | 127 ++++- .../web_record_server.py | 484 +++++++++++++----- 3 files changed, 466 insertions(+), 155 deletions(-) diff --git a/examples/openarms/teleop_openarms_mini.py b/examples/openarms/teleop_openarms_mini.py index e05a148ad..9faf0c90d 100644 --- a/examples/openarms/teleop_openarms_mini.py +++ b/examples/openarms/teleop_openarms_mini.py @@ -39,9 +39,9 @@ follower_config = OpenArmsFollowerConfig( # Configure the OpenArms Mini leader (Feetech motors on serial) leader_config = OpenArmsMiniConfig( - port_right="/dev/ttyACM0", # Serial port for right arm - port_left="/dev/ttyACM1", # Serial port for left arm - id="openarms_minis", + port_right="/dev/ttyACM1", # Serial port for right arm + port_left="/dev/ttyACM0", # Serial port for left arm + id="openarms_mini", use_degrees=True, ) @@ -131,8 +131,6 @@ try: # Determine which follower joint this leader joint controls follower_joint = SWAPPED_JOINTS.get(joint, joint) follower_key = f"{follower_joint}.pos" - if "left" in follower_key: - continue # Get leader position (default 0 if missing) pos = leader_action.get(leader_key, 0.0) @@ -149,7 +147,7 @@ try: # Store in action dict for follower joint_action[follower_key] = pos - #follower.send_action(joint_action) + follower.send_action(joint_action) # Loop timing loop_end = time.perf_counter() diff --git a/examples/openarms_web_interface/App.jsx b/examples/openarms_web_interface/App.jsx index 6342b5551..662eb57e3 100644 --- a/examples/openarms_web_interface/App.jsx +++ b/examples/openarms_web_interface/App.jsx @@ -24,6 +24,7 @@ function App() { // Configuration const [config, setConfig] = useState({ + leader_type: 'openarms', // 'openarms' or 'openarms_mini' leader_left: 'can0', leader_right: 'can1', follower_left: 'can2', @@ -35,6 +36,7 @@ function App() { // Available options const [availableCameras, setAvailableCameras] = useState([]); + const [availableUsbPorts, setAvailableUsbPorts] = useState([]); const canInterfaces = ['can0', 'can1', 'can2', 'can3']; const statusIntervalRef = useRef(null); @@ -192,6 +194,43 @@ function App() { } }; + // Discover USB ports + const discoverUsbPorts = async () => { + try { + const response = await fetch(`${API_BASE}/usb/discover`); + const data = await response.json(); + const ports = data.ports || []; + setAvailableUsbPorts(ports); + + // Auto-fix config if OpenArms Mini is selected and ports are invalid + if (config.leader_type === 'openarms_mini') { + const updated = { ...config }; + let changed = false; + + if (ports.length >= 1 && !ports.includes(config.leader_left)) { + updated.leader_left = ports[0]; + changed = true; + } + + if (ports.length >= 2 && !ports.includes(config.leader_right)) { + updated.leader_right = ports[1]; + changed = true; + } + + if (changed) { + setConfig(updated); + saveConfig(updated); + } + } + + if (ports.length === 0) { + console.warn('No USB ports detected for OpenArms Mini'); + } + } catch (e) { + console.error('Failed to discover USB ports:', e); + } + }; + // Set task only (for pedal use) const setTaskOnly = async () => { if (!task.trim()) { @@ -324,6 +363,7 @@ function App() { loadConfig(); discoverCameras(); + discoverUsbPorts(); fetchStatus(); statusIntervalRef.current = setInterval(fetchStatus, 1000); @@ -335,6 +375,14 @@ function App() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Run only once on mount + // Discover USB ports when leader type changes to Mini + useEffect(() => { + if (config.leader_type === 'openarms_mini') { + discoverUsbPorts(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [config.leader_type]); + return (
@@ -385,9 +433,42 @@ function App() { - {/* CAN Interfaces */} + {/* Leader Type Selection */}
-

CAN Interfaces

+

🎮 Leader Type

+
+ +
+
+ + {/* Leader Interfaces (CAN or USB based on type) */} +
+
+

+ {config.leader_type === 'openarms_mini' + ? `Leader Ports (USB) ${availableUsbPorts.length > 0 ? `(${availableUsbPorts.length} detected)` : ''}` + : 'Leader Interfaces (CAN)'} +

+ {config.leader_type === 'openarms_mini' && ( + + )} +
@@ -409,12 +500,28 @@ function App() { onChange={(e) => updateConfig('leader_right', e.target.value)} disabled={isRecording || robotsReady} > - {canInterfaces.map((iface) => ( - - ))} + {config.leader_type === 'openarms_mini' ? ( + availableUsbPorts.length > 0 ? ( + availableUsbPorts.map((port) => ( + + )) + ) : ( + + ) + ) : ( + canInterfaces.map((iface) => ( + + )) + )} +
+
+ {/* Follower CAN Interfaces */} +
+

Follower Interfaces (CAN)

+