Isaac Sim OmniGraph 入門
「Isaac Sim」の「OmniGraph」についてまとめました。
・Isaac Sim 5.1
1. Isaac Sim OmniGraph
NVIDIAのロボットシミュレータ「Isaac Sim」には、「OmniGraph」というビジュアルプログラミング機能があります。
「OmniGraph」では、処理を「ノード」として配置し、それらを接続することでロボットの処理フローを構築できます。
例えば次のような処理を作ることができます。
・ロボット制御
・センサー処理
・ROS2通信
・シミュレーションイベント処理
「Isaac Sim」では
・ROS / ROS2 Bridge
・センサーアクセス
・コントローラ
・データ生成
などの多くの機能が「OmniGraph」を基盤として動いています。
コードを書かなくてもノードを接続するだけでロボット処理パイプラインを構築できるのが特徴です。
2. OmniGraph の基本構造
「OmniGraph」は主に次の3つの要素で構成されています。
2-1. Node(ノード)
処理の単位です。
例えば次のようなノードがあります。
・Controller
・Sensor
・ROS2 Publish
・Transform
2-2. Port(ポート)
ノードの入力・出力です。
・input
・output
ノードはポートを通してデータを受け渡します。
2-3. Connection(接続)
ノード同士を接続してデータフローを作ります。
Node A → Node B → Node C
このようにして処理パイプラインを構築します。
3. OmniGraph と Action Graph
「Isaac Sim」では、「Action Graph」という言葉もよく登場します。
関係は次の通りです。
OmniGraph(フレームワーク)
└ Action Graph(イベントグラフ)
「Action Graph」は
・Tickイベント
・キーボード入力
・シミュレーションイベント
4. Action Graph の作成
Jetbotロボットを制御するための「Action Graph」を作成してみます。
4-1. 環境の準備
(1) 新しいステージで、メニュー「Create → Physics → Ground Plane」を選択。
(2) 下端の「Content」タブの「Isaac Sim/Robots/NVIDIA/Jetbot/jetbot.usd」をステージ上にドラッグ&ドロップ。
(3) ステージ上の「jetbot.usd」をドラッグして、地面のすぐ上に配置。

4-2. グラフの構築
(1) メニュー「Window → Graph Editors → Action Graph」を選択。
(2) 「New Action Graph」をクリック。
4-3. ノードの配置と接続
(1) 検索バーでノード名を検索して、以下のノードを配置。
・On Playback Tick
シミュレーションが Play 中のあいだ、毎フレーム 1 回ずつ実行信号を出すノード。
・Articulation Controller
ロボットの関節(ジョイント)に位置・速度・トルクなどの指令を送ってロボットを動かすノード。
・Differential Controller
差動二輪ロボットの前進速度と旋回速度から、左右車輪の角速度を計算するノード。
・Constant Token
固定の文字列(token)を出力するノードで、ジョイント名などの識別子を渡すときに使う。
・Make Array
複数の値をまとめて配列(Array)として出力するノードで、複数のジョイント名などを1つにまとめるときに使う。

(2) 「Differential Controller」を選択し、「Property」で以下を設定。
・wheelDistance : 0.1125
・wheelRadius : 0.03
・maxAngularSpeed : 0.2

(3) 「Articulation Controller」を選択し、「Property」で以下を設定。
・robotPath : /World/jetbot

「Add Target」で「jetbot」を選択することもできます。
(4) 「Constant Token」を選択し、「Property」に以下を設定。
・Value : left_wheel_joint

・Value : right_wheel_joint

(5)「Make Array」を選択し、「Property」に以下を設定。
・arraySize : 2
・arrayType : token[]

(6) ノードを以下のように接続。

4-4. シミュレーションの実行
(1) 再生ボタン(▶)を押す。
(2) 「Differential Controller」を選択。
(3) Propertyで「angular」か「linear velocity」の値を変更。
シミュレータのJetbotが動きます。
・Desired Linear Velocity
ロボットを前後方向にどれくらいの速さで動かしたいか(m/s)
・Desired Angular Velocity
ロボットをどれくらいの速さで回転させたいか(rad/s)

5. PythonからのAction Graph操作
5-1. PythonからのAction Graph操作
「Action Graph」は GUI だけでなく、Python API から生成・編集することも可能です。
Python では主に `omni.graph.core` に含まれる Controller API を利用して 「Action Graph」を操作します。この API を使うことで、次のような処理をプログラムから行えます。
・Action Graph の作成
・ノードの追加
・ノード属性の設定
・ノード同士の接続
・グラフ構造の編集
この仕組みにより、GUI 上で行う「Action Graph」の構築作業を Python スクリプトとして再現・自動化できます。
5-2. PythonからAction Graphを操作する用途
Python を用いた「Action Graph」操作は、主に次のような用途で利用されます。
・シミュレーション環境の自動セットアップ
・多数のロボットを含むグラフの一括生成
・実験条件ごとのグラフ構成の切り替え
・GUI 操作のスクリプト化
・大規模シミュレーションの再現性確保
特に、強化学習やロボットシミュレーションの大規模実験では、環境構築を Python スクリプトで完全に自動化することが一般的です。
6. 主要なノード一覧
6-1. Event Source Nodes(イベント発火ノード)
「Action Graph」は イベント駆動型で、必ずこのノードから実行チェーンが始まります。
・代表ノード
・On Tick
・On Playback Tick
・On Keyboard Input
・On Impulse Event
・On Object Change
・On Custom Event
・特徴
・execution input を持たない
・execution output から処理が開始
・例
On Keyboard Input → Branch → WritePrimAttribute
6-2. Flow Control Nodes(制御フロー)
プログラムの if / loop / sequence の役割。
・代表ノード
・Branch
・Flip Flop
・For Each
・For Loop
・Gate
・MultiGate
・Sequence
・Delay
・用途例
・キー入力でトグル動作
・複数アクションの順序制御
・条件分岐
Flow Controlノードは複数の execution 出力を持ちます。
6-3. Action / Utility Nodes
イベント処理やロジック計算を行うノード。
・代表例
・Counter
・Timer
・Script Node
・Constant
・Math nodes
・Compare
・Logic nodes
・例
On Tick → Counter → Branch
6-4. Scene / USD 操作ノード
USD Primや属性を操作。
・代表ノード
・Write Prim Attribute
・Read Prim Attribute
・Create Prim
・Delete Prim
・Transform Prim
・Set Visibility
・例
On Tick
↓
WritePrimAttribute (translate)
6-5. Bundle / Attribute Nodes
USD attribute bundle を扱うノード。
・代表
・Bundle Constructor
・Bundle Inspector
・Extract Attribute
・Insert Attribute
・Rename Attributes
・Copy Attributes From Bundles
これは OmniGraph Node Library のカテゴリ。
6-6. Simulation / Isaac Sim ノード
Isaac Sim拡張が追加するノード(重要)。
・Robotics
・Differential Controller
・Articulation Controller
・Joint State
・Robot State
・Sensors
・Camera
・Lidar
・IMU
・Contact Sensor
・ROS / ROS2
・ROS2 Publish
・ROS2 Subscrib
・ROS2 Clock
・Replicator / Synthetic Data
・Annotator nodes
・Writer nodes
6-7. IO / External Integration ノード
外部装置やIO信号と接続。
・例
・On IO Change
・Read IO
・Write IO
・用途
・PLC信号
・robot controller IO
6-8. Script Node
Pythonコードを直接実行。
・実装関数
startup(db)
compute(db)
cleanup(db)Action Graph内でカスタム処理を実装できます。
6-9. 公式ノードリスト
・OmniGraph Node Library : 全ノードカテゴリ
・Action Graph Concepts : イベントノード / flow control 解説
・Isaac Sim OmniGraph Documentation : Isaac Sim専用ノード
6-10. 実務的なAction Graph構成例
・ロボット制御の例
On Playback Tick
↓
ROS2 Subscribe Twist
↓
Differential Controller
↓
Articulation Controller
・センサー処理例
On Tick
↓
Camera Node
↓
ROS2 Publish
7. 主要なノード
7-1. On Playback Tick
シミュレーションが Play 中のあいだ、毎フレーム1回ずつ実行信号を出すノードです。Articulation Controller など多くのノードは、値を入れただけでは動かず、execIn に実行信号が来て初めて処理されます。

ここでいう「毎フレーム」は、レンダリングフレームです。
つまり On Playback Tick は、
・物理更新
・制御更新
・センサ読取り
・ROS publish
をシミュレーションの進行に合わせて同期実行するための起点になります。
・Subscriber の Exec Out との違い
「ROS2 Subscriber」などには Exec Out があり、メッセージを受け取った時だけ downstream を起動できます。一方「On Playback Tick」は、新しいメッセージが来なくても毎フレーム起動します。
・On Playback Tick
毎フレーム必ず処理したい
例: 制御器、センサ、TF publish、clock publish
・Subscriber.Exec Out
新しいメッセージが来た時だけ処理したい
例: 受信イベントをトリガに1回だけ計算したい処理
7-2. Articulation Controller
ロボットの関節(ジョイント)に位置・速度・トルクなどの指令を送ってロボットを動かすノードです。

・入力
・execIn
実行トリガー。通常は On Playback Tick につないで毎フレーム実行
・targetPrim
対象ロボットの prim。articulation root を持つ prim を指定
・robotPath
ロボットの USD パス。これが設定されている場合は targetPrim は無視
・jointIndices
制御対象ジョイントのインデックス配列
・jointNames
制御対象ジョイントの名前配列
・positionCommand
目標位置。線形は m、回転は rad
・velocityCommand
目標速度。線形は m/s、回転は rad/s
・effortCommand
目標 effort 指令。回転関節ではトルク、直動関節では力
・jointNames と jointIndices の使い分け
どちらか一方を使いますが、実務では「jointNames」を優先することが多いです。理由は、ロボット差し替えや USD 更新でIndex順が変わると壊れやすいからです。
・positionCommand と jointNames または jointIndices の順番と数
「positionCommand」は、「jointNames」または「jointIndices」の順番と数は一致してる必要があります。
jointNames = ["joint2", "joint5"]
positionCommand = [0.1, -0.3]
・同じJointを同時に複数の制御方式で制御しない
同じJointを同時に複数の制御方式では制御できません。
jointNames: ["joint1", "joint2"]
positionCommand: [0.5, -0.2]
velocityCommand: []
effortCommand: []
ただし、ロボット全体で絶対に1種類しか使えないわけではありません。
タイヤ付きアームのように、
・アーム Joint 群 → positionCommand
・車輪 Joint 群 → velocityCommand
のように別の Joint 群へ分けるのは普通です。
・典型的な OmniGraph 構成
典型的な OmniGraph 構成は、次のとおりです。
・On Playback Tick
・ROS2 Subscribe Joint State
・Articulation Controller
/joint_command を subscribe して Articulation Controller に流します。
・/joint_command と sensor_msgs/msg/JointState の組み合わ
「/joint_command」の型として「sensor_msgs/msg/JointState」を使い、そこから
・name → jointNames
・position → positionCommand
・velocity → velocityCommand
・effort → effortCommand
へ渡しています。
つまり「Isaac Sim」側では、「JointState」を状態ではなくコマンドの運搬容器として流用している形です。
・よくあるハマりどころ
(1) 対象 prim が違う
targetPrim または robotPath は、articulation root を持つロボットを指している必要があります。robotPath が埋まっていると targetPrim は無視されます。
(2) 実行トリガーが来ていない
execIn が未接続だと動きません。通常は On Playback Tick を接続します。
(3) Joint順序がずれている
「jointNames」と「positionCommand」の対応順が違うと、別のJointが動きます。
(4) 制御モードに対する物理パラメータが合っていない
position制御では stiffness > 0
velocity制御では stiffness = 0 かつ damping > 0
effort制御では joint drive を使わないか、stiffness = 0 かつ damping = 0
が基本です。
(5) 同Jointに複数モードを同時適用している
同じJointに position と effort を同時に入れないことです。
7-3. Differential Controller
差動二輪ロボットの前進速度と旋回速度から、左右車輪の角速度を計算するノードです。
大事なのは、Differential Controller 自体は車輪を直接動かすノードではないことです。役割はあくまで 車体コマンドを車輪コマンドへ変換することで、実際に joint drive へ送るのは Articulation Controller 側です。
・入力
・wheelRadius
車輪半径。単位は m。
・wheelDistance
左右車輪の距離。単位は m。
・maxLinearSpeed
車体の最大線速度。m/s。
・maxAngularSpeed
車体の最大角速度。rad/s。
・maxWheelSpeed
車輪角速度の上限。rad/s。
・maxAcceleration
線加速度上限。m/s²。
・maxDeceleration
線減速度上限。m/s²。
・maxAngularAcceleration
角加速度上限。rad/s²。
これらが 0.0 のときは、公式には「not set」とされています。
・典型的な OmniGraph 構成
典型的な OmniGraph 構成は、次のとおりです。
・On Playback Tick
・Differential Controller
・Articulation Controller

・よくあるハマりどころ
(1) wheelRadius と wheelDistance が間違っている
この 2 つが違うと、同じ linear velocity / angular velocity を入れても、期待通りに進みません。公式チュートリアルでも JetBot 用に具体値 wheelDistance=0.1125、wheelRadius=0.03 を設定しています。
(2) Articulation Controller 側の jointNames が違う
Differential Controller は左右車輪速度を出すだけです。その値をどの joint に適用するかは Articulation Controller 側なので、left_wheel_joint / right_wheel_joint などの名前がずれていると動きません。
(3) 車輪 joint を velocity 制御できるようにしていない
差動二輪では通常、車輪 joint は velocity control を前提にします。関連ドキュメントでは velocity control 向けに drive 設定を整えることが案内されています。
(4) On Playback Tick につないでいない
Differential Controller も実行トリガーが必要です。
7-4. Isaac Read Simulation Time
シミュレーション開始から何秒経ったか、その時刻を 他ノードへ渡すノードです。
感覚的には、
・On Playback Tick = 「毎ステップ動け」
・Isaac Read Simulation Time = 「その時のシミュレーション時刻はこれ」
という役割分担です。

・出力
・simulationTime
現在のシミュレーション時刻。単位は秒
・Isaac Read System Time との違い
似た名前で混乱しやすいですが、違います。
・Isaac Read Simulation Time
シミュレーション内の時間を返す
・Isaac Read System Time
実機 PC 側のシステム時刻を返す
・主な用途
・ROS2 の /clock を publish するとき
・TF の timestamp を simulation time にしたいとき
・センサメッセージの header stamp を simulation time で揃えたいとき
・複数ノードで同じシミュレーション基準時刻を共有したいとき
7-5. ROS2 Subscribe
ROS 2トピックからメッセージを受け取り、その内容を OmniGraph 内の値として取り出すノードです。
・専用 ROS2 Subscribe ノード
例: ROS2 Subscribe Joint State、ROS2 Subscribe Twist、ROS2 Subscribe AckermannDrive、ROS2 Subscribe Clock
・汎用の ROS2 Subscribe ノード
任意のROS2メッセージ型を指定して subscribe
Play 中だけ有効です。

・入力
・execIn
実行のトリガー。これが来たタイミングで subscribe
・context
ROS2 Context のハンドル
・messagePackage
メッセージのパッケージ名
・messageSubfolder
メッセージのサブフォルダ名(通常は msg)
・messageName
メッセージ名
・topicName
トピック名
・nodeNamespace
ROS2 ノードの名前空間
・qosProfile
QoS 設定文字列
・queueSize
keep last のときのキューサイズ
・execIn と execOut
・execIn
ノード自身を評価するトリガーです。多くの構成では On Playback Tick や On Physics Step につなぎ、毎フレーム / 毎物理ステップ subscriber を評価します。
・execOut
新しいメッセージ受信後や、subscriber の評価完了後に downstream を実行するための出力として使えます。
・ROS2 Context との関係
ROS2 Publish は単体でも見えますが、普通は ROS2 Context と一緒に使います。
意味としては、
・ROS2 Context = どの ROS2 世界に接続するか
・ROS2 Subscribe = そこからどのトピックをsubscribeするか
になります。
・On Playback Tick と On Physics Step
どちらも subscriber の上流トリガーに使えますが、意図が少し違います。
・On Playback Tick
Play中、レンダリングフレーム毎
・On Physics Step
Play中、シミュレーションステップ毎
RL controller のチュートリアルでは、joint state publish / subscribe と Articulation Controller を On Physics Step で動かす構成が示されています。より制御ループを物理更新に合わせたいときはこちらが向きます。
・よくあるハマりどころ
(1) Play していない
ROS2 Bridge のノードは Play 中だけ有効です。
停止中だとsubscribeされません。
(2) execIn が来ていない
topicName や型を設定しても、execIn が来なければ publish されません。普通は On Playback Tick につなぎます。
(3) メッセージ型指定が違う
トピック名は合っていても、publisher が geometry_msgs/msg/Twist、subscriber が sensor_msgs/msg/JointState を期待していたら受け取れません。
(4) topic 名や namespace の勘違い
/joint_command と joint_command の違い、namespace の影響で、意図したトピックを読めていないことがあります。
(5) QoS 不一致
ROS2 ではよくある原因です。
7-6. ROS2 Publish
OmniGraph 内で作ったデータを、任意のROS2トピックへ publish するノードです。
・専用 ROS2 Publisher ノード
例: ROS2 Publish Clock、ROS2 Publish Joint State、ROS2 Publish Transform Tree、ROS2 Publish Image
・汎用の ROS2 Publisher ノード
任意のメッセージ型を指定して publish
Play 中だけ有効です。

・入力
・execIn
実行トリガー。これが来たタイミングで publish
・context
ROS2 Context のハンドル
・messagePackage
メッセージのパッケージ名
・messageSubfolder
メッセージのサブフォルダ名(通常は msg)
・messageName
メッセージ名
・topicName
トピック名
・nodeNamespace
ROS2 ノードの名前空間
・qosProfile
QoS 設定文字列
・queueSize
keep last のときのキューサイズ
・ROS2 Context との関係
ROS2 Publish は単体でも見えますが、普通は ROS2 Context と一緒に使います。
意味としては、
・ROS2 Context = どの ROS2 世界に接続するか
・ROS2 Publish = そこへ何を publish するか
になります。
・QoS と queueSize
queueSize は、送信が追いつかないときにどれだけバッファするかです。
ただしこれは history QoS が keep last のときだけ有効で、qosProfile によって上書きされることがあります。
・高頻度データで多少落ちてもよい → 小さめ queue
・取りこぼしを減らしたい → 適切な QoS 調整
・よくあるハマりどころ
(1) Play していない
ROS2 Bridge のノードは Play 中だけ有効です。
停止中だとpublishされません。
(2) execIn が来ていない
topicName や型を設定しても、execIn が来なければ publish されません。
普通は On Playback Tick につなぎます。
(3) メッセージ型指定が違う
messagePackage, messageSubfolder, messageName の組み合わせがずれると publish できません。たとえば sensor_msgs/msg/JointState なら sensor_msgs / msg / JointState です。
(4) topic 名や namespace の勘違い
nodeNamespace は node 側の namespace で、結果として topic 名にも影響します。意図した topic に出ているか ros2 topic list で確認すると早いです。
(5) QoS 不一致
ROS2 ではよくある原因です。
7-7. ROS2 Camera Helper
Isaac Sim のカメラから ROS 2 用の画像系パイプラインを自動で組むための専用ノードです。
カメラを ROS 2 に流すには、本来は次のような要素が必要です。
・カメラ Prim の指定
・Render Product の生成
・画像や深度の取得
・topic 名の設定
・frame_id の設定
・時刻 stamp の付与
・必要なら segmentation や bbox などの synthetic data 出力
ROS2 Camera Helper はこれらをまとめて処理してくれます。

・入力
・context
ROS2 contextのハンドル(0 ならデフォルトの global context を使う)
・enableSemanticLabels
semantic label の publish を有効化。
instance_segmentation、semantic_segmentation、bbox_2d_tight、bbox_2d_loose、bbox_3d のときだけ意味がある
・enabled
Camera Helper 自体の有効/無効
・execIn
実行トリガー
・frameId
ROS2 メッセージの frame_id(既定値は sim_camera)
・frameSkipCount
何フレームおきに publish するか
(0 なら毎フレーム、1なら1 フレームおき)
・nodeNamespace
ROS2 ノードの名前空間
・qosProfile
QoS 設定文字列
・queueSize
keep last のときのキューサイズ
・renderProductPath
キャプチャ対象の Render Product のパス
・resetSimulationTimeOnStop
True なら Stop 時に simulation time をリセット
・semanticLabelsTopicName
semantic label 用の トピック 名
・stereoOffset
ステレオ baseline を画像平面の x/y 成分で与える値(既定値は [0, 0])
・topicName
センサデータのトピック名(既定値は rgb)
・type
publish するデータ種別(既定値は rgb)
・useSystemTime
True ならシステム時間、False ならシミュレーション時間を メッセージタイムスタンプに使う
・viewport
非推奨
・type
ROS2 Camera Helper では、何を publish するかを「type」で選びます。
・RGB image
・depth image
・point cloud
・instance segmentation
・semantic segmentation
・bbox 2d tight
・bbox 2d loose
・bbox 3d
・Camera Helper は1ノードで全部ではない
ROS2 Camera Helper は便利ですが、実際には Render Product や CameraInfo Helper、TF publish などと一緒に使うことが多いです。
・よくあるハマりどころ
(1) Render Product が違う
renderProductPath が意図したカメラに対応していないと、違う映像や何も出ない状態になります。ノードは Render Product を前提に動きます。
(2) 上流トリガーが違う
On Playback Tick と On Physics Step で publish 周期が変わります。送信タイミングは event node に依存します。
(3) timestamp が想定と違う
/clock、use_sim_time、useSystemTime の整合が取れていないと、RViz や他ノードでズレて見えます。
(4) QoS 不一致
ROS2 ではよくある原因です。
(5) publish しすぎて重い
高解像度 RGB、depth、pointcloud、segmentation を全部高頻度で出すと重くなります。gate で頻度を落とすのが有効です。
