mc_rtc::Configuration general purpose configurationThere are three ways to integrate your robot into mc_rtc:
env/object loaderjson loaderRobotModule implementationenv/object loader:
Best suited for simple robots or static objects and environments.
Convex hulls can still be generated automatically for collision meshes.
json loader:
Adds structured metadata to the robot (e.g., joints, sensors, grippers).
Supports automatic convex hull generation from URDF meshes.
RobotModule:
Provides full control over loading, configuration, and logic.
Fully supports automatic convex generation and caching.
env/object LoaderTo load your robot using this loader, use the following invocation:
#include <mc_rbdyn/RobotLoader.h>
std::string path = "/path/to/description";
std::string name = "name";
auto env = mc_rbdyn::RobotLoader::get_robot_module("env", path, name);
// object will load the robot with a floating base
auto object = mc_rbdyn::RobotLoader::get_robot_module("object", path, name);import mc_rbdyn
path = "/path/to/description"
name = "name"
env = mc_rbdyn.get_robot_module("env", path, name)
# object will load the robot with a floating base
object = mc_rbdyn.get_robot_module("object", path, name)["env", "/path/to/description", "name"]
// object will load the robot with a floating base
["object", "/path/to/description", "name"][env, /path/to/description, name]
# object will load the robot with a floating base
[object, /path/to/description, name]Given a robot description directory located at LOCATION and a robot named NAME, mc_rtc expects your data to be structured as follows:
$LOCATION/urdf/$NAME.urdf$LOCATION/rsdf/$NAME/$LOCATION/convex/$NAME/For a body named BODY, the corresponding convex file should be named:
BODY-ch.txt
Regardless of how your robot is loaded (C++, JSON, or env/object), mc_rtc can generate convex hulls automatically from mesh-type collision geometries in the URDF or SRDF.
Generated convexes are stored in a user-specific local share directory. The base directory is platform-specific :
%APPDATA%/mc_rtc/<robot_name>/$HOME/.local/share/mc_rtc/<robot_name>/BODY-ch.txtBODY_0-ch.txt, BODY_1-ch.txt, etc.json loaderWhen loading the robot, use the following invokation:
#include <mc_rbdyn/RobotLoader.h>
std::string json_path = "/path/to/file.json";
auto rm_json = mc_rbdyn::RobotLoader::get_robot_module("json", json_path);
std::string yaml_path = "/path/to/file.yaml";
auto rm_yaml = mc_rbdyn::RobotLoader::get_robot_module("json", yaml_path);import mc_rbdyn
json_path = "/path/to/file.json"
rm_json = mc_rbdyn.get_robot_module("json", json_path)
yaml_path = "/path/to/file.yaml"
rm_yaml = mc_rbdyn.get_robot_module("json", yaml_path)["json", "/path/to/file.json"]
["json", "/path/to/file.yaml"][json, /path/to/file.json]
[json, /path/to/file.yaml]The json module expects your data to be organized as the env module. However, you can override some expectations by providing the data yourself. Furthermore, you can provide data for force sensors, body sensors, minimal self-collisions set and so-on.
See the JSON/YAML documentation for details on the data expected by the json loader.
RobotModuleIn this approach, we will write a C++ class to provide a RobotModule for our robot and overwrite the data members that we wish to provide. As in the minimal example below:
cmake_minimum_required(VERSION 3.1)
find_package(mc_rtc REQUIRED)
add_robot_simple(MyRobot)
# Shortcut for:
#
# add_robot(MyRobot MyRobot.h MyRobot.cpp)#pragma once
#include <mc_rbdyn/RobotModule.h>
#include <mc_robots/api.h>
struct MC_ROBOTS_DLLAPI MyRobotModule : public mc_rbdyn::RobotModule
{
public:
MyRobotModule(bool fixed);
};
extern "C"
{
ROBOT_MODULE_API void MC_RTC_ROBOT_MODULE(std::vector<std::string> & names)
{
// Names of the robots exported by this module
names = {"MyRobot", "MyRobotFixed"};
}
ROBOT_MODULE_API void destroy(mc_rbdyn::RobotModule * ptr)
{
delete ptr;
}
ROBOT_MODULE_API mc_rbdyn::RobotModule * create(const std::string & name)
{
// At this point name must be one of the supported robots
if(name == "MyRobot") { return new MyRobot(false); }
else
{
return new MyRobot(true);
}
}
}#include "MyRobot.h"
#include <RBDyn/parsers/urdf.h>
// MY_PATH can be provided by CMake for example
MyRobotModule::MyRobotModule(bool fixed) : mc_rbdyn::RobotModule(MY_PATH, "my_robot")
{
init(rbd::parsers::from_urdf_file(urdf_path));
std::vector<double> default_q = {-0.38, -0.01, 0., 0.72, -0.01, -0.33, -0.38, 0.02, 0., 0.72, -0.02, -0.33, 0.,
0.13, 0., 0., 0., 0., -0.052, -0.17, 0., -0.52, 0., 0., 0., 0.,
0., 0., 0., 0., 0., -0.052, 0.17, 0., -0.52, 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.};
const auto & rjo = ref_joint_order();
for(size_t i = 0; i < rjo.size(); ++i) { _stance[rjo[i]] = {default_q[i]}; }
_default_attitude = {{1., 0., 0., 0., 0., 0., 0.8275}};
_forceSensors.push_back(mc_rbdyn::ForceSensor("RightFootForceSensor", "R_ANKLE_P_S", sva::PTransformd::Identity()));
_forceSensors.push_back(mc_rbdyn::ForceSensor("LeftFootForceSensor", "L_ANKLE_P_S", sva::PTransformd::Identity()));
_forceSensors.push_back(mc_rbdyn::ForceSensor("RightHandForceSensor", "R_WRIST_Y_S", sva::PTransformd::Identity()));
_forceSensors.push_back(mc_rbdyn::ForceSensor("LeftHandForceSensor", "L_WRIST_Y_S", sva::PTransformd::Identity()));
_bodySensors.emplace_back("Accelerometer", "PELVIS_S", sva::PTransformd(Eigen::Vector3d(-0.0325, 0, 0.1095)));
_bodySensors.emplace_back("FloatingBase", "PELVIS_S", sva::PTransformd::Identity());
_minimalSelfCollisions = {
{"WAIST_R_S", "L_SHOULDER_Y_S", 0.02, 0.001, 0.}, {"WAIST_R_S", "R_SHOULDER_Y_S", 0.02, 0.001, 0.},
{"PELVIS_S", "R_ELBOW_P_S", 0.05, 0.001, 0.}, {"PELVIS_S", "L_ELBOW_P_S", 0.05, 0.001, 0.},
{"R_WRIST_Y_S", "R_HIP_Y_S", 0.05, 0.025, 0.}, {"L_WRIST_Y_S", "L_HIP_Y_S", 0.05, 0.025, 0.}};
_commonSelfCollisions = _minimalSelfCollisions;
_grippers = {{"l_gripper", {"L_UTHUMB"}, true}, {"r_gripper", {"R_UTHUMB"}, false}};
}See mc_rbdyn::RobotModule documentation for all members that can be provided.
Regardless of how the robot module was created, the framework makes it possible to connect/disconnect exisitng robot modules, actuated or not. This is very useful to provide variants of existing robots modules such as:
In any case the merged robot is a new robot module that can be loaded and used as a single robot in the framework. For now, we do not provide a way to seamlessly replace a robot involved in the controller with a variant created by connecting it to another module. This would be very useful in case of tool changing or for modular robotics to connect multiple actuated modules together.
This feature is planned, in the meanwhile users interested in trying to do this themselves need to:
mc_rbdyn::RobotModule::connect and mc_rbdyn::RobotModule::ConnectionParameters. The main function to use is defined as:RobotModule RobotModule::connect(const mc_rbdyn::RobotModule & other,
const std::string & this_body,
const std::string & other_body,
const std::string & prefix,
const ConnectionParameters & params) const#include <mc_rbdyn/RobotLoader.h>
#include <mc_rbdyn/RobotModule.h>
// Define a "robot_support" robot from a "box" visual description
auto boxYaml =
R"(
name: robot_support
origin:
translation: [0, 0, 0]
rotation: [0, 0, 0]
material:
color:
r: 1.0
g: 0.0
b: 0.0
a: 1.0
geometry:
box:
size: [0.7, 1.2, 1.2]
inertia:
mass: 50
fixed: true
)";
auto boxConfig = mc_rtc::Configuration::fromYAMLData(boxYaml);
// Create a "robot_support" robot module from the visual description above
auto supportRm = mc_rbdyn::robotModuleFromVisual("robot_support", boxConfig);
// Load the panda robot module from an existing robot module
auto pandaRm = mc_rbdyn::RobotLoader::get_robot_module("PandaDefault");
// Transformation between the base of the "world" link of PandaDefault module and the robot_support link
auto box_to_robot = sva::PTransformd(Eigen::Vector3d{0., 0., -1.2});
// Load a new robot module by connecting the existing "PandaDefault" module to our "box" module
auto pandaOnBoxRm = supportRm->connect(*pandaRm, // connect to this other robot module (panda)
"robot_support", // reference link on our module
"world", // to connect to this link on other robot module (panda)
"", // optional prefix to add to link names
mc_rbdyn::RobotModule::ConnectionParameters{}
.name(robot_name)
.X_other_connection(box_to_robot) // connection parameters
);
// Add robot to the controller, where ctl is an mc_control::MCController
auto robot = ctl.loadRobot(*pandaOnBoxRm);Use the mc-rtc/new-robot-module template project to get started quickly. This template provides the barebone structure for a C++ RobotModule or a YAML RobotModule