Sausage Erectos X ブラウザで動くキネマティクス——関節システムの実装

キネマティクス実装──Sausage Erectosの関節システム

Sausage Erectosのカスタマイザーでは、フィギュアのポーズを関節ベースで制御する。この関節システムは、ロボット工学で使われるキネマティクス(運動学)の理論に基づいて設計されている。

フォワードキネマティクス vs インバースキネマティクス

関節の制御には2つの基本的なアプローチがある。

フォワードキネマティクス(FK): 各関節の角度を直接指定し、末端(手先や足先)の位置を計算する。

入力:  肩の角度=30°、肘の角度=45°、手首の角度=10°
出力:  手先の位置(x, y, z)

インバースキネマティクス(IK): 末端の目標位置を指定し、各関節の角度を逆算する。

入力:  手先の目標位置(x, y, z)
出力:  肩の角度、肘の角度、手首の角度

Sausage Erectosでは、基本操作にはFKを、ドラッグ操作にはIKを使用するハイブリッド方式を採用している。

FKは実装が単純で直感的だ。各関節のスライダーを動かすと、連鎖的に子関節が動く。しかし、「手をこの位置に持っていきたい」という操作にはIKのほうが自然だ。

DH(デナビット=ハーテンベルグ)パラメータ

ロボット工学では、関節チェーンの幾何学的関係をDHパラメータで定義する。各関節は4つのパラメータで記述される:

θ (theta): 関節角度(回転関節の変数)
d:         リンクオフセット
a:         リンク長さ
α (alpha): リンクひねり角度

Sausage Erectosの右腕の例:

関節        θ(可変)    d      a       α
------------------------------------------
肩(回転)   θ1        0      0      -π/2
肩(屈伸)   θ2        0      40mm    0
肘          θ3        0      35mm    0
手首        θ4        0      20mm    0

各関節間の変換行列は:

function dhMatrix(theta, d, a, alpha) {
  const ct = Math.cos(theta), st = Math.sin(theta);
  const ca = Math.cos(alpha), sa = Math.sin(alpha);
  return new THREE.Matrix4().set(
    ct, -st*ca,  st*sa, a*ct,
    st,  ct*ca, -ct*sa, a*st,
    0,   sa,     ca,    d,
    0,   0,      0,     1
  );
}

角度制限の実装

現実の関節と同様に、各関節には可動範囲の制限がある。肘は逆方向に曲がらないし、首は360度回転しない。

class Joint {
  constructor(name, axis, minAngle, maxAngle) {
    this.name = name;
    this.axis = axis;          // 回転軸 ('x', 'y', 'z')
    this.minAngle = minAngle;  // 最小角度(ラジアン)
    this.maxAngle = maxAngle;  // 最大角度(ラジアン)
    this.currentAngle = 0;
    this.children = [];
  }

  setAngle(angle) {
    // 制限範囲内にクランプ
    this.currentAngle = Math.max(
      this.minAngle,
      Math.min(this.maxAngle, angle)
    );
    this.updateTransform();
  }

  updateTransform() {
    // 自身の回転を更新
    this.bone.rotation[this.axis] = this.currentAngle;
    // 子関節も連鎖的に更新
    for (const child of this.children) {
      child.updateTransform();
    }
  }
}

Sausage Erectosの主要関節の可動範囲:

首:     左右 ±60°  前後 ±45°
肩:     全方向 ±150°
肘:     屈曲 0〜145°(過伸展は不可)
手首:   全方向 ±80°
股関節: 全方向 ±120°
膝:     屈曲 0〜140°
足首:   上下 ±45°  左右 ±30°

ドラッグ操作のRaycast実装

ユーザーが3D空間内の関節をマウスでドラッグしてポーズを変更する機能は、Raycasterを使って実装している。

function onMouseMove(event) {
  if (!selectedJoint) return;

  // マウス位置からレイを計算
  raycaster.setFromCamera(mouse, camera);

  // ドラッグ平面との交点を計算
  const plane = new THREE.Plane(
    camera.getWorldDirection(new THREE.Vector3()).negate(),
    -selectedJoint.bone.position.dot(
      camera.getWorldDirection(new THREE.Vector3())
    )
  );

  const intersection = new THREE.Vector3();
  raycaster.ray.intersectPlane(plane, intersection);

  // 交点から関節角度を計算(IK)
  solveIK(selectedJoint, intersection);
}

関節の親子関係のツリー構造

Sausage Erectosの骨格は、以下のようなツリー構造で管理されている:

root (腰)
├── spine (背骨)
│   ├── neck (首)
│   │   └── head (頭)
│   ├── shoulder_L (左肩)
│   │   ├── elbow_L (左肘)
│   │   │   └── wrist_L (左手首)
│   └── shoulder_R (右肩)
│       ├── elbow_R (右肘)
│       │   └── wrist_R (右手首)
├── hip_L (左股関節)
│   ├── knee_L (左膝)
│   │   └── ankle_L (左足首)
└── hip_R (右股関節)
    ├── knee_R (右膝)
    │   └── ankle_R (右足首)

親関節が回転すると、すべての子関節も連動して動く。これにより、肩を回すと肘も手首も自然に追従する。

キネマティクスは数学の言葉で「動き」を記述する技術だ。ソーセージのフィギュアにロボット工学の理論を適用する。この過剰さこそが、Sausage Erectosの本質なのかもしれない。

← ブログ一覧に戻る