Merge pull request #341 from borglab/fix/python-plotting

Plotting updates
release/4.3a0
Varun Agrawal 2020-06-08 22:27:43 -05:00 committed by GitHub
commit 08487f7a43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 113 additions and 21 deletions

View File

@ -13,8 +13,9 @@ def set_axes_equal(fignum):
Make axes of 3D plot have equal scale so that spheres appear as spheres, Make axes of 3D plot have equal scale so that spheres appear as spheres,
cubes as cubes, etc.. This is one possible solution to Matplotlib's cubes as cubes, etc.. This is one possible solution to Matplotlib's
ax.set_aspect('equal') and ax.axis('equal') not working for 3D. ax.set_aspect('equal') and ax.axis('equal') not working for 3D.
Input
ax: a matplotlib axis, e.g., as output from plt.gca(). Args:
fignum (int): An integer representing the figure number for Matplotlib.
""" """
fig = plt.figure(fignum) fig = plt.figure(fignum)
ax = fig.gca(projection='3d') ax = fig.gca(projection='3d')
@ -34,7 +35,21 @@ def set_axes_equal(fignum):
def ellipsoid(xc, yc, zc, rx, ry, rz, n): def ellipsoid(xc, yc, zc, rx, ry, rz, n):
"""Numpy equivalent of Matlab's ellipsoid function""" """
Numpy equivalent of Matlab's ellipsoid function.
Args:
xc (double): Center of ellipsoid in X-axis.
yc (double): Center of ellipsoid in Y-axis.
zc (double): Center of ellipsoid in Z-axis.
rx (double): Radius of ellipsoid in X-axis.
ry (double): Radius of ellipsoid in Y-axis.
rz (double): Radius of ellipsoid in Z-axis.
n (int): The granularity of the ellipsoid plotted.
Returns:
tuple[numpy.ndarray]: The points in the x, y and z axes to use for the surface plot.
"""
u = np.linspace(0, 2*np.pi, n+1) u = np.linspace(0, 2*np.pi, n+1)
v = np.linspace(0, np.pi, n+1) v = np.linspace(0, np.pi, n+1)
x = -rx * np.outer(np.cos(u), np.sin(v)).T x = -rx * np.outer(np.cos(u), np.sin(v)).T
@ -51,6 +66,14 @@ def plot_covariance_ellipse_3d(axes, origin, P, scale=1, n=8, alpha=0.5):
Based on Maybeck Vol 1, page 366 Based on Maybeck Vol 1, page 366
k=2.296 corresponds to 1 std, 68.26% of all probability k=2.296 corresponds to 1 std, 68.26% of all probability
k=11.82 corresponds to 3 std, 99.74% of all probability k=11.82 corresponds to 3 std, 99.74% of all probability
Args:
axes (matplotlib.axes.Axes): Matplotlib axes.
origin (gtsam.Point3): The origin in the world frame.
P (numpy.ndarray): The marginal covariance matrix of the 3D point which will be represented as an ellipse.
scale (float): Scaling factor of the radii of the covariance ellipse.
n (int): Defines the granularity of the ellipse. Higher values indicate finer ellipses.
alpha (float): Transparency value for the plotted surface in the range [0, 1].
""" """
k = 11.82 k = 11.82
U, S, _ = np.linalg.svd(P) U, S, _ = np.linalg.svd(P)
@ -74,7 +97,15 @@ def plot_covariance_ellipse_3d(axes, origin, P, scale=1, n=8, alpha=0.5):
def plot_pose2_on_axes(axes, pose, axis_length=0.1, covariance=None): def plot_pose2_on_axes(axes, pose, axis_length=0.1, covariance=None):
"""Plot a 2D pose on given axis 'axes' with given 'axis_length'.""" """
Plot a 2D pose on given axis `axes` with given `axis_length`.
Args:
axes (matplotlib.axes.Axes): Matplotlib axes.
pose (gtsam.Pose2): The pose to be plotted.
axis_length (float): The length of the camera axes.
covariance (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
"""
# get rotation and translation (center) # get rotation and translation (center)
gRp = pose.rotation().matrix() # rotation from pose to global gRp = pose.rotation().matrix() # rotation from pose to global
t = pose.translation() t = pose.translation()
@ -105,22 +136,47 @@ def plot_pose2_on_axes(axes, pose, axis_length=0.1, covariance=None):
def plot_pose2(fignum, pose, axis_length=0.1, covariance=None): def plot_pose2(fignum, pose, axis_length=0.1, covariance=None):
"""Plot a 2D pose on given figure with given 'axis_length'.""" """
Plot a 2D pose on given figure with given `axis_length`.
Args:
fignum (int): Integer representing the figure number to use for plotting.
pose (gtsam.Pose2): The pose to be plotted.
axis_length (float): The length of the camera axes.
covariance (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
"""
# get figure object # get figure object
fig = plt.figure(fignum) fig = plt.figure(fignum)
axes = fig.gca() axes = fig.gca()
plot_pose2_on_axes(axes, pose, axis_length, covariance) plot_pose2_on_axes(axes, pose, axis_length=axis_length,
covariance=covariance)
def plot_point3_on_axes(axes, point, linespec, P=None): def plot_point3_on_axes(axes, point, linespec, P=None):
"""Plot a 3D point on given axis 'axes' with given 'linespec'.""" """
Plot a 3D point on given axis `axes` with given `linespec`.
Args:
axes (matplotlib.axes.Axes): Matplotlib axes.
point (gtsam.Point3): The point to be plotted.
linespec (string): String representing formatting options for Matplotlib.
P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
"""
axes.plot([point.x()], [point.y()], [point.z()], linespec) axes.plot([point.x()], [point.y()], [point.z()], linespec)
if P is not None: if P is not None:
plot_covariance_ellipse_3d(axes, point.vector(), P) plot_covariance_ellipse_3d(axes, point.vector(), P)
def plot_point3(fignum, point, linespec, P=None): def plot_point3(fignum, point, linespec, P=None):
"""Plot a 3D point on given figure with given 'linespec'.""" """
Plot a 3D point on given figure with given `linespec`.
Args:
fignum (int): Integer representing the figure number to use for plotting.
point (gtsam.Point3): The point to be plotted.
linespec (string): String representing formatting options for Matplotlib.
P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
"""
fig = plt.figure(fignum) fig = plt.figure(fignum)
axes = fig.gca(projection='3d') axes = fig.gca(projection='3d')
plot_point3_on_axes(axes, point, linespec, P) plot_point3_on_axes(axes, point, linespec, P)
@ -128,10 +184,16 @@ def plot_point3(fignum, point, linespec, P=None):
def plot_3d_points(fignum, values, linespec="g*", marginals=None): def plot_3d_points(fignum, values, linespec="g*", marginals=None):
""" """
Plots the Point3s in 'values', with optional covariances. Plots the Point3s in `values`, with optional covariances.
Finds all the Point3 objects in the given Values object and plots them. Finds all the Point3 objects in the given Values object and plots them.
If a Marginals object is given, this function will also plot marginal If a Marginals object is given, this function will also plot marginal
covariance ellipses for each point. covariance ellipses for each point.
Args:
fignum (int): Integer representing the figure number to use for plotting.
values (gtsam.Values): Values dictionary consisting of points to be plotted.
linespec (string): String representing formatting options for Matplotlib.
covariance (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
""" """
keys = values.keys() keys = values.keys()
@ -142,11 +204,11 @@ def plot_3d_points(fignum, values, linespec="g*", marginals=None):
key = keys.at(i) key = keys.at(i)
point = values.atPoint3(key) point = values.atPoint3(key)
if marginals is not None: if marginals is not None:
P = marginals.marginalCovariance(key); covariance = marginals.marginalCovariance(key)
else: else:
P = None covariance = None
plot_point3(fignum, point, linespec, P) plot_point3(fignum, point, linespec, covariance)
except RuntimeError: except RuntimeError:
continue continue
@ -154,7 +216,15 @@ def plot_3d_points(fignum, values, linespec="g*", marginals=None):
def plot_pose3_on_axes(axes, pose, axis_length=0.1, P=None, scale=1): def plot_pose3_on_axes(axes, pose, axis_length=0.1, P=None, scale=1):
"""Plot a 3D pose on given axis 'axes' with given 'axis_length'.""" """
Plot a 3D pose on given axis `axes` with given `axis_length`.
Args:
axes (matplotlib.axes.Axes): Matplotlib axes.
point (gtsam.Point3): The point to be plotted.
linespec (string): String representing formatting options for Matplotlib.
P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
"""
# get rotation and translation (center) # get rotation and translation (center)
gRp = pose.rotation().matrix() # rotation from pose to global gRp = pose.rotation().matrix() # rotation from pose to global
origin = pose.translation().vector() origin = pose.translation().vector()
@ -182,13 +252,33 @@ def plot_pose3_on_axes(axes, pose, axis_length=0.1, P=None, scale=1):
def plot_pose3(fignum, pose, axis_length=0.1, P=None): def plot_pose3(fignum, pose, axis_length=0.1, P=None):
"""Plot a 3D pose on given figure with given 'axis_length'.""" """
Plot a 3D pose on given figure with given `axis_length`.
Args:
fignum (int): Integer representing the figure number to use for plotting.
pose (gtsam.Pose3): 3D pose to be plotted.
linespec (string): String representing formatting options for Matplotlib.
P (numpy.ndarray): Marginal covariance matrix to plot the uncertainty of the estimation.
"""
# get figure object # get figure object
fig = plt.figure(fignum) fig = plt.figure(fignum)
axes = fig.gca(projection='3d') axes = fig.gca(projection='3d')
plot_pose3_on_axes(axes, pose, P=P, axis_length=axis_length) plot_pose3_on_axes(axes, pose, P=P,
axis_length=axis_length)
def plot_trajectory(fignum, values, scale=1, marginals=None): def plot_trajectory(fignum, values, scale=1, marginals=None):
"""
Plot a complete 3D trajectory using poses in `values`.
Args:
fignum (int): Integer representing the figure number to use for plotting.
values (gtsam.Values): Values dict containing the poses.
scale (float): Value to scale the poses by.
marginals (gtsam.Marginals): Marginalized probability values of the estimation.
Used to plot uncertainty bounds.
"""
pose3Values = gtsam.allPose3s(values) pose3Values = gtsam.allPose3s(values)
keys = gtsam.KeyVector(pose3Values.keys()) keys = gtsam.KeyVector(pose3Values.keys())
lastIndex = None lastIndex = None
@ -209,11 +299,12 @@ def plot_trajectory(fignum, values, scale=1, marginals=None):
pass pass
if marginals: if marginals:
P = marginals.marginalCovariance(lastKey) covariance = marginals.marginalCovariance(lastKey)
else: else:
P = None covariance = None
plot_pose3(fignum, lastPose, P, scale) plot_pose3(fignum, lastPose, P=covariance,
axis_length=scale)
lastIndex = i lastIndex = i
@ -223,11 +314,12 @@ def plot_trajectory(fignum, values, scale=1, marginals=None):
try: try:
lastPose = pose3Values.atPose3(lastKey) lastPose = pose3Values.atPose3(lastKey)
if marginals: if marginals:
P = marginals.marginalCovariance(lastKey) covariance = marginals.marginalCovariance(lastKey)
else: else:
P = None covariance = None
plot_pose3(fignum, lastPose, P, scale) plot_pose3(fignum, lastPose, P=covariance,
axis_length=scale)
except: except:
pass pass