In this chapter, I'm going to explain a program to define a world coordinate as an environment where a vehicle can move. The program can display x-y world coordinates and manage each object that exists in the world.
All classes for world visualization are located in a directory, src/components/visualization. Firstly, I'm going to introduce a global x-y coordinate visualization class as follows.
global_xy_visualizer.py
"""
global_xy_visualizer.py
Author: Shisato Yano
"""
import matplotlib.pyplot as plt
import matplotlib.animation as anm
class GlobalXYVisualizer:
"""
Visualization class for global 2D X-Y plot
"""
def __init__(self, x_lim, y_lim, time_params, show_zoom=True, gif_name=None):
"""
Constructor
x_lim: MinMax object of x axis
y_lim: MinMax object of y axis
time_params: TimeParameters object
show_zoom: Zoom around vehicle when this flag is true
gif_name: Gif file's name as string
"""
self.objects = []
self.x_lim = x_lim
self.y_lim = y_lim
self.time_params = time_params
self.gif_name = gif_name
self.show_plot = True
self.show_zoom = show_zoomThis class needs to import matplotlib.pyplot and animation to display data plots and an animation. In __init__ method, an empty objects list is defined. Objects that are located in the world are stored in this list.
x_lim and y_lim are objects of MinMax class. This class defines the limits of the x/y axes. These objects are used to set the size of the world visualization.
min_max.py
"""
min_max.py
Author: Shisato Yano
"""
class MinMax:
"""
Limitation values class of axis
"""
def __init__(self, min=0, max=10):
"""
Constructor
min: Minimum value of axis
max: Maximum value of axis
"""
self.min = min
self.max = max
def min_value(self):
"""
Function to get minimum value
"""
return self.min
def max_value(self):
"""
Function to get maximum value
"""
return self.maxtime_params is an object of TimeParameters class to manage the visualization time.
time_parameters.py
"""
time_parameters.py
Author: Shisato Yano
"""
class TimeParameters:
"""
Parameters class for visualization time setting
"""
def __init__(self, span_sec=10, interval_sec=0.1):
self.span_sec = span_sec
self.interval_sec = interval_sec
self.interval_msec = interval_sec * 1000
self.frame_num = int(span_sec / interval_sec) + 1
def get_interval_sec(self):
"""
Function to get interval time parameter[sec]
"""
return self.interval_sec
def get_interval_msec(self):
"""
Function to get interval time parameter[msec]
"""
return self.interval_msec
def get_frame_num(self):
"""
Function to get number of simulation's frames
"""
return self.frame_num
def current_sec(self, index):
"""
Function to get current time[sec] in simulation
index: Index of simulation's frame
"""
return self.interval_sec * index
def simulation_finished(self, index):
"""
Function to check simulation was finished
index: Index of simulation's frame
"""
return (self.interval_sec * index >= self.span_sec)gif_name is a string object for saving the animation's gif file. The boolean show_plot is used to toggle whether the visualization's figure window is displayed when a unit test is executed. While the test is running, the figure window should not be displayed to continue the test. show_zoom determines whether to limit the visualization area to the vicinity of the vehicle or to show the full world.
The member method add_object() is defined to add objects that are located in the world. An object can be added to the objects list by calling this method.
def add_object(self, obj):
"""
Function to register object which should be visualized
Each elements of list need to have update and draw functions
"""
self.objects.append(obj)A member method, not_show_plot() is defined to prevent the figure window from being displayed of the visualization. This method can be used when the unit test is executed.
def not_show_plot(self):
"""
Function to set show plot flag as false
Each data is visualized only when this flag is false
"""
self.show_plot = FalseThe member method update is used to periodically update each object's data and draw the animation. For this process, each object in the list must have an update method and a draw method.
def update(self, i, elems, axes):
"""
Function to update each objects and draw
i: Index of animation frames
elems: List of plot objects
axes: Axes of figure
"""
# remove each elements in list
while elems: elems.pop().remove()
# show current simulation time[sec] as title
time_str = "Time = {0:.2f}[s]".format(self.time_params.current_sec(i))
axes.set_title(time_str, fontsize=15)
# draw and update each object's data
for obj in self.objects:
obj.draw(axes, elems)
if hasattr(obj, "update"): obj.update(self.time_params.get_interval_sec())
# show data between x-y min and max range
if not self.show_zoom or self.time_params.simulation_finished(i):
axes.set_xlim(self.x_lim.min_value(), self.x_lim.max_value())
axes.set_ylim(self.y_lim.min_value(), self.y_lim.max_value())Finally, this draw method is defined to execute simulation including updating and visualization. If a specific GIF filename is assigned to gif_name, the simulation will be saved as a GIF file instead of being displayed.
def draw(self):
"""
Function to define animation's main process
"""
# clear existing plot and close existing figure
plt.clf()
plt.close()
# setting figure and axes
figure = plt.figure(figsize=(8, 8))
axes = figure.add_subplot(111)
axes.set_aspect("equal")
axes.set_xlabel('X[m]', fontsize=20)
axes.set_ylabel('Y[m]', fontsize=20)
# create animation instance
elems = []
if self.show_plot:
print("Simulation start!!")
self.anime = anm.FuncAnimation(figure, self.update, fargs=(elems, axes),
frames=self.time_params.get_frame_num(),
interval=self.time_params.get_interval_msec(),
repeat=False)
if self.gif_name:
self.anime.save(self.gif_name, writer="pillow")
else:
plt.show()
print("Simulation finished!!")
else:
# only when executed as unit test
for i in range(1000): self.update(i, elems, axes)I have prepared a sample program to visualize an empty world.
"""
visualize_world.py
Author: Shisato Yano
"""
# import path setting
import numpy as np
import sys
from pathlib import Path
abs_dir_path = str(Path(__file__).absolute().parent)
relative_path = "/../../src/components/"
sys.path.append(abs_dir_path + relative_path + "visualization")
# import component modules
from global_xy_visualizer import GlobalXYVisualizer
from min_max import MinMax
from time_parameters import TimeParameters
# flag to show plot figure
# when executed as unit test, this flag is set as false
show_plot = True
def main():
"""
Main process function
"""
# set simulation parameters
x_lim, y_lim = MinMax(-30, 30), MinMax(-30, 30)
vis = GlobalXYVisualizer(x_lim, y_lim, TimeParameters(span_sec=20))
# add objects here
# plot figure is not shown when executed as unit test
if not show_plot: vis.not_show_plot()
# show plot figure
vis.draw()
# execute main process
if __name__ == "__main__":
main()You will be able to understand how to use the above world visualization class by reading the code. By executing this, you can see that an empty world simulation is visualized as follows.
