Skip to content

7. Tips for development

Michelle edited this page Apr 15, 2022 · 42 revisions

This page contains various bits of wisdom accumulated over time by your fellow Nimble developers 🧠.

Adding Python bindings

Let's say you're interested in adding Python bindings for something that's sitting in the C++ codebase under dart/<module_path>.cpp.

If the corresponding python/_nimblephysics/<module_path>.cpp file already exists, all you have to do is add the Python binding in the file following pybind11 syntax. You can peek at other existing python bindings in the codebase, and do some pattern matching + reading of pybind11 docs to figure out the exact syntax you need 🙂. Here are some nice examples of different types of Python bindings that you can refer to:

  • enum: See WrtMassBodyNodeEntryType.
  • static function: See NeuralUtils.cpp. The added python bindings are directly callable with nimble.<module>.<function_name> in Python.
  • struct:
    • Skeleton::ContactInverseDynamicsResult.
    • dart::constraint::PgsBoxedLcpSolver::Option

If the python/_nimblephysics/<module_path>.cpp file does not already exist, we can go through the following steps to create one:

  1. Create a file called python/_nimblephysics/<module_path>.cpp.
  2. Add the Python bindings that you need inside of that file.
  3. Update the module.cpp file inside the parent directory of python/_nimblephysics/<module_path>.cpp with the name of the class you've just created a python binding file for.
  4. Don't forget to re-configure the project before you build the python binary!

FAQ / Common mistakes

Forgetting to re-configure the project

Every time you add new files (or switch git branches where the file tree changes), you'll want to re-configure the project under the CMake tab. It's easy to forget this step, and build the wrong file tree. If you've configured your project properly, the file tree in the CMake tab of VSCode should reflect the file tree in the current state of your codebase.

TypeError: Unable to convert function return value to a Python type

If it's a custom class/struct, you need to create a Python binding for it. If you're returning an Eigen type, make sure to add #include <pybind11/eigen.h> at the top of the file.

ImportError...undefined symbol

You may get an import error complaining about an undefined symbol when you execute import nimblephysics in Python. For example, your exact error message might look something like this:

ImportError: /opt/anaconda3/envs/contact/lib/python3.8/site-packages/nimblephysics_libs/_nimblephysics.so: undefined symbol: _ZN4dart6python8LCPUtilsERN8pybind117module_E

Following this post, we can demangle our message with an online demangler to see what the plaintext symbol name actually is.

One possible reason that you're getting this error is that you're telling pybind that a certain module exists (in module.cpp), but the actual Python binding for that module doesn't exist. You may have forgotten to add one, or the file exists but you forgot to re-configure your project when building the Python binary. Double check whether either of these is the case for you.

error: ‘<CLASS>’ is not a member of ‘dart::<MODULE>’; did you mean ‘dart::python::<CLASS>’?

If you see an error like this when building your Python binary, chances are, you forgot to #include the dart/<MODULE>/<CLASS>.hpp header file in the pybind file.

Forgetting to include header files

Sometimes you might forget to include header files in the pybind files. Here's an example error message that explains the problem (you won't get build errors, but you'll get this error when you run the Python code).

TypeError: Unable to convert function return value to a Python type! The signature was
...
Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,
<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
conversions are optional and require extra headers to be included
when compiling your pybind11 module.

For example, if you're using a C++ vector, you'll need to add #include <pybind11/stl.h>.