3軸リアクションホイール倒立振子を作るアドカレ の2日目です。 昨日の構想発表に続き、今日から実装フェーズに入りました。
まずは 「PCの中に実験室を作る」 作業からです。
なぜ実機を作る前にシミュレータなのか?
現状の課題
作りたいのは「3軸倒立振子」ですが、部品はまだ手元にないし、制御ロジックも空っぽです。 この状態でいきなり実機を組んでも、モータが暴走してケーブルを引きちぎり、机から転げ落ちる未来しか見えません。
解決策:デジタルツイン
そこで 「Digital Twin(デジタルツイン)」 のアプローチをとることにしました。 PCの中に重力や物理法則に従う仮想世界を作り、そこでAIを鍛えます。
- Sim (Simulation) : 仮想空間で何万回も失敗させて学習する。
- Real (Reality) : 賢くなったモデルを実機に移植する。
いわゆる Sim2Real です。これなら、どれだけ派手に転んでもリセットボタン一発で元通りです。
ツール選定: PyBulletを採用した理由
ロボットシミュレータにはUnity, Gazebo, Isaac Gym などいろいろありますが、今回は PyBullet を採用しました。
-
理由1:Pythonの完結
- C++やROSの重厚な環境構築をしたくなかった
-
理由2:導入の手軽さ
- pipだけで環境が整うのが魅力的
-
理由3:強化学習との親和性
- OpenAI Gymなどと連携しやすく、Googleの研究でも使われている実績もある
Sim-to-Real: Learning Agile Locomotion For Quadruped Robots
- OpenAI Gymなどと連携しやすく、Googleの研究でも使われている実績もある
開発環境の構築
VS Codeでプロジェクトを作成し、Pythonの仮想環境を用意しました。 実行したコマンドは以下の通りです。
# プロジェクトフォルダの作成
mkdir stm32-ai-cubli
cd stm32-ai-cubli
# 仮想環境を作成 (Windows)
python -m venv .venv
# 仮想環境を有効化
.\.venv\Scripts\Activate.ps1
# PyBulletと数値計算ライブラリを入れる
pip install pybullet numpy
これで環境構築は完了です。
実装:赤い箱を「神の視点」で操る
まずは動作確認として、PyBullet上で 「赤い箱」 を出現させてみることにしました。
ただ落とすだけでは物理演算の実感が湧かないので、[SPACE]キーを押すと「外乱(キック)」を与えて箱をジャンプさせる機能 を実装しました。これは将来的に、AIが「小突かれても耐える」学習をするためのテストも兼ねています。
作成した sim_test.py は以下の通りです。
import pybullet as p
import pybullet_data
import time
import math
# GUIモードで物理エンジンを起動
try:
physicsClient = p.connect(p.GUI)
except Exception as e:
print("Failed to connect to PyBullet GUI:", e)
exit()
# 基本データの読み込み設定
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# 重力の設定(地球と同じ -9.81 m/s^2)
p.setGravity(0, 0, -9.81)
# 床の読み込み
planeId = p.loadURDF("plane.urdf")
# カメラ位置の設定
p.resetDebugVisualizerCamera(cameraDistance=1.0, cameraYaw=0, cameraPitch=-30, cameraTargetPosition=[0,0,0])
# 箱を出現させる
cubeStartPos = [0, 0, 0.5] # 高さ50cmから
angle_45deg = math.pi / 4
# あえて45度傾けて、不安定な状態で出現させる
cubeStartOrientation = p.getQuaternionFromEuler([angle_45deg, angle_45deg, 0])
boxId = p.loadURDF("cube.urdf", cubeStartPos, cubeStartOrientation, globalScaling=0.1)
# 箱の色を赤に変更
p.changeVisualShape(boxId, -1, rgbaColor=[1, 0, 0, 1])
print("------------------------------------------------")
print(" 頂点から落下します!")
print(" [SPACE] キーでジャンプします")
print("------------------------------------------------")
# シミュレーションループ
while True:
p.stepSimulation()
keys = p.getKeyboardEvents()
# スペースキーでジャンプ
if 32 in keys and keys[32] & p.KEY_WAS_TRIGGERED:
print("Jump!")
# 真上に強い力を加える (z軸方向に110N)
p.applyExternalForce(boxId, -1, forceObj=[0, 0, 110], posObj=[0, 0, 0], flags=p.LINK_FRAME)
# ランダムな回転トルクも加える
p.applyExternalTorque(boxId, -1, torqueObj=[5, 5, 0.1], flags=p.LINK_FRAME)
time.sleep(1./240.) # 物理演算速度の調整
# 接続の切断
p.disconnect()
こだわったポイント
-
*p.loadURDF : 標準のキューブモデルを読み込みつつ、getQuaternionFromEuler を使ってあえて45度傾けました。これで着地時の挙動が複雑になります。
-
p.applyExternalForce: ここが今回の肝です。任意のタイミングで外力を加える関数で、今回は「ジャンプ」として実装しましたが、本来は「外乱」の再現に使います。
実行結果:箱、跳ねる
スクリプトを実行してみました。
python sim_test.py
赤い箱が角から地面に落ちて、ゴロンと転がります。 ここで [SPACE]キー を押すと…
箱がピョンと跳ね上がり、回転しながら不規則に転がっていきました。 単純な箱ですが、重力、摩擦、慣性モーメント、そして衝突判定といった物理演算が正しく機能していることを確認できました。
まとめと次回予告
今日はPCの中に「重力のある世界」を構築し、Pythonから物理エンジンを制御して物体に力を加えるところまで実装できました。
しかし、今の「赤い箱」はただの積み木です。リアクションホイールもモータもついていないため、倒れたら自力で起き上がることはできません。
次回 (Day 3) の予定: いよいよハードウェア設計に入ります。 Fusion 360 を使い、実機の寸法(2804モータ)に基づいた 「3軸リアクションホイール」 を設計し、その3DデータをこのPyBulletの世界に召喚しようと思います。
アドベントカレンダー参加中
STM32×AIで「3軸倒立振子」を作る25日間(ひとりアドカレ)Advent Calendar 2025