将np.array([list,list])传入3D绘图器返回空结果的问题排查
3D曲面与等高线绘图为空的问题分析与解决
问题背景
你尝试用轨迹点的x、y、apf列表绘制3D势能曲面和等高线,但通过np.array([x,x])这类方式传递数据后,绘图结果为空。核心问题出在数据结构不符合3D绘图函数的要求,同时代码里还有几处逻辑错误导致数据无效。
核心问题拆解
3D绘图函数的输入要求错误
plot_surface和contourf需要的是二维网格数据:X和Y必须是通过np.meshgrid生成的二维矩阵,代表平面上所有网格点的坐标;Z是对应每个网格点的函数值,同样是二维矩阵。
你用np.array([x,x])只是把一维的轨迹点数组重复了两行,本质还是一维数据的堆叠,无法形成曲面所需的网格结构,自然无法绘制出曲面。
APF值计算的逻辑错误
循环里计算apf.append(APF(...))时,传入的是start.X和start.Y,而不是当前移动的temp.X和temp.Y,导致apf列表里全是初始点的势能值,数据没有变化,即使能绘图也只会是一个平面。轨迹记录的隐藏问题
Point类的move方法是修改自身属性后返回自己,所以trajectory里的所有元素都是同一个Point实例,最后x、y列表里的数值其实都是最后一次移动后的结果(因为每次append的是当前temp的X/Y,但temp是同一个对象,后续修改会影响之前的记录)。
解决方法与修改后的代码
要绘制整个势能曲面,我们需要先生成覆盖目标区域的网格,计算每个网格点的APF值,再用这些网格数据绘图;同时修正轨迹计算和APF记录的逻辑:
import numpy as np import math import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import axes3d from matplotlib import cm class Point(object): def __init__(self,x,y): self.X = x self.Y = y def distance(self, goal): dx = self.X - goal.X dy = self.Y - goal.Y return math.hypot(dx, dy) def move(self, xGrad, yGrad): # 返回新的Point实例,避免修改原对象导致轨迹记录错误 return Point(self.X - xGrad*1e-2, self.Y - yGrad*1e-2) def APF(a, r, currentX, currentY, goalX, goalY, obstacles): attr = a*math.hypot(currentX-goalX, currentY-goalY)**2 repl = 0 for item in obstacles: dist = math.hypot(currentX-item.point.X, currentY-item.point.Y) # 避免距离过小时的除零错误 if dist < 1e-3: repl += float('inf') else: repl += 1/(dist**2) repl *= r return attr + repl def gradX(a, r, h, currentX, currentY, goalX, goalY, obstacles): num1 = APF(a, r, currentX+h, currentY, goalX, goalY, obstacles) num2 = APF(a, r, currentX-h, currentY, goalX, goalY, obstacles) return (num1-num2)/(2*h) def gradY(a, r, h, currentX, currentY, goalX, goalY, obstacles): num1 = APF(a, r, currentX, currentY+h, goalX, goalY, obstacles) num2 = APF(a, r, currentX, currentY-h, goalX, goalY, obstacles) return (num1-num2)/(2*h) class Obstacle(object): def __init__(self, point, radius): self.point = point self.radius = radius if __name__== "__main__" : start = Point(1.0, 1.0) goal = Point(8.0, 8.0) p1 = Point(3.0, 3.0) p2 = Point(5.0, 6.0) p3 = Point(7.0, 6.0) obs1 = Obstacle(p1, 0.5) obs2 = Obstacle(p2, 0.5) obs3 = Obstacle(p3, 0.5) obstacles = [obs1, obs2, obs3] # 1. 生成覆盖区域的网格数据,用于绘制3D曲面 x_grid = np.linspace(0, 9, 50) y_grid = np.linspace(0, 9, 50) X, Y = np.meshgrid(x_grid, y_grid) Z = np.zeros_like(X) # 计算每个网格点的APF值 for i in range(X.shape[0]): for j in range(X.shape[1]): Z[i,j] = APF(0.5, 1, X[i,j], Y[i,j], goal.X, goal.Y, obstacles) # 2. 重新计算轨迹(修正原逻辑错误) trajectory = [start] h = 10e-4 a = 0.5 r = 1 temp = start maxiter = 1000 i = 0 while temp.distance(goal) > 1e-3 and i < maxiter: i += 1 # 计算当前点的梯度 gx = gradX(a, r, h, temp.X, temp.Y, goal.X, goal.Y, obstacles) gy = gradY(a, r, h, temp.X, temp.Y, goal.X, goal.Y, obstacles) # 生成新的点,避免修改原对象 temp = temp.move(gx, gy) trajectory.append(temp) # 提取轨迹的坐标 traj_x = [p.X for p in trajectory] traj_y = [p.Y for p in trajectory] traj_apf = [APF(a, r, p.X, p.Y, goal.X, goal.Y, obstacles) for p in trajectory] # 3. 绘制3D曲面和等高线,同时叠加轨迹 fig = plt.figure(figsize=(12,8)) ax = fig.add_subplot(111, projection='3d') # 绘制3D势能曲面 surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, alpha=0.6, cmap=cm.coolwarm) # 绘制等高线(投影到z=-20的平面) cset = ax.contourf(X, Y, Z, zdir='z', offset=-20, cmap=cm.coolwarm) # 绘制路径轨迹 ax.plot(traj_x, traj_y, traj_apf, color='black', linewidth=2, label='Path') ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('APF Value') ax.set_zlim(-20, np.max(Z)*1.1) ax.legend() plt.colorbar(surf, shrink=0.5, aspect=5) plt.show()
修改说明
- 网格数据生成:用
np.linspace和np.meshgrid生成覆盖目标区域的二维网格,计算每个点的APF值,这样plot_surface就能正确绘制曲面。 - 轨迹记录修正:
move方法返回新的Point实例,避免同一个对象被反复修改导致轨迹记录错误;循环里用当前temp的坐标计算梯度和APF值。 - 除零错误处理:在APF计算中加入距离过小的判断,避免障碍物附近出现除零错误。
内容的提问来源于stack exchange,提问作者user1393214
相关产品推荐
相关产品推荐

