本資料は、ROS1からROS2への移行を進める開発者(初心者〜中級者) を対象にしています。特に、launchファイルの記法、パラメータ管理、substitutionの扱いなど、ROS2特有の仕様に戸惑いやすい点をまとめています。
1. ROS1とROS2の基本的な違い
-
引数と設定方法の違い
- ROS1:
argumentを引数として利用 - ROS2:
configurationがグローバル変数として扱われる
- ROS1:
-
パラメータ管理
- ROS2ではノード単位でパラメータを管理するため、
yamlファイルの設定時に namespaceを一致させる必要がある - namespaceを変更した場合は、
yaml記述も変更が必要だが以下のように記述することで任意のnamespaceに対応することもできる/**/my_node: ros__parameters: node_specific_param: "value" - nav2_common.launch.RewrittenYamlを使うことでyamlファイルのnamespaceや文字の置き換えが出来るので、これを使うことでyamlファイル内にnamespaceを記述しなくて済みnamespace名の変更も簡単に行える
- ROS2ではノード単位でパラメータを管理するため、
-
実行フェーズの違い
- ROS2では「読み込みフェーズ」と「実行フェーズ」があり、読み込みが終わってから実行される
-
LaunchConfigurationの解釈に制約があり、自由度は低め
2. Configurationの扱い
特徴と注意点
-
設定値はグローバル変数のようなふるまいをするため処理順に上書きされる可能性がある
- 例:
include内のlaunch_argumentsで再定義すると、その後は新しい値が使われる
- 例:
-
DeclareLaunchArgument- 指定名のconfigurationが存在しなければ、default値で生成
- default値に「数値を文字列として」指定することはできない
-
LaunchConfiguration- 存在すればその値を取得、なければdefault値を返す
- そのままでは文字列の結合はできないため、以下のように
DeclareLaunchArgumentのdefaultをかませる必要があるDeclareLaunchArgument( 'config_file_name', default_value=[LaunchConfiguration('config_type'), '_config.yaml'] )
Includeとスコープ管理
-
IncludeLaunchDescriptionのlaunch_argumentsは 引数渡しではなく、共通のconfiguration変数として登録 されるイメージ - configurationはグローバル変数扱いになるため、ROS1のようにローカルに使いたい場合は
GroupActionを利用する
3. Substitutionsの扱い
- Substitutionは launch実行時に値が決定 する方式
- Pythonの条件分岐では利用できないため、不便な場合もある → ケースによって使い分けが必要
代表的な置き換え例
| ROS1記法 | ROS2 Substitution |
|---|---|
get_package_share_directory |
FindPackageShare |
os.path.join |
PathJoinSubstitution |
os.environ |
EnvironmentVariable |
4. ROSパラメータの注意点
Python APIでの制約
-
declare_parameterでstring_arrayに空リストをデフォルト値として指定すると、byte_arrayとして認識され正しく動作しない - ROS2ではパラメータ値を辞書型で直接取得できない
yamlファイルの書き方(例)
auto_charger:
ros__parameters:
follower_param:
param1:
param_name: 'spin_start_error_angle'
value: 0.1
param2:
param_name: 'goal_line_length'
value: 0.05
ノード側のオプション指定
allow_undeclared_parameters=True,
automatically_declare_parameters_from_overrides=True,
Python側で辞書型に変換する例
follower_param = self.get_parameters_by_prefix("follower_param")
mappings = {}
for key, value in follower_param.items():
namespace, name = key.split('.', maxsplit=1)
if namespace not in mappings:
mappings[namespace] = {}
if name == 'param_name':
mappings[namespace][name] = value.get_parameter_value().string_value
else:
mappings[namespace][name] = value.get_parameter_value().double_value
5. 機能リファレンス
Configuration関連
| クラス名 | 機能 |
|---|---|
| DeclareLaunchArgument | 存在しなければdefault値でconfigurationを作成 |
| LaunchConfiguration | 存在すれば値を取得、なければdefault値を取得 |
| PushROSNamespace | namespace名をlaunch_configurations['ros_namespace']に書き込む |
| PopLaunchConfigurations | 現在のスコープを破棄し、直前のスコープに戻す |
| PushLaunchConfigurations | 新しいスコープを作成(前状態は保存される) |
| SetLaunchConfiguration | 指定キー名で値を書き込む |
| GroupAction |
scoped=True時、新しいスコープで実行後、元に戻す |
Substitutions関連
| クラス名 | 機能 |
|---|---|
| Command | Linuxコマンドの出力を取得 |
| EnvironmentVariable | 環境変数を取得 |
| FindExecutable | 実行ファイルのパスを取得 |
| FindPackageShare | パッケージのパスを取得 |
| PathJoinSubstitution | パス名を結合 |