0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ゴールに引力、障害物に斥力。ポテンシャル場法の経路計画と局所極小で止まる問題

0
Posted at

「ゴールに引力、障害物に斥力」— これだけでロボットが迷わず進むって、本当でしょうか?

実はこのアイデア、1980年代からある古典的な手法なんです。でも今なお、工場の搬送ロボットやドローンの自動航行で現役で使われています。なぜか?

「計算が超軽い」から。

ディープラーニングみたいにGPUを必要とせず、数千円のマイコンでもリアルタイムに経路を再計算できる。これが現場で重宝される理由です。

でも、弱点もあります。それが**「局所極小」**。ゴールじゃない場所で動けなくなる、ポテンシャル場法のアキレス腱です。

今日は、このシミュレーターを実際に動かしながら、数式→実装→実験→限界まで一気に解説します。

▶ 今回のシミュレーター: ロボット経路計画 シミュレーター (ブラウザで動作・登録不要)


ざっくり本質:「磁石の坂道」を想像してください

ゴールに引力の磁石、障害物に斥力の磁石を置きます。すると空間に「でこぼこの地形」ができます。

  • ゴール付近:くぼ地(低い)
  • 障害物付近:山(高い)
  • それ以外:なだらかな斜面

ロボットは**「高いところを避けて、低いところへ転がる玉」**のように動く。これがポテンシャル場法の本質です。

「ロボットはポテンシャルの坂を下る— ただそれだけ」


数式で理解する

ポテンシャル場法の心臓部は、たった2つの数式です。

引力ポテンシャル

U_{att}= \frac{1}{2}k_{att}\cdot d_{goal}^2
  • $k_{att}$:引力の強さ(係数)
  • $d_{goal}$:ロボットからゴールまでの距離

解釈:「ゴールから遠いほど、強い力で引き寄せられる」— バネの自然長からの伸びに比例する力と同じ形です。

斥力ポテンシャル

U_{rep}= \frac{1}{2}k_{rep}\left(\frac{1}{d_{obs}}- \frac{1}{d_0}\right)^2
  • $k_{rep}$:斥力の強さ(係数)
  • $d_{obs}$:ロボットから障害物までの距離
  • $d_0$:斥力が作用する最大距離(影響半径)

解釈:「障害物に近づくほど急激に反発力が強くなる」— $d_{obs}$が0に近づくと$1/d_{obs}$が爆発的に大きくなる設計です。$d_{obs} > d_0$の範囲では斥力は0(影響なし)。

合成ポテンシャル

U_{total} = U_{att} + \sum_{i} U_{rep,i}

ロボットはこの $U_{total}$ が減少する方向(=勾配の逆方向)に進みます。


コードで実装する ★最重要★

では、実際に動くコードを見てみましょう。このシミュレーターのコア計算をJavaScriptで再現します。

// ポテンシャル場法の1ステップ計算
class PotentialFieldPlanner {
  constructor(params) {
    this.k_att = params.k_att || 1.0;   // 引力係数
    this.k_rep = params.k_rep || 0.5;   // 斥力係数
    this.d0 = params.d0 || 3.0;         // 斥力影響半径
    this.stepSize = params.stepSize || 0.1; // 1ステップの移動量
  }

  // 引力ポテンシャルの勾配(=引力ベクトル)
  calcAttractiveGradient(pos, goal) {
    const dx = goal.x - pos.x;
    const dy = goal.y - pos.y;
    const dist = Math.sqrt(dx*dx + dy*dy);
    if (dist < 0.001) return {x: 0, y: 0}; // ゴール到達
    // U_att = 0.5 * k_att * dist^2 の勾配 = k_att * (pos - goal)
    return {
      x: -this.k_att * dx,  // 勾配の逆方向(減少方向)へ進む
      y: -this.k_att * dy
    };
  }

  // 斥力ポテンシャルの勾配(=斥力ベクトル)
  calcRepulsiveGradient(pos, obstacles) {
    let forceX = 0, forceY = 0;
    for (const obs of obstacles) {
      const dx = pos.x - obs.x;
      const dy = pos.y - obs.y;
      const dist = Math.sqrt(dx*dx + dy*dy);
      if (dist > this.d0) continue; // 影響範囲外
      if (dist < 0.001) {
        // 障害物に激突!大きな斥力を与える
        forceX += 100 * Math.sign(dx || 0.001);
        forceY += 100 * Math.sign(dy || 0.001);
        continue;
      }
      // U_rep = 0.5 * k_rep * (1/dist - 1/d0)^2 の勾配
      // = k_rep * (1/dist - 1/d0) * (1/dist^2) * (pos - obs)/dist
      const coeff = this.k_rep * (1/dist - 1/this.d0) / (dist*dist*dist);
      forceX += coeff * dx;
      forceY += coeff * dy;
    }
    return {x: forceX, y: forceY};
  }

  // 1ステップ進む
  step(pos, goal, obstacles) {
    const attr = this.calcAttractiveGradient(pos, goal);
    const rep = this.calcRepulsiveGradient(pos, obstacles);
    // 合力(勾配の総和)
    const totalX = attr.x + rep.x;
    const totalY = attr.y + rep.y;
    const mag = Math.sqrt(totalX*totalX + totalY*totalY);
    if (mag < 0.001) return null; // 局所極小またはゴール

    // 正規化してstepSizeだけ移動
    return {
      x: pos.x + this.stepSize * (totalX / mag),
      y: pos.y + this.stepSize * (totalY / mag)
    };
  }
}

// 使用例
const planner = new PotentialFieldPlanner({
  k_att: 1.0,
  k_rep: 0.5,
  d0: 3.0,
  stepSize: 0.1
});

let robot = {x: 0, y: 0};
const goal = {x: 10, y: 10};
const obstacles = [{x: 5, y: 5, radius: 1}];

for (let i = 0; i < 200; i++) {
  const next = planner.step(robot, goal, obstacles);
  if (!next) break;
  robot = next;
  console.log(`Step ${i}: (${robot.x.toFixed(2)}, ${robot.y.toFixed(2)})`);
}

このコードが何をしているか:

  1. 引力勾配を計算(ゴール方向へのベクトル)
  2. 各障害物からの斥力勾配を計算(障害物から遠ざかるベクトル)
  3. 両者を足して、その方向に微小移動
  4. これを繰り返す

たったこれだけで、ロボットは障害物を避けながらゴールへ向かうんです。


数値例で確かめる

実際に数値を入れて計算してみましょう。

設定:

  • ロボット初期位置:$(0, 0)$
  • ゴール位置:$(10, 0)$(真右)
  • 障害物:$(5, 0)$に1つ(半径1)
  • パラメータ:$k_{att}=1.0$, $k_{rep}=0.5$, $d_0=3.0$, $stepSize=0.1$

ステップ1の計算:

引力成分:

  • $d_{goal} = \sqrt{(10-0)^2 + (0-0)^2} = 10$
  • 引力勾配の大きさ = $k_{att} \cdot d_{goal} = 1.0 \times 10 = 10$
  • 方向:$(10, 0)$方向 → 引力ベクトル = $(10, 0)$

斥力成分:

  • $d_{obs} = \sqrt{(5-0)^2 + (0-0)^2} = 5$
  • $d_{obs} > d_0(3.0)$ なので、斥力は0

合力: $(10, 0)$ → 真右に移動

  • 次の位置:$(0 + 0.1 \times 1, 0) = (0.1, 0)$

ステップ50(障害物付近)の計算:

  • ロボット位置:$(4.9, 0)$(障害物のすぐ左)
  • $d_{obs} = 0.1 < d_0(3.0)$ → 斥力発生!
  • 斥力勾配:$k_{rep} \cdot (1/0.1 - 1/3) / (0.1^3) \times 0.1 \approx 0.5 \times (10 - 0.333) / 0.001 \times 0.1 = 483.35$
  • 方向:右向き(障害物から遠ざかる)→ 斥力ベクトル = $(-483.35, 0)$
  • 引力ベクトル:$(5.1, 0)$方向に大きさ5.1 → $(5.1, 0)$
  • 合力:$(5.1 - 483.35, 0) = (-478.25, 0)$ → 左向き!

つまり障害物に近づきすぎると、斥力が引力を上回って押し戻される。これで衝突を回避できるんです。

このまま進むと、ロボットは障害物の上側か下側に迂回します。実際のシミュレーションでは、y方向にも少しずつずれながら通過していきます。


シミュレーターで遊ぶ

実際にブラウザで動かしてみましょう。

実験1:基本動作を確認

  1. デフォルト設定のまま「実行」ボタンをクリック
  2. ロボットがゴール(右上)へ一直線に向かう
  3. 途中の障害物を避けて、なめらかな曲線を描く

何が起きているか: 引力と斥力のバランスが取れている状態。斥力影響半径$d_0$が適切なので、障害物に近づきすぎる前に回避行動を開始しています。

実験2:局所極小を体験

  1. 障害物を「U字型」に配置(画面上でドラッグ)
  2. ゴールをU字の中央に設定
  3. 実行

結果: ロボットがU字の入り口付近で停止。動かなくなる。

これが局所極小です。引力と斥力が完全に釣り合い、「坂を下り続ける」という単純な戦略では抜け出せなくなる。ポテンシャル場法最大の弱点です。

実験3:RRT比較機能を試す

  1. 実験2の状態で「RRT」トグルをON
  2. 青い経路(RRT)が生成される

違い: RRTはランダムに探索木を広げるので、U字の壁を乗り越えてゴールに到達できる。ただし経路はガタガタ(滑らかでない)。

教訓:

  • 滑らかさ重視 → ポテンシャル場法
  • 到達性重視 → RRT

実験4:パラメータチューニング

スライダーを動かして変化を観察:

パラメータ 大きくすると 小さくすると
$k_{att}$ 直線的で速いが衝突しやすい 遠回り、低速
$k_{rep}$ 障害物から遠くを迂回 障害物すれすれを通る
$d_0$ 遠くから反応、安全だが迂回大 近づいてから反応、効率的だが危険

現場でハマるポイント

1. 「係数を大きくすれば性能UP」は大間違い

よくある誤解:「斥力を強くすれば安全になる」

実際: $k_{rep}$を極端に大きくすると、障害物周辺の斥力の「丘」が急峻になりすぎます。すると:

  • ロボットが振動(行ったり来たり)
  • 細い通路を通れなくなる(両側から押されて動けない)
  • 計算が不安定になる

対策: $k_{att}=1.0, k_{rep}=0.5$ を基準に、$d_0$から調整する。

2. 「計算が軽い」は罠

確かに1ステップの計算は軽い。でも局所極小にハマると無限ループ。

実装時は必ず:

  • 最大ステップ数(例:1000ステップで打ち切り)
  • 位置の変化が微小ならランダム擾乱を加える
  • 複数回試行して結果の安定性を確認

3. 「点質量」モデルは理想化しすぎ

シミュレーターはロボットを「点」として扱います。でも実機には大きさがあります。

実務では: 障害物の半径に「ロボットの半径+安全マージン」を加えた膨張障害物を使う。例えば幅60cmの搬送ロボットなら、障害物の半径を30cm大きくする。

シミュレーターで障害物の半径を大きくして試すと、この考え方が体感できます。


まとめ

ポテンシャル場法、いかがでしたか?

3つの要点:

  1. 原理はシンプル:引力+斥力の合成で、ロボットはポテンシャルの坂を下る
  2. 実装は20行:勾配計算と微小移動を繰り返すだけ
  3. 限界を知る:局所極小に弱い。RRTなどと組み合わせて使うのが現実解

「計算が軽いから現場で使える」— このシンプルさが、30年以上経った今でも愛用される理由です。

▶ ロボット経路計画 シミュレーター — ブラウザで即動作、登録不要

今日の実験:

  1. デフォルトで実行 → 基本動作を確認
  2. U字障害物で局所極小を体験
  3. RRT比較で違いを体感
  4. パラメータをいじって挙動の変化を観察

NovaSolverでは1,600以上の工学シミュレーターを無料公開中 👉 一覧はこちら

「あ、これって現場のあの現象と同じだ」— そんな発見があるかもしれません。ぜひ遊んでみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?