You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现机械爪抓取放置线性运动动画?解决无动画与卡顿问题

机械爪抓取放置线性运动动画实现问题

目标是实现机械爪抓取放置的线性运动动画,按逻辑编写代码后看不到任何运动效果,且浏览器卡顿无报错。

机械爪与LEGO积木场景

现有代码逻辑

1. 创建机械爪并下移至LEGO处

const grip_pos = new Vector3(
    pick_pos.x,
    pick_pos.y,
    pick_pos.z + 5
);
createGripper(grip_pos, this.scene);
console.log(this.scene.getObjectByName("gripper", true));
const gripper = this.scene.getObjectByName("gripper", true);
// Down
while (gripper.position.y > (pick_pos.z / 0.48)) {
    gripper.position.y -= 0.1;
};

2. 绑定LEGO并移动至放置位置上方

gripper.add(lego);
    
// if Down then Up
if (!gripper.position.y > pick_pos.z / 0.48) {
  while (gripper.position.y < grip_pos) {
    gripper.position.y += step;
  }
  
  if (pick_rot) {
    gripper.rotateY(Math.PI / 2);
  }
}

// Move to Place Position
while (gripper.position.x > place_pos.y / 0.8 + 9.2) {
  gripper.position.x -= step;
}
while (gripper.position.x < place_pos.y / 0.8 + 9.2) {
  gripper.position.x += step;
}

while (gripper.position.z > place_pos.x / 0.8 + 2.8) {
  gripper.position.z -= step;
}
while (gripper.position.z < place_pos.x / 0.8 + 2.8) {
  gripper.position.z += step;
}

3. 放下LEGO后机械爪上升消失

// Place Down
if (
  gripper.position.x === place_pos.y &&
  gripper.position.z === place_pos.x
) {
  while (gripper.position.y > pick_pos.z / 0.48) {
    gripper.position.y -= step;
  }
}
if (place_rot) {
  gripper.rotateY(Math.PI / 2);
}

this.scene.add(lego);
this.scene.remove(gripper);
this.renderer.render(this.scene, this.camera);
cancelAnimationFrame(id);

问题根源与解决思路

1. 同步循环阻塞主线程

直接用while循环修改位置会瞬间完成所有计算,浏览器来不及渲染每一步变化,同时阻塞主线程导致卡顿。Three.js动画必须通过requestAnimationFrame分帧执行,每帧只更新一小段状态。

2. 浮点数精度判断错误

用===判断位置相等几乎永远不成立(浮点数运算存在精度误差),需改为范围判断(如差值小于0.01)。

3. 坐标系转换需验证

确保pick_pos、place_pos的xyz/zyx坐标系转换及缩放逻辑正确,否则目标位置计算错误会导致运动失效。

具体实现方案

替换所有while循环,用状态机结合requestAnimationFrame分步骤执行动画:

// 定义动画状态与目标值
let animationState = "moveDown";
const step = 0.1;
const gripper = this.scene.getObjectByName("gripper", true);
const targetDownY = pick_pos.z / 0.48;
const targetUpY = grip_pos.y;
const targetPlaceX = place_pos.y / 0.8 + 9.2;
const targetPlaceZ = place_pos.x / 0.8 + 2.8;

function animate() {
  switch(animationState) {
    case "moveDown":
      if (gripper.position.y > targetDownY + 0.01) {
        gripper.position.y -= step;
      } else {
        gripper.position.y = targetDownY;
        gripper.add(lego);
        animationState = "moveUp";
        pick_rot && gripper.rotateY(Math.PI / 2);
      }
      break;
    case "moveUp":
      if (gripper.position.y < targetUpY - 0.01) {
        gripper.position.y += step;
      } else {
        gripper.position.y = targetUpY;
        animationState = "moveToPlaceX";
      }
      break;
    case "moveToPlaceX":
      if (Math.abs(gripper.position.x - targetPlaceX) > 0.01) {
        gripper.position.x += gripper.position.x < targetPlaceX ? step : -step;
      } else {
        gripper.position.x = targetPlaceX;
        animationState = "moveToPlaceZ";
      }
      break;
    case "moveToPlaceZ":
      if (Math.abs(gripper.position.z - targetPlaceZ) > 0.01) {
        gripper.position.z += gripper.position.z < targetPlaceZ ? step : -step;
      } else {
        gripper.position.z = targetPlaceZ;
        animationState = "placeDown";
      }
      break;
    case "placeDown":
      if (gripper.position.y > targetDownY + 0.01) {
        gripper.position.y -= step;
      } else {
        gripper.position.y = targetDownY;
        place_rot && gripper.rotateY(Math.PI / 2);
        this.scene.add(lego);
        this.scene.remove(gripper);
        return; // 结束动画
      }
      break;
  }
  this.renderer.render(this.scene, this.camera);
  requestAnimationFrame(animate.bind(this));
}

// 启动动画
requestAnimationFrame(animate.bind(this));

额外注意事项

  • 确保createGripper正确创建名为gripper的对象,避免getObjectByName返回null。
  • 调试时可打印每一帧的gripper.position与目标值,验证位置计算是否正确。
  • 如需更平滑的动画,可使用THREE.MathUtils.lerp实现线性插值,替代固定步长加减。

内容的提问来源于stack exchange,提问作者Bilal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 02:05:33