【PyBullet開発奮闘記】Pythonで船の衝突シミュレーションを自作!「空飛ぶ船」と戦った全記録

ALL

こんにちは、Tech Samuraiです!
ふと、「海の上を走る船が壁にぶつかったら、どれくらいの衝撃があるんだろう?」 そんな素朴な疑問から、物理シミュレーションのプロジェクトが始まりました。

この記事では、私の開発マシンでPythonを使い、物理エンジン**PyBullet**とGUIライブラリ**PySide6**を組み合わせて、船の衝突シミュレーターをゼロから作り上げていく過程を共有します。

しかし、その道のりは平坦ではありませんでした。これは、開発中に私を悩ませた「空飛ぶ船」や「進まない船」といった奇妙な現象とどう戦い、解決したのか。そして最後に、多くの人がつまずくPythonの実行方法の謎、`python script.py`と`python -m package.module`の違いにどう気づいたのか。その全記録です。


Step 1:目標設定と最初の実装

今回のシミュレーションで再現したかった条件は以下の通りです。

  • 質量80kg、速度3m/sの船が、コンクリート壁に衝突する。
  • 船の先端には5mmのゴム板があり、衝撃を和らげる。
  • 船は海に浮いている状態を再現する。

これらの条件を再現するため、オープンソースの物理エンジン**PyBullet**を採用しました。PyBulletは、物体の運動や衝突、力の適用などを高速に計算してくれます。まずはシンプルに、シミュレーション空間に「船」と「壁」のオブジェクトを配置し、船に初速を与えてみることから始めました。


Step 2:最初の壁 – 謎の「空飛ぶ船」

シミュレーションを実行してみると、衝撃的な光景が目に飛び込んできました。船が壁に向かうどころか、**空高く舞い上がっていく**のです。まるでロケットのように…。

原因を調査した結果、問題は**「浮力」**の設定にありました。船を浮かすために私が設定した上向きの力(浮力)が、船の重力(下向きの力)を大幅に上回っていたのです。

  • 船の重力: 80kg × 9.81m/s² = 約785 N
  • 誤った設定の浮力: 約3130 N

これでは浮き上がってしまうのも当然です。解決策は、物理の基本に立ち返ることでした。静かに浮かんでいる状態では**「浮力」と「重力」は釣り合う**はずです。そこで、浮力の値を船の重力と等しくなるように修正しました。

# 修正後の浮力計算
self.buoyancy_force = self.boat_mass * abs(self.gravity)

この修正により、船はようやく水面(?)に留まってくれるようになりました。


Step 3:第二の壁 – 頑固な「進まない船」

船が浮き上がる問題は解決したものの、次に待っていたのは「**船が全く進まない**」という問題でした。初速を与えても、すぐにその場でピタッと停止してしまいます。

これも物理の法則に立ち返ることで原因が分かりました。シミュレーション空間には常に**「水の抵抗」**がかかっています。しかし、それを打ち消すための**「推力(エンジンパワー)」**を実装していなかったのです。

そこで、船が目標速度3m/sで進んでいる時に受ける水の抵抗力と、**全く同じ大きさの「推力」**を、常に船の前進方向に加え続けるようにロジックを修正。これにより、「推力」と「水の抵抗」が釣り合い、船は目標速度を維持して進むことができるようになりました。


Step 4:最後の謎 – 私が `python -m` で実行した理由

シミュレーションはこれで完成しましたが、開発の過程で実行方法にいつもと違いがあります。今回の構成では「python src/.../main.pyのようにファイルを直接指定せず、rye run python -m src...という、少し変わったコマンドで実行しています」

ファイルを直接実行するとImportErrorが発生します。`main.py`の中にあるfrom .gui import MainWindow`の先頭のドット.`の部分で、「そんなモジュールは見つからない」とPythonに怒られてしまうのです。

この違いはPythonの**パッケージシステム**にありました。from .gui ...という書き方は「自分と同じパッケージにいる`gui.py`」という意味の、相対インポートです。ファイルを直接実行すると、Pythonはそのファイルがどのパッケージに属しているか分からなくなるため、この相対インポートが失敗します。

一方、-m` (モジュールとして実行) オプションを付けて実行すると、Pythonはプロジェクトのルートから構造を把握し、「`src.ship_collision_simulator`というパッケージの中の`main`モジュールを実行するんだな」と理解します。これにより、`main.py`から見たgui.py`の相対的な位置関係が正しく解決され、インポートが成功するのです。

この仕組みを理解することで、複数のファイルから成る少し複雑なPythonアプリケーションを開発する上での、知見を一つ増やすことになります。


完成したシミュレーターのコード

これらの試行錯誤を経て完成した、シミュレーションとGUIの全コードは、以下のGitHubリポジトリで公開しています。


https://amzn.to/42pj14Z

まとめ

「船の衝突」という一つのテーマから始まったこのプロジェクトは、単なるシミュレーションの構築に留まらず、物理法則の適用、デバッグ、そしてPythonのパッケージシステムという深い領域への探求へと繋がりました。

開発とは、このように「問題発生 → 原因究明 → 修正」というサイクルの繰り返しです。一つ一つの問題を乗り越えるたびに、プログラムはより現実に近い挙動をするようになり、開発者としての知識も深まっていきます。

皆さんも、身の回りの素朴な疑問をテーマに、シミュレーション開発に挑戦してみてはいかがでしょうか。きっと楽しい発見が待っているはずです。

コメント

タイトルとURLをコピーしました