作为@ImportanceOfBeingErnest noted in a comment,您的代码很好,但 matplotlib 具有 2d 引擎,因此 3d 绘图很容易显示奇怪的伪影。特别是,一次渲染一个对象,因此两个 3d 对象通常要么完全在另一个前面,要么完全在另一个后面,这使得使用 matplotlib 几乎不可能实现联锁 3d 对象的可视化。
我个人的替代建议是mayavi(难以置信的灵活性和可视化,非常陡峭的学习曲线),但是我想展示一个通常可以完全消除问题的技巧。这个想法是使用表面之间的隐形桥梁将两个独立的对象变成一个对象。该方法的可能缺点是
- 您需要将两个曲面都绘制为曲面而不是
contour3D,并且
- 输出在很大程度上依赖于透明度,因此您需要一个可以处理这种情况的后端。
免责声明:我从now-defunct Stack Overflow Documentation project 的 matplotlib 主题的贡献者那里学到了这个技巧,但不幸的是我不记得那个用户是谁。
为了在您的用例中使用此技巧,我们基本上必须将 contour3D 调用转为另一个 plot_surface 调用。我认为这总体上并没有那么糟糕。如果您发现生成的图形有太多面无法交互使用,您可能需要重新考虑切割平面的密度。我们还必须明确定义逐点颜色图,其 alpha 通道有助于在两个表面之间建立透明桥梁。由于我们需要将两个表面缝合在一起,因此表面的至少一个“平面内”尺寸必须匹配;在这种情况下,我确保“y”上的点在两种情况下是相同的。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
def f(theta1, theta2):
return theta1**2 + theta2**2
fig, ax = plt.subplots(figsize=(6, 6),
subplot_kw={'projection': '3d'})
# plane data: X, Y, Z, C (first three shaped (nx,ny), last one shaped (nx,ny,4))
x,z = np.meshgrid(np.linspace(-1,1,100), np.linspace(0,2,100)) # <-- you can probably reduce these sizes
X = x.T
Z = z.T
Y = 0 * np.ones((100, 100))
# colormap for the plane: need shape (nx,ny,4) for RGBA values
C = np.full(X.shape + (4,), [0,0,0.5,1]) # dark blue plane, fully opaque
# surface data: theta1_grid, theta2_grid, J_grid, CJ (shaped (nx',ny) or (nx',ny,4))
r = np.linspace(-1,1,X.shape[1]) # <-- we are going to stitch the surface along the y dimension, sizes have to match
theta1_grid, theta2_grid = np.meshgrid(r,r)
J_grid = f(theta1_grid, theta2_grid)
# colormap for the surface; scale data to between 0 and 1 for scaling
CJ = plt.get_cmap('binary')((J_grid - J_grid.min())/J_grid.ptp())
# construct a common dataset with an invisible bridge, shape (2,ny) or (2,ny,4)
X_bridge = np.vstack([X[-1,:],theta1_grid[0,:]])
Y_bridge = np.vstack([Y[-1,:],theta2_grid[0,:]])
Z_bridge = np.vstack([Z[-1,:],J_grid[0,:]])
C_bridge = np.full(Z_bridge.shape + (4,), [1,1,1,0]) # 0 opacity == transparent; probably needs a backend that supports transparency!
# join the datasets
X_surf = np.vstack([X,X_bridge,theta1_grid])
Y_surf = np.vstack([Y,Y_bridge,theta2_grid])
Z_surf = np.vstack([Z,Z_bridge,J_grid])
C_surf = np.vstack([C,C_bridge,CJ])
# plot the joint datasets as a single surface, pass colors explicitly, set strides to 1
ax.plot_surface(X_surf, Y_surf, Z_surf, facecolors=C_surf, rstride=1, cstride=1)
ax.set_xlabel(r'$\theta_1$',fontsize='large')
ax.set_ylabel(r'$\theta_2$',fontsize='large')
ax.set_zlabel(r'$J(\theta_1,\theta_2)$',fontsize='large')
ax.set_title(r'Fig.2 $J(\theta_1,\theta_2)=(\theta_1^2+\theta_2^2)$',fontsize='x-large')
plt.tight_layout()
plt.show()
两个角度的结果:
如您所见,结果相当不错。您可以开始使用表面的各个透明度,看看是否可以使该横截面更明显。您还可以将桥的不透明度切换为 1,以查看您的表面实际上是如何缝合在一起的。总而言之,我们要做的就是获取您现有的数据,确保它们的大小匹配,并定义明确的颜色图和表面之间的辅助桥。