You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib小球碰撞动画报错:'NoneType'无'_get_view'属性的解决

解决Matplotlib小球碰撞动画中的AttributeError错误

错误原因

你创建的plt.Circle图形对象未添加到坐标轴中,启用blit=True时,Matplotlib无法获取这些元素的视图属性,从而触发'NoneType' object has no attribute '_get_view'错误。此外,未设置坐标轴范围,可能导致小球跑出可视区域。

修正步骤

  • 将创建的每个Circle对象通过ax.add_patch()添加到坐标轴
  • 为坐标轴设置固定范围,匹配场景边界值
  • 确保animate函数返回已添加到轴上的图形元素列表

修正后的完整代码

import numpy as np
from random import randint
import math
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation

np_rnd = np.random.default_rng()


class Ball:
    def __init__(self, x_, y_, r_, m_, v_speed_, v_accel_):
        self.m = m_
        self.x = x_
        self.y = y_
        self.x0 = x_
        self.y0 = y_
        self.r = r_
        self.v_speed = v_speed_
        self.v_accel = v_accel_
        self.t = 0


def get_cmap(n, name='hsv'):
    return plt.cm.get_cmap(name, n)


FPS = 77

X_MIN = -1
X_MAX = 100

Y_MIN = -1
Y_MAX = 100

RADIUS_MIN = 1
RADIUS_MAX = 3

MASS_MAX = 6

SPEED_MAX = 0.5
ACCEL_MAX = 0

N = 5
T = 1


ball_list = []
xy_balls = np.c_[np_rnd.integers(X_MIN+RADIUS_MAX, X_MAX-RADIUS_MAX,(N)),
                 np_rnd.integers(Y_MIN+RADIUS_MAX, Y_MAX-RADIUS_MAX,(N))]

r_balls = np_rnd.integers(RADIUS_MIN, RADIUS_MAX, N)
color_balls = np.arange(0, N)

fig, ax = plt.subplots()
# 设置坐标轴范围,固定显示区域
ax.set_xlim(X_MIN, X_MAX)
ax.set_ylim(Y_MIN, Y_MAX)

ax.plot([X_MIN, X_MAX], [0, 0], color='black', linewidth=0.5)
ax.annotate("x", xy=(X_MAX - 2, 0))
ax.plot([0, 0], [Y_MIN, Y_MAX], color='black', linewidth=0.5)
ax.annotate("y", xy=(0, Y_MAX - 2))
ax.annotate("0", xy=(2, 2))

cmap = get_cmap(N)
for i in range(N):
    ball_list.append(Ball(xy_balls[i][0],
                          xy_balls[i][1],
                          r_balls[i],
                          randint(1, MASS_MAX),
                          np_rnd.uniform(0, SPEED_MAX, size=2),
                          np_rnd.uniform(0, ACCEL_MAX, size=2)
                          )
                     )

# 创建Circle对象并添加到坐标轴
balls_plt = []
for center, radius, color in zip(xy_balls, r_balls, color_balls):
    circle = plt.Circle(center, radius=radius, color=cmap(color))
    ax.add_patch(circle)
    balls_plt.append(circle)


def checkBorder(ball):
    if (ball.x > X_MAX) or (ball.x < X_MIN):
        ball.x = (X_MAX - 1) if ball.x > X_MAX else X_MIN + 1
        ball.x0 = ball.x
        ball.y0 = ball.y
        ball.v_speed[0] = -(ball.v_speed[0] + ball.t * ball.v_accel[0])
        ball.v_speed[1] = ball.v_speed[1] + ball.t * ball.v_accel[1]
        ball.v_accel[0] = -ball.v_accel[0]
        ball.t = 0

    if (ball.y > Y_MAX) or (ball.y < Y_MIN):
        ball.y = (Y_MAX - 1) if ball.y > Y_MAX else Y_MIN + 1
        ball.x0 = ball.x
        ball.y0 = ball.y
        ball.v_speed[0] = ball.v_speed[0] + ball.t * ball.v_accel[0]
        ball.v_speed[1] = -(ball.v_speed[1] + ball.t * ball.v_accel[1])
        ball.v_accel[1] = -ball.v_accel[1]
        ball.t = 0


def checkBite(ball1, ball2):
    D = math.sqrt(((ball1.x - ball2.x) ** 2) + ((ball1.y - ball2.y) ** 2))

    if D < (ball1.r + ball2.r):
        ball1.v_speed[0] = ((ball1.m - ball2.m) * ball1.v_speed[0] + 2 * ball2.m * ball2.v_speed[0]) / (
                    ball1.m + ball2.m)
        ball1.v_speed[1] = ((ball1.m - ball2.m) * ball1.v_speed[1] + 2 * ball2.m * ball2.v_speed[1]) / (
                    ball1.m + ball2.m)
        ball2.v_speed[0] = ((ball2.m - ball1.m) * ball2.v_speed[0] + 2 * ball1.m * ball1.v_speed[0]) / (
                    ball1.m + ball2.m)
        ball2.v_speed[1] = ((ball2.m - ball1.m) * ball2.v_speed[1] + 2 * ball1.m * ball1.v_speed[1]) / (
                    ball1.m + ball2.m)

        ball1.x0 = ball1.x + (ball1.v_speed[0] * ball1.r)
        ball1.y0 = ball1.y + (ball1.v_speed[1] * ball1.r)
        ball2.x0 = ball2.x + (ball2.v_speed[0] * ball2.r)
        ball2.y0 = ball2.y + (ball2.v_speed[1] * ball2.r)
        ball1.t = T
        ball2.t = T


def setCoord(ball):
    ball.x = ball.x0 + ball.v_speed[0] * ball.t + (ball.v_accel[0] * (ball.t ** 2)) / 2
    ball.y = ball.y0 + ball.v_speed[1] * ball.t + (ball.v_accel[1] * (ball.t ** 2)) / 2
    ball.t += T


def animate(i, balls):
    for j in range(len(balls)):
        # 取消注释启用小球碰撞检测
        # for k in range(j+1, len(balls)):
        #     checkBite(balls[j], balls[k])
        checkBorder(balls[j])
        setCoord(balls[j])
        balls_plt[j].center = (balls[j].x, balls[j].y)

    return balls_plt


anim = FuncAnimation(fig=fig, func=animate, fargs=(ball_list,),
                     frames=np.linspace(0, FPS * 2, num=1000, dtype=np.intc, endpoint=False),
                     interval=1000 / FPS, blit=True, repeat=True)

plt.show()

关键修正点说明

  1. 添加图形到坐标轴:循环创建Circle对象时,用ax.add_patch(circle)将每个圆添加到坐标轴,确保Matplotlib能追踪到这些元素
  2. 固定坐标轴范围:通过ax.set_xlim和ax.set_ylim锁定显示区域,避免小球移动导致坐标轴自动缩放
  3. 返回正确的更新元素:animate函数直接返回已添加到轴上的balls_plt列表,确保blit机制能正确更新这些元素

内容的提问来源于stack exchange,提问作者Nord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:05:34