Rework adaptive extrapolation: deceleration detection + dead zone + debug HUD
Replace variance-based confidence (caused constant lag) with targeted deceleration detection: compares recent speed (last 25% of safe window) to average speed. During steady movement ratio≈1 → zero lag. Only reduces extrapolation when actual braking is detected. - AdaptiveSensitivity: now a power exponent (0.1-5.0, default 1.0) - AdaptiveDeadZone: new parameter (default 0.8) to ignore normal speed fluctuations and only react to real deceleration - DebugAntiRecoilHUD: real-time display of ratio, confidence, speeds, errors - EndPlay: auto-close CSV file when stopping play (no more locked files) - Python script updated to match new deceleration-based algorithm Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -131,75 +131,71 @@ def simulate_adaptive(frames: List[Frame], sensitivity: float) -> List[Tuple[flo
|
||||
pos_errors = []
|
||||
aim_errors = []
|
||||
|
||||
# Sliding window for velocity estimation
|
||||
# Sliding window for velocity estimation (matches C++ safe window ~18 samples)
|
||||
window_size = 12
|
||||
|
||||
SMALL = 1e-10
|
||||
|
||||
for i in range(window_size + 1, len(frames) - 1):
|
||||
# Build velocity pairs from recent real positions and aims
|
||||
pos_velocities = []
|
||||
aim_velocities = []
|
||||
weights = []
|
||||
pos_vels = []
|
||||
aim_vels = []
|
||||
for j in range(1, min(window_size, i)):
|
||||
dt = frames[i - j].timestamp - frames[i - j - 1].timestamp
|
||||
if dt > 1e-6:
|
||||
pos_vel = vec_scale(vec_sub(frames[i - j].real_pos, frames[i - j - 1].real_pos), 1.0 / dt)
|
||||
aim_vel = vec_scale(vec_sub(frames[i - j].real_aim, frames[i - j - 1].real_aim), 1.0 / dt)
|
||||
pos_velocities.append(pos_vel)
|
||||
aim_velocities.append(aim_vel)
|
||||
w = (window_size - j) ** 2
|
||||
weights.append(w)
|
||||
pos_vels.append(pos_vel)
|
||||
aim_vels.append(aim_vel)
|
||||
|
||||
if len(pos_velocities) < 2:
|
||||
if len(pos_vels) < 4:
|
||||
continue
|
||||
|
||||
total_w = sum(weights)
|
||||
n = len(pos_vels)
|
||||
|
||||
# Weighted average velocity (position)
|
||||
# Weighted average velocity (recent samples weighted more, matching C++)
|
||||
total_w = 0.0
|
||||
avg_pos_vel = (0.0, 0.0, 0.0)
|
||||
avg_aim_vel = (0.0, 0.0, 0.0)
|
||||
for pv, av, w in zip(pos_velocities, aim_velocities, weights):
|
||||
avg_pos_vel = vec_add(avg_pos_vel, vec_scale(pv, w / total_w))
|
||||
avg_aim_vel = vec_add(avg_aim_vel, vec_scale(av, w / total_w))
|
||||
for k in range(n):
|
||||
w = (k + 1) ** 2
|
||||
avg_pos_vel = vec_add(avg_pos_vel, vec_scale(pos_vels[k], w))
|
||||
avg_aim_vel = vec_add(avg_aim_vel, vec_scale(aim_vels[k], w))
|
||||
total_w += w
|
||||
avg_pos_vel = vec_scale(avg_pos_vel, 1.0 / total_w)
|
||||
avg_aim_vel = vec_scale(avg_aim_vel, 1.0 / total_w)
|
||||
|
||||
# Weighted variance (position and aim separately)
|
||||
pos_var_sum = 0.0
|
||||
aim_var_sum = 0.0
|
||||
for pv, av, w in zip(pos_velocities, aim_velocities, weights):
|
||||
pd = vec_sub(pv, avg_pos_vel)
|
||||
ad = vec_sub(av, avg_aim_vel)
|
||||
pos_var_sum += (pd[0]**2 + pd[1]**2 + pd[2]**2) * w
|
||||
aim_var_sum += (ad[0]**2 + ad[1]**2 + ad[2]**2) * w
|
||||
pos_variance = pos_var_sum / total_w
|
||||
aim_variance = aim_var_sum / total_w
|
||||
# Deceleration detection: recent speed (last 25%) vs average speed
|
||||
recent_start = max(0, n - max(1, n // 4))
|
||||
recent_count = n - recent_start
|
||||
recent_pos_vel = (0.0, 0.0, 0.0)
|
||||
recent_aim_vel = (0.0, 0.0, 0.0)
|
||||
for k in range(recent_start, n):
|
||||
recent_pos_vel = vec_add(recent_pos_vel, pos_vels[k])
|
||||
recent_aim_vel = vec_add(recent_aim_vel, aim_vels[k])
|
||||
recent_pos_vel = vec_scale(recent_pos_vel, 1.0 / recent_count)
|
||||
recent_aim_vel = vec_scale(recent_aim_vel, 1.0 / recent_count)
|
||||
|
||||
# Separate confidences with RELATIVE variance (matching C++ code)
|
||||
pos_speed2 = sum(v**2 for v in avg_pos_vel)
|
||||
aim_speed2 = sum(v**2 for v in avg_aim_vel)
|
||||
avg_pos_speed = vec_len(avg_pos_vel)
|
||||
avg_aim_speed = vec_len(avg_aim_vel)
|
||||
recent_pos_speed = vec_len(recent_pos_vel)
|
||||
recent_aim_speed = vec_len(recent_aim_vel)
|
||||
|
||||
# Confidence = (recentSpeed / avgSpeed) ^ sensitivity
|
||||
pos_confidence = 1.0
|
||||
if avg_pos_speed > SMALL:
|
||||
pos_ratio = min(recent_pos_speed / avg_pos_speed, 1.0)
|
||||
pos_confidence = pos_ratio ** sensitivity
|
||||
|
||||
aim_confidence = 1.0
|
||||
if avg_aim_speed > SMALL:
|
||||
aim_ratio = min(recent_aim_speed / avg_aim_speed, 1.0)
|
||||
aim_confidence = aim_ratio ** sensitivity
|
||||
|
||||
# Extrapolation time
|
||||
extrap_dt = frames[i].extrap_time if frames[i].extrap_time > 0 else 0.011
|
||||
|
||||
pos_confidence = 1.0
|
||||
if pos_speed2 > SMALL:
|
||||
pos_rel_var = pos_variance / pos_speed2
|
||||
pos_confidence = 1.0 / (1.0 + pos_rel_var * sensitivity)
|
||||
# Low-speed protection: if extrapolation offset < 1cm, blend confidence toward 1
|
||||
pos_extrap_offset = math.sqrt(pos_speed2) * extrap_dt
|
||||
pos_offset_significance = max(0.0, min(pos_extrap_offset / 1.0, 1.0)) # 1cm threshold
|
||||
pos_confidence = 1.0 + (pos_confidence - 1.0) * pos_offset_significance
|
||||
|
||||
aim_confidence = 1.0
|
||||
if aim_speed2 > SMALL:
|
||||
aim_rel_var = aim_variance / aim_speed2
|
||||
aim_confidence = 1.0 / (1.0 + aim_rel_var * sensitivity)
|
||||
# Low-speed protection: if aim extrapolation offset is tiny, blend toward 1
|
||||
aim_extrap_offset = math.sqrt(aim_speed2) * extrap_dt
|
||||
aim_offset_significance = max(0.0, min(aim_extrap_offset / 0.01, 1.0)) # 0.01 unit threshold
|
||||
aim_confidence = 1.0 + (aim_confidence - 1.0) * aim_offset_significance
|
||||
|
||||
# Predict
|
||||
# Predict from last safe position
|
||||
pred_pos = vec_add(frames[i - 1].real_pos, vec_scale(avg_pos_vel, extrap_dt * pos_confidence))
|
||||
pred_aim_raw = vec_add(frames[i - 1].real_aim, vec_scale(avg_aim_vel, extrap_dt * aim_confidence))
|
||||
pred_aim = vec_normalize(pred_aim_raw)
|
||||
@@ -214,15 +210,15 @@ def simulate_adaptive(frames: List[Frame], sensitivity: float) -> List[Tuple[flo
|
||||
|
||||
|
||||
def find_optimal_parameters(frames: List[Frame]) -> dict:
|
||||
"""Search for optimal AdaptiveSensitivity (no scaling factor needed)."""
|
||||
print("\nSearching for optimal AdaptiveSensitivity...")
|
||||
"""Search for optimal AdaptiveSensitivity (power curve exponent for deceleration detection)."""
|
||||
print("\nSearching for optimal AdaptiveSensitivity (power exponent)...")
|
||||
print("-" * 60)
|
||||
|
||||
best_score = float('inf')
|
||||
best_sensitivity = 1.0
|
||||
|
||||
# Search grid — only sensitivity, no more scaling factor
|
||||
sensitivities = [0.1, 0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 7.5, 10.0, 15.0, 20.0, 30.0, 50.0, 75.0, 100.0, 150.0, 200.0]
|
||||
# Search grid — exponent range 0.1 to 5.0
|
||||
sensitivities = [0.1, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0]
|
||||
|
||||
results = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user