What is OMPL?
The open motion planning library (OMPL) is a well-established C++ library for sampling-based motion planning, which contains implementations of many state-of-the-art planning algorithms. The library is designed in a way that it allows the user to easily solve a variety of complex motion planning problems with minimal input. OMPL facilitates the addition of new motion planning algorithms, and it can be conveniently interfaced with other software components. A simple graphical user interface (GUI) built on top of the library, a number of tutorials, demos, and programming assignments are designed to teach students about sampling-based motion planning. The library is also available for use through Robot Operating System (ROS). OMPL Original Publication. However, it intends to remain as a planning library, excluding any physics engine or robot model.
Troubleshooting OMPL Python Bindings on Ubuntu
Installing the Open Motion Planning Library (OMPL) with Python bindings is a notoriously difficult task. This guide explores why the process is so complex and provides a verified “success path” for a manual source build on Ubuntu.
The Challenge: Why is OMPL Integration Hard?
Building Python bindings for a massive C++ project like OMPL is a “dependency nightmare” for several architectural reasons:
- Functional Dependency: OMPL is a pure planning library; it does not include a simulator or robotic models. It relies on external libraries for physics engines and kinematics, leading to a tight coupling between C++ and Python objects.
- Memory Management: C++ and Python handle memory differently. Python uses garbage collection, whereas C++ does not. Accessing a C++ object that has been destroyed results in immediate segmentation faults or core dumps. After all, Python is an interpreted language whereas C++/ C is a compiled language.
- Thread Management: Python uses GIL lock so it can run into thread-safe issue.
- Architectural Complexity: OMPL is written in a strictly object-oriented fashion, utilizing 300+ classes, heavy inheritance, and complex callbacks. This makes writing a working binding source code extremely difficult and later also difficult to maintain.
- Legacy Technical Debt: Throughout its 15-year history, OMPL has relied on legacy projects like Boost, which frequently causes versioning conflicts during the binding process.
- ABI Mismatches: Pre-compiled binaries (from Pip or Conda) often fail because they were built against different library versions than those on a local machine.
The “Beaten Path”: What to Avoid
Before attempting a manual build, note that these common shortcuts often fail:
- Windows: OMPL on Windows is C++ only; no Python bindings are supported.
-
Standard Package Managers: Commands like
pip install omplorconda install -c conda-forge omplfrequently result in segmentation faults during runtime.
For example,
python -m pip install ompl Pip install pre-compiled library, wheel for version 1.7.0 does not work. This experience is quite common for others, See Pull Request and Issue
This results in segmentation fault and core dump when OMPL is loaded in python.
conda install -c conda-forge ompl conda install ompl=1.5.2 --channel conda-forge conda install ompl=1.6.0 --channel conda-forge
Anaconda forge pre-compiled libraries all fails as well.
To debug segmentation fault, I tried gdb -ex "run" -ex "bt" --args python3 ./ompl-1.6.0/demos/RigidBodyPlanning.py
However, this printout messages are not informative of the errors.
warning: Error disabling address space randomization: Operation not permitted
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x00005630a054bb74 in _Py_Dealloc ()
#0 0x00005630a054bb74 in _Py_Dealloc ()
#1 0x0000148d5db89615 in register__util_Exception_class() () from /usr/lib/python3/dist-packages/ompl/util/_util.so
#2 0x0000148d5db99752 in init_module__util() () from /usr/lib/python3/dist-packages/ompl/util/_util.so
#3 0x0000148d5daa86e3 in boost::python::handle_exception_impl(boost::function0<void>) () from /usr/lib/x86_64-linux-gnu/libboost_python310.so.1.74.0
#4 0x0000148d5daa9c69 in boost::python::detail::init_module(PyModuleDef&, void (*)()) () from /usr/lib/x86_64-linux-gnu/libboost_python310.so.1.74.0
#5 0x00005630a067811a in ?? ()
#6 0x00005630a05838e9 in ?? ()
#7 0x00005630a0573cbe in _PyEval_EvalFrameDefault ()
#8 0x00005630a058368c in _PyFunction_Vectorcall ()
#9 0x00005630a0572bdb in _PyEval_EvalFrameDefault ()
#10 0x00005630a058368c in _PyFunction_Vectorcall ()
#11 0x00005630a056dd17 in _PyEval_EvalFrameDefault ()
#12 0x00005630a058368c in _PyFunction_Vectorcall ()
#13 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#14 0x00005630a058368c in _PyFunction_Vectorcall ()
#15 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#16 0x00005630a058368c in _PyFunction_Vectorcall ()
#17 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#18 0x00005630a058368c in _PyFunction_Vectorcall ()
#19 0x00005630a0582ac5 in ?? ()
#20 0x00005630a0656d2f in _PyObject_CallMethodIdObjArgs ()
#21 0x00005630a0596375 in PyImport_ImportModuleLevelObject ()
#22 0x00005630a05706eb in _PyEval_EvalFrameDefault ()
#23 0x00005630a0651d36 in ?? ()
#24 0x00005630a0651c06 in PyEval_EvalCode ()
#25 0x00005630a065734d in ?? ()
#26 0x00005630a05838e9 in ?? ()
#27 0x00005630a0573cbe in _PyEval_EvalFrameDefault ()
#28 0x00005630a058368c in _PyFunction_Vectorcall ()
#29 0x00005630a0572bdb in _PyEval_EvalFrameDefault ()
#30 0x00005630a058368c in _PyFunction_Vectorcall ()
#31 0x00005630a056dd17 in _PyEval_EvalFrameDefault ()
#32 0x00005630a058368c in _PyFunction_Vectorcall ()
#33 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#34 0x00005630a058368c in _PyFunction_Vectorcall ()
#35 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#36 0x00005630a058368c in _PyFunction_Vectorcall ()
#37 0x00005630a0582ac5 in ?? ()
--Type <RET> for more, q to quit, c to continue without paging--cccc
#38 0x00005630a0656d2f in _PyObject_CallMethodIdObjArgs ()
#39 0x00005630a0596375 in PyImport_ImportModuleLevelObject ()
#40 0x00005630a05a61cc in ?? ()
#41 0x00005630a0582e32 in ?? ()
#42 0x00005630a059132b in PyObject_Call ()
#43 0x00005630a0573cbe in _PyEval_EvalFrameDefault ()
#44 0x00005630a058368c in _PyFunction_Vectorcall ()
#45 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#46 0x00005630a058368c in _PyFunction_Vectorcall ()
#47 0x00005630a0582ac5 in ?? ()
#48 0x00005630a0656d2f in _PyObject_CallMethodIdObjArgs ()
#49 0x00005630a0596c17 in PyImport_ImportModuleLevelObject ()
#50 0x00005630a05706eb in _PyEval_EvalFrameDefault ()
#51 0x00005630a0651d36 in ?? ()
#52 0x00005630a0651c06 in PyEval_EvalCode ()
#53 0x00005630a065734d in ?? ()
#54 0x00005630a05838e9 in ?? ()
#55 0x00005630a0573cbe in _PyEval_EvalFrameDefault ()
#56 0x00005630a058368c in _PyFunction_Vectorcall ()
#57 0x00005630a0572bdb in _PyEval_EvalFrameDefault ()
#58 0x00005630a058368c in _PyFunction_Vectorcall ()
#59 0x00005630a056dd17 in _PyEval_EvalFrameDefault ()
#60 0x00005630a058368c in _PyFunction_Vectorcall ()
#61 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#62 0x00005630a058368c in _PyFunction_Vectorcall ()
#63 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#64 0x00005630a058368c in _PyFunction_Vectorcall ()
#65 0x00005630a0582ac5 in ?? ()
#66 0x00005630a0656d2f in _PyObject_CallMethodIdObjArgs ()
#67 0x00005630a0596375 in PyImport_ImportModuleLevelObject ()
#68 0x00005630a05a61cc in ?? ()
#69 0x00005630a0582e32 in ?? ()
#70 0x00005630a059132b in PyObject_Call ()
#71 0x00005630a0573cbe in _PyEval_EvalFrameDefault ()
#72 0x00005630a058368c in _PyFunction_Vectorcall ()
#73 0x00005630a056dbd0 in _PyEval_EvalFrameDefault ()
#74 0x00005630a058368c in _PyFunction_Vectorcall ()
#75 0x00005630a0582ac5 in ?? ()
#76 0x00005630a0656d2f in _PyObject_CallMethodIdObjArgs ()
#77 0x00005630a0596c17 in PyImport_ImportModuleLevelObject ()
#78 0x00005630a05706eb in _PyEval_EvalFrameDefault ()
#79 0x00005630a0651d36 in ?? ()
#80 0x00005630a0651c06 in PyEval_EvalCode ()
#81 0x00005630a0678678 in ?? ()
#82 0x00005630a0672ccf in ?? ()
#83 0x00005630a0678415 in ?? ()
#84 0x00005630a0677958 in _PyRun_SimpleFileObject ()
#85 0x00005630a0677637 in _PyRun_AnyFileObject ()
#86 0x00005630a066bbfe in Py_RunMain ()
#87 0x00005630a0645c4d in Py_BytesMain ()
#88 0x0000148d5ec28d90 in __libc_start_call_main (main=main@entry=0x5630a0645c10, argc=argc@entry=2, argv=argv@entry=0x7fffd656c6b8) at ../sysdeps/nptl/libc_start_call_main.h:58
#89 0x0000148d5ec28e40 in __libc_start_main_impl (main=0x5630a0645c10, argc=2, argv=0x7fffd656c6b8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffd656c6a8) at ../csu/libc-start.c:392
#90 0x00005630a0645b45 in _start ()
- Manual Dependency Guessing: Never try to download and compile individual packages one by one; apart from massive time draining, it leads to massive configuration confusion. Generally speaking, this behavior should always be avoided for all large projects. Rather than do-it-yourself, run the official Cmake or bash script.
The Successful Path: Compiling from Source
To get OMPL working (1.6.0 version from official github site), you must use a specific, sequential approach. This method was verified on an Ubuntu machine (even within a Docker/tini container). In this tutorial, the Ubuntu machine is:
uname -r
cat /proc/version
5.15.0-83-generic
Linux version 5.15.0-83-generic (buildd@lcy02-amd64-027) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #92-Ubuntu SMP Mon Aug 14 09:30:42 UTC 2023
1. Preparation
Start by removing all existing OMPL traces from /usr/share/, /usr/local/share/, and your local site-packages to ensure a clean slate. This is critical as many have reported conflicting versions (ROS) bring about program crash.
2. Environment & Dependency Setup
Use the official OMPL installation script to fetch dependencies, but do not allow the script to run the build automatically, as the default configuration likely won’t match your system.
Critical Version Constraints:
-
Numpy: Use
numpy>=1.24,<2.0. Numpy 2.x introduces API changes that break the build. - PyGCCXML: You must force version 2.2.1. The latest 3.x versions will fail.
-
Boost: Ensure you have
libboost-python1.74-devandlibboost-numpy1.74-dev. - Pip: Pip argument uses –break-system-package ( this input option is only supported in newer pip, so update pip prevent not recognized error)
3. Strategic Code Modification
Make sure you run the official installation script like this:
chmod +x <script name, install-ompl-ubuntu.sh>
To prevent silent binding failures—specifically regarding string handling—you must edit generate_bindings.py inside ompl-1.6.0/py-bindings at line 194:
# Change line 194 to enforce proper string handling
try:
self.ompl_ns.class_(f'SpecificParam< std::string >').rename('SpecificParamString')
except:
self.ompl_ns.class_(f'SpecificParam< std::basic_string< char >>').rename('SpecificParamString')
4. The Build Execution
If you are using a Python virtual environment, you must pass the specific paths to CMake so it targets the correct interpreter and library folders.
# Configuration within your Virtual Env
cmake ../.. \
-DPYTHON_EXEC="${VIRTUAL_ENV}/bin/python" \
-DOMPL_PYTHON_INSTALL_DIR="${VIRTUAL_ENV}/lib/python3.10/site-packages" \
-DCMAKE_INSTALL_PREFIX="${VIRTUAL_ENV}" \
-DBoost_PYTHON_LIBRARY=/usr/lib/x86_64-linux-gnu/libboost_python310.so \
-DBoost_INCLUDE_DIR=/usr/include \
-DBoost_LIBRARY_DIR=/usr/lib/x86_64-linux-gnu
As a sanity check, I chose build_demo to be on to make sure the demos (C++) work at least. This is important because the demos assume we build them inside the OMPL build tree with OMPL_BUILD_DEMOS=ON, or we include them in our own code and provide a proper build system (include dirs + linking) after OMPL is installed
Boost library is very critical as much of the C++ rely on it, and therefore its bindings must be exact. For OMPL 1.6.0, use libboost-python1.74-dev libboost-numpy1.74-dev.
Run the build commands line-by-line. Do not move to the next command if the previous one shows an error, fix them before moving on:
cmake --build build -t update_bindings && \
cmake --build build && \
sudo cmake --install build
Verification
After installation, verify that ALL shared objects are correctly compiled and linked inside /root/robotics/ompl-1.6.0/build/lib. (_base.so _control.so _geometric.so _util.so libompl.so libompl.so.1.6.0 libompl.so.17 py_std_function.so). You can print out the linking with:
python3 -c "import ompl; print(ompl.__file__)"
python3 -c "from ompl import base as ob; print(ob.StateSpace.__name__)"
ompl file: /root/robotics/16/ompl-py310/lib/python3.10/site-packages/ompl/__init__.py
ldd /root/robotics/16/ompl-py310/lib/python3.10/site-packages/ompl/util/_util.so
linux-vdso.so.1 (0x00007ffef2de8000)
libompl.so.17 => /root/robotics/16/ompl-py310/lib/libompl.so.17 (0x000015370b180000)
libboost_python310.so.1.74.0 => /lib/x86_64-linux-gnu/libboost_python310.so.1.74.0 (0x000015370b134000)
libpython3.10.so.1.0 => /lib/x86_64-linux-gnu/libpython3.10.so.1.0 (0x000015370ab5d000)
libtriangle-1.6.so => /lib/libtriangle-1.6.so (0x000015370ab3a000)
libboost_serialization.so.1.74.0 => /lib/x86_64-linux-gnu/libboost_serialization.so.1.74.0 (0x000015370aaf8000)
libboost_filesystem.so.1.74.0 => /lib/x86_64-linux-gnu/libboost_filesystem.so.1.74.0 (0x000015370aad6000)
libboost_system.so.1.74.0 => /lib/x86_64-linux-gnu/libboost_system.so.1.74.0 (0x000015370aad1000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x000015370a8a5000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x000015370a7be000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x000015370a79e000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x000015370a575000)
/lib64/ld-linux-x86-64.so.2 (0x000015370b9f0000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x000015370a542000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x000015370a526000)
Finally, run an official Python demo to confirm everything is operational:
python3 ./demos/RigidBodyPlanning.py
run an official C++ demo
/root/robotics/ompl-1.6.0/build/bin/demo_RigidBodyPlanning
Future Outlook
There is a “New Hope” for OMPL. Key developers are working on a new generation of bindings using LLM agents to bridge the C++/Python gap. This approach promises more “pythonic,” maintainable, and stable bindings in the near future.
Indeed, I am so enthusiastic that I read their technical note: Python Bindings for a Large C++ Robotics Library: The Case of OMPL by Weihang Guo, Theodoros Tyrovouzis, and Lydia E. Kavraki.
The takeaways are 1) use LLM agents that are well-versed in both C++/C and Python, 2) agent fills in the template code with well-meaning contexts, not from scratch, 3) expert review.
In conclusion, I look forward to using their new Python bindings generated by nanobind and AI.