Open13

ROS2 typesupport C

nonanonnononanonno

ROS2 のメッセージ定義から生成されるrosidl_typesupport_cの構造体及び関数群をまとめる。基となる定義は以下のものとする。

MyMsg.msg
int32 int_value
Other other_value
Other[] dynamic_array
Other[3] static_array
MySrv.srv
int32 int_request
---
int32 int_response
Fibonacci.action
int32 order
---
int32[] sequence
---
int32[] sequence
nonanonnononanonno

msg,srv,actionはまずidlファイルに変換される。msgなどの仕様は About ROS2 interfacesを、idlの仕様についてはIDL - Interface Definition and Language Mappingを参照。OMG IDL 4.2 specificationのサブセットになっている。ROS2のドキュメントでの参照は7章からになっているが、これはIDLの文法の説明が7章からであるため。文法はEBNFで記述されているため、IDLパーサーを作る際はこれを素直に記述した方が良さそう。

nonanonnononanonno

IDL変換 - 基本形

msgなどを変換すると基本的に以下のような構造になる

MyMsg.idl
/// コメント

module my_package {  // <- パッケージ名
  module msg {  // <- msg|srv|action

    typedef my_package::msg::Other my_pacakge__msg__Other; // 固定長配列用
    typedef my_package__msg__Other my_package__msg__Other__3[3]; 固定長配列用

    struct MyMsg {
      int32 int_value;
      my_package::msg::Other other_value; // 同一パッケージであっても名前空間付き
      sequence<my_package::msg::Other> dynamic_array; // 動的配列はテンプレートに
      my_package__msg__Other__3 static_array; // ↑ でtypedef されたものが使われる
    };
  };
};

定数を定義している場合は追加の名前空間が発生する。

MyConstants.idl
module my_package {
  module msg {
    module MyConstants_Constants {
      const boolean BOOL_CONST = TRUE;
    };
    struct MyConstants {
      ...
    };
  };
};

デフォルト値はアノテーションによって付与される。

MyDefaults.idl
module my_package {
  module msg {
    module MyDefaults {
      @default (value=TRUE)
      boolean bool_value;
    };
  };
};

モジュールのネストは基本的に2つ(パッケージ名、メッセージの種別)か3つ(+定数)だが、ベンダー依存っぽいパッケージで例外的に多いものがあったはず。ぱっと見つからなかったので見つかったら追記する。

nonanonnononanonno

msg -> idl

↑ で説明した通り。1つのIDLファイルに1つの構造体がある。

srv -> idl

1つのIDLファイルに以下のような2つの構造体が作られる。

MySrv.idl
module my_package {
  module srv {
    struct MySrv_Request {
      int32 int_request;
    };
    struct MySrv_Response {
      int32 int_response;
    };
  };
};

action -> idl

1つのIDLファイルに以下のような3つの構造体が作られる。

MyAction.idl
module my_package {
  module action {
    struct Fibonacci_Goal {
      int32 order;
    };
    struct Fibonacci_Result {
      sequence<int32> sequence;
    };
    struct Fibonacci_Feedback {
      sequence<int32> sequence;
    };
  };
};
nonanonnononanonno

構造体

IDLにおける各構造体(+α)に対してそれぞれ

  • 定数の定義(定数は構造体に紐付くのでsrvのように2つある場合は2箇所に表れる)
  • include の記述(依存している定義に基づく。重複した場合はコメントアウト(雑では…)
  • 構造体を定義
  • 動的配列用の構造体を定義

を行う。actionの場合、IDLに記述されている(goal,result,feedback)ものだけではなく、actionのプロトコル用に増える。

rosidl idl c
msg MyMsg MyMsg
srv MySrv_Request
MySrv_Response
MySrv_Request
MySrv_Response
action MyAction_Goal
MyAction_Result
MyAction_Feedback
MyAction_Goal
MyAction_Result
MyAction_Feedback
MyAction_SendGoal_Request
MyAction_SendGoal_Response
MyAction_GetResult_Request
MyAction_GetResult_Response
MyAction_FeedbackMessage

ここでは省略しているが、構造体は名前空間に基づいて修飾される。これは、[*namespaces, struct].join('__')みたいな感じになる。典型的には<pkg>__<type>__<struct>

nonanonnononanonno

Actionの関係性。Actionは、実態としてはServiceとMessageの組み合わせで実現されている。そのための情報が付与される。

nonanonnononanonno

関数

構造体毎に関数を作る。msg,srv,actionに違いはない。配列用と配列でないもの用(構造体用とする)とで組み合わせに違いはないが、引数が若干違う。

構造体用

  • bool MyMsg__init(MyMsg * msg)
  • void MyMsg__fini(MyMsg * msg)
  • MyMsg* MyMsg__create()
  • void MyMsg__destroy(MyMsg * msg)
  • bool MyMsg__are_equal(const MyMsg * lhs, const MyMsg * rhs)
  • bool MyMsg__copy(const MyMsg * input, MyMsg * output)

配列用

  • bool MyMsg__Sequence__init(MyMsg__Sequence * array, size_t size)
  • void MyMsg__Sequence__fini(MyMsg__Seqnence * array)
  • MyMsg__Sequence * MyMsg__Sequence__create(size_t size)
  • void MyMsg__Sequence__destroy(MyMsg__Sequence * array)
  • bool MyMsg__Sequence__are_equal(const MyMsg__Sequence * hls, const MyMsg__Sequence * rhs)
  • bool MyMsg__Sequence__copy(const MyMsg__Sequence * input, MyMsg__Seqnence * output)

なお、are_equalcopyは 2022年1月以降に増えたもので、Foxyにもバックポートされている。

nonanonnononanonno

Typesupport

Typesupportはmsg,srv,actionで若干変化がある。

msg

const rosidl_message_type_support_t *
ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(
  rosidl_typesupport_c,
  @(',\n  '.join(message.structure.namespaced_type.namespaced_name()))
)();

ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAMEはマクロ関数、D関数と考えると以下のようになる。

string ROSIDL_TYPESUPPORT_INTERFACE__MESSAGE_SYMBOL_NAME(
  string typesupport_name,
  string package_name,
  string interface_type,
  string message_name
) {
  return [
    typesupport_name,
    "get_message_type_support_handle",
    package_name,
    interface_type,
    message_name
  ].join("__");
}

srv

RequestとResponse構造体に対して msgと同じTypesupport関数、それからsrv向けの関数が追加される。

const rosidl_service_type_support_t *
ROSIDL_TYPESUPPORT_INTERFACE__SERVICE_SYMBOL_NAME(
  rosidl_typesupport_c,
  @(',\n  '.join(service.namespaced_type.namespaced_name()))
)();

mypkg::srv::MySrvがあった時、

  • mypkg::srv::MySrv__Request -> msg用
  • mypkg::srv::MySrv__Response -> msg用
  • mypkg::srv::MySrv -> srv用

となる。

ROSIDL_TYPESUPPORT_INTERFACE__SERVICE_SYMBOL_NAMEもまたマクロ関数で、D関数で考えると以下のようになる。

string ROSIDL_TYPESUPPORT_INTERFACE__SERVICE_SYMBOL_NAME (
  string typesupport_name,
  string package_name,
  string interface_type,
  string service_name
) {
  return [
    typesupport_name,
    "get_service_type_support_handle",
    package_name,
    interface_type,
    message_name
  ].join("__");
}

action

actionはmsgとsrvの組み合わせなので、それらのTypesupport と action用の関数が追加される。

const rosidl_action_type_support_t *
ROSIDL_TYPESUPPORT_INTERFACE__ACTION_SYMBOL_NAME(
  rosidl_typesupport_c,
  @(',\n  '.join(action.namespaced_type.namespaced_name()))
)();

mypkg::action::MyActionがあった時、

  • mypkg::action::MyAction_Goal -> msg用
  • mypkg::action::MyAction_Result -> msg用
  • mypkg::action::MyAction_Feedback -> msg用
  • mypkg::action::MyAction_SendGoal -> srv用
  • mypkg::action::MyAction_GetResult -> srv用
  • mypkg::action::MyAction_FeedbackMessage -> msg用
  • mypkg::action::MyAction -> action用

となる。

ROSIDL_TYPESUPPORT_INTERFACE__ACTION_SYMBOL_NAMEもまたマクロ関数で、D関数で考えると以下のようになる。

string ROSIDL_TYPESUPPORT_INTERFACE__SERVICE_SYMBOL_NAME (
  string typesupport_name,
  string package_name,
  string interface_type,
  string service_name
) {
  return [
    typesupport_name,
    "get_action_type_support_handle",
    package_name,
    interface_type,
    message_name
  ].join("__");
}