plot.py
Go to the documentation of this file.
1 """Various plotting utlities."""
2 
3 # pylint: disable=no-member, invalid-name
4 
5 import matplotlib.pyplot as plt
6 import numpy as np
7 from matplotlib import patches
8 from mpl_toolkits.mplot3d import Axes3D # pylint: disable=unused-import
9 
10 import gtsam
11 
12 
13 def set_axes_equal(fignum):
14  """
15  Make axes of 3D plot have equal scale so that spheres appear as spheres,
16  cubes as cubes, etc.. This is one possible solution to Matplotlib's
17  ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
18 
19  Args:
20  fignum (int): An integer representing the figure number for Matplotlib.
21  """
22  fig = plt.figure(fignum)
23  ax = fig.gca(projection='3d')
24 
25  limits = np.array([
26  ax.get_xlim3d(),
27  ax.get_ylim3d(),
28  ax.get_zlim3d(),
29  ])
30 
31  origin = np.mean(limits, axis=1)
32  radius = 0.5 * np.max(np.abs(limits[:, 1] - limits[:, 0]))
33 
34  ax.set_xlim3d([origin[0] - radius, origin[0] + radius])
35  ax.set_ylim3d([origin[1] - radius, origin[1] + radius])
36  ax.set_zlim3d([origin[2] - radius, origin[2] + radius])
37 
38 
39 def ellipsoid(rx, ry, rz, n):
40  """
41  Numpy equivalent of Matlab's ellipsoid function.
42 
43  Args:
44  rx (double): Radius of ellipsoid in X-axis.
45  ry (double): Radius of ellipsoid in Y-axis.
46  rz (double): Radius of ellipsoid in Z-axis.
47  n (int): The granularity of the ellipsoid plotted.
48 
49  Returns:
50  tuple[numpy.ndarray]: The points in the x, y and z axes to use for the surface plot.
51  """
52  u = np.linspace(0, 2*np.pi, n+1)
53  v = np.linspace(0, np.pi, n+1)
54  x = -rx * np.outer(np.cos(u), np.sin(v)).T
55  y = -ry * np.outer(np.sin(u), np.sin(v)).T
56  z = -rz * np.outer(np.ones_like(u), np.cos(v)).T
57 
58  return x, y, z
59 
60 
61 def plot_covariance_ellipse_3d(axes, origin, P, scale=1, n=8, alpha=0.5):
62  """
63  Plots a Gaussian as an uncertainty ellipse
64 
65  Based on Maybeck Vol 1, page 366
66  k=2.296 corresponds to 1 std, 68.26% of all probability
67  k=11.82 corresponds to 3 std, 99.74% of all probability
68 
69  Args:
70  axes (matplotlib.axes.Axes): Matplotlib axes.
71  origin (gtsam.Point3): The origin in the world frame.
72  P (numpy.ndarray): The marginal covariance matrix of the 3D point
73  which will be represented as an ellipse.
74  scale (float): Scaling factor of the radii of the covariance ellipse.
75  n (int): Defines the granularity of the ellipse. Higher values indicate finer ellipses.
76  alpha (float): Transparency value for the plotted surface in the range [0, 1].
77  """
78  k = 11.82
79  U, S, _ = np.linalg.svd(P)
80 
81  radii = k * np.sqrt(S)
82  radii = radii * scale
83  rx, ry, rz = radii
84 
85  # generate data for "unrotated" ellipsoid
86  xc, yc, zc = ellipsoid(rx, ry, rz, n)
87 
88  # rotate data with orientation matrix U and center c
89  data = np.kron(U[:, 0:1], xc) + np.kron(U[:, 1:2], yc) + \
90  np.kron(U[:, 2:3], zc)
91  n = data.shape[1]
92  x = data[0:n, :] + origin[0]
93  y = data[n:2*n, :] + origin[1]
94  z = data[2*n:, :] + origin[2]
95 
96  axes.plot_surface(x, y, z, alpha=alpha, cmap='hot')
97 
98 
99 def plot_pose2_on_axes(axes, pose, axis_length=0.1, covariance=None):
100  """
101  Plot a 2D pose on given axis `axes` with given `axis_length`.
102 
103  Args:
104  axes (matplotlib.axes.Axes): Matplotlib axes.
105  pose (gtsam.Pose2): The pose to be plotted.
106  axis_length (float): The length of the camera axes.
107  covariance (numpy.ndarray): Marginal covariance matrix to plot
108  the uncertainty of the estimation.
109  """
110  # get rotation and translation (center)
111  gRp = pose.rotation().matrix() # rotation from pose to global
112  t = pose.translation()
113  origin = t
114 
115  # draw the camera axes
116  x_axis = origin + gRp[:, 0] * axis_length
117  line = np.append(origin[np.newaxis], x_axis[np.newaxis], axis=0)
118  axes.plot(line[:, 0], line[:, 1], 'r-')
119 
120  y_axis = origin + gRp[:, 1] * axis_length
121  line = np.append(origin[np.newaxis], y_axis[np.newaxis], axis=0)
122  axes.plot(line[:, 0], line[:, 1], 'g-')
123 
124  if covariance is not None:
125  pPp = covariance[0:2, 0:2]
126  gPp = np.matmul(np.matmul(gRp, pPp), gRp.T)
127 
128  w, v = np.linalg.eig(gPp)
129 
130  # k = 2.296
131  k = 5.0
132 
133  angle = np.arctan2(v[1, 0], v[0, 0])
134  e1 = patches.Ellipse(origin, np.sqrt(w[0]*k), np.sqrt(w[1]*k),
135  np.rad2deg(angle), fill=False)
136  axes.add_patch(e1)
137 
138 
139 def plot_pose2(fignum, pose, axis_length=0.1, covariance=None,
140  axis_labels=('X axis', 'Y axis', 'Z axis')):
141  """
142  Plot a 2D pose on given figure with given `axis_length`.
143 
144  Args:
145  fignum (int): Integer representing the figure number to use for plotting.
146  pose (gtsam.Pose2): The pose to be plotted.
147  axis_length (float): The length of the camera axes.
148  covariance (numpy.ndarray): Marginal covariance matrix to plot
149  the uncertainty of the estimation.
150  axis_labels (iterable[string]): List of axis labels to set.
151  """
152  # get figure object
153  fig = plt.figure(fignum)
154  axes = fig.gca()
155  plot_pose2_on_axes(axes, pose, axis_length=axis_length,
156  covariance=covariance)
157 
158  axes.set_xlabel(axis_labels[0])
159  axes.set_ylabel(axis_labels[1])
160 
161  return fig
162 
163 
164 def plot_point3_on_axes(axes, point, linespec, P=None):
165  """
166  Plot a 3D point on given axis `axes` with given `linespec`.
167 
168  Args:
169  axes (matplotlib.axes.Axes): Matplotlib axes.
170  point (gtsam.Point3): The point to be plotted.
171  linespec (string): String representing formatting options for Matplotlib.
172  P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
173  """
174  axes.plot([point[0]], [point[1]], [point[2]], linespec)
175  if P is not None:
176  plot_covariance_ellipse_3d(axes, point, P)
177 
178 
179 def plot_point3(fignum, point, linespec, P=None,
180  axis_labels=('X axis', 'Y axis', 'Z axis')):
181  """
182  Plot a 3D point on given figure with given `linespec`.
183 
184  Args:
185  fignum (int): Integer representing the figure number to use for plotting.
186  point (gtsam.Point3): The point to be plotted.
187  linespec (string): String representing formatting options for Matplotlib.
188  P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
189  axis_labels (iterable[string]): List of axis labels to set.
190 
191  Returns:
192  fig: The matplotlib figure.
193 
194  """
195  fig = plt.figure(fignum)
196  axes = fig.gca(projection='3d')
197  plot_point3_on_axes(axes, point, linespec, P)
198 
199  axes.set_xlabel(axis_labels[0])
200  axes.set_ylabel(axis_labels[1])
201  axes.set_zlabel(axis_labels[2])
202 
203  return fig
204 
205 
206 def plot_3d_points(fignum, values, linespec="g*", marginals=None,
207  title="3D Points", axis_labels=('X axis', 'Y axis', 'Z axis')):
208  """
209  Plots the Point3s in `values`, with optional covariances.
210  Finds all the Point3 objects in the given Values object and plots them.
211  If a Marginals object is given, this function will also plot marginal
212  covariance ellipses for each point.
213 
214  Args:
215  fignum (int): Integer representing the figure number to use for plotting.
216  values (gtsam.Values): Values dictionary consisting of points to be plotted.
217  linespec (string): String representing formatting options for Matplotlib.
218  marginals (numpy.ndarray): Marginal covariance matrix to plot the
219  uncertainty of the estimation.
220  title (string): The title of the plot.
221  axis_labels (iterable[string]): List of axis labels to set.
222  """
223 
224  keys = values.keys()
225 
226  # Plot points and covariance matrices
227  for key in keys:
228  try:
229  point = values.atPoint3(key)
230  if marginals is not None:
231  covariance = marginals.marginalCovariance(key)
232  else:
233  covariance = None
234 
235  fig = plot_point3(fignum, point, linespec, covariance,
236  axis_labels=axis_labels)
237 
238  except RuntimeError:
239  continue
240  # I guess it's not a Point3
241 
242  fig = plt.figure(fignum)
243  fig.suptitle(title)
244  fig.canvas.set_window_title(title.lower())
245 
246 
247 def plot_pose3_on_axes(axes, pose, axis_length=0.1, P=None, scale=1):
248  """
249  Plot a 3D pose on given axis `axes` with given `axis_length`.
250 
251  Args:
252  axes (matplotlib.axes.Axes): Matplotlib axes.
253  point (gtsam.Point3): The point to be plotted.
254  linespec (string): String representing formatting options for Matplotlib.
255  P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
256  """
257  # get rotation and translation (center)
258  gRp = pose.rotation().matrix() # rotation from pose to global
259  origin = pose.translation()
260 
261  # draw the camera axes
262  x_axis = origin + gRp[:, 0] * axis_length
263  line = np.append(origin[np.newaxis], x_axis[np.newaxis], axis=0)
264  axes.plot(line[:, 0], line[:, 1], line[:, 2], 'r-')
265 
266  y_axis = origin + gRp[:, 1] * axis_length
267  line = np.append(origin[np.newaxis], y_axis[np.newaxis], axis=0)
268  axes.plot(line[:, 0], line[:, 1], line[:, 2], 'g-')
269 
270  z_axis = origin + gRp[:, 2] * axis_length
271  line = np.append(origin[np.newaxis], z_axis[np.newaxis], axis=0)
272  axes.plot(line[:, 0], line[:, 1], line[:, 2], 'b-')
273 
274  # plot the covariance
275  if P is not None:
276  # covariance matrix in pose coordinate frame
277  pPp = P[3:6, 3:6]
278  # convert the covariance matrix to global coordinate frame
279  gPp = gRp @ pPp @ gRp.T
280  plot_covariance_ellipse_3d(axes, origin, gPp)
281 
282 
283 def plot_pose3(fignum, pose, axis_length=0.1, P=None,
284  axis_labels=('X axis', 'Y axis', 'Z axis')):
285  """
286  Plot a 3D pose on given figure with given `axis_length`.
287 
288  Args:
289  fignum (int): Integer representing the figure number to use for plotting.
290  pose (gtsam.Pose3): 3D pose to be plotted.
291  linespec (string): String representing formatting options for Matplotlib.
292  P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
293  axis_labels (iterable[string]): List of axis labels to set.
294 
295  Returns:
296  fig: The matplotlib figure.
297  """
298  # get figure object
299  fig = plt.figure(fignum)
300  axes = fig.gca(projection='3d')
301  plot_pose3_on_axes(axes, pose, P=P,
302  axis_length=axis_length)
303 
304  axes.set_xlabel(axis_labels[0])
305  axes.set_ylabel(axis_labels[1])
306  axes.set_zlabel(axis_labels[2])
307 
308  return fig
309 
310 
311 def plot_trajectory(fignum, values, scale=1, marginals=None,
312  title="Plot Trajectory", axis_labels=('X axis', 'Y axis', 'Z axis')):
313  """
314  Plot a complete 2D/3D trajectory using poses in `values`.
315 
316  Args:
317  fignum (int): Integer representing the figure number to use for plotting.
318  values (gtsam.Values): Values containing some Pose2 and/or Pose3 values.
319  scale (float): Value to scale the poses by.
320  marginals (gtsam.Marginals): Marginalized probability values of the estimation.
321  Used to plot uncertainty bounds.
322  title (string): The title of the plot.
323  axis_labels (iterable[string]): List of axis labels to set.
324  """
325  fig = plt.figure(fignum)
326  axes = fig.gca(projection='3d')
327 
328  axes.set_xlabel(axis_labels[0])
329  axes.set_ylabel(axis_labels[1])
330  axes.set_zlabel(axis_labels[2])
331 
332  # Plot 2D poses, if any
333  poses = gtsam.utilities.allPose2s(values)
334  for key in poses.keys():
335  pose = poses.atPose2(key)
336  if marginals:
337  covariance = marginals.marginalCovariance(key)
338  else:
339  covariance = None
340 
341  plot_pose2_on_axes(axes, pose, covariance=covariance,
342  axis_length=scale)
343 
344  # Then 3D poses, if any
345  poses = gtsam.utilities.allPose3s(values)
346  for key in poses.keys():
347  pose = poses.atPose3(key)
348  if marginals:
349  covariance = marginals.marginalCovariance(key)
350  else:
351  covariance = None
352 
353  plot_pose3_on_axes(axes, pose, P=covariance,
354  axis_length=scale)
355 
356  fig.suptitle(title)
357  fig.canvas.set_window_title(title.lower())
358 
359 
360 def plot_incremental_trajectory(fignum, values, start=0,
361  scale=1, marginals=None,
362  time_interval=0.0):
363  """
364  Incrementally plot a complete 3D trajectory using poses in `values`.
365 
366  Args:
367  fignum (int): Integer representing the figure number to use for plotting.
368  values (gtsam.Values): Values dict containing the poses.
369  start (int): Starting index to start plotting from.
370  scale (float): Value to scale the poses by.
371  marginals (gtsam.Marginals): Marginalized probability values of the estimation.
372  Used to plot uncertainty bounds.
373  time_interval (float): Time in seconds to pause between each rendering.
374  Used to create animation effect.
375  """
376  fig = plt.figure(fignum)
377  axes = fig.gca(projection='3d')
378 
379  poses = gtsam.utilities.allPose3s(values)
380  keys = gtsam.KeyVector(poses.keys())
381 
382  for key in keys[start:]:
383  if values.exists(key):
384  pose_i = values.atPose3(key)
385  plot_pose3(fignum, pose_i, scale)
386 
387  # Update the plot space to encompass all plotted points
388  axes.autoscale()
389 
390  # Set the 3 axes equal
391  set_axes_equal(fignum)
392 
393  # Pause for a fixed amount of seconds
394  plt.pause(time_interval)
def plot_incremental_trajectory(fignum, values, start=0, scale=1, marginals=None, time_interval=0.0)
Definition: plot.py:362
def plot_3d_points(fignum, values, linespec="g*", marginals=None, title="3D Points", axis_labels=('X axis', 'Y axis', 'Z axis'))
Definition: plot.py:207
def plot_pose3_on_axes(axes, pose, axis_length=0.1, P=None, scale=1)
Definition: plot.py:247
def plot_trajectory(fignum, values, scale=1, marginals=None, title="Plot Trajectory", axis_labels=('X axis', 'Y axis', 'Z axis'))
Definition: plot.py:312
FastVector< Key > KeyVector
Define collection type once and for all - also used in wrappers.
Definition: Key.h:86
def ellipsoid(rx, ry, rz, n)
Definition: plot.py:39
def plot_covariance_ellipse_3d(axes, origin, P, scale=1, n=8, alpha=0.5)
Definition: plot.py:61
def plot_pose2(fignum, pose, axis_length=0.1, covariance=None, axis_labels=('X axis', 'Y axis', 'Z axis'))
Definition: plot.py:140
def set_axes_equal(fignum)
Definition: plot.py:13
Values allPose3s(const Values &values)
Extract all Pose3 values.
Values allPose2s(const Values &values)
Extract all Pose3 values.
def plot_pose3(fignum, pose, axis_length=0.1, P=None, axis_labels=('X axis', 'Y axis', 'Z axis'))
Definition: plot.py:284
def plot_pose2_on_axes(axes, pose, axis_length=0.1, covariance=None)
Definition: plot.py:99
Map< Matrix< T, Dynamic, Dynamic, ColMajor >, 0, OuterStride<> > matrix(T *data, int rows, int cols, int stride)
def plot_point3(fignum, point, linespec, P=None, axis_labels=('X axis', 'Y axis', 'Z axis'))
Definition: plot.py:180
def plot_point3_on_axes(axes, point, linespec, P=None)
Definition: plot.py:164


gtsam
Author(s):
autogenerated on Sat May 8 2021 02:43:27