hello turtlesim
はじめに
ロボティクスの制御用プラットフォームとして広く使われているROS2を勉強し始めようと思います。
まずはROS2の入門編として有名な、turtlesimをお試ししました。
turtlesimはROSの頃からhello world的な存在で、ROSの創始者が学習用にカメを動かすデモを作ったのが始まりのようです。
Udemyのコース学習内容の記事になります。
環境
- windows 11(25H2)
- WSL2 + Ubuntu 24.04
- Docker osrf/ros:jazzy-desktop
- 2026/5 に新しいLTSのROS2出るようですね...。Lyrical Luth
やったこと
dockerでROS2のimageをpullして環境構築、turtlesimを動かしてみました。
- トピック通信でturtleのtwist動作
環境構築
imageをpull
docker pull osrf/ros:jazzy-desktop
コンテナを実行
docker run -it --rm --net=host --env DISPLAY=$DISPLAY --env WAYLAND_DISPLAY=$WAYLAND_DISPLAY --env XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR --env QT_X11_NO_MITSHM=1 --volume /tmp/.X11-unix:/tmp/.X11-unix osrf/ros:jazzy-desktop
コンテナ内でros2を実行
ros2 run turtlesim turtlesim_node
turtlesim実行画面

ROS2:ノード間通信
ROS2では各プログラムがノードとして独立で動作する。ノード間は以下の通信機構で連携する。
- Topic
- 非同期、一方向、連続配信(ラジオ放送のようなイメージ)
- センサー値や状態の通知向き
- Service
- 同期、即時応答(関数呼び出しのようなイメージ)
- 設定変更、1回のみの問い合わせ向き
- Action
- 非同期、進捗あり、キャンセル可能(長時間のタスク依頼のようなイメージ)
- ナビゲーション、移動向き
通信イメージ
Topic:
Publisher ---> Topic ---> Subscriber
Service:
Client ---> Server
<---
Action:
Client ---> Goal送信 ---> Server
Client <--- Feedback(進捗) <--- Server
...
Client <--- result(最終結果) <--- Server
turtleを動かす
ROS2のノード間通信でturtleを動かす。
docker内の別プログラム同士で通信させるため、カメの画面を表示しながら、トピックコマンドを送信する。
ROS dockerを起動
docker run -it --rm --net=host --env DISPLAY=$DISPLAY --env WAYLAND_DISPLAY=$WAYLAND_DISPLAY --env XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR --env QT_X11_NO_MITSHM=1 --volume /tmp/.X11-unix:/tmp/.X11-unix osrf/ros:jazzy-desktop
ROS2上でturtlesimを動かす
ros2 run turtlesim turtlesim_node
別のubuntu windowから、先ほど実行したdockerコンテナでexecする。
docker ps -a # NAME列で実行名確認(frosty_gagarin)
docker exec -it frosty_gagarin bash # execで実行1
exec bashで接続すると環境変数が設定されてないので、sourseで設定
source /opt/ros/jazzy/setup.bash
トピック通信実行
ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0}, angular: {z: 1.8}}"
- /turtle1/cmd_vel: turtlesimが速度命令を受け取るトピック名。cmd: command, vel: velocity
- geometry_msgs/msg/twist: 送るメッセージ型 (linear, angular)
- linear: 直線速度 x:2.0 => 前方向へ2.0進む。
- angular: 角速度 z:1.8 => Z軸周りに1.8回転
デフォルトでは繰り返しpublishされるので、継続的に動き続ける、円を描く動作をする。
トピック通信は1:nで一方向に送られるため、turtlesimを複数起動しておけば、すべてのカメが同じ動作をする。
元のturtlesim実行画面で、turtlesimを3プロセス実行
ros2 run turtlesim turtlesim_node &
ros2 run turtlesim turtlesim_node &
ros2 run turtlesim turtlesim_node &
再度回転コマンド実行
ros2 topic pub /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0}, angular: {z: 1.8}}"

3匹のカメが回る。更新頻度はばらつくのか、カメの位置は異なっている。
トピックデータの観察
トピック通信をほかのプロセスから受信して確認することが出来る。
もう一つ他のウィンドウを開いてdocker execで入って別プロセスを実行。
ros2 topic echo /turtle1/cmd_vel
図にするとこんな感じで、同じコンテナ内で複数プロセスを実行し、トピック通信で1:nの通信を試した。

つまずいたところ
docker-run execでbash実行する場合は、sourceで環境設定をしないとros2コマンドが見つからないエラーとなる。
忘れてるとなぜ実行できなくなるかわからなくなるので、.zshrcに以下の関数を追加しておいた。
# ROS Docker を GUI 対応で起動し、連番のコンテナ名を自動付与する関数
# 引数を渡すと docker run にそのまま追加で渡します。
# 例: docker-ros /bin/bash
# docker-ros ros5 /bin/bash
# docker-ros --rm
#
# コンテナ名を省略した場合は ros1, ros2, ... の連番を自動で決定します。
# すでに存在する rosN コンテナの番号を見て次の番号を選びます。
docker-ros() {
local container_name="$1"
if [[ -z "$container_name" ]]; then
local last
last=$(docker ps -a --format '{{.Names}}' | awk '/^ros[0-9]+$/ { print substr($0, 4) }' | sort -n | tail -1)
local next=$(( ${last:-0} + 1 ))
container_name="ros${next}"
else
shift
fi
docker run -it --rm \
--name "$container_name" \
--net=host \
--env DISPLAY=$DISPLAY \
--env WAYLAND_DISPLAY=$WAYLAND_DISPLAY \
--env XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR \
--env QT_X11_NO_MITSHM=1 \
--volume /tmp/.X11-unix:/tmp/.X11-unix \
-v ${PWD}/work:/work \
osrf/ros:jazzy-desktop "$@"
}
# 既存コンテナに入るための関数
# 引数が指定されなければ ros1 に接続し、指定した場合はそのコンテナ名で実行します。
# コンテナ内では ROS 環境を先に source した状態で bash を起動します。
# 例: docker-ros-exec
# docker-ros-exec ros2
# docker-ros-exec ros3 ls /usr
#
docker-ros-exec() {
local target
if [[ $# -gt 0 ]]; then
target=$1
shift
else
target=ros1
fi
if [[ $# -eq 0 ]]; then
docker exec -it "$target" bash -lc 'source /opt/ros/jazzy/setup.bash && exec bash'
else
docker exec -it "$target" bash -c 'source /opt/ros/jazzy/setup.bash && exec "$@"' dummy "$@"
fi
}
まとめ
windows11上でwsl + ubuntu24.04を動作させ、dockerでros2(jazzy-desktop)でturtlesimを使い、トピック通信を実行した。
トピック通信はpublisherが1, subscriberがnの1:n, 非同期通信という基本的な通信方式で、カメを動かす指令を出してカメを動かしながら、そのコマンドを別プロセスで受信して動作確認をした。
Discussion