Collada Kinematics Tweening到期望位置与three.js和tween.js

huangapple go评论59阅读模式
英文:

Collada Kinematics Tweening to desired position with three.js and tween.js

问题

我想要复制现有的示例加载 Collada 骨骼动画并使用它们 ,并使用我的自己的模型,为此我创建了以下类:

import { Object3D, MathUtils } from "three";
import { ColladaLoader } from "three/addons/loaders/ColladaLoader.js";
import yumi_path from "../assets/dae/yumi.dae";
import { Tween, Easing, update } from "@tweenjs/tween.js";

export class YuMiMotion {
  constructor(scene, joint_values) {
    this.scene = scene;
    this.target_joint_values = joint_values;

    this.loader = new ColladaLoader();
    this.yumi_model = new Object3D();
    this.kinematics;
    this.kinematicsTween;
    this.tweenParameters = {};
    this.loadYuMi();
    this.startMotion();
  }

  startMotion = () => {
    if (this.kinematics == undefined) {
      console.log("运动学尚未加载!");
      this.anim_frame_id1 = requestAnimationFrame(this.startMotion);
    } else {
      console.log(this.kinematics);
      this.setupTween();
      cancelAnimationFrame(this.anim_frame_id1);
      this.animate();
    }
  }

  animate = () => {
    update();
    this.anim_frame_id2 = requestAnimationFrame(this.animate);
  }

  loadYuMi = async() => {
    const onLoad = (result, yumi) => {
      const model = result.scene.children[0];
      yumi.add(model.clone(true));
      yumi.traverse(function (child) {
        if (child.isMesh) {
          child.material.flatShading = true;
        }
      });
      this.kinematics = result.kinematics;
    };

    await this.loader.load(yumi_path, (collada) =>
      onLoad(collada, this.yumi_model, this.kinematics)
    ),
      undefined,
      function (error) {
        console.log("发生错误", error);
      };

    this.yumi_model.position.set(0.0, 0.0, -6.0);
    this.yumi_model.rotation.set(-Math.PI / 2, 0.0, -Math.PI / 2);
    this.yumi_model.scale.set(20, 20, 20);

    this.scene.add(this.yumi_model);
  }

  setupTween = () => {

    const duration = MathUtils.randInt(1000, 5000);
    const target = {};

    for (const prop in this.target_joint_values) {
      const joint = this.kinematics.joints[prop];
      const old = this.tweenParameters[prop];
      const position = old ? old : joint.zeroPosition;
      this.tweenParameters[prop] = position;
      target[prop] = this.target_joint_values[prop];
    }

    this.kinematicsTween = new Tween(this.tweenParameters).to(target, duration).easing(Easing.Quadratic.Out);
    this.kinematicsTween.onUpdate((object) => {
      for (const prop in this.kinematics.joints) {
        if (prop in this.kinematics.joints) {
          if (!this.kinematics.joints[prop].static) {
            this.kinematics.setJointValue(prop, object[prop]);
          }
        }
      }
    });

    this.kinematicsTween.start();
    setTimeout(this.setupTween, duration);
  }
}

然后我如下调用这个类:

let target_position = {
  gripper_l_joint: 0.01,
  gripper_l_joint_m: 0.01,
  gripper_r_joint: 0.01,
  gripper_r_joint_m: 0.01,
  yumi_joint_1_l: 10.0,
  yumi_joint_1_r: 10.0,
  yumi_joint_2_l: 20.0,
  yumi_joint_2_r: 20.0,
  yumi_joint_3_l: 30.0,
  yumi_joint_3_r: 30.0,
  yumi_joint_4_l: 40.0,
  yumi_joint_4_r: 40.0,
  yumi_joint_5_l: 50.0,
  yumi_joint_5_r: 50.0,
  yumi_joint_6_l: 60.0,
  yumi_joint_6_r: 60.0,
  yumi_joint_7_l: 70.0,
  yumi_joint_7_r: 70.0,
};
new YuMiMotion(this.scene, target_position);

我已经创建了一个示例库以重现我的示例

请问如何使用Three.jsTween.js来正确移动我的模型到所需的关节位置?提前感谢。

英文:

I want to replicate the existent example of loading Collada kinematics and using them with my own model, to do so I have created a class as follows:

import { Object3D, MathUtils } from "three";
import { ColladaLoader } from "three/addons/loaders/ColladaLoader.js";
import yumi_path from "../assets/dae/yumi.dae";
import { Tween, Easing, update } from "@tweenjs/tween.js";

export class YuMiMotion {
  constructor(scene, joint_vlaues) {
    this.scene = scene;
    this.tgt_jnt_vals = joint_vlaues;

    this.loader = new ColladaLoader();
    this.yumi_model = new Object3D();
    this.kinematics;
    this.kinematicsTween;
    this.tweenParameters = {};
    this.loadYuMi();
    this.startMotion();
  }

  startMotion = () => {
    if (this.kinematics == undefined) {
      console.log("Kinematics Still not loaded!");
      this.anim_frame_id1 = requestAnimationFrame(this.startMotion);
    }
    else {
      console.log(this.kinematics);
      this.setupTween();
      cancelAnimationFrame(this.anim_frame_id1);
      this.animate();
     }    
  }

  animate = () => { 
    update();
    this.anim_frame_id2 = requestAnimationFrame(this.animate);
  }
  
  loadYuMi = async() => {
    const onLoad = (result, yumi) => {
      const model = result.scene.children[0];
      yumi.add(model.clone(true));
      yumi.traverse(function (child) {
        if (child.isMesh) {
          child.material.flatShading = true;
        }
      });
      this.kinematics = result.kinematics;
    };

    await this.loader.load(yumi_path, (collada) =>
      onLoad(collada, this.yumi_model, this.kinematics)
    ),
      undefined,
      function (error) {
        console.log("An error happened", error);
      };

    this.yumi_model.position.set(0.0, 0.0, -6.0);
    this.yumi_model.rotation.set(-Math.PI / 2, 0.0, -Math.PI / 2);
    this.yumi_model.scale.set(20, 20, 20);

    this.scene.add(this.yumi_model);
  }

  setupTween =() =>{

    const duration = MathUtils.randInt( 1000, 5000 );
    const target = {};

    for (const prop in this.tgt_jnt_vals) {
          const joint = this.kinematics.joints[ prop ];
          const old = this.tweenParameters[ prop ];
          const position = old ? old : joint.zeroPosition;
          this.tweenParameters[ prop ] = position;
      target[prop] = this.tgt_jnt_vals[prop]; //MathUtils.randInt( joint.limits.min, joint.limits.max );
      // console.log('target:', prop, this.tgt_jnt_vals[prop]);
    }

    this.kinematicsTween = new Tween( this.tweenParameters ).to( target, duration ).easing( Easing.Quadratic.Out );
    this.kinematicsTween.onUpdate( ( object )=> {
      for ( const prop in this.kinematics.joints ) {
        if ( prop in this.kinematics.joints) {
          if ( ! this.kinematics.joints[ prop ].static ) {
            this.kinematics.setJointValue( prop, object[ prop ] );
          }
        }
      }
    });
    // console.log("Tween Parameters", this.tweenParameters);
    // console.log("kinematics tween", this.kinematicsTween);
    this.kinematicsTween.start();
    setTimeout( this.setupTween, duration );
  }
}

And I'm calling this class as follows:

let target_position = {
  gripper_l_joint: 0.01,
  gripper_l_joint_m: 0.01,
  gripper_r_joint: 0.01,
  gripper_r_joint_m: 0.01,
  yumi_joint_1_l: 10.0,
  yumi_joint_1_r:  10.0,
  yumi_joint_2_l:  20.0,
  yumi_joint_2_r:  20.0,
  yumi_joint_3_l:  30.0,
  yumi_joint_3_r:  30.0,
  yumi_joint_4_l:  40.0,
  yumi_joint_4_r:  40.0,
  yumi_joint_5_l:  50.0,
  yumi_joint_5_r:  50.0,
  yumi_joint_6_l:  60.0,
  yumi_joint_6_r:  60.0,
  yumi_joint_7_l:  70.0,
  yumi_joint_7_r:  70.0,

};
new YuMiMotion(this.scene, target_position);

I have created a repo to reproduce my example here.

Can you please tell me how can I Move my model properly to the desired joints position using Three.js and Tween.js? thanks in advance.

答案1

得分: 0

以下是代码的翻译部分:

Motion Class

import { Tween, Easing } from "@tweenjs/tween.js";
import { ColladaLoader } from "three/examples/jsm/loaders/ColladaLoader.js";
import yumi_path from "../assets/dae/yumi.dae";

export class LoadMoveRobot {
  constructor(scene) {
    this.scene = scene;
    this.yumi;
    this.kinematics;
    this.duration = 1000;
    this.target = null;
    this.tweenParameters = {};
    this.joints = {
      left: [
        "yumi_joint_1_l",
        "yumi_joint_2_l",
        "yumi_joint_3_l",
        "yumi_joint_4_l",
        "yumi_joint_5_l",
        "yumi_joint_6_l",
        "yumi_joint_7_l",
      ],
      right: [
        "yumi_joint_1_r",
        "yumi_joint_2_r",
        "yumi_joint_3_r",
        "yumi_joint_4_r",
        "yumi_joint_5_r",
        "yumi_joint_6_r",
        "yumi_joint_7_r",
      ],
      both: [
        "yumi_joint_1_l",
        "yumi_joint_2_l",
        "yumi_joint_3_l",
        "yumi_joint_4_l",
        "yumi_joint_5_l",
        "yumi_joint_6_l",
        "yumi_joint_7_l",
        "yumi_joint_1_r",
        "yumi_joint_2_r",
        "yumi_joint_3_r",
        "yumi_joint_4_r",
        "yumi_joint_5_r",
        "yumi_joint_6_r",
        "yumi_joint_7_r",
      ],
    };
    this.end_effector = {
      left:  ["gripper_l_joint", "gripper_l_joint_m"],
      right: ["gripper_r_joint", "gripper_r_joint_m"],
      both: [
        "gripper_l_joint",
        "gripper_l_joint_m",
        "gripper_r_joint",
        "gripper_r_joint_m",
      ],
    };
    this.loadRobot();
  }

  loadRobot = () => {
    const loader = new ColladaLoader();
    loader.load(yumi_path, (collada) => {
      this.yumi = collada.scene;
      this.yumi.traverse((child) => {
        if (child.isMesh) {
          // model does not have normals
          child.material.flatShading = true;
        }
      });

      this.yumi.position set(0.0, 0.0, -6.0);
      this.yumi.rotation.set(-Math.PI / 2, 0.0, -Math.PI / 2);
      this.yumi.scale.set(20, 20, 20);

      this.yumi.updateMatrix();

      this.kinematics = collada.kinematics;

      // Add the COLLADA
      this.scene.add(this.yumi);

      this.moveJointValues("left");
      // this.moveXYZValues();
      this.controlGripper("both", 2);
    });
  };

  moveJointValues = (arm, target) => {
    if (target !== undefined) {
      this.target = target;
    }

    if (this.kinematics === undefined) {
      /**
       * TODO: target object is undefined in the recursive calls!
       * It will be nice to replace setTimeout by a Promise.
       */
      setTimeout(this.moveJointValues.bind(null, arm, target), 3000);
    } else {
      this.joints[arm].forEach((joint) => {
        if (!this.kinematics.joints[joint].static) {
          const old = this.tweenParameters[joint];
          const position = old ? old : this.kinematics.getJointValue(joint);
          this.tweenParameters[joint] = position;
        }
      });
      this.kinematicsTween = new Tween(this.tweenParameters)
        .to(this.target, this.duration)
        .easing(Easing.Quadratic.Out)
        .onUpdate((object) => {
          this.joints[arm].forEach((joint) => {
            if (!this.kinematics.joints[joint].static) {
              this.kinematics.setJointValue(joint, object[joint]);
            }
          });
        })
        .start();
    }
  };

  controlGripper = (arm, act_idx, val = null) => {
    if (act_idx === 1) {
      this.end_effector[arm].forEach((finger) =>
        this.kinematics.setJointValue(finger, 0.0)
      );
    } else if (act_idx === 2) {
      this.end_effector[arm].forEach((finger) =>
        this.kinematics.setJointValue(finger, 0.025)
      );
    } else if (act_idx === 3) {
      if (val !== null) {
        if (val >= 0.0 && val <= 0.25) {
          this.end_effector[arm].forEach((finger) =>
            this.kinematics.setJointValue(finger, val)
          );
        } else {
          console.warn("Gripper finger values has to be between 0.0 and 0.025!");
        }
      } else {
        console.warn("Wrong Gripper Move_To value!");
      }
    } else {
      console.warn("Gripper Parameters has to be 1, 2, or 3!");
    }
  };
}

Animating the motion

import { update } from "@tweenjs/tween.js";

animate(){
      requestAnimationFrame( this.animate );
      this.renderer.render(this.scene, this.camera);
      update();
}

let target_position = {
  gripper_l_joint: 0.0125,      // 0.0 --> 0.025    , Prismatic
  gripper_l_joint_m: 0.0125,    // 0.0 --> 0.025    , Prismatic
  gripper_r_joint: 0.0125,      // 0.0 --> 0.025    , Prismatic
  gripper_r_joint_m: 0.0125,    // 0.0 --> 0.025    , Prismatic
  yumi_joint_1_l: 0.0,          // -168.5 --> 168.5 , Revolute
  yumi_joint_1_r: 0.0,          // -168.5 --> 168.5 , Revolute
  yumi_joint_2_l: -130.0,        // -143.5 --> 43.5  , Revolute
  yumi_joint_2_r: -130.0,        // -143.5 --> 43.5  , Revolute
  yumi_joint_3_l: 30,       // -123.5 --> 80.0  , Revolute
  yumi_joint_3_r: 30,       // -123.5 --> 80.0  , Revolute
  yumi_joint_4_l: 0.0,          // -

<details>
<summary>英文:</summary>

The problem was animating the motion, this problem was solved by calling the animation loop outside the motion class:
#### Motion Class
```js
import { Tween, Easing } from &quot;@tweenjs/tween.js&quot;;
import { ColladaLoader } from &quot;three/examples/jsm/loaders/ColladaLoader.js&quot;;
import yumi_path from &quot;../assets/dae/yumi.dae&quot;;


export class LoadMoveRobot {
  constructor(scene) {
    this.scene = scene;
    this.yumi;
    this.kinematics;
    this.duration = 1000;
    this.target = null;
    this.tweenParameters = {};
    this.joints = {
      left: [
        &quot;yumi_joint_1_l&quot;,
        &quot;yumi_joint_2_l&quot;,
        &quot;yumi_joint_3_l&quot;,
        &quot;yumi_joint_4_l&quot;,
        &quot;yumi_joint_5_l&quot;,
        &quot;yumi_joint_6_l&quot;,
        &quot;yumi_joint_7_l&quot;,
      ],
      right: [
        &quot;yumi_joint_1_r&quot;,
        &quot;yumi_joint_2_r&quot;,
        &quot;yumi_joint_3_r&quot;,
        &quot;yumi_joint_4_r&quot;,
        &quot;yumi_joint_5_r&quot;,
        &quot;yumi_joint_6_r&quot;,
        &quot;yumi_joint_7_r&quot;,
      ],
      both: [
        &quot;yumi_joint_1_l&quot;,
        &quot;yumi_joint_2_l&quot;,
        &quot;yumi_joint_3_l&quot;,
        &quot;yumi_joint_4_l&quot;,
        &quot;yumi_joint_5_l&quot;,
        &quot;yumi_joint_6_l&quot;,
        &quot;yumi_joint_7_l&quot;,
        &quot;yumi_joint_1_r&quot;,
        &quot;yumi_joint_2_r&quot;,
        &quot;yumi_joint_3_r&quot;,
        &quot;yumi_joint_4_r&quot;,
        &quot;yumi_joint_5_r&quot;,
        &quot;yumi_joint_6_r&quot;,
        &quot;yumi_joint_7_r&quot;,
      ],
    };
    this.end_effector = {
      left:  [&quot;gripper_l_joint&quot;, &quot;gripper_l_joint_m&quot;],
      right: [&quot;gripper_r_joint&quot;, &quot;gripper_r_joint_m&quot;],
      both: [
        &quot;gripper_l_joint&quot;,
        &quot;gripper_l_joint_m&quot;,
        &quot;gripper_r_joint&quot;,
        &quot;gripper_r_joint_m&quot;,
      ],
    };
    this.loadRobot();
  }

  loadRobot = () =&gt; {
    const loader = new ColladaLoader();
    loader.load(yumi_path, (collada) =&gt; {
      this.yumi = collada.scene;
      this.yumi.traverse((child) =&gt; {
        if (child.isMesh) {
          // model does not have normals
          child.material.flatShading = true;
        }
      });

      this.yumi.position.set(0.0, 0.0, -6.0);
      this.yumi.rotation.set(-Math.PI / 2, 0.0, -Math.PI / 2);
      this.yumi.scale.set(20, 20, 20);

      this.yumi.updateMatrix();

      this.kinematics = collada.kinematics;

      // Add the COLLADA
      this.scene.add(this.yumi);

      this.moveJointValues(&quot;left&quot;);
      // this.moveXYZValues();
      this.controlGripper(&quot;both&quot;, 2);
    });
  };

  moveJointValues = (arm, target) =&gt; {
    if (target !== undefined) {
      this.target = target;
    }

    if (this.kinematics === undefined) {
      /**
       * TODO: target object is undefined in the recursive calls!
       * It will be nice to replace setTimeout by a Promise.
       */
      setTimeout(this.moveJointValues.bind(null, arm, target), 3000);
    } else {
      this.joints[arm].forEach((joint) =&gt; {
        if (!this.kinematics.joints[joint].static) {
          const old = this.tweenParameters[joint];
          const position = old ? old : this.kinematics.getJointValue(joint);
          this.tweenParameters[joint] = position;
        }
      });
      this.kinematicsTween = new Tween(this.tweenParameters)
        .to(this.target, this.duration)
        .easing(Easing.Quadratic.Out)
        .onUpdate((object) =&gt; {
          this.joints[arm].forEach((joint) =&gt; {
            if (!this.kinematics.joints[joint].static) {
              this.kinematics.setJointValue(joint, object[joint]);
            }
          });
        })
        .start();
    }
  };

  controlGripper = (arm, act_idx, val = null) =&gt; {
    if (act_idx === 1) {
      this.end_effector[arm].forEach((finger) =&gt;
        this.kinematics.setJointValue(finger, 0.0)
      );
    } else if (act_idx === 2) {
      this.end_effector[arm].forEach((finger) =&gt;
        this.kinematics.setJointValue(finger, 0.025)
      );
    } else if (act_idx === 3) {
      if (val !== null) {
        if (val &gt;= 0.0 &amp;&amp; val &lt;= 0.25) {
          this.end_effector[arm].forEach((finger) =&gt;
            this.kinematics.setJointValue(finger, val)
          );
        } else {
          console.warn(
            &quot;Gripper finger values has to be between 0.0 and 0.025!&quot;
          );
        }
      } else {
        console.warn(&quot;Wrong Gripper Move_To value!&quot;);
      }
    } else {
      console.warn(&quot;Gripper Parameters has to be 1, 2, or 3!&quot;);
    }
  };
}

Animating the motion

import { update } from &quot;@tweenjs/tween.js&quot;;

animate(){
      requestAnimationFrame( this.animate );
      this.renderer.render(this.scene, this.camera);
      update();
}

let target_position = {
  gripper_l_joint: 0.0125,      // 0.0 --&gt; 0.025    , Prismatic
  gripper_l_joint_m: 0.0125,    // 0.0 --&gt; 0.025    , Prismatic
  gripper_r_joint: 0.0125,      // 0.0 --&gt; 0.025    , Prismatic
  gripper_r_joint_m: 0.0125,    // 0.0 --&gt; 0.025    , Prismatic
  yumi_joint_1_l: 0.0,          // -168.5 --&gt; 168.5 , Revolute
  yumi_joint_1_r: 0.0,          // -168.5 --&gt; 168.5 , Revolute
  yumi_joint_2_l: -130.0,        // -143.5 --&gt; 43.5  , Revolute
  yumi_joint_2_r: -130.0,        // -143.5 --&gt; 43.5  , Revolute
  yumi_joint_3_l: 30,       // -123.5 --&gt; 80.0  , Revolute
  yumi_joint_3_r: 30,       // -123.5 --&gt; 80.0  , Revolute
  yumi_joint_4_l: 0.0,          // -290.0 --&gt; 290.0 , Revolute
  yumi_joint_4_r: 0.0,          // -290.0 --&gt; 290.0 , Revolute
  yumi_joint_5_l: 40.0,         // -88.0 --&gt; 130.0  , Revolute
  yumi_joint_5_r: 40.0,         // -88.0 --&gt; 130.0  , Revolute
  yumi_joint_6_l: 0.0,          // -229.0 --&gt; 229.0 , Revolute
  yumi_joint_6_r: 0.0,          // -229.0 --&gt; 229.0 , Revolute
  yumi_joint_7_l: 135.0,          // -168.0 --&gt; 168.0 , Revolute
  yumi_joint_7_r: -135.0           // -168.0 --&gt; 168.0 , Revolute
};

const LMR_ = new LoadMoveRobot(this.scene);
LMR_.moveJointValues(&#39;left&#39;, target_position);

this.animate();

huangapple
  • 本文由 发表于 2023年2月8日 18:21:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75384346.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定