Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Python and Matlab Bindings #53

Closed
GiulioRomualdi opened this issue Jun 22, 2020 · 19 comments
Closed

Introduce Python and Matlab Bindings #53

GiulioRomualdi opened this issue Jun 22, 2020 · 19 comments
Assignees
Labels

Comments

@GiulioRomualdi
Copy link
Member

I think it may be useful to generate Python and Matlab bindings for the libraries developed in this repository.

The prerequisite to generate the Matlab bindings is an experimental version of Swig. This can be found in the repository: https://github.com/robotology-dependencies/swig/

This task does not have a high priority but we should keep it in mind

@DanielePucci
Copy link
Member

Nice idea @GiulioRomualdi, probably this can be also useful to the @dic-iit/reinforcement-learning guys

@traversaro
Copy link
Collaborator

Probably looking into iDynTree can provide good insight on how to set up this. A tricky aspect may be how to properly generate bindings for data structures that are actually wrapped in iDynTree bindings, for example iDynTree::VectorDynSize. In theory it is possible to import the iDynTree.i of iDynTree to permit to do so, but in practice I never tried.

@diegoferigo
Copy link
Member

@DanielePucci Indeed! I had a super quick look to the code and it seems that the public APIs use quite a lot non-STL types. In my experience it could be tricky to bind them with swig.

@GiulioRomualdi
Copy link
Member Author

@DanielePucci

Nice idea @GiulioRomualdi, probably this can be also useful to the @dic-iit/reinforcement-learning guys

I was thinking also to the @dic-iit/ironcub team. At least for the Simulator #46

@GiulioRomualdi
Copy link
Member Author

GiulioRomualdi commented Jul 14, 2020

Another interesting tool for generating python bindings is pybind11.
In Drake they replaced SWIG bindings with Pybind11 RobotLocomotion/drake#4949

@diegoferigo
Copy link
Member

It's a powerful alternative if you just need Python. The beauty of SWIG is that it can be adapted with small effort also for other languages, and we always relied on it. I remember a benchmark that also showed that SWIG has fairly good performances wrt to the other solutions. The trade off is the steep learning curve of its .i files.

@traversaro
Copy link
Collaborator

Note that pybind11 is not directly a replacement for SWIG, as it is basically a replacement for some lower level aspects of SWIG, but while for SWIG the code that wrap every class/function/method is automatically generate by the parsing of the .h file, with pybind11 you need to manually write which class/function/method you want to wrap, see for example all the files present in https://github.com/RobotLocomotion/drake/tree/master/bindings/pydrake .

There are indeed a few tools that work on the top of pybind11 and that combined with pybind11 are able to provide the functionality similar to the one of SWIG , you can find some of them listed in https://github.com/jslee02/awesome-cpp-python-binding-generator#pybind11 and RobotLocomotion/drake#7889 .

@traversaro
Copy link
Collaborator

Related PR : robotology/superquadric-lib#32 .

@GiulioRomualdi
Copy link
Member Author

I will convert this issue into an epic and I will open the associated issue for python and matlab

@GiulioRomualdi GiulioRomualdi self-assigned this Oct 19, 2020
@traversaro
Copy link
Collaborator

I did not know that latest version of MATLAB come with a C++ FFI (Foreign Function Interface) named clibgen. It seems to be still quite limited to C++98 and a few C++11 support (see https://www.mathworks.com/help/matlab/matlab_external/limitations-to-cpp-support-in-matlab-cpp-library-interface.html) but it could be a nice option, perhaps for simplified APIs for interoperability. An introductory blog post is https://blogs.mathworks.com/developer/2019/07/11/cpp-interface/ .

@traversaro
Copy link
Collaborator

Given #131, if the goal is always to get MATLAB bindings as well a possible strategy is to provide nice Python bindings and then access the resulting Python library from MATLAB (instead of directly accessing the C++ library), by using MATLAB support to call Python, see https://www.mathworks.com/help/matlab/call-python-libraries.html . I have no clue of the performance of this method.

Back in time I think this was the strategy used by Drake (see RobotLocomotion/drake#5981) but I guess that at some point then they just dropped MATLAB support, not sure if for limitation of this approach or just because the lack of interest in MATLAB.

@S-Dafarra
Copy link
Member

Given #131, if the goal is always to get MATLAB bindings as well a possible strategy is to provide nice Python bindings and then access the resulting Python library from MATLAB (instead of directly accessing the C++ library), by using MATLAB support to call Python, see https://www.mathworks.com/help/matlab/call-python-libraries.html . I have no clue of the performance of this method.

I thought the same few days ago, but then I thought that since the passage from bindings to C++ code is usually slow, having two of these in the pipeline would have been a performance killer, but I agree it would need to be tested.

@traversaro
Copy link
Collaborator

Example of use of a Python library in MATLAB: CATIA-Systems/FMPy#228 .

@Marvin-TMG
Copy link

Marvin-TMG commented Feb 4, 2021

Example of use of a Python library in MATLAB: CATIA-Systems/FMPy#228 .

Thanks for posting this, besides calling existing Python libraries from Matlab, I am also interested in finding a way to call C++ libraries in Matlab and Python. For Python, it seems that pybind11 is quite convenient, if the resulting Python module is callable from Matlab, that would be very convenient.
Matlab introduced a feature to auto generate bindings for C++ dlls, I wonder if something similar is available for pybind11? I.e. some code to parse a C++ header file and auto resolve the macros for pybind.

Thanks for your input!

@traversaro
Copy link
Collaborator

Matlab introduced a feature to auto generate bindings for C++ dlls, I wonder if something similar is available for pybind11? I.e. some code to parse a C++ header file and auto resolve the macros for pybind.

There are a few such projects, that are similar to SWIG permit to start directly from header files and generate bindings, but that differently from SWIG build on the top of pybind11. Unfortunately, we still did not explored their use, but you can find a list of them in:

@Marvin-TMG
Copy link

Matlab introduced a feature to auto generate bindings for C++ dlls, I wonder if something similar is available for pybind11? I.e. some code to parse a C++ header file and auto resolve the macros for pybind.

There are a few such projects, that are similar to SWIG permit to start directly from header files and generate bindings, but that differently from SWIG build on the top of pybind11. Unfortunately, we still did not explored their use, but you can find a list of them in:

Thanks, this is very usefull!

@GiulioRomualdi
Copy link
Member Author

For the time being, we decided to support only python bindings

@prashanthr05
Copy link
Collaborator

With a brief investigation to understand if we can try to call the python bindings generated by bipedal-locomotion-framework from Matlab, I tried to replicate the test_schmitt_trigger_detector.py in Matlab and it seems to work.

It was one monolithic ctrl+c, ctrl+v topped with few python-matlab data mapping specific modifications (Necessary documentation in https://it.mathworks.com/help/matlab/call-python-libraries.html).

Matlab version

>> version

ans =

    '9.9.0.1495850 (R2020b) Update 1'

Ensure to have the bindings in pythonpath.
Firstly, for the current matlab session, I set pyenv('Version', '/usr/bin/python3'). Then I run the script below.

Matlab code
py.importlib.import_module('bipedal_locomotion_framework.bindings');
ph = py.bipedal_locomotion_framework.bindings.StdParametersHandler();
ph.set_parameter_vector_string("contacts", py.list({'right'}));
ph.set_parameter_vector_float("contact_make_thresholds", py.list({100}));
ph.set_parameter_vector_float("contact_break_thresholds", py.list({10}));
ph.set_parameter_vector_float("contact_make_switch_times", py.list({0.2}));
ph.set_parameter_vector_float("contact_break_switch_times", py.list({0.2, 0.2}));

detector = py.bipedal_locomotion_framework.bindings.SchmittTriggerDetector();
assert(detector.initialize(ph) == false)

ph.set_parameter_vector_float("contact_break_switch_times", py.list({0.2}));
assert(detector.initialize(ph))

assert(detector.reset_contacts())

% rise signal
assert(detector.set_timed_trigger_input("right", 0.1, 120.0))
assert(detector.advance())
assert(detector.set_timed_trigger_input("right", 0.2, 120.0))
assert(detector.advance())
assert(detector.set_timed_trigger_input("right", 0.3, 120.0))
assert(detector.advance())

% contact state should turn true
right_contact = detector.get("right");
assert(right_contact.is_active)
assert(right_contact.switch_time == 0.3)

% fall signal
assert(detector.set_timed_trigger_input("right", 0.4, 7.0))
assert(detector.advance())
assert(detector.set_timed_trigger_input("right", 0.5, 7.0))
assert(detector.advance())
assert(detector.set_timed_trigger_input("right", 0.6, 7.0))
assert(detector.advance())

%contact state should turn false
right_contact = detector.get("right");
assert(right_contact.is_active == false)
assert(right_contact.switch_time == 0.6)

%add a new contact
params = py.bipedal_locomotion_framework.bindings.SchmittTriggerParams();
params.off_threshold = 10;
params.on_threshold = 100;
params.switch_off_after = 0.2;
params.switch_on_after = 0.2;
detector.add_contact("left", false, params, 0.6);
contacts = detector.get();
assert(double(py.len(keys(contacts))) == 2);
assert(contacts{'right'}.is_active == false);

% test multiple measurement updates
right_input = py.bipedal_locomotion_framework.bindings.SchmittTriggerInput();
right_input.time = 0.7;
right_input.value = 120;
left_input = py.bipedal_locomotion_framework.bindings.SchmittTriggerInput();
left_input.time = 0.7;
left_input.value = 120;
timed_inputs = py.dict(pyargs('right',right_input,'left',left_input));
assert(detector.set_timed_trigger_inputs(timed_inputs))


% test removing a contact
assert(detector.remove_contact("left"));
contacts = detector.get();
assert(double(py.len(keys(contacts))) == 1);

%test resetting a contact
assert(detector.reset_contact("right", true, params));
right_contact = detector.get("right");
assert(right_contact.is_active == true);

@traversaro
Copy link
Collaborator

This was quite an interesting PR related to this: #578 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants