Live plotting from SimObjects using Python and matplotlib

This way of achieving live plotting depends on having Python (version >=3.6) and matplotlib installed on the computer running the simulation. Multiple Python installations exist, such as Spyder or Anaconda, but the clean version from www.python.org is recommended (because of comatibility issues with zlib).

This tutorial will introde an example project demonstrating setup of the plotting, before instruct how to include the plotting functionalities in your own SimObjects and tailor plots to your needs.

Compiling the example code

First, clone the project from code.sintef.no,

git clone ssh://git@git.code.sintef.no/fhsim/fhsimpyplot.git

then build and compile the project (assuming you want visualization),

mkdir buildvis
cd buildvis
cmake -DPY_PLOT=1 -DFH_VISUALIZATION=1 -Ax64 ..
cmake --build . --target install --config release

Note that the variable PY_PLOT is set to 1 to enable the plotting functions. If you don’t have Python installed, CMake will notify you and remove all Python dependencies when building and compiling the project.

To run a test using the SimObjects, simply execute FhRtVis.exe with the SpringMass.xml file that can be found in the input-folder.


Live-plotting of vertical position of mass using matplotlib and Python

Note that the labels and title in the plot-window can be set in the configuration file, see SpringMass.xml for more information.

Adding live plotting to a SimObject

To include live-plotting functionalities in a SimObject:

  1. The Python plotting functions must be copied from the folder Python in the fhsimPython-library to your own library.

  2. Add the following to the outer CMakeLists.txt file:

     if(PY_PLOT)
         FIND_PACKAGE(PythonInterp 3 REQUIRED)
         FIND_PACKAGE(PythonLibs 3 REQUIRED)
         if( PYTHONINTERP_FOUND AND PYTHONLIBS_FOUND )
             message("*** Installing numpy...")
             execute_process(COMMAND pip install numpy)
             include_directories(Python/matplotlib)
             include_directories(${PYTHON_INCLUDE_PATH}/../Lib/site-packages/numpy/core/include)
             include_directories(${PYTHON_INCLUDE_PATH}/../Lib/site-packages/numpy/)
             include_directories(${PYTHON_LIBRARIES})
             include_directories(${PYTHON_INCLUDE_DIRS})
             link_directories(${PYTHON_INCLUDE_PATH}/../libs/)
             add_definitions(-DMATPLOTLIBCPP_PYTHON_HEADER=Python.h)
             set(LIVEPLOT ON) 
             set(Python_SRC Python/plotfunctions/PlotFunctions.cpp)
             set(Python_H Python/matplotlib/matplotlibcpp.h Python/plotfunctions/PlotFunctions.h Python/plotfunctions/LivePlot.h)
         else()
             set(Python_SRC Python/plotfunctions/PlotFunctions.cpp) 
             set(Python_H Python/plotfunctions/PlotFunctions.h Python/plotfunctions/LivePlot.h)
             set(LIVEPLOT OFF)    
         endif(PYTHONINTERP_FOUND AND PYTHONLIBS_FOUND)
     else()    
         set(Python_SRC Python/plotfunctions/PlotFunctions.cpp) 
         set(Python_H Python/plotfunctions/PlotFunctions.h Python/plotfunctions/LivePlot.h)        
         set(LIVEPLOT OFF) 
     endif(PY_PLOT)
     configure_file(Python/LivePlot.h.cmake ../Python/plotfunctions/LivePlot.h)
     include_directories(Python/plotfunctions)
     source_group(PlotFunctions\\Source FILES ${Python_SRC})
     source_group(PlotFunctions\\Header FILES ${Python_H})
    
  3. Include ${Python_SRC} and ${Python_H} to your target in CMakeLists.txt. See the CMakeLists.txt-file for the fhsimPyplot-library for more information.

  4. Include PlotFunctions.h in your simobject source file (as done in CMass.cpp). In PythonFunctions there are two plotting-functions defined, namely PlotSingle() and Plot3DPos(). More will be added. If you want, you can create tailored plot-functions in PlotFunctions.h/PlotFunctions.cpp. This is briefly discussed in the following.

Creating tailored plot-functions

Creating new plot functions is quite simple. Each plot function in PlotFunctions.h/PlotFunctions.cpp are linked to the matplotlibcpp.h-file. If you, as an example, want to create a function which plots two data-vectors, x1 and x2, in two subplots in the figure, you can add something similar to the following code:


void Plotx1x2(std::vector<double> time, std::vector<double> x1, std::vector<double> x2)
{
	// Plotting
	// Clear previous plot
	matplotlibcpp::clf();

	// Adding graph title
	matplotlibcpp::title("Plot title");

	matplotlibcpp::subplot(2, 1, 1);
	matplotlibcpp::grid(true);
	matplotlibcpp::named_plot("Data from x1", time, x1);
	matplotlibcpp::legend();
	matplotlibcpp::ylabel("[unit from x1]");

	matplotlibcpp::subplot(2, 1, 2);
	matplotlibcpp::grid(true);
	matplotlibcpp::named_plot("Data from x2", time, x2);
	matplotlibcpp::legend();
	matplotlibcpp::ylabel(["Unit from x2"]);
	matplotlibcpp::xlabel("Time [s]"); 	

	// Display plot continuously
	matplotlibcpp::pause(0.01);
}

Note that syntax for creating a plot is quite similar to the syntax used by matplotlib in python.