Running C++ ROS in Jupyter using Xeus-Cling

Introduction

Jupyter notebook is a web application that you can use to create and share documents containing live code, equations, visualizations, and text. There are already several ROS libraries in Jupyter Notebook, for example, jupyter-ros, but existing documentation is mostly available for python. Do you know that Jupyter also allows you to run C++ libraries, including ROS?

The following image displays C++ code in a Jupyter Notebook.

Screenshot of C++ ROS Subscriber in Jupyter Notebook

This is made possible by xeus, a library meant to facilitate the implementation of kernels for Jupyter. The kernel for the C++ programming language is called xeus-cling and an alternative Python kernel for Jupyter xeus-python.

Why is running C++ in Jupyter notebook important?

We have 2 main reasons for this:

  1. PCL, the most popular library to do 3D point cloud processing, doesn’t have a python binding. So, we’re stuck with C++, at least until their python binding project(pcl/clang-bind) becomes stable.
  2. Developing C++ is a hassle when you just want to do simple applications. Compilation can take a lot of time. Having it interpreted instead of compiled is nice.

Enough, just show me the code!

Prerequisite:

Before you start you will need to install the following tools:

  • Conda or Mamba
  • Xeus-Cling, e.g. conda install -c conda-forge xeus-cling
  • Boost from conda that matches the version installed in your computer, e.g. conda install -c conda-forge boost==1.71.0 for Ubuntu 20.04
  • ROS or other 3rd party libraries. After reading this post, you would be able to set up any other 3rd party libraries. ROS turns out to be one of the most difficult 3rd parties that I know to be set up in Jupyter Notebook.

Simple Example: Setting up Eigen in Jupyter Notebook

Let’s start with a simple library, eigen3, which is a header-only library. If you install the libeigen3-dev via apt, the header files will be stored in /usr/include/eigen3. So, we just need to add #pragma cling add_include_path("/usr/include/eigen3") before adding the eigen3 code.

Try it out by opening up a jupyter notebook and type out the following code:

#pragma cling add_include_path("/usr/include/eigen3")

#include <Eigen/Eigen>
#include <iostream>

Eigen::Vector3f v1(1, 2, 3);
Eigen::Vector3f v2(4, 5, 6);
auto res = v1.dot(v2);

std::cout << res << std::endl;
Screenshot of Eigen3 in Jupyter Notebook

Setting up an arbitrary 3rd party library with Xeus-Cling

Doing this is pretty straightforward. Basically, there are 3 types of settings that you need to set up explicitly in the Jupyter notebook as mentioned in xeus-cling documentation:

  1. #pragma cling add_include_path("include_directory")
    This is the path to the header files(.h or .hpp) of the library
  2. #pragma cling add_library_path("lib_directory")
    This is the path to the compiled lib files (lib*.so, lib*.a) of the library.
  3. #pragma cling load(“libname”)
    This is the library name that is going to be loaded. e.g. if the libopencv_core.so, you’d need to add #pragma cling load("opencv_core"). It is also possible to put the entire path, for example #pragma cling load ("/usr/local/lib/opencv_core.so") and skip the #pragma cling add_library_path(“”)

PKG-CONFIG to the rescue!

Unfortunately, the hardest part of setting up the 3rd party library for C++ is figuring out all these paths. Fortunately, pkg-config --cflags --libs [libname] helps us to figure out all these shenanigan flags to link a 3rd party C++ library.

For example, to check out what you need to setup roscpp is pkg-config --cflags --libs roscpp and the output is :

$ pkg-config --cflags --libs roscpp

-I/opt/ros/noetic/include -L/opt/ros/noetic/lib -lroscpp -lpthread /usr/lib/x86_64-linux-gnu/libboost_chrono.so.1.71.0 ...(redacted because too long and not important)

Note: please try it out yourself, it’s easy.

You can see the pattern here:

  1. -I is for the #pragma cling add_include_path.
  2. -L is for the #pragma cling add_library_path, and
  3. -l and /path/to/lib[libname].so is for the #pragma cling load.

Finally: Running ROS with Xeus-Cling in Jupyter Notebook

Now that we’ve finished explaining everything you need to know about setting up the 3rd party library, let’s beat the boss, which is setting up ROS for Jupyter notebook.

Since it’s annoying to manually create #pragma cling ... for all the flags in the roscpp package, we have automated this for you! You can clone this repository or copy just the generate_cling_3rd_party.py

All the codes are open source and available in https://github.com/rapyuta-robotics/jupyter_ros_utils

To use it, run the following command:

python3 generate_cling_3rd_party.py roscpp

The command will create a file called load_roscpp.h, and you can just add #include “load_roscpp.h” in your jupyter notebook’s cell. You can also use this script to load other library, e.g. ./generate_cling_3rd_party.py pcl_common-1.10, this will create the file named load_pcl_common-1.10.h to be loaded in the jupyter notebook.

Now that we’re ready, let’s add #include "load_roscpp.h" in your jupyter notebook and run the ROS publisher and the ROS Subscriber nodes.

C++ ROS Publisher in Jupyter Notebook
C++ ROS Subscriber in Jupter Notebook

Update July 14: You can now try out our notebook samples from Binder with this URL: https://mybinder.org/v2/gh/rapyuta-robotics/jupyter_ros_utils/HEAD?filepath=cpp%2Fnotebooks

All the codes are open source and available in https://github.com/rapyuta-robotics/jupyter_ros_utils

FAQ:
Q: My notebook cell is stuck, why does it happen?
A: Remember to run the roscore.

Author:
Robotics Perception Engineer at Rapyuta Robotics

Enquire now

Give us a call or fill in the form below and we will contact you. We endeavor to answer all inquiries within 24 hours on business days.