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

棒球场地动画绘制故障排查:动态点无法连续移动问题

棒球场地动画故障修复方案

问题排查

原代码无法生成连续动画的核心原因有三个:

  1. 画布全清与重复绘制:plt.clf()会清除整个画布,每次update里重新调用field.draw()会重复绘制场地,配合blit=True时,blit机制无法正确跟踪更新元素,导致动画停滞。
  2. 数据筛选逻辑错误:df['timestamp'] <= frame会保留所有历史记录,导致散点数量不断累加,而非显示当前时刻的最新位置。
  3. blit使用不当:每次update重新创建散点对象,blit无法识别这些新对象,无法完成局部更新。

修复后的代码

import pandas as pd
from sportypy.surfaces import MiLBField
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import numpy as np

num_rows = 50

data = {
    'game_str': ['game_01'] * num_rows,
    'play_id': [10] * num_rows,
    'timestamp': np.random.randint(180000, 181000, size=num_rows),
    'player_position': np.random.randint(1, 11, size=num_rows),
    'field_x': np.random.uniform(-150, 150, size=num_rows),
    'field_y': np.random.uniform(-150, 150, size=num_rows),
    'ball_position_x': np.random.uniform(0.0, 2.0, size=num_rows),
    'ball_position_y': np.random.uniform(0.0, 300.0, size=num_rows),
    'ball_position_z': np.random.uniform(0.0, 10.0, size=num_rows)
}

df = pd.DataFrame(data).sort_values(by='timestamp')

# 预处理数据:确保每个时间戳下,每个球员/球只有一条最新记录
df = df.groupby(['timestamp', 'player_position']).last().reset_index()

# 分离球与球员的数据(假设player_position=10代表球)
ball_df = df[df['player_position'] == 10][['timestamp', 'ball_position_x', 'ball_position_y']].rename(columns={
    'ball_position_x': 'x',
    'ball_position_y': 'y'
})
players_df = df[df['player_position'] != 10][['timestamp', 'player_position', 'field_x', 'field_y']]

# 获取排序后的唯一时间戳列表
timestamps = sorted(df['timestamp'].unique())

# 初始化画布与场地
fig = plt.figure()
field = MiLBField()
field.draw(display_range='full')  # 仅绘制一次场地

# 初始化球员和球的散点对象(复用这些对象,仅更新坐标)
initial_players = players_df[players_df['timestamp'] == timestamps[0]]
player_scatter = field.scatter(initial_players['field_x'], initial_players['field_y'], color='blue', label='Players')

initial_ball = ball_df[ball_df['timestamp'] == timestamps[0]]
ball_scatter = field.scatter(initial_ball['x'], initial_ball['y'], color='red', s=100, label='Ball')

plt.legend()

def update(frame):
    # 获取当前时间戳对应的位置数据
    current_ts = frame
    current_players = players_df[players_df['timestamp'] == current_ts]
    current_ball = ball_df[ball_df['timestamp'] == current_ts]
    
    # 更新球员散点坐标
    if not current_players.empty:
        player_scatter.set_offsets(current_players[['field_x', 'field_y']].values)
    
    # 更新球的散点坐标
    if not current_ball.empty:
        ball_scatter.set_offsets(current_ball[['x', 'y']].values)
    
    # 返回需要更新的艺术家对象,配合blit实现局部刷新
    return player_scatter, ball_scatter

# 创建动画:使用唯一时间戳作为帧,设置更新间隔
ani = FuncAnimation(fig, update, frames=timestamps, blit=True, interval=50)

plt.show()

修复说明

  • 数据预处理:通过分组去重,确保每个时刻每个实体(球员/球)只有一条有效位置记录,避免散点冗余。
  • 复用绘图对象:初始化时创建球员和球的散点,在update中仅更新坐标,而非重新创建,配合blit=True实现高效局部更新。
  • 一次性绘制场地:避免重复绘制场地导致的闪烁和性能损耗。
  • 精准帧数据匹配:直接获取当前时间戳对应的位置数据,保证显示的是当前时刻的实时位置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 12:43:19