0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ROS 2メッセージとJSONを相互変換するためのC++ライブラリ json_converter の紹介

Last updated at Posted at 2025-12-18

はじめに

ROS 2 Advent Calendar 19日目の記事です。

本記事では、ROS 2メッセージとJSONデータの相互変換機能を提供するC++ライブラリである json_converter_cpp を紹介します。Pythonには既に類似のライブラリ rclpy_message_converter がありますが、本ライブラリはC++環境で、ROS 2システムと外部データ形式との連携や、メッセージデータの永続化などを可能にするために開発しました。

C++でROS 2メッセージをJSONで扱うための他のアプローチとして、以下のプロジェクトも参考になります。

使い方

ビルド方法

json_converter_cpp は、標準的なROS 2パッケージとして構成されています。ビルドには、ROS 2の依存関係に加え、JSON処理ライブラリとして RapidJSON が必要です。

使い方サンプル (C++)

ROS 2メッセージとJSONを相互変換するC++での基本的な組み込み方法を、型が既知の場合と実行時に決まる場合に分けて示します。

1. 型がコンパイル時に既知の場合

標準のROS 2メッセージ型(例: std_msgs::msg::String)を直接扱う方法です。

#include "json_converter_cpp/converter.hpp"
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <iostream>
#include <std_msgs/msg/string.hpp> // 必要なメッセージヘッダ

json_converter_cpp::Converter converter;

// --- ROS 2 messageからJSONへの変換 ---
std_msgs::msg::String msg;
msg.data = "hello";

rapidjson::Document doc;
doc.SetObject();
auto& allocator = doc.GetAllocator();

// 変換実行
if (converter.to_json(msg, doc, allocator)) {
  rapidjson::StringBuffer buffer;
  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  doc.Accept(writer);
  std::cout << "Message to JSON: " << buffer.GetString() << std::endl; // 出力: {"data":"hello"}
}

// --- JSONからROS 2 messageへの変換 ---
rapidjson::Document input;
input.Parse(R"({"data": "world"})");

std_msgs::msg::String output;
// 変換実行
if (converter.to_msg(input, output)) {
  std::cout << "JSON to Message: " << output.data << std::endl; // 出力: world
}

2. 型が実行時に決まる場合 (SerializedMessage)

トピック受信やrosbagからの読み込みなど、シリアライズされたデータ(rclcpp::SerializedMessage)を、型名を指定して扱う方法です。

#include "json_converter_cpp/converter.hpp"
#include <rclcpp/serialization.hpp>
#include <rapidjson/document.h>
#include <rapidjson/stringbuffer.h>
#include <rapidjson/writer.h>
#include <iostream>

json_converter_cpp::Converter converter;

// --- SerializedMessageからJSONへの変換 (実行時の動的型読み込み) ---
rclcpp::SerializedMessage serialized_msg;
// ... (トピックやrosbagなどから serialized_msg を受信/ロードする処理) ...
// ここでは、std_msgs/msg/String::data="test_data" がシリアライズされていると仮定

rapidjson::Document doc;
doc.SetObject();
auto& allocator = doc.GetAllocator();

// 変換実行: 型名とSerializedMessageを指定
if (converter.to_json("std_msgs/msg/String", serialized_msg, doc, allocator)) {
  rapidjson::StringBuffer buffer;
  rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
  doc.Accept(writer);
  std::cout << "SerializedMessage to JSON: " << buffer.GetString() << std::endl; // 出力: {"data":"test_data"}
}

// --- JSONからSerializedMessageへの変換 ---
rapidjson::Document input;
input.Parse(R"({"data": "world"})");

rclcpp::SerializedMessage output;
// 変換実行: 型名と出力用SerializedMessageを指定
if (converter.to_msg("std_msgs/msg/String", input, output)) {
  // ... (シリアライズされた output をパブリッシュしたり利用したりする処理) ...
  std::cout << "JSON to SerializedMessage successful." << std::endl;
}

3. 型が実行時に決まる場合 (Generic Client/Service向け)

rclcpp::GenericClient など、デシリアライズはしたいが、具体的なメッセージ型が実行時に決まる場合に、std::shared_ptr<void>void* を介してメッセージオブジェクトを扱う方法です。

#include "json_converter_cpp/converter.hpp"
#include <rapidjson/document.h>
#include <iostream>
#include <memory>

json_converter_cpp::Converter converter;
// サービスの型を例として使用: example_interfaces/srv/AddTwoInts

// --- JSONからメッセージオブジェクトへの変換 ---
rapidjson::Document request_doc;
request_doc.Parse(R"({"a": 5, "b": 3})");

std::shared_ptr<void> request_msg;
// 変換実行: 型名とJSONドキュメント、出力用メッセージポインタを指定
if (converter.to_msg("example_interfaces/srv/AddTwoInts::Request", request_doc, request_msg)) {
  // request_msgをGenericClientなどで利用
  std::cout << "JSON to Service Request Message successful." << std::endl;
}

// --- メッセージオブジェクトからJSONへの変換 ---
// GenericClientから受け取ったレスポンスオブジェクトのポインタを仮定
// (例: example_interfaces/srv/AddTwoInts::Response のデータで sum=8 を含む)
int sum_value = 8;
void* response_ptr = &sum_value; // 簡略化のためintポインタを使用していますが、実際はメッセージオブジェクトのポインタ

rapidjson::Document response_doc;
response_doc.SetObject();
auto& allocator = response_doc.GetAllocator();

// 変換実行: 型名とメッセージポインタ、出力用JSONドキュメントを指定
if (converter.to_json("example_interfaces/srv/AddTwoInts::Response", response_ptr,
                      response_doc, allocator)) {
  // Output: {"sum":8}
  // (JSON出力コードを省略)
  std::cout << "Message Object to JSON successful." << std::endl;
}

サンプルツールの応用例(Generic Pub/Sub, Service Client)

json_converter のリポジトリでは、ライブラリの利用方法を具体的に示すための汎用的なCLIツールが提供されています。これらは、JSONデータを用いた通信の実際のフローを確認するための応用例となります。

  • Generic Publisher/Subscriber: 任意のトピック名とメッセージ型を指定し、JSONファイルから読み込んだデータをROS 2メッセージとしてパブリッシュしたり、受信したメッセージをJSON形式で出力したりするツールとして応用できます。
  • Generic Service Client: JSON形式でリクエストデータを準備し、任意のサービスへコールを実行したり、レスポンスをJSONとして受け取ったりするクライアントツールとして応用できます。

使い方の詳細は、リポジトリのREADMEを参照してください。これらのサンプルツールは、ROS 2と外部システム間のデータ連携レイヤーを構築する際の、具体的な実装のヒントとして活用できます。

設計

json_converter_cpp は、ROS 2の typesupport の仕組みを活用して、メッセージとJSONの相互変換を実現しています。

変換の仕組み

内部的には、以下の2つのtypesupportライブラリを組み合わせて実装されています。

1. rosidl_typesupport_introspection_cpp

ROS 2メッセージの型情報(メタデータ)を提供します。これには、フィールド名(例: "x", "y", "z")、各フィールドの型、メモリ上の配置(オフセット)などが含まれます。

この情報を使用することで、C++ Message Object ↔ JSON の直接的な変換を実現しています。

2. rosidl_typesupport_cpp

SerializedMessage ↔ C++ Message Object の変換を行います。

SerializedMessage は、ネットワーク通信やrosbagでの保存に使われる、RMW実装に依存したバイト列(CDR形式など)です。rosidl_typesupport_cppは、実行時に選択されたRMW実装に対応するシリアライザ/デシリアライザを切り替えて提供してくれるので、それをrclcpp::SerializationBaseに渡して、シリアライズ/デシリアライズをおこなっています。

SerializedMessageとJSONの変換フロー

SerializedMessage とJSONを変換する際、json_converter_cpp は、上記2つのtypesupportを連携させて、以下のフローで処理を行います。

SerializedMessage
↓ rosidl_typesupport_cpp でデシリアライズ
C++ Message Object
↓ rosidl_typesupport_introspection_cpp で変換
JSON

技術的には SerializedMessage から直接JSONに変換することも可能ですが、本ライブラリではCDR形式などのRMW実装依存の詳細から分離し、安定した中間形式であるC++ Message Objectを経由する方法を採用しています。

まとめ

本記事では、ROS 2メッセージとJSONの相互変換を目的としたC++ライブラリ json_converter_cpp の概要と基本的な利用方法、そしてその設計思想を紹介しました。ROS 2システムの外部システム連携やデータ永続化の仕組みを構築する際の参考として、ぜひご活用ください。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?