Visualization using the Matplotlib

Building the visualizer

The overall plan here is to derive a class that we will call MplPendulumVisualizer from MplVisualizer. In its constructor, we will lay down all the elements we want to use to visualize the system. In our case these will be the beam on which the cart is moving, the cart and of course the pendulum. Later on, the method pymoskito.MplVisualizer.update_scene() will be called repeatedly from the GUI to update the visualization.

We will start off with the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# -*- coding: utf-8 -*-
import numpy as np
import pymoskito as pm

import matplotlib as mpl
import matplotlib.patches
import matplotlib.transforms


class MplPendulumVisualizer(pm.MplVisualizer):

    # parameters
    x_min_plot = -.85
    x_max_plot = .85
    y_min_plot = -.6
    y_max_plot = .6

    cart_height = .1
    cart_length = .2

    beam_height = .01
    beam_length = 1

    pendulum_shaft_height = 0.027
    pendulum_shaft_radius = 0.020

    pendulum_height = 0.5
    pendulum_radius = 0.005

On top, we import some modules we’ll need later on. Once this is done we derive our MplPendulumVisualizer from MplVisualizer. What follows below are some parameters for the matplotlib canvas and the objects we want to draw, fell free to adapt them as you like!

In the first part of the constructor, we set up the canvas:

30
31
32
33
34
35
36
37

    def __init__(self, q_widget, q_layout):
        # setup canvas
        pm.MplVisualizer.__init__(self, q_widget, q_layout)
        self.axes.set_xlim(self.x_min_plot, self.x_max_plot)
        self.axes.set_ylim(self.y_min_plot, self.y_max_plot)
        self.axes.set_aspect("equal")

Afterwards, our “actors” are created:

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        self.beam = mpl.patches.Rectangle(xy=[-self.beam_length/2,
                                              -(self.beam_height
                                                + self.cart_height/2)],
                                          width=self.beam_length,
                                          height=self.beam_height,
                                          color="lightgrey")

        self.cart = mpl.patches.Rectangle(xy=[-self.cart_length/2,
                                              -self.cart_height/2],
                                          width=self.cart_length,
                                          height=self.cart_height,
                                          color="dimgrey")

        self.pendulum_shaft = mpl.patches.Circle(
            xy=[0, 0],
            radius=self.pendulum_shaft_radius,
            color="lightgrey",
            zorder=3)

        t = mpl.transforms.Affine2D().rotate_deg(180) + self.axes.transData
        self.pendulum = mpl.patches.Rectangle(
            xy=[-self.pendulum_radius, 0],
            width=2*self.pendulum_radius,
            height=self.pendulum_height,
            color=pm.colors.HKS07K100,
            zorder=2,
            transform=t)

Note that a transformation object is used to get the patch in the correct place and orientation. We’ll make more use of transformations later. For now, all that is left to do for the constructor is to add our actors t the canvas:

68
69
70
71
72
        self.axes.add_patch(self.beam)
        self.axes.add_patch(self.cart)
        self.axes.add_patch(self.pendulum_shaft)
        self.axes.add_patch(self.pendulum)

After this step, the GUI knows how our system looks like. Now comes the interesting part: We use the systems state vector (the first Equation in introduction) which we obtained from the simulation to update our drawing:

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
    def update_scene(self, x):
        cart_pos = x[0]
        phi = np.rad2deg(x[1])

        # cart and shaft
        self.cart.set_x(cart_pos - self.cart_length/2)
        self.pendulum_shaft.center = (cart_pos, 0)

        # pendulum
        ped_trafo = (mpl.transforms.Affine2D().rotate_deg(phi)
                     + mpl.transforms.Affine2D().translate(cart_pos, 0)
                     + self.axes.transData)
        self.pendulum.set_transform(ped_trafo)

        # update canvas
        self.canvas.draw()


As defined by our model, the first element of the state vector x yields the cart position, while the pendulum deflection (in rad) is given by x[1] . Firstly, cart and the pendulum shaft are moved. This can either be done via set_x() or by directly overwriting the value of the center attribute. For the pendulum however, a transformation chain is build. It consists of a rotation by the pendulum angle phi followed by a translation to the current cart position. The last component is used to compensate offsets from the rendered window.

Lastly but important: The canvas is updated vie a call to self.canvas.draw()

The complete class can be found under:

pymoskito/examples/simple_pednulum/visualizer_mpl.py

Registering the visualizer

To get our visualizer actually working, we need to register it. For the simple main.py of our example this would mean adding the following lines:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# -*- coding: utf-8 -*-
import pymoskito as pm

# import custom modules
import model
import controller
import visualizer_mpl


if __name__ == '__main__':
    # register model
    pm.register_simulation_module(pm.Model, model.PendulumModel)

    # register controller
    pm.register_simulation_module(pm.Controller, controller.BasicController)

    # register visualizer
    pm.register_visualizer(visualizer_mpl.MplPendulumVisualizer)

    # start the program
    pm.run()

After starting the program, this is what you should see in the top right corner:

Matplotlib visualization of the simple inverse pendulum

Fig. 8 Matplotlib visualization of the simple pendulum system

If you are looking for a fancier animation, check out the VTK Tutorial.