8bitkick
commited on
Commit
·
98fa906
1
Parent(s):
d82254a
Head pose
Browse files
reachy_mini_app_example/src/RobotManager.js
CHANGED
|
@@ -7,6 +7,10 @@ export class RobotManager {
|
|
| 7 |
this.jointMap = {};
|
| 8 |
this.statusCallback = statusCallback;
|
| 9 |
this.logged = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
|
| 11 |
// URL configuration
|
| 12 |
this.URDF_URL = new URLSearchParams(location.search).get("urdf")
|
|
@@ -65,15 +69,26 @@ export class RobotManager {
|
|
| 65 |
this.robot = urdfRobot;
|
| 66 |
this.robot.rotation.x = -Math.PI / 2; // Rotate 90 degrees around X axis
|
| 67 |
|
| 68 |
-
// Build joint map
|
|
|
|
| 69 |
this.robot.traverse((child) => {
|
| 70 |
if (child.isURDFJoint) {
|
| 71 |
this.jointMap[child.name] = child;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
}
|
| 73 |
});
|
| 74 |
|
| 75 |
// Convert robot to blue wireframe after delay
|
| 76 |
-
this.applyWireframeMaterial();
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
// Clean up the blob URL
|
| 79 |
URL.revokeObjectURL(blobUrl);
|
|
@@ -125,8 +140,41 @@ export class RobotManager {
|
|
| 125 |
}
|
| 126 |
|
| 127 |
// Update head pose if available
|
| 128 |
-
if (data.head_pose
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
this.jointMap['yaw_body'].setJointValue(data.head_pose.yaw);
|
| 131 |
}
|
| 132 |
}
|
|
@@ -150,4 +198,81 @@ export class RobotManager {
|
|
| 150 |
getRobot() {
|
| 151 |
return this.robot;
|
| 152 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
}
|
|
|
|
| 7 |
this.jointMap = {};
|
| 8 |
this.statusCallback = statusCallback;
|
| 9 |
this.logged = false;
|
| 10 |
+
this.clonedHead = null;
|
| 11 |
+
this.originalHead = null;
|
| 12 |
+
this.baseHeadPosition = new THREE.Vector3();
|
| 13 |
+
this.baseHeadRotation = new THREE.Euler();
|
| 14 |
|
| 15 |
// URL configuration
|
| 16 |
this.URDF_URL = new URLSearchParams(location.search).get("urdf")
|
|
|
|
| 69 |
this.robot = urdfRobot;
|
| 70 |
this.robot.rotation.x = -Math.PI / 2; // Rotate 90 degrees around X axis
|
| 71 |
|
| 72 |
+
// Build joint map and log robot structure
|
| 73 |
+
console.log('Robot structure:');
|
| 74 |
this.robot.traverse((child) => {
|
| 75 |
if (child.isURDFJoint) {
|
| 76 |
this.jointMap[child.name] = child;
|
| 77 |
+
console.log(` Joint: ${child.name}`, child);
|
| 78 |
+
}
|
| 79 |
+
if (child.isURDFLink) {
|
| 80 |
+
console.log(` Link: ${child.name}`, child);
|
| 81 |
+
}
|
| 82 |
+
if (child.name && (child.name.includes('head') || child.name.includes('frame'))) {
|
| 83 |
+
console.log(` Found head/frame object: ${child.name}`, child);
|
| 84 |
}
|
| 85 |
});
|
| 86 |
|
| 87 |
// Convert robot to blue wireframe after delay
|
| 88 |
+
//this.applyWireframeMaterial();
|
| 89 |
+
|
| 90 |
+
// Clone head after robot is fully loaded
|
| 91 |
+
this.cloneHeadForPoseControl();
|
| 92 |
|
| 93 |
// Clean up the blob URL
|
| 94 |
URL.revokeObjectURL(blobUrl);
|
|
|
|
| 140 |
}
|
| 141 |
|
| 142 |
// Update head pose if available
|
| 143 |
+
if (data.head_pose) {
|
| 144 |
+
// Apply head pose transformations to the cloned head (if it exists)
|
| 145 |
+
if (this.clonedHead) {
|
| 146 |
+
// Apply transformations directly to the cloned head
|
| 147 |
+
// Apply pose relative to the base head position
|
| 148 |
+
if (data.head_pose.x !== undefined) {
|
| 149 |
+
const newX = this.baseHeadPosition.x + data.head_pose.x - 0.01;
|
| 150 |
+
this.clonedHead.position.x = newX;
|
| 151 |
+
}
|
| 152 |
+
if (data.head_pose.y !== undefined) {
|
| 153 |
+
const newY = this.baseHeadPosition.y + data.head_pose.y;
|
| 154 |
+
this.clonedHead.position.y = newY;
|
| 155 |
+
}
|
| 156 |
+
if (data.head_pose.z !== undefined) {
|
| 157 |
+
const newZ = this.baseHeadPosition.z + data.head_pose.z + 0.03;
|
| 158 |
+
this.clonedHead.position.z = newZ;
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
// Apply rotations relative to base rotation
|
| 162 |
+
if (data.head_pose.roll !== undefined) {
|
| 163 |
+
const newRoll = this.baseHeadRotation.x - data.head_pose.pitch;
|
| 164 |
+
this.clonedHead.rotation.x = newRoll;
|
| 165 |
+
}
|
| 166 |
+
if (data.head_pose.pitch !== undefined) {
|
| 167 |
+
const newPitch = this.baseHeadRotation.y - data.head_pose.yaw;
|
| 168 |
+
this.clonedHead.rotation.y = newPitch;
|
| 169 |
+
}
|
| 170 |
+
if (data.head_pose.yaw !== undefined) {
|
| 171 |
+
const newYaw = this.baseHeadRotation.z - data.head_pose.roll;
|
| 172 |
+
this.clonedHead.rotation.z = newYaw;
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
// Also update body yaw joint if it exists (for overall body orientation)
|
| 177 |
+
if (data.head_pose.yaw !== undefined && this.jointMap['yaw_body']) {
|
| 178 |
this.jointMap['yaw_body'].setJointValue(data.head_pose.yaw);
|
| 179 |
}
|
| 180 |
}
|
|
|
|
| 198 |
getRobot() {
|
| 199 |
return this.robot;
|
| 200 |
}
|
| 201 |
+
|
| 202 |
+
cloneHeadForPoseControl() {
|
| 203 |
+
// Wait a bit for all STL meshes to finish loading
|
| 204 |
+
setTimeout(() => {
|
| 205 |
+
this.robot.traverse((child) => {
|
| 206 |
+
if ((child.name === 'xl_330')) {
|
| 207 |
+
this.originalHead = child;
|
| 208 |
+
|
| 209 |
+
// Clone the head link for independent control
|
| 210 |
+
this.clonedHead = child.clone();
|
| 211 |
+
this.clonedHead.name = 'cloned_head';
|
| 212 |
+
|
| 213 |
+
// Position the cloned head at the original head's position relative to the robot
|
| 214 |
+
const worldPosition = new THREE.Vector3();
|
| 215 |
+
const worldRotation = new THREE.Quaternion();
|
| 216 |
+
const worldScale = new THREE.Vector3();
|
| 217 |
+
child.matrixWorld.decompose(worldPosition, worldRotation, worldScale);
|
| 218 |
+
|
| 219 |
+
// Convert world position to robot local coordinates
|
| 220 |
+
const robotInverseMatrix = new THREE.Matrix4().copy(this.robot.matrixWorld).invert();
|
| 221 |
+
const localPosition = worldPosition.clone().applyMatrix4(robotInverseMatrix);
|
| 222 |
+
|
| 223 |
+
// Set position relative to robot, not world
|
| 224 |
+
this.clonedHead.position.copy(localPosition);
|
| 225 |
+
|
| 226 |
+
// For rotation, we need to account for the robot's rotation
|
| 227 |
+
const robotRotation = new THREE.Quaternion().setFromEuler(this.robot.rotation);
|
| 228 |
+
const localRotation = worldRotation.clone().premultiply(robotRotation.invert());
|
| 229 |
+
this.clonedHead.quaternion.copy(localRotation);
|
| 230 |
+
this.clonedHead.scale.copy(worldScale);
|
| 231 |
+
|
| 232 |
+
// Store the base position and rotation for relative pose control (in local robot coordinates)
|
| 233 |
+
this.baseHeadPosition.copy(localPosition);
|
| 234 |
+
this.baseHeadRotation.setFromQuaternion(localRotation);
|
| 235 |
+
|
| 236 |
+
// Hide the original head and add the cloned head to the scene
|
| 237 |
+
child.visible = false;
|
| 238 |
+
this.robot.add(this.clonedHead);
|
| 239 |
+
|
| 240 |
+
return; // Exit traverse after finding the head
|
| 241 |
+
}
|
| 242 |
+
});
|
| 243 |
+
}, 3000); // Wait 3 seconds for STL meshes to load
|
| 244 |
+
}
|
| 245 |
+
|
| 246 |
+
debugHeadMovement() {
|
| 247 |
+
// Removed debug movement tracking for cleaner performance
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
stopDebugHeadMovement() {
|
| 251 |
+
// Removed debug movement tracking for cleaner performance
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
// Debug function to manually test head positioning
|
| 255 |
+
testHeadMovement(x = 0, y = 0, z = 0, roll = 0, pitch = 0, yaw = 0) {
|
| 256 |
+
if (!this.clonedHead) {
|
| 257 |
+
console.warn('❌ Cloned head not available for testing');
|
| 258 |
+
return;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
// Simulate head pose data
|
| 262 |
+
const testData = {
|
| 263 |
+
head_pose: { x, y, z, roll, pitch, yaw }
|
| 264 |
+
};
|
| 265 |
+
|
| 266 |
+
this.updateJoints(testData);
|
| 267 |
+
}
|
| 268 |
+
|
| 269 |
+
// Reset head to base position
|
| 270 |
+
resetHeadPosition() {
|
| 271 |
+
if (!this.clonedHead) {
|
| 272 |
+
return;
|
| 273 |
+
}
|
| 274 |
+
|
| 275 |
+
this.clonedHead.position.copy(this.baseHeadPosition);
|
| 276 |
+
this.clonedHead.rotation.setFromEuler(this.baseHeadRotation);
|
| 277 |
+
}
|
| 278 |
}
|