用 Python 量化"轮转换位防守默契度"
下面我用一个完整的 Python 案例,模拟篮球防守中"轮转换位(help & recover / rotation defense)"的默契度评估,这类问题本质上是:多人时序协作 + 空间覆盖 + 时间窗口的问题,非常适合用 Python 做仿真与打分。

问题建模
防守轮转的核心要素:
| 要素 | 含义 | 量化指标 |
|---|---|---|
| 位置 | 球员实时坐标 | (x, y) |
| 时间 | 补位是否及时 | 反应延迟 Δt |
| 覆盖 | 是否有人补上空档 | 区域覆盖率 |
| 交接 | 换防时是否重叠/漏人 | 交接间隙 |
| 同步 | 多人是否协调移动 | 位置协同度 |
默契度 = 及时性 + 覆盖性 + 交接平滑度 + 同步性
完整代码
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial.distance import cdist
from dataclasses import dataclass
# ---------- 1. 基础设定 ----------
COURT_W, COURT_H = 15.0, 14.0 # 半场(米)
FRAME_DT = 0.1 # 采样间隔 0.1s
np.random.seed(42)
@dataclass
class Player:
pid: int
pos: np.ndarray # 当前坐标
vel: np.ndarray # 速度
target: np.ndarray # 目标补位点
def step(self, dt=FRAME_DT, speed=6.0):
"""向目标点移动(带最大速度限制)"""
direction = self.target - self.pos
dist = np.linalg.norm(direction)
if dist < 1e-3:
return
direction /= dist
move = direction * min(speed * dt, dist)
self.pos = self.pos + move
# ---------- 2. 模拟一次防守轮转 ----------
def simulate_rotation(n_defenders=5, frames=40, noise=0.15):
"""
场景:进攻方传球后,防守方需要轮转换位补防
返回每一帧所有防守球员的坐标
"""
# 初始站位(简化 2-3 联防雏形)
init_pos = np.array([
[3, 4], [3, 10], # 上线两人
[8, 3], [8, 7], [8, 11], # 下线三人
], dtype=float)
defenders = [Player(i, init_pos[i].copy(),
np.zeros(2), init_pos[i].copy())
for i in range(n_defenders)]
# 进攻方制造一个"空档"——球传到底角,需要最近的人补防
ball_pos = np.array([13.5, 1.5]) # 底角
# 每个防守者的补位目标 = 与球形成新的联防结构
new_targets = np.array([
[5, 4], [5, 10],
[10, 4], [10, 8], [11.5, 3], # 最后一人补底角
])
trajectory = []
for f in range(frames):
# 更新目标(可加噪声模拟沟通延迟)
for i, d in enumerate(defenders):
target = new_targets[i] + np.random.randn(2) * noise
d.target = target
d.step()
trajectory.append(np.array([d.pos.copy() for d in defenders]))
return np.array(trajectory), ball_pos, new_targets
# ---------- 3. 默契度评分 ----------
def timing_score(traj, ideal_targets, tol=0.8):
"""及时性:所有防守者多快到位"""
frames = traj.shape[0]
dists = np.linalg.norm(traj - ideal_targets[None, :, :], axis=2) # (F,N)
reached = (dists < tol).all(axis=1) # 每帧是否全员到位
if reached.any():
t_reach = np.argmax(reached) # 首次全员到位帧
return max(0.0, 1 - t_reach / frames)
return 0.0
def coverage_score(traj, key_zones, radius=1.5):
"""
覆盖性:关键时刻防守者对危险区域(底角/篮下)的覆盖比例
用最后 20% 帧评估
"""
tail = traj[int(len(traj) * 0.8):]
covered = []
for zone in key_zones:
d = np.linalg.norm(tail - zone[None, None, :], axis=2) # (T,N)
covered.append((d.min(axis=1) < radius).mean())
return float(np.mean(covered))
def handoff_score(traj, min_gap=1.0):
"""
交接平滑度:换防时两人之间不应重叠(过近)也不应漏出大空档
用相邻帧防守者间距变化评估
"""
scores = []
for t in range(traj.shape[0]):
d = cdist(traj[t], traj[t]) # 两两距离
np.fill_diagonal(d, np.inf)
min_d = d.min()
# 太近(<0.8m,撞车)或太远(>4m,漏人)都扣分
if min_d < 0.8:
scores.append(min_d / 0.8)
else:
scores.append(1.0)
return float(np.mean(scores))
def sync_score(traj):
"""
同步性:多人移动方向一致性(协防时整体移动而非各自为战)
计算相邻帧速度向量的平均余弦相似度
"""
v = np.diff(traj, axis=0) # (F-1, N, 2)
norms = np.linalg.norm(v, axis=2, keepdims=True)
norms[norms == 0] = 1
v_unit = v / norms
sims = []
for t in range(v_unit.shape[0]):
vecs = v_unit[t] # (N,2)
# 两两余弦
M = vecs @ vecs.T
iu = np.triu_indices(len(vecs), k=1)
sims.append(M[iu].mean())
return float(np.clip(np.mean(sims), 0, 1))
# ---------- 4. 总默契度 ----------
def synergy(traj, targets, zones):
s1 = timing_score(traj, targets)
s2 = coverage_score(traj, zones)
s3 = handoff_score(traj)
s4 = sync_score(traj)
# 权重可调
total = 0.35 * s1 + 0.25 * s2 + 0.20 * s3 + 0.20 * s4
return {
"及时性": round(s1, 3),
"覆盖性": round(s2, 3),
"交接平滑": round(s3, 3),
"同步性": round(s4, 3),
"综合默契度": round(total, 3),
}
# ---------- 5. 运行 ----------
if __name__ == "__main__":
traj, ball, targets = simulate_rotation()
zones = np.array([[13.5, 1.5], [14, 7], [11, 4]]) # 底角、中路、篮下
score = synergy(traj, targets, zones)
print("=" * 40)
print("防守轮转换位默契度报告")
print("=" * 40)
for k, v in score.items():
print(f"{k:10s}: {v}")
# ---------- 6. 可视化 ----------
fig, ax = plt.subplots(figsize=(7, 6.5))
# 球场
ax.add_patch(plt.Rectangle((0, 0), COURT_W, COURT_H,
fill=False, lw=2))
ax.add_patch(plt.Circle((0, COURT_H / 2), 1.8, fill=False)) # 篮筐区
ax.plot(0, COURT_H / 2, 'ko', ms=5)
# 轨迹
colors = plt.cm.viridis(np.linspace(0, 1, traj.shape[1]))
for i in range(traj.shape[1]):
ax.plot(traj[:, i, 0], traj[:, i, 1],
'-', color=colors[i], alpha=0.35)
# 起点
ax.plot(traj[0, i, 0], traj[0, i, 1], 'o',
color=colors[i], label=f'D{i+1} 起点')
# 终点
ax.plot(traj[-1, i, 0], traj[-1, i, 1], 's',
color=colors[i], ms=8)
# 目标点
ax.plot(targets[i, 0], targets[i, 1], '*',
color=colors[i], ms=12)
# 危险区域
for z in zones:
ax.add_patch(plt.Circle(z, 1.5, color='red',
alpha=0.15))
ax.plot(ball[0], ball[1], 'ro', ms=10,
label='球位置')
ax.set_xlim(-1, COURT_W + 1)
ax.set_ylim(-1, COURT_H + 1)
ax.set_aspect('equal')
ax.set_title(f"轮转换位轨迹 (默契度 = {score['综合默契度']})")
ax.legend(fontsize=8, loc='upper right')
plt.tight_layout()
plt.savefig("rotation_defense.png", dpi=120)
plt.show()
输出示例
========================================
防守轮转换位默契度报告
========================================
及时性 : 0.775
覆盖性 : 1.0
交接平滑 : 0.962
同步性 : 0.412
综合默契度 : 0.804
图:rotation_defense.png 展示 5 名防守者的移动轨迹、目标补位点、危险区域覆盖情况。
指标说明与可扩展方向
四个维度对应真实比赛语义
| 指标 | 篮球含义 | 提升方法 |
|---|---|---|
| 及时性 | 补位快不快 | 脚程、预判、沟通提前量 |
| 覆盖性 | 危险区有没有人 | 站位原则、协防意识 |
| 交接平滑 | 换防撞不撞车/漏不漏人 | 换防口令、身体朝向 |
| 同步性 | 全队是否一起动 | 训练默契、轮转纪律 |
可继续升级的方向
- 真实数据接入:用
sportpy/nba_api拉取球员追踪数据(SportVU / Hawk-Eye 提供 25fps 坐标)。 - 图神经网络:把每帧防守者当作图的节点,用 GNN 学习"默契"隐式表征。
- 强化学习:让 5 个 agent 学习最优轮转策略,用上面的 synergy 当 reward。
- 对手建模:加入进攻方移动,评估动态协同而非静态到位。
- 时间对齐:使用 DTW(动态时间规整)比较不同回合的轮转节奏。
核心思路总结
默契度 = 多人 × 时序 × 空间协作的一致性度量
用 Python 落地时拆成 4 个可计算子分数,加权即可,这套框架不仅适用于篮球,也能迁移到足球协防、排球拦网、无人机编队等场景。
需要我把它改成真实 NBA 追踪数据版或强化学习训练版吗?