Spaces:
Running
Running
Implement client-side duration control for Spaces deployment
Browse files- README.md +3 -3
- frontend/app.js +17 -10
README.md
CHANGED
|
@@ -67,9 +67,9 @@ uv run python app.py
|
|
| 67 |
## Current Limitations
|
| 68 |
|
| 69 |
- Sound and picture actions are not yet implemented in execution
|
| 70 |
-
- **On HuggingFace Spaces**: Due to browser security policies (HTTPS → HTTP localhost),
|
| 71 |
-
-
|
| 72 |
-
-
|
| 73 |
|
| 74 |
## Deployment to HuggingFace Spaces
|
| 75 |
|
|
|
|
| 67 |
## Current Limitations
|
| 68 |
|
| 69 |
- Sound and picture actions are not yet implemented in execution
|
| 70 |
+
- **On HuggingFace Spaces**: Due to browser security policies (HTTPS → HTTP localhost), the demo uses a WebSocket fallback instead of the REST API `/goto` endpoint.
|
| 71 |
+
- Timing is approximated by repeatedly sending the target position over the specified duration
|
| 72 |
+
- For smoother interpolated movements with proper minimum-jerk trajectories, run the demo locally
|
| 73 |
|
| 74 |
## Deployment to HuggingFace Spaces
|
| 75 |
|
frontend/app.js
CHANGED
|
@@ -405,23 +405,30 @@ async function executeIRAction(action) {
|
|
| 405 |
await sleep(action.duration * 1000 + 100);
|
| 406 |
|
| 407 |
} catch (fetchError) {
|
| 408 |
-
// Fallback to WebSocket (for Spaces deployment)
|
| 409 |
-
|
| 410 |
-
console.warn('Using WebSocket fallback (duration may not be accurate):', fetchError.message);
|
| 411 |
|
| 412 |
if (!state.robotWs || state.robotWs.readyState !== WebSocket.OPEN) {
|
| 413 |
throw new Error('Robot WebSocket not connected');
|
| 414 |
}
|
| 415 |
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
|
| 421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
-
|
| 424 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 425 |
}
|
| 426 |
}
|
| 427 |
|
|
|
|
| 405 |
await sleep(action.duration * 1000 + 100);
|
| 406 |
|
| 407 |
} catch (fetchError) {
|
| 408 |
+
// Fallback to WebSocket with client-side interpolation (for Spaces deployment)
|
| 409 |
+
console.warn('Using WebSocket interpolation fallback for duration:', fetchError.message);
|
|
|
|
| 410 |
|
| 411 |
if (!state.robotWs || state.robotWs.readyState !== WebSocket.OPEN) {
|
| 412 |
throw new Error('Robot WebSocket not connected');
|
| 413 |
}
|
| 414 |
|
| 415 |
+
// Simulate duration by sending intermediate targets
|
| 416 |
+
// Send updates at 10Hz (every 100ms) for smooth movement
|
| 417 |
+
const updateInterval = 100; // ms
|
| 418 |
+
const numSteps = Math.max(1, Math.floor((action.duration * 1000) / updateInterval));
|
| 419 |
|
| 420 |
+
for (let step = 0; step <= numSteps; step++) {
|
| 421 |
+
const wsMessage = {};
|
| 422 |
+
if (head_pose) wsMessage.target_head_pose = head_pose;
|
| 423 |
+
if (action.antennas) wsMessage.target_antennas = action.antennas;
|
| 424 |
+
if (action.body_yaw !== null) wsMessage.target_body_yaw = action.body_yaw;
|
| 425 |
|
| 426 |
+
state.robotWs.send(JSON.stringify(wsMessage));
|
| 427 |
+
|
| 428 |
+
if (step < numSteps) {
|
| 429 |
+
await sleep(updateInterval);
|
| 430 |
+
}
|
| 431 |
+
}
|
| 432 |
}
|
| 433 |
}
|
| 434 |
|