pybulletの公式サンプル129個を全部解説する

# はじめに
お疲れ様です。秋並です。

pybulletはpythonで使用できる物理エンジンの一つで、ロボティクスや機械学習などの分野に使用されています。

pybulletでは、以下のリポジトリにて公式のサンプルが用意されています。

https://github.com/bulletphysics/bullet3/tree/master/examples/pybullet/examples

<br>一方で、pybulletのサンプルコードは129個(pythonコード128個+inoコード1個)と大量に存在します。

また、サンプルコードについて解説しているようなサイトも見当たりませんでした。
<br>そこで、せっかくなので私が

**全ての公式サンプルを解説**

したいと思います。

<br>pybulletの公式サンプルコードは学習に役立つものもたくさんあるため、この記事を読むことで、

::: note
pybulletで何ができるかを知ることができる
:::

と思っているので、是非ご覧ください。

# pybulletの公式サンプル一覧
以下が公式サンプルの一覧になります。

::: note
解説記事が投稿されているコードは、コード名をクリックするとその記事に飛ぶようになっています。
:::
| コード名 | 概要 |実行動画|
|--|--|--|
|[addPlanarReflection.py](https://qiita.com/akinami/items/f28383f72917753c5295)|ray機能を使って、マウスでクリックしたオブジェクトの色を変える|![addPlanarReflection.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/8fc2eaf4-5b0f-bbb4-27c2-931cb8fb73f0.gif)|
|[batchRayCast.py](https://qiita.com/akinami/items/ea690df8bd9a33605bf2)|ray機能を使用した2DLiDARの模擬|![batchRayCast.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/0181075e-b3ba-a99a-3cd6-a7f3536a7395.gif)|
|[biped2d_pybullet.py](https://qiita.com/akinami/items/7f41d0501a0d867a06dd)|ランダムな凸凹した地形を生成し、その上に二足歩行ロボットを生成する|![biped2d_pybullet.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/88fe330e-5565-9967-1845-0f5f0f74765b.gif)|
|[changeDynamicsMass.py](https://qiita.com/akinami/items/525d43d7481b475722a5)|2つのオブジェクトを読み込み、片方の動力学特性を変更する|![changeDynamicsMass.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/7c71f599-8cd3-08d1-1cb2-f91ec99c2b3e.gif)|
|[changeTexture.py](https://qiita.com/akinami/items/12f3409d8e9bd16cf21d)|オブジェクトのテクスチャを動的に変更する|![changeTexture.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/8f609c6e-2ed3-7115-dd0d-4b9bb6f30582.gif)|
|[collisionFilter.py](https://qiita.com/akinami/items/555f4c6fbfad1472d5af)|読み込んだ「床」と「ボックス」間の衝突を有効化する|![collisionFilter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/fbd847b5-9713-8918-bca4-8218321686fe.gif)|
|[commandLogAndPlayback.py](https://qiita.com/akinami/items/890157e1d97d117a6702)|実行したシミュレーションデータをログファイルに保存したのちに、そのログファイルを読み込みシミュレーションを再現する|![commandLogAndPlayback.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/1bd3718b-a003-3a09-0391-5da792929a1f.gif)|
|[configureDebugVisualizer.py](https://qiita.com/akinami/items/e6ab3ef92efef65f6261)|光源の位置を時間とともに変化させる|![configureDebugVisualizer.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/5c065cec-0088-e508-49b8-0417151d4c73.gif)|
|[constraint.py](https://qiita.com/akinami/items/feeaecf80fb117f91f96)|オブジェクトが回転しながら、直線上を行ったり来たりする|![constraint.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/4cd008d1-1302-a046-8821-42a46877222b.gif)|
|[contactFriction.py](https://qiita.com/akinami/items/46d0c73775392305efa6)|ボックスに生じる法線力と横方向の摩擦力を計算し、（ターミナル上に）表示|![contactFriction.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/3f9f67ac-2854-e8e7-3fca-8ea989e9ca93.gif)|
|createMesh.py|複数のオブジェクトを生成し、追従するカメラで撮影する|![createMesh.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/6e484659-0647-8afe-ada9-4353f017835c.gif)|
|createMultiBodyBatch.py|大量のオブジェクトを計算効率が最適化されたモードで生成する|![createMultiBodyBatch.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/26c85c4e-96a2-000a-cfc4-66829ff1f975.gif)|
|createMultiBodyLinks.py|複数の物体に、プロパティを設定し生成する| 後日gif画像追加予定 |
|createObstacleCourse.py|複数のオブジェクトを生成し、追従するカメラで撮影する|後日gif画像追加予定|
|createSphereMultiBodies.py|寺院のモデル内に、球体と立方体の衝突物体を生成する|![createObstacleCourse.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/03072cc9-7e91-a331-c625-6f0f5fdd6556.gif)|
|createTexturedMeshVisualShape.py|ray機能を使用して、マウスでクリックした物体の色を変える|後日色が変わることが分かる動画に変更予定![createTexturedMeshVisualShape.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/d9c624f5-4141-47ee-9907-75d94d62078c.gif)|
|[createVisualShape.py](https://qiita.com/akinami/items/53e0dee2208ff8c33ef4)|オブジェクトを生成する|![createVisualShape.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/c76d55e8-5697-fb3b-a852-e9cf7790b2df.gif)|
|createVisualShapeArray.py|複数のオブジェクトを生成する|![createVisualShapeArray.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/7f4215d0-2f33-393f-ac3c-37c913658e00.gif)|
|[debugDrawItems.py](https://qiita.com/akinami/items/003d35103f7613f95f19)|kukaのiiwaロボットアームを動かし、先端に「tip」と表示する|![debugDrawItems.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/416e3001-9d2d-f5f6-68a7-6ac5d09af50f.gif)|
|deformable_add_remove_objects.py|布を生成する<br>（マウスで操作すると布と分かりやすい）|後日、布のオブジェクトと分かりやすいgifに変更予定![deformable_add_remove_objects.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/3d9a0f7e-eb08-9c63-ff9a-f7bc0e4a8e72.gif)|
|deformable_anchor.py|ボックスと布を生成し、布の各頂点を表示する|後日、布と分かりやすいgif画像に変更予定 ![deformable_anchor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/d5ffdcc3-d336-f28b-5bdd-b119f22fddda.gif)|
|[deformable_ball.py](https://qiita.com/akinami/items/40a43567df51beda74cf)|剛体のボックスとソフトボディのボールを生成する|![deformable_ball.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/0fe60394-c719-f8ad-c7cb-0ca4e92a81e9.gif)|
|[deformable_torus.py](https://qiita.com/akinami/items/e3f7a5e379d4728fd103)|剛体のボックスとソフトボディのドーナツ状の物体を生成する|![deformable_torus.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/071e74fb-1e5e-f8ee-22b8-3acf764e316b.gif)|
|draw_frames.py|ボックスの様々な参照フレームを可視化する|![draw_frames.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/bb3f38db-18bf-0285-2dcd-914c0ac8746c.gif)|
|dumpLog.py|ログファイルを読み取り、その内容を解析する|pybulletは使用されません。<br>そのまま実行すると、`FileNotFoundError: [Errno 2] No such file or directory: 'log.bin'`と表示され実行できないため、適当なログファイルを用意する必要があります|
|dumpVrLog.py|VRシミュレーションのログファイルを読み取り、ボタンの状態を解析する|pybulletは使用されません。<br>そのまま実行すると、`FileNotFoundError: [Errno 2] No such file or directory: 'data/example_log_vr.bin'`と表示され実行できないため、適当なログファイルを用意する必要があります|
|eglRenderTest.py|EGLプラグインを使用し、取得したカメラ画像をオフスクリーンレンダリングする|`DIRECT`モードで実行されるため、gui画面は表示されません|
|experimentalCcdSphereRadius.py|地面と球状のオブジェクトを複数生成し、接触情報を（ターミナル上に）表示する [^1] |後日動画変更予定![experimentalCcdSphereRadius.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/3ad0a3f9-a50f-38db-f93f-493b8c453d1c.gif)|
|externalTorqueControlledSphere.py|球体をキーボードでコントロールし、球体に追従するようにカメラを動かす|後日動画変更予定![externalTorqueControlledSphere.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/5acb46e9-3a24-4a70-b5eb-ba290e78afba.gif)|
|fileIOPlugin.py|外部プラグインを使用し、ZIPファイルからオブジェクトをロードし表示する|![fileIOPlugin.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/70e679ed-44b5-9b39-812f-eaf0754713f8.gif)|
|forcetorquesensor.py|力/トルクセンサを有効化し、ジョイントの状態を取得する|guiモードで起動しますが、一瞬で終了します|
|frictionCone.py|複数のボールに異なる初速度を設定し、軌道を確認する|後日、複数のボールの動画に変更予定![frictionCone.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/9d59ae70-2f0b-8026-2445-49b0c5338d4d.gif)|
|getAABB.py|オブジェクトの軸平行境界ボックス（AABB）を表示する|![getAABB.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/710f5489-0697-a5ff-445d-e9f496e1e402.gif)|
|getCameraImageTest.py|様々なレンダリングで画像を撮影し、結果をmatplotlibで表示する|![getCameraImageTest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/fe380672-4728-0c23-abd5-1424d94d2876.gif)|
|getClosestPoints.py|球とボックス間の最短距離を可視化する|![getClosestPoints.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/d0d5b068-51ee-25b0-b4b0-230ceece9734.gif)|
|[getTextureUid.py](https://qiita.com/akinami/items/04e529a6b4b4a267e3f6)|床のテクスチャを変更して、一定時間経過したらテクスチャを元に戻す|![getTextureUid.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/60ae7aaf-e431-41c3-ec96-edf469dde5f2.gif)|
|graphicsClient.py|**別ターミナルで、grapcServer.pyを起動した状態で実行したください** <br><br>`p.GRAPHICS_SERVER_TCP`モードでpybulletを起動し、ロボットを生成する|実行内容が `graphicsServer.py`で起動したgui画面に反映されます|
|graphicsServer.py|`GRAPHICS_SERVER`モードでpubulletを起動する|![graphicsServer_grpcClient.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/756f7698-6fb4-3e46-8063-6a3d13252716.gif)|
|grpcClient.py|ローカルまたはリモートのGRPCサーバーに接続し、ロボットを生成する|私の環境では、`AttributeError: module 'pybullet' has no attribute 'GRPC'`と表示され実行できませんでした|
|grpcServer.py|GRPCプラグイン使用してシミュレーションを実行する|私の環境では、`Cannot load grpcPlugin`と表示され実行できませんでした|
|hand.py|VR用のハンドコントローラから得られたセンサデータをもとに、シミュレーション上のハンドを制御する|実機のハンドコントローラが必要ですが、私が所持していないため実行できていません。|
|hand.ino|**Arduino用のファイル。** ピンから取得した値を表示する| -
|heightfield.py|3種類の方法で複雑な地形を生成する [^2] |![heightfield.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/fd031bfe-ccb4-5bcb-3520-e87ad00137e3.gif)|
|[hello_pybullet.py](https://qiita.com/akinami/items/3cfd9c6dbeb996742035)|オブジェクトを生成する|![hello_pybullet.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/96de376c-f720-cc57-ce5a-a20de7a9a74d.gif)|
|humanoid_benchmark.py|ヒューマノイドを読み込んで、シミュレーションを実行し、パフォーマンスのベンチマークを`pybullet_humanoid_timings.json`に出力する。[^3]|`DIRECT`モードで実行されるため、gui画面は表示されません|
|humanoid_knee_position_control.py|guiのスライダーを使用して、ヒューマノイドロボットの膝を制御する|![humanoid_knee_position_control.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/0fc45388-88e8-c3af-f9e8-f1dd3d02def2.gif)|
|humanoid_manual_control.py|guiスライダーを使用して、ヒューマノイドロボットの各関節を制御する|![humanoid_manual_control.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/580f158c-9ba7-741a-839c-07df7ac563c7.gif)|
|humanoidMotionCapture.py|5種類のPD制御により、ヒューマノイドロボットの動作を制御する|![humanoidMotionCapture.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/01ecabb0-831f-8835-1985-eb933f8b1292.gif)|
|ik_end_effector_orientation.py|逆運動学を使用して、kukaのiiwaロボットアームのエンドエフェクタを指定した位置/姿勢に制御する|後日gif画像追加予定|
|integrate.py|ボックスを1秒間落下させ、ボックスの最終位置/姿勢をターミナルに表示します|guiモードで起動しますが、一瞬で終了します|
|internalEdge.py|床の上に小さなオブジェクトを生成し、接触情報を（ターミナル上に）表示する [^4] |![internalEdge.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/ddfd1181-40e5-431d-e9c7-fb2bb61e063e.gif)|
|inverse_dynamics.py|トルク制御を実行し、その結果をmatplotlibで描画する<br>※コードにバグがあるのか、私の環境ではmatplotlibの結果が正常に表示されていません|![inverse_dynamics.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/b8b10303-4aad-b458-e2d9-b3fd7d0a43dd.gif)|
|inverse_kinematics_husky_kuka.py|「移動ロボットhusky」に「kukaのiiwaロボットアーム」を搭載したモデルを使用して、逆運動学でロボットアームの手先が円を描くように制御する|![inverse_kinematics_husky_kuka.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/a6703132-50ff-fe41-72a0-9e2d0fc462da.gif)|
|inverse_kinematics_pole.py|Sawyerロボットアームを制御し、軌道を描画する|![inverse_kinematics_pole.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/cc20f30c-3622-db22-3e58-8654666ad649.gif)|
|inverse_kinematics.py|kukaのiiwaロボットアームで、逆運動学を使用して、手先が円を描くように制御する|![inverse_kinematics.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/5ae330e0-5e10-434c-9112-8e694664ef68.gif)|
|inverted_pendulum_tendon_actuation.py|弦で作動する逆振り子の PID 制御を行い、制御結果をmatplotlibで描画する|![inverted_pendulum_tendon_actuation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/d98233f9-b6b9-873f-a9c0-832dcef42cd4.gif)|
|jacobian.py|ヤコビ行列を計算する|gui画面は表示されず、計算結果がターミナル上に表示されます|
|jointFrictionAndMotor.py|トルク制御されているドアのパラメータをguiスライダーで動的に変更する|![jointFrictionAndMotor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/78ca6de8-73f0-d508-a68b-d6efb5c6f0d1.gif)|
|jointFrictionDamping.py|速度制御されているモータのパラメータを、guiスライダーで動的に変更する|![jointFrictionDamping.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/040f48b7-77f3-2953-7778-dc85460bdfc3.gif)|
|kuka_grasp_block_playback.py|「kukaのiiwaロボットアームで物体をつかむ」シミュレーションデータが保存されたログファイルを読み込み、guiスライダーでステップごとにシミュレーションを再現する|![kuka_grasp_block_playback.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/312ab417-0563-21ed-c88e-12994d98b2ad.gif)|
|kuka_with_cube_playback.py|`kuka_with_cube.py`で実行されたシミュレーションのログファイルを読み込み、同じシミュレーションを再現する|後日gif画像追加予定|
|kuka_with_cube.py|逆運動学を使用し、エンドエフェクタの位置を周期的に更新する<br>実行したシミュレーションはログファイルに保存される |後日gif画像追加予定<!--![kuka_with_cube.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/af9e8c09-3afa-477b-30fe-cbb4a13f226d.gif) -->|
|[load_soft_body.py](https://qiita.com/akinami/items/0eb546a037382b3dd42f)|剛体のボックスと、ソフトボディのウサギを生成する|![load_soft_body.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/98c27431-aa85-8ff4-b0d2-b5d45851cda8.gif)|
|[loadingBench.py](https://qiita.com/akinami/items/e1bdd576e8c7915624ce)|性能評価のベンチマークに使用されるような大規模なオブジェクトを生成する|![loadingBench.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/45df1c68-c8c0-afb7-2de1-674f5face27e.gif)|
|logMinitaur.py|四足歩行ロボットを生成し、ロボットの状態を`LOG00048.TXT`に出力する|gif画像追加予定|
|[manyspheres.py](https://qiita.com/akinami/items/72ada10eb953b2423234)|複数の球体を生成し、guiスライダーで重力方向を変更する|![manyspheres.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/181d55db-02d4-1466-9122-0d29fe27b10f.gif)|
|mimicJointConstraint.py|ジョイント間で設定された歯車比で、回転を同期させる|![mimicJointConstraint.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/46de00d3-e3e6-98a3-e601-2d7b26d298cd.gif)|
|minitaur_evaluate.py|四足歩行ロボットminitaurの動作を評価するための関数がまとめられたコード|`minitaur_test.py` で使用されるコードであるため、単体では動作しません|
|minitaur_test.py|四足歩行ロボットminitaurを制御し、その制御結果を評価する|![minitaur_test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/648f6506-401a-7372-6f4c-b16c88ee183f.gif)|
|minitaur.py|四足歩行ロボットminitaurをモデル化し制御するためのクラスが定義されているコード|`minitaur_test.py` で使用されるコードであるため、単体では動作しません|
|[motorMaxVelocity.py](https://qiita.com/akinami/items/5bcc37ad02e4440a4552)|カートポール制御を行い、制御時の「位置」と「速度」をターミナル上に表示する|![motorMaxVelocity.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/d815cb0c-fe6d-fc61-0467-900511643613.gif)|
|otherPhysicsEngine.py|PhysXとよばれるbulletとは別の物理エンジンを使用し、ドミノ倒しをシミュレーションする|設定が足りていないのか、私の環境では`AttributeError: module 'pybullet' has no attribute 'PhysX'`と表示され実行できませんでした|
|pdControl.py|複数のpdコントローラでカートポールを制御する。<br>※ guiスライダーでパラメータを動的に変更可能|![pdControl.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/2dfae9e9-b813-4f9f-9da7-8271a5d83dd4.gif)|
|pdControllerExplicit.py|「PDコントローラ」を定義したコード|`pdControl.py`で使用するため、単体では動作しません|
|pdControllerStable.py|「安定したPDコントローラ」を定義したコード|`pdControl.py`で使用するため、単体では動作しません|
|pointCloudFromCameraImage.py|取得した画像情報をもとに、ポイントクラウドを生成する|![pointCloudFromCameraImage.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/83bf4b74-bd30-e2e3-5104-91cc33e48bc0.gif)|
|profileTiming.py|シミュレーションを実行し、タイミングイベントをログファイル`single_step_no_stepsim_chrome_about_tracing.json`に記録する|後日gif画像追加予定|
|[projective_texture.py](https://qiita.com/akinami/items/5787d9db28f60917b51b)|オブジェクトを生成し、テクスチャを適用（なぜかテクスチャ適用部分がコメントアウトされている）|![projective_texture.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/d8c07a74-fd78-e0d3-07da-14f1179a7d15.gif)|
|quadruped_playback.py|ログファイル`LOG00076.TXT`を読み込み、四足歩行ロボットのシミュレーションを再現する|`SHARED_MEMORY`モードで実行されるためguiは表示されません|
|quadruped_setup_playback.py|四足歩行ロボットを読み込み、制約を設定する|`SHARED_MEMORY`モードで実行されるためguiは表示されません|
|quadruped.py|四足歩行ロボットをシミュレーションする|私の環境では、gui画面が表示されませんでした|
|racecar_differential.py|移動ロボットを生成し、guiスライダーで速度などを制御する|![racecar_differential.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/763a1d1a-6e5c-8338-7794-3f0c3a38a9dc.gif)|
|racecar.py|スタジアムの環境に移動ロボットを生成し、guiスライダーで速度などを制御する|![racecar.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/909b00a8-eb20-5f2d-4428-8c3d9aca47a8.gif)|
|[reduced_deformable_cube.py](https://qiita.com/akinami/items/b50cc119fbd5afa9339a)|変形するボックスの上から剛体のボックスを落とす|![reduced_deformable_cube.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/9edacd33-1d75-94e1-7983-133427a42f42.gif)|
|[reduced_deformable_torus.py](https://qiita.com/akinami/items/ec063a89bdefc832e1cb)|変形するドーナツ状のオブジェクトと、剛体のボックスを2台ずつ生成する|![reduced_deformable_torus.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/26d780f9-f204-5bce-3af8-6620dfca82be.gif)|
|renderPlugin.py|r2d2を生成し、追従するカメラで画像を撮影する|![renderPlugin.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/168970f3-4d6e-edbd-5409-14bc209496d4.gif)|
|rendertest_sync.py|複数のレンダリングでミュレーションを実行し、パフォーマンス（FPS）を計測する|私の環境では`AttributeError: module 'tensorflow' has no attribute 'set_random_seed'`と表示され、実行できませんでした|
|rendertest.py|複数のレンダリングでミュレーションを実行し、パフォーマンス（FPS）を計測する<br>matplotlibでリアルタイムの画像を表示する|![rendertest.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/78b20a8a-0152-2612-7cc3-72a5d13485c2.gif)|
|reset_dynamic_info.py|特定の物体の慣性ボックス（慣性モーメントを視覚的に表すボックス）を描画する|私の環境では`AttributeError: module 'pybullet' has no attribute 'DYNAMICS_INFO_REPORT_INERTIA'`と表示され実行できませんでした|
|restitution.py|球体を生成し、guiスライダでパラメータを動的に変更する|![restitution.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/bafe5c39-32ad-46d7-09b8-08eca371abe5.gif)|
|robotcontrol.py|移動ロボットhuskyを生成し、速度制御する|後日gif画像追加予定|
|[rollPitchYaw.py](https://qiita.com/akinami/items/01fc69af131717aeb60f)|guiスライダーでオブジェクトの位置(x,y,z)と姿勢(r,p,y)を動的に変更する|![rollPitchYaw.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/f8989084-bb3a-7c5e-ad02-b83dfd504bf8.gif)|
|rotationalFriction.py|回転するボックスの上に、球体を生成する|![rotationalFriction.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/95cfb0bd-4a0c-30a0-ce43-fd16a3e81dd3.gif)|
|[satCollision.py](https://qiita.com/akinami/items/af864b163008949a966b)|sat（衝突検出アルゴリズム）を有効化した状態でオブジェクトを生成する|![satCollision.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/8f1c2788-c58d-9a13-365b-63a60cf83ac6.gif)|
|saveRestoreState.py|実行したシミュレーションデータをログファイルに保存したのちに、そのログファイルを読み込みシミュレーションを再現する|![saveRestoreState.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/ba952214-ac0b-b228-44fb-7ca0e0c082c8.gif)|
|saveWorld.py|現在のシミュレーションの状態を保存する|`SHARED_MEMORY`モードで起動するため、gui画面は表示されません|
|sceneAabb.py|シミュレーション内のすべてのAABB(軸平行境界ボックス)を計算し、最小値と最大値をターミナルに表示する|後日、gif動画追加予定|
|segmask_linkindex.py|"1"キーを入力すると、セグメントされたピクセルをターミナル上に表示する<br><br>"s"キーをクリックすると、セグメンテーションマスクのオプションを切り替える|後日、gif画像追加予定<br><br>私の環境では、"1"キーを入力すると、`ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()`と表示され終了してしまいました<!--![segmask_linkindex.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/b24bd6e0-8566-c4b2-8e03-2feee8b5a506.gif)-->|
|[shiftCenterOfMass.py](https://qiita.com/akinami/items/71ef0af16bb2417af32b)|質量の位置を中心から移動したボックスを生成する|![shiftCenterOfMass.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/3c47058f-3075-53af-24c8-c56efeeeb6e1.gif)|
|[signedDistanceField.py](https://qiita.com/akinami/items/f0188e6476b755b9f95b)|凹状のボックス内に、アヒルと球体を生成する|![signedDistanceField.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/1645e829-c1ec-a9ec-8ea9-8af34ac227d1.gif)|
|sleeping.py|25台のロボットを生成し、各ステップごとにロボットがスリープ状態にならないように状態を更新する|![sleeping.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/73ab25fa-7d48-36b3-83ab-14daa3f7e4bd.gif)|
|[snake.py](https://qiita.com/akinami/items/d4f71d1abd065b327427)|蛇型ロボットを生成し、キーボードの左右キーで制御する|![snake.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/38e6dbfc-e4e4-ce36-c6bb-bd4adaa69382.gif)|
|[soccerball.py](https://qiita.com/akinami/items/27768b17e504baf294c8)|大きさの異なる球体を8個生成する|![soccerball.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/74d31fcb-9c70-aa81-a050-f6dd0e4de9cb.gif)|
|spherical_joint_limit.py|ジョイントのあるオブジェクトを生成し、guiスライダーで重力方向を変更する|![spherical_joint_limit.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/b527737f-7047-0bb9-4596-e5c70e4fa8ac.gif)|
|switchConstraintSolver.py|物理エンジンの制約ソルバーとしてLCP (Linear Complementarity Problem) のDantzigアルゴリズムを設定し、ボックスを生成する|（以降、qiitaへひと月にアップロードできる画像の上限が100MBである関係で、4月以降に追加します）|
|test_inertia.py|剛体のボックスと、ソフトボディのウサギを生成する||
|test.py|r2d2の位置と姿勢をターミナル上に表示し、5秒経過するとシミュレーションが終了する||
|testPlugin.py|テスト用のプラグインをロードする||
|testrender_egl.py|EGLレンダリングを使用し、リアルタイムでレンダリングしている画像をmatplotlibで描画する||
|testrender_np.py|リアルタイムでレンダリングしている画像をmatplotlibで描画する||
|testrender.py|リアルタイムでレンダリングしている画像をmatplotlibで描画する||
|transparent.py|guiスライダーで球体のオブジェクトの色と透明度を変更する||
|urdfEditor.py|読み込んだurdfファイルを編集し、編集した後のurdfファイルからロボットを生成する||
|userData.py|データの追加/削除、データ同期などのデータの操作を行う|guiモードで起動しないのでgui画面は表示されません|
|vhacd.py|オブジェクトに凸分解（V-HACD, Volumetric Hierarchical Approximate Convex Decomposition）を実施し、その結果を`duck_vhacd.obj`に保存する|`DIRECT`モードで起動するので、gui画面は表示されません|
|video_sync_mp4.py|シミュレーションをmp4形式で出力する||
|vr_kitchen_setup_vrSyncPython.py|VRコントローラのセンサ値をもとに、シミュレーションの制御を行う|実機のハンドコントローラが必要ですが、私が所持していないため実行できていません（以下、vrとつくコードはすべて同様）|
|vr_kuka_control.py| VR コントローラーを使用して、ロボットアームとグリッパーを制御する||
|vr_kuka_pr2_move.py| VRコントローラを使用して、グリッパーを制御する||
|vr_kuka_setup_vrSyncPlugin.py|VR コントローラーによるロボット操作シミュレーションを設定する||
|vr_kuka_setup_vrSyncPython.py|VRコントローラーを使用して、ロボットアームを操作する||
|vr_kuka_setup.py|ロボットアームを使用するための物理シミュレーションの設定を行う||
|vr_racecar_differential.py|移動ロボットを制御する||
|vrEvent.py|VRコントローラーを使って線を描画する||
|vrhand_vive_tracker.py|VRグローブを使用してロボットハンドを制御する||
|vrhand.py|VRグローブを使用してロボットハンドを制御する||
|vrminitaur.py|VRコントローラーを使用してロボットを追跡する||
|vrtracker.py|VRコントローラーを使用して3D空間に線を描画する||
|widows.py|テーブルとロボットアームを生成し、追従するカメラで画像を撮影する||

# さいごに
pybulletはお手軽に使えますが、情報が少ない印象があったのでこの機会にまとめてみました。

みなさんのお役に立てれば幸いです。

# 余談
今回書いた記事で1か月の画像の上限が100MBであること初めて知りました。もうちょっと増やして欲しいと思ったりして、、、笑

[^1]: デフォルトのコードでは、最初の一度だけしか接触情報がターミナルに表示されないようになっています。しかし、最初の時点ではオブジェクトは宙に浮いた状態なので、ターミナル上に`()`のように空のタプルが返されてしまいます（どこにも接触していないため）。そのため、リアルタイムで床との接触情報を表示したい場合は、50行目～52行目の内容を53行目のwhileループ内に入れる必要があります。
[^2]: コード内16行目の`heightfieldSource`を「 `useProgrammatic` `useTerrainFromPNG` `useDeepLocoCSV`」 のいずれかに設定することで対応するモードで地形を生成できます（デフォルトでは、`useProgrammatic`に設定されています）。
[^3]: 出力されたjsonファイルは `about://tracing`で可視化できます。
[^4]: [^1]と同様の理由で、ターミナル上に`()`のように空のタプルが返されてしまうため、リアルタイムで床との接触情報を表示したい場合は、38行目～40行目の内容を41行目のwhileループ内に入れる必要があります。