mujoco icon indicating copy to clipboard operation
mujoco copied to clipboard

High level C++ API

Open BenjaminNavarro opened this issue 3 years ago • 6 comments
trafficstars

I'm considering using mujoco as the default robot simulator in my lab but after a quick look at the API it seems a bit difficult to apprehend, especially for novice programmers.

Have you ever though of providing a higher level C++ API, maybe using Eigen to wrap all the vector/matrices, so that simple simulations can be written in just a few lines?

Some time ago I tested RaiSim, which seems to follow a similar design philosophy as mujoco (except for the licensing), and the API was quite straightforward. I could do something like:

raisim::World sim;
sim.setTimeStep(0.001);
auto robot = sim.addArticulatedSystem("path/to/robot.urdf");

// set initial state
MyRobotState state;
robot->setState(state.jointPosition(), state.jointVelocity());
robot->setGeneralizedForce(state.jointTorque());
sim.integrate();

while(true) {
	state.jointPosition() = robot->getGeneralizedCoordinate().e();
	state.jointVelocity() = robot->getGeneralizedVelocity().e();
	
	auto torques = my_controller(state);

	robot->setGeneralizedForce(torques);
	sim.integrate();
}

Have you even though of providing such an interface? Does something similar already exists and I missed it?

I understand the motivation behind the C API but I'm sure a C++ wrapper around it would simplify mujoco usage and facilitate its adoption.

BenjaminNavarro avatar May 13 '22 12:05 BenjaminNavarro

Hi @BenjaminNavarro have you thought about using mc-rtc for writing your controller? It has an interface with mujoco called “mc-mujoco” which may be of use to you. You can also refer to the source code of mc-mujoco if you want to see how to set the torque commands computed by your controller.

rohanpsingh avatar May 13 '22 14:05 rohanpsingh

Well, mc-rtc could be an option but I don't think that relying on a particular control framework to get such functionality is a good idea.

Since mujoco exists outside of any control framework it would be better if mujoco provides a high-level API directly so that it can be reused in any framework afterwards, or even outside of a specific framework if a user wishes so.

BenjaminNavarro avatar May 13 '22 15:05 BenjaminNavarro

Hi @BenjaminNavarro,

Have you looked at the Programming chapter in the documentation?

The simulation loop you wrote above would look something like

mjModel* m = mj_loadXML("mymodel.xml", NULL, errstr, errstr_sz);
// possibly inspect errstr for model loading issues
mjData* d = mj_makeData(m);

// set initial state
mju_copy(d->qpos, my_initial_pos, m->nq);
mju_copy(d->qvel, my_initial_vel, m->nv);

// call mj_forward if my_controller looks at state-dependent quantities
mj_forward(m, d);

// simulation loop
while (d->time<10) {
  my_controller(m, d);  // writes controls into d->ctrl
  mj_step(m, d);
}

Straightforward, no?

If not, can you say what is it that you think a C++ API would give you that the C API does not?

yuvaltassa avatar May 13 '22 17:05 yuvaltassa

Sure this snippet is quite straightforward but even there I see room for improvements using a C++ API:

  1. No raw pointers. mjModel, mjData, etc can be wrapped in std::unique_ptr which can take care of deallocations (omitted in this snippet) when the pointer goes out of scope without being moved before
  2. If we assume Eigen is used to wrap the data, copies become just d->qpos = my_initial_pos, removing the fear of passing the wrong length and entering undefined behavior territory
  3. Error handling. Having to pass a preallocated char array + its size to get error messages is not very user friendly. Could make use of exceptions, something like std::expected (3rd party implems available), or at least take an std::string by ref instead of the pointer + size pair

And these points are just to mimic the current interface and just making it easier and safer to use but I'm sure that some of this stuff can be wrapped in a higher level API making simple things even simpler, e.g:

auto sim = mj::load("path_to_model.urdf"); // or .mjb, the format can be detected by the function

sim.state.joint_position.setRandom();
sim.state.joint_velocity.setZero();

sim.forward();
while(sim.time < 10) {
    sim.control.joint_force = my_controller(sim.state.joint_position, sim.state.joint_velocity);
    sim.step();
}

EDIT: we can of course see something similar for the visualization part

BenjaminNavarro avatar May 23 '22 11:05 BenjaminNavarro

MuJoCo's "raw data/pointers in C" approach was such a nice change after struggling with other libraries' maze of C++ that I personally paid for a license (back when MuJoCo wasn't open source). I was able to get running quicker and even make some customizations that needed some deeper access (Jacobians for semi-arbitrary closed loop mechanisms). The documentation style helps a lot too - just one massive page that I can search or skim.

Maybe there is room for both? But here's one customer's vote for keeping things as they are (good old C).

jay-j avatar Jul 08 '22 03:07 jay-j

Just to be clear, I didn't propose to replace the current API with another one, but to complement it. Most probably by writing a C++ wrapper around the current C API. The C API is needed anyway to simplify the binding to other languages.

BenjaminNavarro avatar Jul 11 '22 10:07 BenjaminNavarro