diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 5214ffe094..323b64e9f2 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -41,7 +41,8 @@ repos:
src/mc_control/samples/ExternalForces/etc/ExternalForces.in.yaml|
src/mc_control/samples/LIPMStabilizer/etc/LIPMStabilizer.in.yaml|
src/mc_control/fsm/states/data/StabilizerStanding.yaml|
- tests/controllers/TestPythonState.in.yaml
+ tests/controllers/TestPythonState.in.yaml|
+ tests/global_controller_configuration/mc_rtc.in.yaml
)$
- id: debug-statements
- id: destroyed-symlinks
diff --git a/doc/_i18n/en.yml b/doc/_i18n/en.yml
index c66d65df16..8a8d6bd4f6 100644
--- a/doc/_i18n/en.yml
+++ b/doc/_i18n/en.yml
@@ -178,3 +178,4 @@ tutorials:
title: Supporting MC_RTC_BUILD_STATIC in your code
debug-lssol-output-6:
title: Debugging LSSOL output 6
+or: or
diff --git a/doc/_i18n/en/tutorials/introduction/configuration.html b/doc/_i18n/en/tutorials/introduction/configuration.html
index 51f4f95277..b07522c293 100644
--- a/doc/_i18n/en/tutorials/introduction/configuration.html
+++ b/doc/_i18n/en/tutorials/introduction/configuration.html
@@ -30,7 +30,13 @@
These entries cover most needs you might have |
- {% include mc_rtc_configuration_row.html entry="MainRobot" desc="This entry dictates the main robot used by all controllers. Most interface cannot infer the correct module to use based on the simulation environment so this is the user's responsibility to make sure the two match." example="MainRobot: JVRC1" %}
+{% capture MainRobot_example2 %}
+# Support renaming the robot
+MainRobot:
+ name: MyRobot
+ module: JVRC1
+{% endcapture %}
+ {% include mc_rtc_configuration_row.html entry="MainRobot" desc="This entry dictates the main robot used by all controllers. Most interface cannot infer the correct module to use based on the simulation environment so this is the user's responsibility to make sure the two match." example="MainRobot: JVRC1" example2=MainRobot_example2%}
Enabled |
Provides a list of enabled controllers. See the list of all available sample controllers. |
diff --git a/doc/_i18n/jp.yml b/doc/_i18n/jp.yml
index a67b44f36f..955a464819 100644
--- a/doc/_i18n/jp.yml
+++ b/doc/_i18n/jp.yml
@@ -179,3 +179,4 @@ tutorials:
title: MC_RTC_BUILD_STATICをプログラムでサポートする方法
debug-lssol-output-6:
title: LSSOL output 6のデバッグ
+or: または
diff --git a/doc/_i18n/jp/tutorials/introduction/configuration.html b/doc/_i18n/jp/tutorials/introduction/configuration.html
index 17089bf4a7..125c4fd683 100644
--- a/doc/_i18n/jp/tutorials/introduction/configuration.html
+++ b/doc/_i18n/jp/tutorials/introduction/configuration.html
@@ -30,7 +30,13 @@
このエントリは、通常考えられるほとんどのニーズをカバーします。 |
- {% include mc_rtc_configuration_row.html entry="MainRobot" desc="このエントリは、すべてのコントローラーで使用されるメインロボットを指定します。ほとんどのインターフェイスは、どのモジュールを使用すべきかをシミュレーション環境に基づいて推測することはできません。そのため、シミュレーション環境に応じてユーザーがモジュールを指定する必要があります。" example="MainRobot: JVRC1" %}
+{% capture MainRobot_example2 %}
+# ロボット名の変更をサポート
+MainRobot:
+ name: MyRobot
+ module: JVRC1
+{% endcapture %}
+ {% include mc_rtc_configuration_row.html entry="MainRobot" desc="このエントリは、すべてのコントローラーで使用されるメインロボットを指定します。ほとんどのインターフェイスは、どのモジュールを使用すべきかをシミュレーション環境に基づいて推測することはできません。そのため、シミュレーション環境に応じてユーザーがモジュールを指定する必要があります。" example="MainRobot: JVRC1" example2=MainRobot_example2 %}
Enabled |
有効なコントローラーのリストを提供します。利用可能なサンプルコントローラーのリストを参照してください。. |
diff --git a/doc/_includes/mc_rtc_configuration_row.html b/doc/_includes/mc_rtc_configuration_row.html
index 338e7a9b5f..0ee5d693a7 100644
--- a/doc/_includes/mc_rtc_configuration_row.html
+++ b/doc/_includes/mc_rtc_configuration_row.html
@@ -1,5 +1,11 @@
{{include.entry}} |
{{include.desc}} |
- {% highlight yaml %}{{include.example}}{% endhighlight %} |
+
+ {% highlight yaml %}{{include.example}}{% endhighlight %}
+ {% if include.example2 %}
+ {% t or %}
+ {% highlight yaml %}{{include.example2}}{% endhighlight %}
+ {% endif %}
+ |
diff --git a/include/mc_control/mc_global_controller.h b/include/mc_control/mc_global_controller.h
index 03001827a0..4454836d17 100644
--- a/include/mc_control/mc_global_controller.h
+++ b/include/mc_control/mc_global_controller.h
@@ -15,9 +15,6 @@
#include
#include
-#include
-#include
-#include
namespace mc_control
{
@@ -878,7 +875,6 @@ struct MC_CONTROL_DLLAPI MCGlobalController
*/
bool running = false;
-public:
/*! \brief Store the controller configuration */
struct MC_CONTROL_DLLAPI GlobalConfiguration
{
@@ -887,29 +883,33 @@ struct MC_CONTROL_DLLAPI MCGlobalController
* \param conf Configuration file that should be loaded
*
* \param Main robot module, if null use the MainRobot entry in conf to initialize it
+ *
+ * \param conf_only If true, only load the specified configuration file
*/
- GlobalConfiguration(const std::string & conf, std::shared_ptr rm = nullptr);
+ GlobalConfiguration(const std::string & conf,
+ std::shared_ptr rm = nullptr,
+ bool conf_only = false);
inline bool enabled(const std::string & ctrl);
bool verbose_loader = true;
bool init_attitude_from_sensor = false;
- std::string init_attitude_sensor = "";
+ std::string init_attitude_sensor;
- std::vector robot_module_paths = {};
+ std::vector robot_module_paths;
std::shared_ptr main_robot_module;
- std::vector observer_module_paths = {};
+ std::vector observer_module_paths;
- std::vector global_plugin_paths = {};
- std::vector global_plugins = {};
- std::vector global_plugins_autoload = {};
+ std::vector global_plugin_paths;
+ std::vector global_plugins;
+ std::vector global_plugins_autoload;
std::unordered_map global_plugin_configs;
- std::vector controller_module_paths = {};
- std::vector enabled_controllers = {};
- std::string initial_controller = "";
+ std::vector controller_module_paths;
+ std::vector enabled_controllers;
+ std::string initial_controller;
std::unordered_map controllers_configs;
double timestep = 0.002;
bool include_halfsit_controller = true;
@@ -921,8 +921,8 @@ struct MC_CONTROL_DLLAPI MCGlobalController
bool enable_gui_server = true;
double gui_timestep = 0.05;
- std::vector gui_server_pub_uris{};
- std::vector gui_server_rep_uris{};
+ std::vector gui_server_pub_uris;
+ std::vector gui_server_rep_uris;
Configuration config;
@@ -936,8 +936,8 @@ struct MC_CONTROL_DLLAPI MCGlobalController
private:
using duration_ms = std::chrono::duration;
GlobalConfiguration config;
- std::string current_ctrl = "";
- std::string next_ctrl = "";
+ std::string current_ctrl;
+ std::string next_ctrl;
MCController * controller_ = nullptr;
MCController * next_controller_ = nullptr;
std::unique_ptr> controller_loader_;
@@ -945,7 +945,7 @@ struct MC_CONTROL_DLLAPI MCGlobalController
std::vector observers_;
std::map observersByName_;
- std::unique_ptr server_ = nullptr;
+ std::unique_ptr server_;
std::unique_ptr> plugin_loader_;
struct PluginHandle
@@ -981,7 +981,7 @@ struct MC_CONTROL_DLLAPI MCGlobalController
void start_log();
void setup_log();
void setup_plugin_log();
- std::map setup_logger_ = {};
+ std::map setup_logger_;
/** Timers and performance measure */
duration_ms global_run_dt{0};
diff --git a/include/mc_rbdyn/RobotLoader.h b/include/mc_rbdyn/RobotLoader.h
index 56198f40e9..4d5bd5cd5a 100644
--- a/include/mc_rbdyn/RobotLoader.h
+++ b/include/mc_rbdyn/RobotLoader.h
@@ -132,6 +132,7 @@ struct MC_RBDYN_DLLAPI RobotLoader
std::lock_guard guard{mtx};
init(true);
robot_loader->clear();
+ aliases.clear();
}
/** Check if a robot is available
diff --git a/src/mc_control/MCController.cpp b/src/mc_control/MCController.cpp
index d2b435804a..32239faeee 100644
--- a/src/mc_control/MCController.cpp
+++ b/src/mc_control/MCController.cpp
@@ -115,7 +115,7 @@ static inline std::shared_ptr make_solver(double dt, MCCont
}
}
-MCController::MCController(const std::vector> & robots_modules,
+MCController::MCController(const std::vector> & robot_modules,
double dt,
const mc_rtc::Configuration & config,
ControllerParameters params)
@@ -129,7 +129,14 @@ MCController::MCController(const std::vectorlogger(logger_);
qpsolver->gui(gui_);
qpsolver->controller(this);
- for(auto rm : robots_modules) { loadRobot(rm, rm->name); }
+
+ std::string main_robot_name = config.find("MainRobot", "name").value_or(robot_modules[0]->name);
+ loadRobot(robot_modules[0], main_robot_name);
+ for(auto it = std::next(robot_modules.cbegin()); it != robot_modules.end(); ++it)
+ {
+ const auto & rm = *it;
+ loadRobot(rm, rm->name);
+ }
/* Load robot-specific configuration depending on parameters */
auto load_robot_config_into = config;
if(params.load_robot_config_)
@@ -192,7 +199,7 @@ MCController::MCController(const std::vectoraddCollisions(solver(), robots_modules[0]->minimalSelfCollisions());
+ selfCollisionConstraint->addCollisions(solver(), robot_modules[0]->minimalSelfCollisions());
compoundJointConstraint.reset(new mc_solver::CompoundJointConstraint(robots(), 0, timeStep));
postureTask = std::make_shared(solver(), 0, 10.0, 5.0);
/** Load additional robots from the configuration */
@@ -247,19 +254,15 @@ MCController::MCController(const std::vector{});
- mc_rbdyn::RobotModulePtr rm = nullptr;
- if(params.size() == 0) { rm = mc_rbdyn::RobotLoader::get_robot_module(module); }
- else if(params.size() == 1) { rm = mc_rbdyn::RobotLoader::get_robot_module(module, params.at(0)); }
- else if(params.size() == 2)
+ auto params = [&]() -> std::vector
{
- rm = mc_rbdyn::RobotLoader::get_robot_module(module, params.at(0), params.at(1));
- }
- else
- {
- mc_rtc::log::error_and_throw("Controller only handles robot modules that require two parameters at most");
- }
+ auto module = rconfig("module");
+ if(module.isArray()) { return module.operator std::vector(); }
+ std::vector params = rconfig("params", std::vector{});
+ params.insert(params.begin(), module.operator std::string());
+ return params;
+ }();
+ auto rm = mc_rbdyn::RobotLoader::get_robot_module(params);
if(!rm) { mc_rtc::log::error_and_throw("Failed to load {} as specified in configuration", rname); }
auto & robot = loadRobot(rm, rname);
load_robot_config(robot);
diff --git a/src/mc_control/Ticker.cpp b/src/mc_control/Ticker.cpp
index 888dacc46f..4ddee28e33 100644
--- a/src/mc_control/Ticker.cpp
+++ b/src/mc_control/Ticker.cpp
@@ -4,6 +4,8 @@
#include
+#include
+
#include
namespace bfs = boost::filesystem;
diff --git a/src/mc_control/mc_global_controller.cpp b/src/mc_control/mc_global_controller.cpp
index 1260e2068f..86cfed7202 100644
--- a/src/mc_control/mc_global_controller.cpp
+++ b/src/mc_control/mc_global_controller.cpp
@@ -26,8 +26,6 @@
#include
#include
-#include
-#include
namespace mc_control
{
@@ -35,12 +33,12 @@ namespace mc_control
MCGlobalController::PluginHandle::~PluginHandle() {}
MCGlobalController::MCGlobalController(const std::string & conf, std::shared_ptr rm)
-: MCGlobalController(GlobalConfiguration(conf, rm))
+: MCGlobalController(GlobalConfiguration(conf, std::move(rm)))
{
}
MCGlobalController::MCGlobalController(const GlobalConfiguration & conf)
-: config(conf), current_ctrl(""), next_ctrl(""), controller_(nullptr), next_controller_(nullptr)
+: config(conf), controller_(nullptr), next_controller_(nullptr)
{
// Display configuration information
if(conf.enable_gui_server)
diff --git a/src/mc_control/mc_global_controller_configuration.cpp b/src/mc_control/mc_global_controller_configuration.cpp
index e28414c7e1..ad81a611a5 100644
--- a/src/mc_control/mc_global_controller_configuration.cpp
+++ b/src/mc_control/mc_global_controller_configuration.cpp
@@ -3,9 +3,13 @@
*/
#include
+
#include
+
#include
+
#include
+#include
#include
namespace bfs = boost::filesystem;
@@ -16,28 +20,32 @@ namespace mc_control
{
MCGlobalController::GlobalConfiguration::GlobalConfiguration(const std::string & conf,
- std::shared_ptr rm)
+ std::shared_ptr rm,
+ bool conf_only)
{
- // Load default configuration file
- std::string globalPath(mc_rtc::CONF_PATH);
- if(bfs::exists(globalPath))
+ if(!conf_only)
{
- mc_rtc::log::info("Loading default global configuration {}", globalPath);
- config.load(globalPath);
- }
+ // Load default configuration file
+ std::string globalPath(mc_rtc::CONF_PATH);
+ if(bfs::exists(globalPath))
+ {
+ mc_rtc::log::info("Loading default global configuration {}", globalPath);
+ config.load(globalPath);
+ }
#ifndef WIN32
- auto config_path = bfs::path(std::getenv("HOME")) / ".config/mc_rtc/mc_rtc.conf";
+ auto config_path = bfs::path(std::getenv("HOME")) / ".config/mc_rtc/mc_rtc.conf";
#else
- // Should work for Windows Vista and up
- auto config_path = bfs::path(std::getenv("APPDATA")) / "mc_rtc/mc_rtc.conf";
+ // Should work for Windows Vista and up
+ auto config_path = bfs::path(std::getenv("APPDATA")) / "mc_rtc/mc_rtc.conf";
#endif
- // Load user's local configuration if it exists
- if(!bfs::exists(config_path)) { config_path.replace_extension(".yaml"); }
- if(bfs::exists(config_path))
- {
- mc_rtc::log::info("Loading additional global configuration {}", config_path.string());
- config.load(config_path.string());
+ // Load user's local configuration if it exists
+ if(!bfs::exists(config_path)) { config_path.replace_extension(".yaml"); }
+ if(bfs::exists(config_path))
+ {
+ mc_rtc::log::info("Loading additional global configuration {}", config_path.string());
+ config.load(config_path.string());
+ }
}
// Load extra configuration
if(bfs::exists(conf))
@@ -45,6 +53,7 @@ MCGlobalController::GlobalConfiguration::GlobalConfiguration(const std::string &
mc_rtc::log::info("Loading additional global configuration {}", conf);
config.load(conf);
}
+ else if(conf_only) { mc_rtc::log::error_and_throw("Required to load {} only but this is not available", conf); }
///////////////////////
// General options //
@@ -72,56 +81,46 @@ MCGlobalController::GlobalConfiguration::GlobalConfiguration(const std::string &
if(rm) { main_robot_module = rm; }
else
{
- if(!config.has("MainRobot") || config("MainRobot").size() == 0)
+ auto main_robot_params = [&]() -> std::vector
{
- std::string robot_name = config("MainRobot", std::string{"JVRC1"});
- if(mc_rbdyn::RobotLoader::has_robot(robot_name))
+ auto main_robot_cfg = config.find("MainRobot");
+ if(!main_robot_cfg) { return {"JVRC1"}; }
+ if(main_robot_cfg->isArray()) { return main_robot_cfg->operator std::vector(); }
+ if(main_robot_cfg->isObject())
{
- try
- {
- main_robot_module = mc_rbdyn::RobotLoader::get_robot_module(robot_name);
- }
- catch(const mc_rtc::LoaderException & exc)
- {
- mc_rtc::log::error_and_throw("Failed to create {} to use as a main robot", robot_name);
- }
- }
- else
- {
- mc_rtc::log::error_and_throw("Trying to use {} as main robot but this robot cannot be loaded", robot_name);
+ auto module_cfg = (*main_robot_cfg)("module");
+ if(module_cfg.isArray()) { return module_cfg.operator std::vector(); }
+ return {module_cfg.operator std::string()};
}
+ return {main_robot_cfg->operator std::string()};
+ }();
+ if(!mc_rbdyn::RobotLoader::has_robot(main_robot_params[0]))
+ {
+ mc_rtc::log::error_and_throw("No loadable robot with module {}", main_robot_params[0]);
}
- else
+ try
{
- std::vector params = config("MainRobot");
- if(mc_rbdyn::RobotLoader::has_robot(params[0]))
- {
- try
- {
- if(params.size() == 1) { main_robot_module = mc_rbdyn::RobotLoader::get_robot_module(params[0]); }
- else if(params.size() == 2)
- {
- main_robot_module = mc_rbdyn::RobotLoader::get_robot_module(params[0], params[1]);
- }
- else if(params.size() == 3)
- {
- main_robot_module = mc_rbdyn::RobotLoader::get_robot_module(params[0], params[1], params[2]);
- }
- else { throw mc_rtc::LoaderException("Too many parameters given to MainRobot"); }
- }
- catch(const mc_rtc::LoaderException &)
- {
- mc_rtc::log::error_and_throw("Failed to create main robot using parameters {}", config("MainRobot").dump());
- }
- }
- else
- {
- mc_rtc::log::error_and_throw("Trying to use {} as main robot but this robot cannot be loaded", params[0]);
- }
+ main_robot_module = mc_rbdyn::RobotLoader::get_robot_module(main_robot_params);
+ }
+ catch(const mc_rtc::LoaderException & exc)
+ {
+ mc_rtc::log::error_and_throw(
+ "[mc_rtc::LoaderException] Failed to create [{}] to use as a main robot, exception: {}",
+ mc_rtc::io::to_string(main_robot_params), exc.what());
+ }
+ catch(const std::exception & exc)
+ {
+ mc_rtc::log::error_and_throw("[std::exception] Failed to create [{}] to use as a main robot, exception: {}",
+ mc_rtc::io::to_string(main_robot_params), exc.what());
+ }
+ catch(...)
+ {
+ mc_rtc::log::error_and_throw("[General exception] Failed to create [{}] to use as a main robot",
+ mc_rtc::io::to_string(main_robot_params));
}
}
main_robot_module->expand_stance();
- if(main_robot_module->ref_joint_order().size() == 0) { main_robot_module->make_default_ref_joint_order(); }
+ if(main_robot_module->ref_joint_order().empty()) { main_robot_module->make_default_ref_joint_order(); }
/////////////////
// Observers //
diff --git a/src/mc_rbdyn/RobotLoader.cpp b/src/mc_rbdyn/RobotLoader.cpp
index b53a1c127a..ae0f75a5f4 100644
--- a/src/mc_rbdyn/RobotLoader.cpp
+++ b/src/mc_rbdyn/RobotLoader.cpp
@@ -98,12 +98,15 @@ void RobotLoader::init(bool skip_default_path)
if(!skip_default_path) { default_path.push_back(mc_rtc::MC_ROBOTS_INSTALL_PREFIX); }
robot_loader.reset(new mc_rtc::ObjectLoader("MC_RTC_ROBOT_MODULE", default_path, verbose_));
for(const auto & p : default_path) { handle_aliases_dir(bfs::path(p) / "aliases"); }
+ if(!skip_default_path)
+ {
#ifndef WIN32
- handle_aliases_dir(bfs::path(std::getenv("HOME")) / ".config/mc_rtc/aliases/");
+ handle_aliases_dir(bfs::path(std::getenv("HOME")) / ".config/mc_rtc/aliases/");
#else
- // Should work for Windows Vista and up
- handle_aliases_dir(bfs::path(std::getenv("APPDATA")) / "mc_rtc/aliases/");
+ // Should work for Windows Vista and up
+ handle_aliases_dir(bfs::path(std::getenv("APPDATA")) / "mc_rtc/aliases/");
#endif
+ }
}
catch(const mc_rtc::LoaderException & exc)
{
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 95d297dbe0..9920ddd8b7 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -252,3 +252,5 @@ target_link_libraries(gui_Schema PUBLIC mc_control)
mc_rtc_test(test_filters mc_filter SpaceVecAlg::SpaceVecAlg)
mc_rtc_test(test_interpolation mc_control)
+
+add_subdirectory(global_controller_configuration)
diff --git a/tests/global_controller_configuration/CMakeLists.txt b/tests/global_controller_configuration/CMakeLists.txt
new file mode 100644
index 0000000000..5814b4471b
--- /dev/null
+++ b/tests/global_controller_configuration/CMakeLists.txt
@@ -0,0 +1,56 @@
+set(ROBOT_MODULE_PATH "${CMAKE_BINARY_DIR}/src/mc_robots")
+if(CMAKE_CONFIGURATION_TYPES)
+ set(ROBOT_MODULE_PATH "${ROBOT_MODULE_PATH}/$")
+endif()
+
+function(make_configuration VAR_OUT MAIN_ROBOT)
+ set(CFG_CMAKE_OUT "${CMAKE_CURRENT_BINARY_DIR}/${VAR_OUT}/etc/mc_rtc.cmake.yaml")
+ set(CFG_OUT "${CMAKE_CURRENT_BINARY_DIR}/${VAR_OUT}/etc/$/mc_rtc.yaml")
+ configure_file(mc_rtc.in.yaml "${CFG_CMAKE_OUT}" @ONLY)
+ file(
+ GENERATE
+ OUTPUT "${CFG_OUT}"
+ INPUT "${CFG_CMAKE_OUT}"
+ )
+ set(${VAR_OUT}
+ "${CFG_OUT}"
+ PARENT_SCOPE
+ )
+endfunction()
+
+make_configuration(MAIN_ROBOT_SIMPLE_CFG "JVRC1")
+make_configuration(MAIN_ROBOT_SIMPLE_VECTOR_CFG "[JVRC1]")
+make_configuration(MAIN_ROBOT_OBJECT_CFG "{module: JVRC1, name: JVRC1}")
+make_configuration(MAIN_ROBOT_OBJECT_VECTOR_CFG "{module: [JVRC1], name: JVRC1}")
+
+set(CPP_CMAKE_OUT
+ "${CMAKE_CURRENT_BINARY_DIR}/testGlobalControllerConfiguration.cmake.cpp"
+)
+set(CPP_OUT
+ "${CMAKE_CURRENT_BINARY_DIR}/$/testGlobalControllerConfiguration.cpp"
+)
+configure_file(testGlobalControllerConfiguration.in.cpp "${CPP_CMAKE_OUT}" @ONLY)
+file(
+ GENERATE
+ OUTPUT "${CPP_OUT}"
+ INPUT "${CPP_CMAKE_OUT}"
+)
+
+add_executable(testGlobalControllerConfiguration "${CPP_OUT}")
+target_link_libraries(
+ testGlobalControllerConfiguration
+ PUBLIC mc_rtc::mc_control Boost::unit_test_framework Boost::disable_autolinking
+)
+if(NOT Boost_USE_STATIC_LIBS)
+ target_link_libraries(testGlobalControllerConfiguration PUBLIC Boost::dynamic_linking)
+endif()
+target_compile_definitions(
+ testGlobalControllerConfiguration PRIVATE -DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN
+)
+set_target_properties(
+ testGlobalControllerConfiguration PROPERTIES FOLDER tests/framework
+)
+add_test(NAME testGlobalControllerConfiguration
+ COMMAND testGlobalControllerConfiguration
+)
+generate_msvc_dot_user_file(testGlobalControllerConfiguration)
diff --git a/tests/global_controller_configuration/mc_rtc.in.yaml b/tests/global_controller_configuration/mc_rtc.in.yaml
new file mode 100644
index 0000000000..5a4413aa89
--- /dev/null
+++ b/tests/global_controller_configuration/mc_rtc.in.yaml
@@ -0,0 +1,12 @@
+MainRobot: @MAIN_ROBOT@
+ClearRobotModulePath: true
+RobotModulePaths: ["@ROBOT_MODULE_PATH@"]
+
+ClearControllerModulePath: true
+Enabled: [Posture]
+
+ClearObserverModulePath: true
+ObserverPipelines: {}
+
+ClearGlobalPluginPath: true
+Plugins: []
diff --git a/tests/global_controller_configuration/testGlobalControllerConfiguration.in.cpp b/tests/global_controller_configuration/testGlobalControllerConfiguration.in.cpp
new file mode 100644
index 0000000000..5fc8e9458f
--- /dev/null
+++ b/tests/global_controller_configuration/testGlobalControllerConfiguration.in.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015-2024 CNRS-UM LIRMM, CNRS-AIST JRL
+ */
+
+#include
+
+#include
+
+#include
+
+/** Make sure the robot loader has nothing available when we start a test case */
+struct TestGlobalControllerConfigurationFixture
+{
+ TestGlobalControllerConfigurationFixture()
+ {
+ mc_rtc::Loader::debug_suffix = "";
+ mc_rbdyn::RobotLoader::clear();
+ std::cout << "AVAILABLE ROBOTS:\n";
+ for(const auto & r : mc_rbdyn::RobotLoader::available_robots()) { std::cout << "- " << r << "\n"; }
+ BOOST_REQUIRE(mc_rbdyn::RobotLoader::available_robots().empty());
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(MainRobotSimple, TestGlobalControllerConfigurationFixture)
+{
+ mc_control::MCGlobalController::GlobalConfiguration cfg("@MAIN_ROBOT_SIMPLE_CFG@", nullptr, true);
+ BOOST_REQUIRE(cfg.main_robot_module);
+ BOOST_REQUIRE(cfg.main_robot_module->name == "jvrc1");
+}
+
+BOOST_FIXTURE_TEST_CASE(MainRobotSimpleVector, TestGlobalControllerConfigurationFixture)
+{
+ mc_control::MCGlobalController::GlobalConfiguration cfg("@MAIN_ROBOT_SIMPLE_VECTOR_CFG@", nullptr, true);
+ BOOST_REQUIRE(cfg.main_robot_module);
+ BOOST_REQUIRE(cfg.main_robot_module->name == "jvrc1");
+}
+
+BOOST_FIXTURE_TEST_CASE(MainRobotObject, TestGlobalControllerConfigurationFixture)
+{
+ mc_control::MCGlobalController::GlobalConfiguration cfg("@MAIN_ROBOT_OBJECT_CFG@", nullptr, true);
+ BOOST_REQUIRE(cfg.main_robot_module);
+ BOOST_REQUIRE(cfg.main_robot_module->name == "jvrc1");
+}
+
+BOOST_FIXTURE_TEST_CASE(MainRobotObjectVector, TestGlobalControllerConfigurationFixture)
+{
+ mc_control::MCGlobalController::GlobalConfiguration cfg("@MAIN_ROBOT_OBJECT_VECTOR_CFG@", nullptr, true);
+ BOOST_REQUIRE(cfg.main_robot_module);
+ BOOST_REQUIRE(cfg.main_robot_module->name == "jvrc1");
+}