今回はカメをプログラムで動かしていきます。
colcon(ROS 2のビルドツール)のインストール
ROS2のリポジトリからパッケージとして導入します。
sudo apt install python3-colcon-common-extensions
インストールが成功したかバージョンチェック
colcon version-check
colcon-argcomplete 0.3.3: up-to-date
colcon-bash 0.5.0: up-to-date
colcon-cd 0.2.1: local version is newer than latest release (0.1.1)
colcon-cmake 0.2.29: up-to-date
colcon-core 0.20.1: up-to-date
colcon-defaults 0.2.9: up-to-date
colcon-devtools 0.3.0: up-to-date
colcon-installed-package-information 0.2.1: up-to-date
colcon-library-path 0.2.1: up-to-date
colcon-metadata 0.2.5: up-to-date
colcon-notification 0.3.0: up-to-date
colcon-output 0.2.13: up-to-date
colcon-override-check 0.0.1: up-to-date
colcon-package-information 0.4.0: up-to-date
colcon-package-selection 0.2.10: up-to-date
colcon-parallel-executor 0.3.0: newer version available (0.4.0)
colcon-pkg-config 0.1.0: up-to-date
colcon-powershell 0.4.0: newer version available (0.5.0)
colcon-python-setup-py 0.2.9: up-to-date
colcon-recursive-crawl 0.2.3: up-to-date
colcon-ros 0.5.0: up-to-date
colcon-test-result 0.3.8: up-to-date
colcon-zsh 0.5.0: up-to-date
ワークスペースの準備
ROS2では、自分のプログラムを置くための専用フォルダ(ワークスペース)を作ります。
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
次に、Python用の「パッケージ」を新規作成します。
ros2 pkg create --build-type ament_python --node-name my_turtle_node my_robot_controller
これで、my_robot_controller という名前のプロジェクトの雛形ができあがりました。
Pythonコードを書く
雛形として生成されたファイルを書き換えます。
エディタ(VSCodeやGeditなど)で、~/ros2_ws/src/my_robot_controller/my_robot_controller/my_turtle_node.py を開き、中身を以下のコードに置き換えて保存してください。
vi my_robot_controller/my_robot_controller/my_turtle_node.py
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist # 速度メッセージの型
class TurtleController(Node):
def __init__(self):
super().__init__('my_turtle_node')
# /turtle1/cmd_vel というトピックにメッセージを送る「パブリッシャ」を作成
self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
# 0.1秒ごとに実行されるタイマーを作成
self.timer = self.create_timer(0.1, self.move_turtle)
def move_turtle(self):
msg = Twist()
msg.linear.x = 2.0 # 前進速度
msg.angular.z = 1.0 # 回転速度(これで円を描く)
self.publisher_.publish(msg)
def main(args=None):
rclpy.init(args=args)
node = TurtleController()
rclpy.spin(node) # プログラムを動かし続ける
rclpy.shutdown()
Gemini解説
このコードは、シミュレーター上のカメ(Turtlesim)に「円を描いて動け!」という命令を送り続ける内容になっています。
🚀 プログラムの全体像
ROS2では、機能ごとに「ノード」という単位でプログラムを分割します。今回のプログラムの役割は以下の通りです。
準備部分(インポート)
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
-
rclpy: ROS2をPythonで動かすための基本ライブラリ -
Node: ノードを作るためのテンプレート -
Twist: ロボットの**速度(直線・回転)**を表現するための専用のデータ形式です
クラスの定義(ノードの設計図)
class TurtleController(Node):
def __init__(self):
super().__init__('my_turtle_node') # ノードに名前を付ける
# パブリッシャ(送信役)の作成
self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
# タイマー(定期実行)の作成
self.timer = self.create_timer(0.1, self.move_turtle)
-
パブリッシャ:
/turtle1/cmd_velという「トピック(掲示板のようなもの)」に、速度データ(Twist型)を投げ込む役割を作ります -
タイマー: 「0.1秒ごとに
move_turtleという関数を実行してね」という予約をしています
動かす命令を作る(実行部分)
def move_turtle(self):
msg = Twist()
msg.linear.x = 2.0 # 前進する速さ
msg.angular.z = 1.0 # 左に回る速さ
self.publisher_.publish(msg) # 命令を送信
ここで具体的な速度を指定しています。
-
linear.x: 前に進む力 -
angular.z: 独楽(こま)のように回転する力 - **「真っ直ぐ進みながら、少しずつ左に曲がる」**という命令を出し続けるため、カメは円を描きます
メイン関数
def main(args=None):
rclpy.init(args=args) # ROS2の通信機能を開始
node = TurtleController() # 設計図からノードを実体化
rclpy.spin(node) # 停止命令が出るまで実行し続ける
rclpy.shutdown() # 後片付け
💡 重要なポイントのまとめ
| 用語 | 役割 |
|---|---|
| Node (ノード) | プログラムの本体 |
| Topic (トピック) | データの通り道 |
| Publish (パブリッシュ) | データを送信すること |
| Twist (メッセージ型) | データの形式 |
🛠️ ちょっとやってみてほしいこと
もし環境があれば、msg.linear.x や msg.angular.z の数値を書き換えてみてください。
-
linear.x = 5.0にすると、円が大きくなります -
angular.z = 0.0にすると、カメは壁にぶつかるまで真っ直ぐ突き進みます
ビルドして実行する
コードが書けたら、ROS2に「この新しいプログラムを使っていいよ」と認識させる作業(ビルド)が必要です。
1. ビルドを実行
cd ~/ros2_ws
colcon build
2. 環境設定の読み込み
これを行わないと自作ノードが見つかりません。
source install/setup.bash
3. 実行
あらかじめ turtlesim_node を別ターミナルで起動しておいてください。
ros2 run my_robot_controller my_turtle_node
何が起きたのか?
これを実行すると、カメがくるくると円を描いて回り始めます。
- あなたが書いたプログラムが Publisher(配信者) になりました
- 0.1秒ごとに「前進 2.0、回転 1.0」というデータを、トピック
/turtle1/cmd_velに流し続けています - Turtlesimはそれを受け取り、忠実に動き続けています
いまは「ただ回るだけ」の動きを、例えば**「3秒間進んだら、90度曲がる」**といった命令を組み込めば、カメに正方形を描かせることもできます。
次にステップアップして、**「カメが壁へぶつかりそうになったら止まる」といった、センサー情報を利用したプログラムを作っていきましょう。
これはロボットが「周りの状況を見て、自分で判断する」**という知能を持つための基本となります。
※これには Subscriber(購読者) の知識が必要になります。
今回は、カメの位置情報(センサーデータに相当)を受け取り、**「もし壁に近づきすぎたら止まる」**というプログラムに改造してみましょう。
「Subscriber(購読者)」の仕組み
これまでは「命令を送る(Publisher)」だけでしたが、今回はカメの状態(Pose)を「受け取る(Subscriber)」機能を追加します。
-
Topic名:
/turtle1/pose -
Message型:
turtlesim/msg/Pose(x座標、y座標、向きなどが入っています)
Pythonコードを改造する
先ほど作成した my_turtle_node.py の中身を、以下のように書き換えてみましょう。
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from turtlesim.msg import Pose # 位置情報の型を追加
class TurtleSafetyController(Node):
def __init__(self):
super().__init__('safety_node')
# 1. 命令を送る(Publisher)
self.publisher_ = self.create_publisher(Twist, '/turtle1/cmd_vel', 10)
# 2. 位置情報を受け取る(Subscriber)
# 第2引数はメッセージ型、第3引数はトピック名、第4引数はデータ受信時に実行する関数
self.subscription = self.create_subscription(
Pose, '/turtle1/pose', self.pose_callback, 10)
def pose_callback(self, msg):
# カメの位置情報 (x, y) をチェック
# Turtlesimの画面は 0.0 ~ 11.0 の範囲です
cmd = Twist()
if msg.x > 9.0 or msg.x < 2.0 or msg.y > 9.0 or msg.y < 2.0:
# 壁に近いときは止まる(またはゆっくり回転)
self.get_logger().info('壁が近い!停止します')
cmd.linear.x = 0.0
cmd.angular.z = 1.0
else:
# 安全なときは前進
cmd.linear.x = 2.0
cmd.angular.z = 0.0
self.publisher_.publish(cmd)
def main(args=None):
rclpy.init(args=args)
node = TurtleSafetyController()
rclpy.spin(node)
rclpy.shutdown()
Gemini解説
このコードは、ROS2のもう一つの超重要機能である**「Subscriber(サブスクライバ)」**を使ったプログラムです。
前回のコードは「一方的に命令を送るだけ」でしたが、今回は**「カメの今の場所(センサー情報)を見て、行動を変える」**という、より知的なロボットらしい動きになっています。
🛠️ 今回のポイント:情報の「受け取り」
このプログラムは、カメの現在地を常にチェックし、**「壁にぶつかりそうになったら自動で止まって回転する」**という安全装置(セーフティ)の役割を果たします。
データの受け取り役(Subscriber)の作成
self.subscription = self.create_subscription(
Pose, # メッセージの型(位置情報)
'/turtle1/pose', # トピック名(どこから受け取るか)
self.pose_callback, # データが届いた時に実行する関数
10 # キューサイズ(予備の保存枠)
)
-
/turtle1/pose: Turtlesim(シミュレーター)が、カメの座標(x, y)や向きを常に配信しているトピックです -
self.pose_callback: データが届くたびに、この関数が自動的に呼び出されます。「データが来たらこれをして!」という予約です
条件分岐(コールバック関数の中身)
ここがこのプログラムの「脳」にあたる部分です。
def pose_callback(self, msg):
# Turtlesimの画面サイズは約 0.0 ~ 11.0
if msg.x > 9.0 or msg.x < 2.0 or msg.y > 9.0 or msg.y < 2.0:
# 【危険エリア】壁が近い!
self.get_logger().info('壁が近い!停止します')
cmd.linear.x = 0.0 # 停止
cmd.angular.z = 1.0 # その場でくるくる回って方向転換
else:
# 【安全エリア】
cmd.linear.x = 2.0 # 前進
cmd.angular.z = 0.0 # まっすぐ
- **
msg.x/msg.y**: 受け取ったメッセージ(Pose型)の中に、今のカメの座標が入っています -
if文: 座標が端っこ(2.0未満、または9.0より大きい)になったら「危ない!」と判断します
ロガー(ログ出力)
self.get_logger().info('壁が近い!停止します')
- これはPythonの
print()に似ていますが、ROS2の標準的な画面出力方法です - いつ、どのノードが、どんな状況なのかをターミナルに表示してくれるので、デバッグ(不具合探し)にとても便利です
🏃♂️ このプログラムを動かすとどうなる?
- カメは最初、広い場所(安全エリア)にいるので真っ直ぐ進みます
- 壁に近づくと
pose_callbackがそれを検知し、命令を**「停止+回転」**に切り替えます - 回転して向きが変わり、カメの目の前が「安全エリア」に向くと、再び真っ直ぐ進み始めます
結果として、**「部屋の中を掃除しながら、壁に当たらないように動き回るお掃除ロボット」**のような動きになります!
💡 次のステップへのヒント
このコードを少し改造するだけで、もっと面白くなります。
-
もっと複雑な条件: 「もし
msg.xが 5.0 を超えたら、スピードを2倍にする」など - ランダムな動き: 壁に当たった時の回転角度をランダムにすれば、よりお掃除ロボットっぽくなります
補足
上記のようにGeminiは解説してくれたのですが、実際の動作はカメが回転し始めるとずっと回転していて、他のターミナルから前進の命令を出す必要があったような気がしたので確認したところ
このプログラムの「お茶目な」弱点
このアルゴリズムには一つ面白い欠点があります。壁際での挙動: 壁に近づいて「回転」が始まると、カメはその場で回ります。
安全の判断: 回転してカメの向きが変わっても、「今いる座標 (x, y)」が壁に近いままだと、プログラムは「まだ危ない!」と判断して回転を続けます。
脱出: 回転し続けて、カメが移動して(あるいは回転の勢いで少し座標がズレて)「安全エリア」に座標が入った瞬間に、また「前進」に切り替わります。
と回答がありました。
ちゃんとカメの向きを入れてあげないと想定通りには動きません。
動かしてみる
コードを保存したら、もう一度ビルドして実行します。
1. ビルド(ワークスペースのルートで)
cd ~/ros2_ws
colcon build
2. 実行
source install/setup.bash
ros2 run my_robot_controller my_turtle_node
カメが画面の端(壁)に近づくと、ログに「壁が近い!」と表示され、カメが安全な方向を向くために回転し始めます
今回学んだこと
- フィードバック制御: 「現在の状態(Pose)」を見て「次の動き(Twist)」を決めるロボット制御の基本ができました
-
Callback(コールバック): データが届いた瞬間に特定の関数(
pose_callback)を動かす仕組みを使いこなせるようになりました

