LoginSignup
1
1

More than 1 year has passed since last update.

ROS2の最小構成parameter:初級 -ROS1 style-

Last updated at Posted at 2019-07-18

ROS2関係トップページへ
ROS2レクチャー:初級 -ROS1 style-

【前:ROS2のparameter概要
【次:YAMLファイルによるROS2のパラメータ設定

ここではparameterに関するservice側とclietn側のプログラムを作成する.
clientのプログラムとしては非同期に取得・設定を行うAsyncParameterClientと同期的に行うSyncParameterClientがある.ここでは同期型のSyncParameterClientを紹介する.

基本的な機能は,パラメータの宣言・値の設定・取得となり,それぞれ以下の関数となる.詳しくは以下の通り.

  • 宣言
    • declare_parameter
      • パラメータの宣言
      • dashing diademataから宣言なしでは値の設定・取得ができなくなった.これにより「パラメータの名前を打ち間違っちゃって似た名前のものが複数できる」などがなくなった.
    • undeclare_parameter
      • パラメータの無効化
    • has_parameter
      • パラメータの存在確認
  • 値の設定
    • set_parameters
  • 値の取得
    • get_parameter
    • get_parameters

ROS2のparameter概要の記載通りserviceとしてのparameterとclientとしてのparameterがあるので公式のAPIのrclcpp::NodeとParameters:に記載がある.

parameter serviceについてはfuture worksで.

準備

terminal
$ cd ~/ros2_studies_ws/
$ ros2 pkg create minimal_parameter_ros1_like --dependencies rclcpp

Parameter

作成物:

  • src/ros1_like_parameter_holder_main.cpp
    • パラメータを保持するnode
    • node名: param_holder
  • src/ros1_like_parameter_async_user_main.cpp
    • param_holderに接続し,値を参照したり設定したりするnode
  • src/ros1_like_parameter_sync_user_main.cpp
    • param_holderに接続し,値を参照したり設定したりするnode

プログラム : src/ros1_like_parameter_holder_main.cpp

src/ros1_like_parameter_holder_main.cpp
#include <rclcpp/rclcpp.hpp>
#include <chrono>

int main(int argc, char * argv[]){
  using namespace std::chrono_literals;
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("param_holder");

  auto param1 = node->declare_parameter("foo",0);
  auto param2 = node->declare_parameter("bar", "ok");
  auto results = node->set_parameters({
    rclcpp::Parameter("foo",2),
    rclcpp::Parameter("bar","hello")
  });
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

概要

パラメータを保持するnodeとして作成.
nodeを作り,保持するパラメータを準備し,spinでnodeを実行したままにする.
このnode自身がパラメータを定義し,保持・管理する(nodeがserviceとなっている).よって,declare_parameterやget_parameter,setparametersなどはnodeが行っている(後述するclient側ではnodeがget,setせずparam_clientがしていることに注意).またwait_for_serviceもしていない(service元のnodeが自分なので接続待ちをしない).

説明

7行目:nodeの設定.名前をparam_holderとする.
9~14行目:設定するparameterの宣言.名前だけでも良いし,デフォルト値を設定してもよい.
11~14行目:設定するparameterの準備.
15行目:parameterを持つnodeの実行.

Parameter

set_parameters関数の中でParameterを使用している.また公式のAPIにも記述があるが補足.

パラメータはキー(要素名?)と値の対にて表される.辞書型の変数.ここで,ROS2ではParameterクラスとParameterValueクラスが存在し,両方ともパラメータを表すのに使えそう.公式のAPIによるとParameterValueはパラメータを保持するためのクラスで,Parameterも同じ.ただParameterクラスはget, setに関する関数テンプレートを使っていて任意のパラメータを扱えるよう.

プログラム : src/ros1_like_parameter_sync_user_main.cpp

src/ros1_like_parameter_sync_user_main.cpp
#include <rclcpp/rclcpp.hpp>
#include <chrono>

int main(int argc, char * argv[]){
  using namespace std::chrono_literals;
  rclcpp::init(argc, argv);
  auto node = rclcpp::Node::make_shared("param_sync_user");
  auto param_client = std::make_shared<rclcpp::SyncParametersClient>(node, "param_holder");
  std::stringstream ss;

  while(!param_client->wait_for_service(1s)){
    if(!rclcpp::ok()){
      RCLCPP_ERROR(node->get_logger(), "Interrupted while waiting for the service.");
      return 1;
    }
    RCLCPP_INFO(node->get_logger(), "waiting for service...");
  }

  auto parameters_get_results = param_client->get_parameters({"foo", "bar"});
  for(auto &param : parameters_get_results){
    ss << "\nParameter name:" << param.get_name();
    ss << "\nParameter type:" << param.get_type_name();
    ss << "\nParameter value:" << param.value_to_string();
  }
  RCLCPP_INFO(node->get_logger(), ss.str().c_str());
  ss.str("");
  ss.clear(std::stringstream::goodbit);

  auto list_results = param_client->list_parameters({"foo","bar"},10);
  for(auto &param_name : list_results.names){
    ss << "\n" << param_name;
  }
  for(auto &prefix : list_results.prefixes){
    ss << "\n" <<  prefix;
  }
  RCLCPP_INFO(node->get_logger(), ss.str().c_str());
  ss.str("");
  ss.clear(std::stringstream::goodbit);

  auto parameter_set_results = param_client->set_parameters({
    rclcpp::Parameter("foo",3),
    rclcpp::Parameter("bar","welcome")
    //rclcpp::Parameter("bar",10) // OK:別種のものを代入することは可能
//    rclcpp::Parameter("hoge","hige") // NG:パラメータを増やしても無効?
  });
  for(auto &result : parameter_set_results){
    if(!result.successful){
      RCLCPP_ERROR(node->get_logger(), "Failed to set parameter: %s", result.reason.c_str());
    }
  }

  parameters_get_results = param_client->get_parameters({"foo", "bar"});
  for(auto &param : parameters_get_results){
    ss << "\nParameter name:" << param.get_name();
    ss << "\nParameter type:" << param.get_type_name();
    ss << "\nParameter value:" << param.value_to_string();
  }
  RCLCPP_INFO(node->get_logger(), ss.str().c_str());
  ss.str("");
  ss.clear(std::stringstream::goodbit);

  rclcpp::shutdown();
  return 0;
}

概要

param_holderに接続し,そのnodeが持っているパラメータを参照・変更・また参照するプログラム.
パラメータを取得・変更する場合,パラメータを保持しているnodeと接続しなければいけない.そのため,8行目のようにparameter clientとしての設定を行っている.引数は二つであり,一つ目がSyncParametersClientの機能を持たせるnodeへのポインタ,二つ目がparameterのデータを保持しているparameter nodeの名前(serviceとなるnodeの名前)である.
同期型なので,spin関数を用いなくてもget_parametersやset_parametersなどを実行したらすぐに取得・設定が行われる.
またget_parameter(get_parameters)で得られた返り値は型や名前など色々な情報を含む.パラメータの値そのものを得るためには,param.value_to_string()などの関数を使用する.代表的なものは以下の通り.その他は公式のAPIのrclcpp::Parameterを参照.

  • param.as_bool()
  • param.as_int()
  • param.as_double()
  • param.as_string()

説明

8行目:SyncParametersClientとして設定.二つ目の引数で参照するparameter nodeの名前を渡している.
9~16行目:parameterの取得,表示.
18~27行目:parameterのlist.詳細情報?18行目の二つ目の引数10は深さで,対象parameterの名前を深さ10まで取得するらしい.
29~40行目:paramter設定.値を変えることは可能(30,31行目).また値の種類を超えて変えることも可能(32行目).しかし新しいキーを設定することはできない模様(エラーにはならない).
42~51行目:parameter取得.

package.xmlとCMakeLists.txt

<package format="3">
  <depend>rclcpp></depend>
CMakeLists.txt
find_package(rclcpp REQUIRED)

add_executable(ros1_like_param_holder_test
  src/ros1_like_parameter_holder_main.cpp
)
ament_target_dependencies(ros1_like_param_holder_test
  rclcpp
)

add_executable(ros1_like_param_sync_user_test
  src/ros1_like_parameter_sync_user_main.cpp
)
ament_target_dependencies(ros1_like_param_sync_user_test
  rclcpp
)

install(TARGETS
  ros1_like_param_holder_test
  ros1_like_param_sync_user_test
  DESTINATION lib/${PROJECT_NAME}
)

ビルド・実行

ビルド

terminal
$ cd ~/ros2_studies_ws/
$ colcon build --symlink-install --packages-up-to minimal_parameter_ros1_like
$ . install/local_setup.bash

実行

param_holderとuser用のterminalを起動.

terminal1
$ cd ~/ros2_studies_ws/
$ . install/local_setup.bash
$ ros2 run minimal_parameter_ros1_like ros1_like_param_holder_test
terminal2
$ cd ~/ros2_studies_ws/
$ . install/local_setup.bash
$ ros2 run minimal_parameter_ros1_like ros1_like_param_sync_user_test

terminal2に出てくるメッセージを見て考える.
またterminal1のparam_holderを終了させないまま,async_userを2回,3回実行させると結果はどうなるか?

メモ

parameterはserviceを提供する側とサービスを提供されるclient側がある.parameterの値を保持し他に与えるnodeはserviceとなり,それを参照したり値を変更したりするものがclientとなる.clientが他のnodeの場合,「serviceを行うnodeはだれか?」という名前の情報が必要である.一方で,自身で自身のparameterを使用する場合(つまりservice=clientとなる場合),別に名前を知る必要はなく,自分のparameterにアクセスすればよいだけである.このことから,service≠clientの場合,clientはAsyncClientsParameterとしてparameter clientの機能を持ち,serviceに接続しなければいけない.一方,service=clientの場合,自分のparameterにアクセスするのでnode->get_parameterやthis->parameter,はてはget_parameterのみでアクセスできる.

つまり同一nodeの場合,「他のノードのparameter機能に接続してパラメータをいじる」ではなく,「nodeのパラメータに直接アクセス」になる.よってこの場合,wait_for_serviceしなくていいんじゃなかろうか.いいんです.

【前:ROS2のparameter概要
【次:YAMLファイルによるROS2のパラメータ設定

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