mc_rtc::Configuration 設定mc_rtc::Schema の紹介JSON Schemaは、JSONオブジェクトから指定されたデータ構造への期待されるデータ形式を文書化する仕様です。
mc_rtcでは、オブジェクトをフレームワークにロードする際の期待されるデータを文書化するために使用されます。
mc_rtc内でコードを記述する際に、おそらくmc_rtc::Configuration 設定オブジェクトから多くのパラメータをロードするシナリオに遭遇するでしょう。
この構成オブジェクトをデータ構造にロードするだけでなく、オブジェクトの現在の状態をmc_rtc::Configurationファイルに保存し、それをディスクに保存すること、パラメータをオンラインで編集できるようにすること、構成形式を文書化することにも関心を持つでしょう。
すべてのコードを手動で記述することは可能ですが、特に構造からフィールドを追加または削除する場合は面倒でエラーが発生しやすいです。
これがmc_rtc::Schemaが登場する場所です。その目標は、シンプルな構造のように振る舞いながらも、追加の機能を備えた構造を記述できるようにすることです。
#include <mc_rtc/Schema.h>
struct SimpleSchema
{
MC_RTC_NEW_SCHEMA(SimpleSchema)
#define MEMBER(...) MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER(SimpleSchema, __VA_ARGS__)
MEMBER(bool, useFeature, "魔法の機能を使用する")
MEMBER(double, weight, "タスクの重み")
MEMBER(std::vector<std::string>, names, "いくつかの名前")
MEMBER(sva::ForceVecd, wrench, "目標トルク")
MEMBER(sva::PTransformd, pt, "いくつかの変換")
#undef MEMBER
};
MEMBERマクロを使用する場合は、以下のプリプロセッサの制限に注意してください。
プリプロセッサの制限により、複数のテンプレート・パラメータに依存する型をマクロに直接渡すことはできません。例えば、以下のようにするとコンパイルエラーになります。
MEMBER(std::map<std::string, double>, jointValues, "関節名と値のマップ")
これは、プリプロセッサが引数を次のように分割するために発生します:
std::map<std::stringstd::map<std::stringjointValues"関節名と値のマップ"これを避けるには、明示的に型のエイリアスを指定します。
using MapType = std::map<std::string, double>;
MEMBER(MapType, jointValues, "関節の名前と値のマップ")
このオプションを使用できない場合は、代わりに BOOST_IDENTIY_TYPE を使用することができます。
上記のコードをMicrosoft Visual C++ Compiler(MSVC)で使用する場合、MEMBERの定義を次のように変更する必要があります:
#define MEMBER(...) MC_RTC_PP_ID(MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER(SimpleSchema, __VA_ARGS__))
これは、MSVCのプリプロセッサの問題を回避するためのものです。
この方法で定義した構造は通常のC++構造体として使用されます:
void do_foo(const sva::ForceVecd &);
// この構造体はデフォルトで構築できます(デフォルトについては後で説明します)
SimpleSchema simple;
// メンバーをシンプルな構造体としてアクセスできます
do_foo(simple.wrench);
// メンバーは指定した型のものなので、次のようにもできます
do_foo(simple.pt * simple.wrench);
ただし、この構造には追加の機能が組み込まれています:
// mc_rtc::Configuration値からロード
simple.load(config);
// mc_rtc::Configurationオブジェクトに保存
simple.save(config);
// mc_rtc::Configurationのユーザー定義変換も利用できます
config.add("simple", simple);
SimpleSchema simple2 = config("simple");
// コンソールに出力
mc_rtc::log::info("simple:\n{}", simple.dump(true, true));
// simpleをインプレースで編集するGUIフォームを追加
simple.addToGUI(*controller.gui(), {"フォームカテゴリ"}, "フォーム名",
[this]() {
// この時点でsimpleオブジェクトは新しい値で更新されています
on_simple_update();
});
また、集成初期化または指定された初期化(C++20から)を使用して構造を初期化することもできます:
SimpleSchema simple{true, 42.42, {"a", "b"}, wrench, pt};
最後に、ここで作成した構造体は、手動で作成した単純な構造体と同じサイズです。
MC_RTC_SCHEMA_MEMBER マクロ前の例では、MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER マクロを使用しました。実際には、より一般的な MC_RTC_SCHEMA_MEMBER マクロがあり、そのシグネチャは次のようになっています:
MC_RTC_SCHEMA_MEMBER(T, TYPE, NAME, DESCRIPTION, FLAGS, DEFAULT, ...)
パラメータの使用方法は次のとおりです:
Tはこのメンバーが表示されるスキーマオブジェクトの型です。TYPEはメンバー変数の型です。NAMEはメンバー変数の名前です。DESCRIPTIONはメンバー変数のドキュメント文字列であり、生成されたフォームでも使用されます。FLAGSはメンバーに関する追加情報を追加するために使用され、メンバーが必要かどうかなどを特に示します。DEFAULTはこのメンバーのデフォルト値を指定します。... / NAMESはこれらの追加のパラメータで、2つの目的に使用されます:
TYPEがstd::stringの場合、有効な値を提供するために使用できます。TYPEがstd::variant<T...>の場合、変数の代替名を割り当てるために使用されます。TYPEについて少なくともロード/セーブのシナリオでは、どの TYPE でも使用できることを意図していますが、フォームベースのエディションではすべての TYPE がサポートされるとは限りません。
もし、ある TYPE がサポートされていない場合、コンパイルエラーが発生します。
現在のところ、mc_rtc::Configurationオブジェクトを通してロード/セーブできるほとんどの型と、スキーマベースの型、そのような型の std::vector と std::map がサポートされている。
以下はそのような型を使用した例である:
struct ComposeSchema
{
MC_RTC_NEW_SCHEMA(ComposeSchema)
#define MEMBER(...) MC_RTC_PP_ID(MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER(ComposeSchema, __VA_ARGS__))
MEMBER(int, integer, "整数値")
MEMBER(SimpleSchema, simple, "シンプルなスキーマ")
MEMBER(std::vector<SimpleSchema>, many, "複数のシンプルスキーマ")
#undef MEMBER
};
FLAGSについて現在、2つのフラグがあります:
mc_rtc::schema::Requiredmc_rtc::schema::Interactivemc_rtc::schema::Requiredメンバーが必要な場合:
それ以外の場合、メンバーは設定で存在する場合にのみ読み込まれます。メンバーは常にConfigurationオブジェクトに保存されますが、必要かどうかにかかわらずです。
mc_rtc::schema::Interactiveスキーマは次の型を対話型として扱うことができます:
Eigen::Vector3dsva::PTransformdこれらの型のメンバーがフォームに追加されると、対話型フォーム要素が使用されます。ただし、これが常に意味があるわけではありません。たとえば、3DゲインはEigen::Vector3dで表すことができ、この値を編集するために3Dマーカーを使用する必要はありません。
この動作を制御するには、対応する対話型フラグを設定します。基本的なMC_RTC_SCHEMA_MEMBER以外のすべてのマクロでは、フラグがデフォルトで設定されています。
次の例は、MC_RTC_SCHEMA_MEMBERマクロの呼び出しでこれらのフラグを使用する方法を示しています:
struct InteractiveSchema
{
MC_RTC_NEW_SCHEMA(InteractiveSchema)
#define MEMBER(...) MC_RTC_PP_ID(MC_RTC_SCHEMA_MEMBER(InteractiveSchema, __VA_ARGS__))
MEMBER(Eigen::Vector3d,
point,
"3Dポイント",
mc_rtc::schema::Required | mc_rtc::schema::Interactive,
Eigen::Vector3d::Zero())
MEMBER(Eigen::Vector3d, gain, "3Dゲイン", mc_rtc::schema::Required, Eigen::Vector3d::Ones())
MEMBER(Eigen::Vector3d, gainOpt, "3Dオプショナルゲイン", mc_rtc::schema::None, Eigen::Vector3d::Zero())
#undef MEMBER
};
DEFAULTについてこれにより、メンバーのデフォルト値を指定できます。代入演算子の右側で有効な値であれば、何でも構いません。DEFAULTマクロを使用している場合、これはmc_rtc::Default
0(したがってboolの場合はfalse)sva::PTransformdの場合は単位変換行列sva::ForceVecd、sva::MotionVecd、sva::ImpedanceVecd、sva::AdmittanceVecdの場合はゼロstd::stringの場合は空の文字列std::map<K, V> の場合std::variant<T>の場合はmc_rtc::Default<T>ですEigen::MatrixXd や Eigen::VectorXd などの型については、適切なデフォルト値を推測することができないため、明示的に指定する必要があります。これらの要素には MC_RTC_SCHEMA_MEMBER を使用してください。
MC_RTC_SCHEMA_MEMBER(MySchema, Eigen::MatrixXd, matrix, "Some matrix", mc_rtc::schema::Required, Eigen::MatrixXd::Identity(12, 4))
NAMESについてこれらの追加パラメータには2つの可能な使用例があります:
TYPEがstd::stringの場合、有効な値のリストを提供する場合TYPEがstd::variant<T...>の場合、バリアントの代替名を割り当てる場合この例では、バリアントとして表されるゲインを持っています。これはdoubleスカラーまたはEigen::Vector3dである可能性があります。mc_rtc::schema::ChoicesがMEMBERマクロに渡され、これらの名前がGUIで選択肢を表示する際に使用されます:
struct SimpleVariant
{
MC_RTC_NEW_SCHEMA(SimpleVariant)
using gain_t = std::variant<double, Eigen::Vector3d>;
#define MEMBER(...) MC_RTC_PP_ID(MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER(SimpleVariant, __VA_ARGS__))
MEMBER(gain_t, stiffness, "タスク剛性", mc_rtc::schema::Choices({"スカラー", "次元"}))
#undef MEMBER
};
MC_RTC_NEW_SCHEMA および MC_RTC_SCHEMA マクロこれまでのすべての例で、MC_RTC_NEW_SCHEMAを使用してスキーマ宣言を導入しました。その名前が示すように、このマクロは新しいスキーマ宣言を導入するために使用されます。
このマクロは、作成している構造体の名前を単一の引数として受け取ります。
ただし、スキーマを追加のメンバーで拡張する場合、MC_RTC_SCHEMAマクロを代わりに使用することがあります:
struct ExtendedSchema : public SimpleSchema
{
MC_RTC_SCHEMA(ExtendedSchema, SimpleSchema)
#define MEMBER(...) MC_RTC_PP_ID(MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER(ExtendedSchema, __VA_ARGS__))
MEMBER(double, extendedGain, "拡張アルゴリズムのゲイン")
#undef MEMBER
};
このようにして、スキーマを拡張することができます。