167 lines
6.4 KiB
Python
167 lines
6.4 KiB
Python
import numpy as np
|
|||
|
|
import matplotlib.pyplot as plt
|
||
|
|
from matplotlib import cm
|
||
|
|
import matplotlib.patheffects as path_effects
|
||
|
|
from mpl_toolkits.mplot3d import Axes3D
|
||
|
|
|
||
|
|
|
||
|
|
# ==========================================
|
||
|
|
# 核心理论模块:通信迟滞容忍的动力学风险场发生器
|
||
|
|
# (Latency-Tolerant Kinematic Risk Field Generator)
|
||
|
|
# ==========================================
|
||
|
|
def generate_kinematic_risk_field(X, Y, obj):
|
||
|
|
"""
|
||
|
|
根据车辆的物理状态和通信迟滞,生成高斯风险势能场
|
||
|
|
"""
|
||
|
|
x0, y0 = obj['pos']
|
||
|
|
vx, vy = obj['vel']
|
||
|
|
L, W = obj['size']
|
||
|
|
dt = obj['latency'] # V2X 通信延迟 (秒)
|
||
|
|
|
||
|
|
# 1. 运动学位置补偿 (由于延迟,目标在真实物理世界已经往前移动了)
|
||
|
|
x_real = x0 + vx * dt
|
||
|
|
y_real = y0 + vy * dt
|
||
|
|
|
||
|
|
# 2. 时空耦合:协方差膨胀 (速度越快、延迟越高,沿运动方向的不确定性风险拖尾越长)
|
||
|
|
speed = np.hypot(vx, vy)
|
||
|
|
# 基础物理边界 (方差,代表车辆本身的尺寸)
|
||
|
|
sigma_x = L / 2.0
|
||
|
|
sigma_y = W / 2.0
|
||
|
|
|
||
|
|
if speed > 0.1:
|
||
|
|
# ⚠️ 顶刊核心公式:沿着运动方向剧烈拉伸协方差!
|
||
|
|
stretch_factor = 1.0 + 0.8 * speed * dt
|
||
|
|
sigma_x_dilated = sigma_x * stretch_factor
|
||
|
|
theta = np.arctan2(vy, vx)
|
||
|
|
else:
|
||
|
|
sigma_x_dilated = sigma_x
|
||
|
|
theta = 0.0
|
||
|
|
|
||
|
|
# 3. 旋转协方差矩阵,对齐到运动方向
|
||
|
|
cos_t, sin_t = np.cos(theta), np.sin(theta)
|
||
|
|
R = np.array([[cos_t, -sin_t],
|
||
|
|
[sin_t, cos_t]])
|
||
|
|
S = np.array([[sigma_x_dilated ** 2, 0],
|
||
|
|
[0, sigma_y ** 2]])
|
||
|
|
Cov = R @ S @ R.T # 膨胀后的 2D 协方差矩阵
|
||
|
|
Cov_inv = np.linalg.inv(Cov)
|
||
|
|
|
||
|
|
# 4. 计算二维高斯势能曲面 (Mahalanobis Distance)
|
||
|
|
dx = X - x_real
|
||
|
|
dy = Y - y_real
|
||
|
|
# 矢量化二次型计算
|
||
|
|
E = np.exp(-0.5 * (Cov_inv[0, 0] * dx ** 2 + 2 * Cov_inv[0, 1] * dx * dy + Cov_inv[1, 1] * dy ** 2))
|
||
|
|
return E * obj['risk_weight']
|
||
|
|
|
||
|
|
|
||
|
|
# ==========================================
|
||
|
|
# 场景构建:BEV 上帝视角物理空间初始化
|
||
|
|
# ==========================================
|
||
|
|
# 设定一个 50米 x 50米 的路口物理网格 (高分辨率)
|
||
|
|
x_grid = np.linspace(0, 50, 300)
|
||
|
|
y_grid = np.linspace(0, 50, 300)
|
||
|
|
X, Y = np.meshgrid(x_grid, y_grid)
|
||
|
|
|
||
|
|
# 定义场景中的交通参与者 (完美复现你的任务书痛点)
|
||
|
|
objects = [
|
||
|
|
{
|
||
|
|
'name': '高速来车 (带极端V2X延迟)',
|
||
|
|
'pos': (10, 25), 'vel': (18, 0), # 速度 18m/s (约65km/h)向右
|
||
|
|
'size': (4.8, 2.0), 'latency': 0.4, # 恐怖的 400ms 网络延迟!
|
||
|
|
'risk_weight': 1.0
|
||
|
|
},
|
||
|
|
{
|
||
|
|
'name': '非标事件: 散落物/掉落轮胎',
|
||
|
|
'pos': (35, 12), 'vel': (0, 0), # 静止
|
||
|
|
'size': (1.5, 1.5), 'latency': 0.0,
|
||
|
|
'risk_weight': 0.8
|
||
|
|
},
|
||
|
|
{
|
||
|
|
'name': '鬼探头行人 (突然窜出)',
|
||
|
|
'pos': (28, 42), 'vel': (0, -4), # 速度 4m/s 向下横穿
|
||
|
|
'size': (0.8, 0.8), 'latency': 0.1,
|
||
|
|
'risk_weight': 0.9
|
||
|
|
}
|
||
|
|
]
|
||
|
|
|
||
|
|
# ==========================================
|
||
|
|
# 计算全局连续风险势能场 (Superposition of Risk Fields)
|
||
|
|
# ==========================================
|
||
|
|
Total_Risk_Field = np.zeros_like(X)
|
||
|
|
for obj in objects:
|
||
|
|
E_obj = generate_kinematic_risk_field(X, Y, obj)
|
||
|
|
Total_Risk_Field = np.maximum(Total_Risk_Field, E_obj) # 多风险源叠加取极值
|
||
|
|
|
||
|
|
# ==========================================
|
||
|
|
# 惊艳大招:计算势能梯度(多车协同调速排斥力场) F = -∇E
|
||
|
|
# ==========================================
|
||
|
|
dEy, dEx = np.gradient(Total_Risk_Field)
|
||
|
|
Force_X = -dEx
|
||
|
|
Force_Y = -dEy
|
||
|
|
|
||
|
|
# ==========================================
|
||
|
|
# 顶刊级数据可视化渲染 (Matplotlib 画图)
|
||
|
|
# ==========================================
|
||
|
|
fig = plt.figure(figsize=(16, 7), facecolor='#111111') # 暗黑极客底色
|
||
|
|
fig.suptitle("End-to-End Continuous Risk Potential Field for V2X Cooperative Driving",
|
||
|
|
fontsize=18, fontweight='bold', color='white', y=0.98)
|
||
|
|
|
||
|
|
# --- 子图 1: 3D 连续风险势能面 ---
|
||
|
|
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
|
||
|
|
ax1.set_facecolor('#111111')
|
||
|
|
surf = ax1.plot_surface(X, Y, Total_Risk_Field, cmap=cm.inferno, alpha=0.9, rstride=3, cstride=3, linewidth=0,
|
||
|
|
antialiased=True)
|
||
|
|
|
||
|
|
ax1.set_title("(a) 3D Spatiotemporal Risk Surface\n【任务2】时空演化连续风险曲面", fontsize=14, fontweight='bold',
|
||
|
|
color='white', pad=15)
|
||
|
|
ax1.set_xlabel("BEV X-Coordinate (m)", color='white')
|
||
|
|
ax1.set_ylabel("BEV Y-Coordinate (m)", color='white')
|
||
|
|
ax1.set_zlabel("Risk Potential Energy ($E_{risk}$)", color='white')
|
||
|
|
ax1.tick_params(colors='white')
|
||
|
|
ax1.xaxis.pane.fill = False
|
||
|
|
ax1.yaxis.pane.fill = False
|
||
|
|
ax1.zaxis.pane.fill = False
|
||
|
|
ax1.view_init(elev=40, azim=-45) # 绝佳的观察视角
|
||
|
|
|
||
|
|
# --- 子图 2: 2D 梯度力场与规控闭环 ---
|
||
|
|
ax2 = fig.add_subplot(1, 2, 2)
|
||
|
|
ax2.set_facecolor('#111111')
|
||
|
|
# 画势能等高线
|
||
|
|
contour = ax2.contourf(X, Y, Total_Risk_Field, levels=30, cmap=cm.inferno, alpha=0.8)
|
||
|
|
|
||
|
|
# 画排斥力场 (Quiver 矢量箭头)
|
||
|
|
step = 10 # 箭头采样稀疏度
|
||
|
|
Q = ax2.quiver(X[::step, ::step], Y[::step, ::step], Force_X[::step, ::step], Force_Y[::step, ::step],
|
||
|
|
color='cyan', scale=1.5, width=0.003, alpha=0.9)
|
||
|
|
|
||
|
|
|
||
|
|
# 标注解释
|
||
|
|
def add_label(x, y, text):
|
||
|
|
ax2.text(x, y, text, color='white', ha='center', fontsize=10,
|
||
|
|
path_effects=[path_effects.withStroke(linewidth=2, foreground='k')])
|
||
|
|
|
||
|
|
|
||
|
|
add_label(35, 15, "Static Debris\n(Isotropic Field)")
|
||
|
|
add_label(20, 25, "Latency-Dilated Comet Tail\n($\Delta t = 400ms$)")
|
||
|
|
add_label(28, 38, "Crossing Pedestrian")
|
||
|
|
|
||
|
|
ax2.set_title("(b) Gradient-Driven Repulsive Force Field ($-\\nabla E$)\n【任务3】风险梯度驱动的多车协同调速诱导力场",
|
||
|
|
fontsize=14, fontweight='bold', color='white', pad=15)
|
||
|
|
ax2.set_xlabel("BEV X-Coordinate (m)", color='white')
|
||
|
|
ax2.set_ylabel("BEV Y-Coordinate (m)", color='white')
|
||
|
|
ax2.tick_params(colors='white')
|
||
|
|
ax2.set_xlim(0, 50)
|
||
|
|
ax2.set_ylim(0, 50)
|
||
|
|
ax2.set_aspect('equal')
|
||
|
|
ax2.grid(color='white', linestyle='--', linewidth=0.3, alpha=0.2)
|
||
|
|
|
||
|
|
# 添加 Colorbar
|
||
|
|
cbar = fig.colorbar(surf, ax=[ax1, ax2], shrink=0.5, aspect=15, pad=0.05)
|
||
|
|
cbar.set_label('Collision Probability / Risk Intensity', color='white')
|
||
|
|
cbar.ax.yaxis.set_tick_params(color='white')
|
||
|
|
plt.setp(plt.getp(cbar.ax.axes, 'yticklabels'), color='white')
|
||
|
|
|
||
|
|
plt.tight_layout(pad=3.0)
|
||
|
|
plt.show()
|
||
|
|
# 如果你想保存高清原图,取消下一行的注释
|
||
|
|
# plt.savefig("v2x_risk_field.png", dpi=300, facecolor=fig.get_facecolor(), edgecolor='none')
|