0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JetbotとIssac Simとの連携

0
Posted at

はじめに

前回の記事で、Waveshare JetBot AI Kit(Jetson Nano)の Docker 環境を ROS 2 Humble に移行しました。今回はその環境を NVIDIA Isaac Sim 5.1.0 とつなぎ、1つの /cmd_vel で実機の JetBot と Sim 上の JetBot を同時に動かすところまで進めます。

JetBot にはホイールエンコーダがないため、実機の位置を Sim に反映させることはできません。そこで、同じ速度指令を実機と Sim に流して並走させる方式にしました。あわせて、通信が途切れたときに実機が暴走しないよう、watchdog ノードも追加します。

環境

項目 内容
PC Ubuntu 24.04、RTX GPU、Isaac Sim 5.1.0(standalone)
PC の ROS 環境 Isaac Sim 内部の Humble ライブラリ + osrf/ros:humble-desktop Docker
ロボット Waveshare JetBot AI Kit(Jetson Nano、JetPack 4.6.x / L4T R32.7.1)
ロボットの ROS 環境 dustynv/ros:humble-pytorch-l4t-r32.7.1 ベースの jetbot_ros コンテナ
通信 Wi-Fi、Fast DDS(UDP のみ)

構成

flowchart LR

subgraph PC["RTX PC(Ubuntu 24.04)"]

T\["操作端末\<br/\>Humble Docker\<br/\>teleop / topic pub"\]

S\["Isaac Sim 5.1.0\<br/\>内部 Humble\<br/\>OmniGraph"\]

end

subgraph J["Jetson Nano(Docker, Humble)"]

W\["cmd\_vel\_watchdog\<br/\>0.5 秒受信なし → 停止"\]

M\["motors\_waveshare"\]

end

T -- "/cmd_vel" --> S

T -- "/cmd_vel(Wi-Fi)" --> W

W -- "/jetbot/cmd_vel" --> M

ポイントは次の3つです。

  • Isaac Sim は Ubuntu 24.04 だと既定で内部 Jazzy を読み込むので、Jetson に合わせて内部 Humble を明示的に選ぶ
  • PC・Isaac Sim・Jetson の3か所で ROS_DOMAIN_ID と Fast DDS の設定ファイルを揃える
  • 実機側は watchdog を挟んで、指令が途切れたら止まるようにする

1. 共通設定(PC・Jetson 共通)

Isaac Sim、PC の Docker、Jetson のコンテナの3か所すべてで、同じ値を使います。

export ROS_DOMAIN_ID=30

export RMW_IMPLEMENTATION=rmw_fastrtps_cpp

export FASTRTPS_DEFAULT_PROFILES_FILE=$HOME/.ros/fastdds.xml

Docker をまたぐと Fast DDS の共有メモリ転送が失敗しやすいため、UDP のみを使うプロファイルにします。

<?xml version="1.0" encoding="UTF-8" ?>

<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">

<transport_descriptors>

\<transport\_descriptor\>

  \<transport\_id\>UdpTransport\</transport\_id\>

  \<type\>UDPv4\</type\>

\</transport\_descriptor\>

</transport_descriptors>

<participant profile_name="udp_transport_profile" is_default_profile="true">

\<rtps\>

  \<userTransports\>

    \<transport\_id\>UdpTransport\</transport\_id\>

  \</userTransports\>

  \<useBuiltinTransports\>false\</useBuiltinTransports\>

\</rtps\>

</participant>

</profiles>

PC で作成したら Jetson にもコピーします。Jetson 側に ~/.ros がないと scp が失敗するので、先にフォルダを作っておきます。

ssh <user>@<JETSON_IP> 'mkdir -p ~/.ros'

scp ~/.ros/fastdds.xml <user>@<JETSON_IP>:~/.ros/fastdds.xml

:::note warn ~/.ros/fastdds.xml が存在しない状態で docker run -v でマウントすると、Docker が同名の空ディレクトリを作ってしまいます。必ずファイルを作ってからコンテナを起動してください。間違えて作られた場合は rmdir ~/.ros/fastdds.xml で削除します。 :::

2. Isaac Sim 5.1.0 を内部 Humble で起動する

Isaac Sim 5.1 の内部 ROS ライブラリは Python 3.11 でビルドされています。そのため、Isaac Sim を起動するターミナルでは、システムの ROS(/opt/ros/*/setup.bash)を source しないでください。

App Selector で起動する(GUI)

export ROS_DOMAIN_ID=30

export FASTRTPS_DEFAULT_PROFILES_FILE=$HOME/.ros/fastdds.xml

cd ~/isaac-sim-standalone-5.1.0-linux-x86_64

./isaac-sim.selector.sh

App Selector の ROS Options で次のように設定し、Isaac Sim Full を START します。

  • ROS Bridge Extension: isaacsim.ros2.bridge
  • Use Internal ROS 2 Libraries: humble

起動したら、Script Editor で次を実行して humble と表示されることを確認します。

import os; print(os.environ.get("ROS_DISTRO"))

:::note info App Selector は 5.1 で非推奨になっています。スクリプトで起動する場合は、ROS_DISTRO=humble を設定し、LD_LIBRARY_PATH に <isaac-sim>/exts/isaacsim.ros2.bridge/humble/lib を追加してから isaac-sim.sh を実行します(LD_LIBRARY_PATH への追加は1ターミナルにつき1回だけにします)。 :::

シーンを作る

Stage に次の4つを用意します。

Prim 作り方 注意
/World/jetbot Isaac Sim Assets の Robots/NVIDIA/Jetbot をドラッグ パスは小文字の jetbot になる。Translate Z = 0.05
/World/GroundPlane Create → Physics → Ground Plane 衝突判定付きの床
/World/PhysicsScene Create → Physics → Physics Scene これがないと Play で JetBot が落下する
/World/JetbotROS 次の OmniGraph スクリプトで作成

OmniGraph で /cmd_vel を差動駆動につなぐ

Window → Script Editor に貼り付けて実行します。

import omni.graph.core as og

import omni.usd

# 作り直し用:既存のグラフを削除

stage = omni.usd.get_context().get_stage()

if stage.GetPrimAtPath("/World/JetbotROS"):

stage.RemovePrim("/World/JetbotROS")

og.Controller.edit(

{"graph\_path": "/World/JetbotROS", "evaluator\_name": "execution"},

{

    og.Controller.Keys.CREATE\_NODES: \[

        ("Tick", "omni.graph.action.OnPlaybackTick"),

        ("Ctx", "isaacsim.ros2.bridge.ROS2Context"),

        ("SubTwist", "isaacsim.ros2.bridge.ROS2SubscribeTwist"),

        ("BreakLin", "omni.graph.nodes.BreakVector3"),

        ("BreakAng", "omni.graph.nodes.BreakVector3"),

        ("Diff", "isaacsim.robot.wheeled\_robots.DifferentialController"),

        ("Art", "isaacsim.core.nodes.IsaacArticulationController"),

    \],

    og.Controller.Keys.SET\_VALUES: \[

        ("SubTwist.inputs:topicName", "/cmd\_vel"),

        ("Diff.inputs:wheelRadius", 0.0325),

        ("Diff.inputs:wheelDistance", 0.1125),

        ("Art.inputs:targetPrim", "/World/jetbot"),

        ("Art.inputs:jointNames", \["left\_wheel\_joint", "right\_wheel\_joint"\]),

    \],

    og.Controller.Keys.CONNECT: \[

        ("Tick.outputs:tick", "SubTwist.inputs:execIn"),

        ("Ctx.outputs:context", "SubTwist.inputs:context"),

        ("SubTwist.outputs:execOut", "Diff.inputs:execIn"),

        ("SubTwist.outputs:linearVelocity", "BreakLin.inputs:tuple"),

        ("SubTwist.outputs:angularVelocity", "BreakAng.inputs:tuple"),

        ("BreakLin.outputs:x", "Diff.inputs:linearVelocity"),

        ("BreakAng.outputs:z", "Diff.inputs:angularVelocity"),

        ("Tick.outputs:tick", "Art.inputs:execIn"),

        ("Diff.outputs:velocityCommand", "Art.inputs:velocityCommand"),

    \],

},

)

print("OK")

車輪半径 0.0325 m、車輪間隔 0.1125 m は暫定値です。実機に合わせる場合は実測してください。

ノードの型名はバージョンによって変わることがあります。unrecognized type エラーが出たら、次のコマンドで正式な型名を確認できます。

import omni.graph.core as og

names = og.get_registered_nodes()

for key in ["Break", "Twist", "Differential", "ArticulationController"]:

print(key, \[n for n in names if key in n\])

設定が終わったら、File → Save As でシーンを .usd として保存しておくと、次回からはシーンを開くだけで使えます。

3. PC 側の操作端末(Humble Docker)

Ubuntu 24.04 には Humble を apt でインストールできないので、操作用のツールは Docker で動かします。

xhost +local:

docker run -it --rm --net=host --ipc=host \

-e DISPLAY -e ROS_DOMAIN_ID=30 \

-e FASTRTPS_DEFAULT_PROFILES_FILE=/fastdds.xml \

-v $HOME/.ros/fastdds.xml:/fastdds.xml \

--name ros_humble osrf/ros:humble-desktop bash

コンテナ内で次を実行します。

source /opt/ros/humble/setup.bash

apt update && apt install -y ros-humble-teleop-twist-keyboard

Isaac Sim で Play ▶ した状態で ros2 topic list を実行し、/cmd_vel が見えれば Isaac Sim とつながっています。

ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1}}"

これで Sim の JetBot が前進します。

4. Jetson 側(motors + watchdog)

watchdog ノード

jetbot_ros の motors ノードは、最後に受け取った速度で回り続けます。Wi-Fi が切れると実機が止まらなくなるため、/cmd_vel を中継しつつ、0.5 秒受信がなければ停止指令を送るノードを間に挟みます。jetbot_ros 本体のコードは変更しません。

コンテナは --rm で起動するので、スクリプトは Jetson 本体に置いてマウントします。

import time

import rclpy

from rclpy.node import Node

from geometry_msgs.msg import Twist

class CmdVelWatchdog(Node):

def \_\_init\_\_(self):

    super().\_\_init\_\_('cmd\_vel\_watchdog')

    self.timeout \= self.declare\_parameter('timeout', 0.5).value

    self.pub \= self.create\_publisher(Twist, '/jetbot/cmd\_vel', 10\)

    self.create\_subscription(Twist, '/cmd\_vel', self.on\_cmd, 10\)

    self.last \= 0.0

    self.stopped \= True

    self.create\_timer(0.1, self.check)

    self.get\_logger().info(f'watchdog started (timeout={self.timeout}s)')

def on\_cmd(self, msg):

    self.last \= time.monotonic()

    self.stopped \= False

    self.pub.publish(msg)

def check(self):

    if not self.stopped and time.monotonic() \- self.last \> self.timeout:

        self.pub.publish(Twist())

        self.stopped \= True

        self.get\_logger().warn('cmd\_vel timeout \-\> STOP')

def stop(self):

    self.pub.publish(Twist())

def main():

rclpy.init()

node \= CmdVelWatchdog()

try:

    rclpy.spin(node)

except KeyboardInterrupt:

    pass

finally:

    node.stop()

    node.destroy\_node()

    rclpy.shutdown()

if __name__ == '__main__':

main()

コンテナ起動

sudo docker run -it --rm \

--runtime nvidia \

--privileged \

--net=host --ipc=host \

-v /tmp/argus_socket:/tmp/argus_socket \

-e ROS_DOMAIN_ID=30 \

-e FASTRTPS_DEFAULT_PROFILES_FILE=/fastdds.xml \

-v $HOME/.ros/fastdds.xml:/fastdds.xml \

-v $HOME/jetbot_tools:/jetbot_tools \

jetbot_ros:humble-r32.7.1-stable bash

オプション 役割
--runtime nvidia GPU・JetPack のライブラリをコンテナ内で使う
--privileged モーターと OLED の I2C にアクセスする
-v /tmp/argus_socket:... CSI カメラ(nvarguscamerasrc)を使う
--net=host --ipc=host PC と DDS で通信する
-v $HOME/jetbot_tools:... watchdog スクリプトを読み込む

ノード起動

ros2 run jetbot_ros motors_waveshare &

python3 /jetbot_tools/cmd_vel_watchdog.py &

motors ノードは jetbot 名前空間で起動するため、購読するトピックは /cmd_vel ではなく /jetbot/cmd_vel です。watchdog がこの差を吸収します。

$ ros2 node info /jetbot/motors

/jetbot/motors

Subscribers:

/jetbot/cmd\_vel: geometry\_msgs/msg/Twist

:::note info watchdog を使わない場合は、ros2 run jetbot_ros motors_waveshare --ros-args -r /jetbot/cmd_vel:=/cmd_vel でリマップすれば、/cmd_vel を直接受けられます。 :::

5. 動作確認

最初は車輪を浮かせた状態で試してください。

  1. Isaac Sim で Play ▶ し、JetBot が床の上で静止することを確認する
  2. Jetson のコンテナで ros2 topic list を実行し、/cmd_vel が見えることを確認する(Wi-Fi 越しなので数秒かかることがあります)
  3. PC の Docker から前進指令を送る

ros2 topic pub -r 10 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.1}}"

実機の車輪と Sim の JetBot が同時に動けば成功です。Ctrl+C で送信を止めると、0.5 秒後に watchdog が cmd_vel timeout -> STOP を出して実機が止まります。

キーボードで操作する場合は次のコマンドを使います。

ros2 run teleop_twist_keyboard teleop_twist_keyboard

teleop_twist_keyboard はキーを押したときだけ指令を送るので、0.5 秒のタイムアウトだと、キーを押し続けていないと止まります。余裕がほしい場合は、python3 /jetbot_tools/cmd_vel_watchdog.py --ros-args -p timeout:=1.0 で起動してください。

ハマりどころ

症状 原因 対処
unrecognized type 'omni.graph.nodes.Break3Vector' ノード型名の誤り 正しくは omni.graph.nodes.BreakVector3
Play すると JetBot が落下する PhysicsScene がない Create → Physics → Physics Scene を追加
/cmd_vel を送っても Sim の JetBot が動かない Stage 上のパスが小文字の /World/jetbot targetPrim をパスに合わせる
コンテナ内の /fastdds.xml がディレクトリになっている ホスト側にファイルがない状態でマウントした 空ディレクトリを削除し、ファイルを作ってから起動し直す
実機が /cmd_vel に反応しない motors が /jetbot/cmd_vel を購読している watchdog で中継する(またはリマップする)
Sim は止まるのに実機が止まらない ros2 topic pub --once の停止指令が、Wi-Fi 越しの discovery が終わる前に送られて失われた -r 10 -t 20 のように複数回送る。watchdog で根本対策

ros2 topic pub -r 10 -t 20 /cmd_vel geometry_msgs/msg/Twist "{linear: {x: 0.0}}"

Jetson から PC のトピックが見えない場合は、まず Isaac Sim が Play 中かを確認し、次に sudo ufw status でファイアウォールが UDP を止めていないかを確認してください。Wi-Fi ルータがマルチキャストを通さない環境では、fastdds.xml の <builtin><initialPeersList> に相手の IP を書くと見つけられるようになります。

おわりに

Isaac Sim 5.1 を内部 Humble で起動し、DDS の設定を3か所で揃えるだけで、Jetson Nano(JetPack 4.6 / Humble)の JetBot と Sim を同じ /cmd_vel で動かせました。

次は、実機の CSI カメラ映像を圧縮して PC に送り、Isaac Sim のカメラ映像と並べて表示するところに挑戦する予定です。

参考

0
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?