双球碰撞Matplotlib动画实现报错:ValueError: setting an array element with a sequence问题排查与修复
Fixing "ValueError: setting an array element with a sequence" in Matplotlib Ball Collision Animation
Yep, that error is absolutely caused by improper array handling in your animation setup. Let's break down what's going wrong and fix it step by step.
What's Triggering the Error?
Looking at your code, here are the key issues:
- Incorrect coordinate list structure: You packed the entire
PosXandPosYarrays intoxy_list, but you actually need a structure where each element maps to a single ball's coordinates across all frames. - Mismatched radius and coordinate data: The unnecessary transpose on the
Radiusarray created a mismatch when pairing it with your coordinate lists. - Wrong indexing in the update function: When setting the circle's center,
xy[files]pulls an entire row of coordinates instead of the specific (x,y) pair for each ball at the current frame.
Corrected Code
Here's the fixed version with clear improvements:
import os import numpy as np import matplotlib.pyplot as plt from matplotlib.patches import Circle import matplotlib.animation as animation # 从文本文件读取数据 Filepath = '/Users/zhengzhiying/Desktop/DEM(CLion)/freefalling_1/' os.chdir(Filepath) Flowname = 'Flow' Filenumber = len(os.listdir()) Data_Number = 3 # 重构数据结构:每个索引对应一个球,子列表存储该球的所有帧数据 ID = [] Type = [] Phase = [] Radius = [] PosX = [] PosY = [] VelX = [] VelY = [] Vel_Rot = [] # 先读第一帧获取球的数量,初始化每个球的数据列表 first_flow_path = f"{Filepath}{Flowname}0.txt" with open(first_flow_path, 'r') as fd: non_emptyline = fd.read().splitlines() num_balls = len(non_emptyline) for _ in range(num_balls): ID.append([]) Type.append([]) Phase.append([]) Radius.append([]) PosX.append([]) PosY.append([]) VelX.append([]) VelY.append([]) Vel_Rot.append([]) # 遍历每个文件,填充每个球的帧数据 for file_idx in range(Data_Number): Flow_path = f"{Filepath}{Flowname}{file_idx}.txt" with open(Flow_path, 'r') as fd: non_emptyline = fd.read().splitlines() for ball_idx, line in enumerate(non_emptyline): s = line.split() ID[ball_idx].append(int(s[0])) Type[ball_idx].append(int(s[1])) Phase[ball_idx].append(int(s[2])) Radius[ball_idx].append(float(s[3])) PosX[ball_idx].append(float(s[4])) PosY[ball_idx].append(float(s[5])) VelX[ball_idx].append(float(s[6])) VelY[ball_idx].append(float(s[7])) Vel_Rot[ball_idx].append(float(s[8])) # 转换为numpy数组(可选,直接用列表也可) ID = np.asarray(ID) Type = np.asarray(Type) Phase = np.asarray(Phase) Radius = np.asarray(Radius) PosX = np.asarray(PosX) PosY = np.asarray(PosY) VelX = np.asarray(VelX) VelY = np.asarray(VelY) Vel_Rot = np.asarray(Vel_Rot) # 创建画布 fig = plt.figure(figsize=(6, 6), dpi=100) ax = plt.axes(xlim=(1.0, 3.0), ylim=(-1.0, 2.0)) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_aspect('equal') ax.tick_params(top=True, right=True, labeltop=True, labelright=True) # 为每个球创建初始Circle补丁 patch_list = [] for i in range(num_balls): initial_xy = (PosX[i][0], PosY[i][0]) patch = Circle(xy=initial_xy, radius=Radius[i][0], color='b', linewidth=1.0, animated=True) patch_list.append(patch) h_text = ax.text(0.9, 0.9, 'Time: {}'.format(0), ha='center', va='center', transform=ax.transAxes) def init(): for p in patch_list: ax.add_patch(p) return patch_list + [h_text] def pos_update(frame): h_text.set_text('Time: {}'.format(frame/1e7)) # 遍历每个球,更新当前帧的位置 for i, p in enumerate(patch_list): current_xy = (PosX[i][frame], PosY[i][frame]) p.set_center(current_xy) return patch_list + [h_text] anim = animation.FuncAnimation(fig, pos_update, init_func=init, frames=Data_Number, blit=True) plt.show()
Key Improvements Explained
Data Structure Refactor:
- Now each list index represents a single ball, with sublists storing that ball's data across all frames. This aligns perfectly with how we need to access positions for animation updates.
- We first read the first frame to get the total number of balls, which lets us initialize the correct number of data containers upfront.
Proper Patch Initialization:
- We create one
Circlepatch per ball, using the first frame's coordinates as the starting position. This ensures each patch maps directly to a single ball's data.
- We create one
Correct Frame Update Logic:
- In
pos_update, we iterate over each ball's patch and fetch its exact (x,y) coordinates for the current frame usingPosX[i][frame]andPosY[i][frame], then update the circle's center with that pair.
- In
Removed Unnecessary Transpose:
- The
Radiusarray no longer needs transposing, as our new data structure keeps each ball's radius aligned with its position data.
- The
内容的提问来源于stack exchange,提问作者CircuitKiller
相关产品推荐
相关产品推荐

