Repository navigation
7. Tips for development
This page contains various bits of wisdom accumulated over time by your fellow Nimble developers 🧠.
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 withnimble.<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:
- Create a file called
python/_nimblephysics/<module_path>.cpp. - Add the Python bindings that you need inside of that file.
- Update the
module.cppfile inside the parent directory ofpython/_nimblephysics/<module_path>.cppwith the name of the class you've just created a python binding file for. - Don't forget to re-configure the project before you build the python binary!
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.
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.
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.
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.
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>.