(degrees: number, direction: 'left' | 'right')
| 368 | |
| 369 | // Execute turn command with odometry feedback |
| 370 | const executeTurn = async (degrees: number, direction: 'left' | 'right'): Promise<void> => { |
| 371 | // Validate odometry before starting |
| 372 | if (!isValidOdometry()) { |
| 373 | throw new Error('Invalid odometry data - cannot execute turn'); |
| 374 | } |
| 375 | |
| 376 | const radians = (degrees * Math.PI) / 180; |
| 377 | const startOrientation = { ...odometry.pose.pose.orientation }; |
| 378 | const startYaw = getYawFromQuaternion(startOrientation); |
| 379 | const angularVel = direction === 'left' ? ANGULAR_VELOCITY : -ANGULAR_VELOCITY; |
| 380 | const slowAngularVel = angularVel * 0.3; // 30% speed for final approach |
| 381 | |
| 382 | // Add minimum execution time based on expected rotation speed |
| 383 | const minExecutionTime = Math.max(500, (radians / Math.abs(ANGULAR_VELOCITY)) * 1000 * 0.8); // 80% of theoretical time |
| 384 | const maxExecutionTime = (radians / Math.abs(ANGULAR_VELOCITY)) * 1000 * 3; // 3x theoretical time as absolute max |
| 385 | const startTime = Date.now(); |
| 386 | |
| 387 | // Debug logging |
| 388 | console.log('Starting turn:', { |
| 389 | degrees, |
| 390 | radians, |
| 391 | direction, |
| 392 | startYaw, |
| 393 | angularVel, |
| 394 | minExecutionTime, |
| 395 | maxExecutionTime |
| 396 | }); |
| 397 | |
| 398 | return new Promise((resolve, reject) => { |
| 399 | let lastYaw = startYaw; |
| 400 | let totalAngleTurned = 0; |
| 401 | let stuckCounter = 0; |
| 402 | |
| 403 | const intervalId = setInterval(() => { |
| 404 | if (executionRef.current.abortController?.signal.aborted) { |
| 405 | clearInterval(intervalId); |
| 406 | publishVelocity({ |
| 407 | linear: new Vector3({ x: 0, y: 0, z: 0 }), |
| 408 | angular: new Vector3({ x: 0, y: 0, z: 0 }) |
| 409 | }); |
| 410 | reject(new Error('Execution aborted')); |
| 411 | return; |
| 412 | } |
| 413 | |
| 414 | const currentOrientation = odometry.pose.pose.orientation; |
| 415 | const currentYaw = getYawFromQuaternion(currentOrientation); |
| 416 | const elapsedTime = Date.now() - startTime; |
| 417 | |
| 418 | // Calculate angle turned in this iteration |
| 419 | const deltaAngle = calculateAngleDifference(lastYaw, currentYaw); |
| 420 | |
| 421 | // Accumulate total angle turned (handling wrap-around) |
| 422 | if (direction === 'left' && deltaAngle > 0) { |
| 423 | totalAngleTurned += deltaAngle; |
| 424 | } else if (direction === 'right' && deltaAngle < 0) { |
| 425 | totalAngleTurned += Math.abs(deltaAngle); |
| 426 | } |
| 427 |
no test coverage detected