开合控制调整:切向触发只看 Y 轴,三挡位只控制释放、触发固定灵敏参数;自适应负载改看切向 X;恢复原灵敏档张开逻辑与排查前夹持力参数。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cheng
2026-09-28 15:36:05 +08:00
parent c064cdb191
commit 6393be9518
11 changed files with 1091 additions and 501 deletions
+194 -122
View File
@@ -1,31 +1,33 @@
"""Grip behavior presets: extreme rapid open/close vs stable hold."""
"""Grip behavior presets: sensitive / stable / firm (open/release only)."""
from __future__ import annotations
import copy
# rapid = 迅速开合:优先跟手释放 / 再武装
# stable = 夹持稳定:优先防误开、夹得住
GRIP_BEHAVIOR_MODES = ("rapid", "stable")
# sensitive = 灵敏:跟手释放,当前默认调参
# stable = 较稳定:原十分稳定档,明显更难误开
# firm = 十分稳定:约高一个数量级门槛,故意开合才响应
GRIP_BEHAVIOR_MODES = ("sensitive", "stable", "firm")
GRIP_BEHAVIOR_LABELS = {
"rapid": "迅速开合",
"stable": "夹持稳定",
"sensitive": "灵敏",
"stable": "较稳定",
"firm": "十分稳定",
}
# Keys owned by a behavior profile. Selecting a mode rewrites these fields.
GRIP_BEHAVIOR_KEYS = (
"filter_alpha",
"shear_filter_alpha",
# Close/re-arm trigger always uses the sensitive values, regardless of mode.
GRIP_TRIGGER_KEYS = (
"trigger_normal_force",
"trigger_shear_force",
"open_wait_stable_samples",
"open_wait_stable_range_n",
"open_wait_stable_trend_n",
"grip_settle_seconds",
"force_ramp_step",
"force_ramp_interval",
"adaptive_grip_enabled",
"adaptive_force_interval",
)
# Keys rewritten by the behavior mode. Trigger params are not included —
# they stay locked to the sensitive profile via GRIP_TRIGGER_KEYS.
GRIP_BEHAVIOR_KEYS = (
"release_open_speed",
"release_open_accel",
"release_open_decel",
@@ -51,6 +53,11 @@ GRIP_BEHAVIOR_KEYS = (
"release_require_slope_settle",
"release_slope_hold_seconds",
"release_settle_normal_only",
"release_shear_oscillation_open",
"release_shear_oscillation_window_seconds",
"release_shear_oscillation_min_flips",
"release_shear_oscillation_amplitude",
"release_shear_oscillation_confirm_samples",
"release_side_shear_enabled",
"release_side_normal_enabled",
"release_shear_onset_enabled",
@@ -59,136 +66,201 @@ GRIP_BEHAVIOR_KEYS = (
"recover_stable_seconds",
)
_SENSITIVE_TRIGGER = {
# Close trigger must stay above force-display noise (~0.05N).
"trigger_normal_force": 0.12,
"trigger_shear_force": 0.12,
"open_wait_stable_samples": 8,
"open_wait_stable_range_n": 0.025,
"open_wait_stable_trend_n": 0.015,
}
_SENSITIVE_PROFILE = {
**_SENSITIVE_TRIGGER,
"release_open_speed": 55,
"release_open_accel": 70,
"release_open_decel": 70,
"release_arm_delay_seconds": 0.28,
"release_min_hold_seconds": 0.65,
"release_settle_seconds": 0.12,
"release_min_change": 0.08,
"release_touch_window_seconds": 0.14,
"release_side_touch_change": 0.12,
"release_confirm_window_seconds": 0.14,
"release_sample_timeout_seconds": 0.2,
"release_confirm_samples": 4,
"release_slow_confirm_samples": 8,
"release_normal_rate": 2.6,
"release_shear_rate": 2.4,
"release_slow_rate": 0.22,
"release_normal_change": 0.42,
"release_shear_change": 0.35,
"release_curve_history": 30,
"release_slope_window_seconds": 0.8,
"release_slope_settle_rate": 0.22,
"release_slope_min_points": 10,
"release_require_slope_settle": True,
"release_slope_hold_seconds": 0.45,
"release_settle_normal_only": True,
# Intentional rub/twist: oscillating shear opens even before plateau latch.
"release_shear_oscillation_open": True,
"release_shear_oscillation_window_seconds": 0.4,
"release_shear_oscillation_min_flips": 3,
"release_shear_oscillation_amplitude": 0.15,
"release_shear_oscillation_confirm_samples": 2,
"release_side_shear_enabled": True,
"release_side_normal_enabled": True,
"release_shear_onset_enabled": True,
"release_normal_onset_enabled": True,
"rearm_seconds": 0.55,
"recover_stable_seconds": 0.55,
}
_STABLE_PROFILE = {
"release_open_speed": 45,
"release_open_accel": 60,
"release_open_decel": 60,
"release_arm_delay_seconds": 0.55,
"release_min_hold_seconds": 1.2,
"release_settle_seconds": 0.22,
"release_min_change": 0.16,
"release_touch_window_seconds": 0.22,
"release_side_touch_change": 0.24,
"release_confirm_window_seconds": 0.28,
"release_sample_timeout_seconds": 0.3,
"release_confirm_samples": 8,
"release_slow_confirm_samples": 12,
"release_normal_rate": 4.0,
"release_shear_rate": 3.6,
"release_slow_rate": 0.14,
"release_normal_change": 0.7,
"release_shear_change": 0.6,
"release_curve_history": 42,
"release_slope_window_seconds": 1.2,
"release_slope_settle_rate": 0.12,
"release_slope_min_points": 14,
"release_require_slope_settle": True,
"release_slope_hold_seconds": 0.9,
"release_settle_normal_only": True,
"release_shear_oscillation_open": True,
"release_shear_oscillation_window_seconds": 0.6,
"release_shear_oscillation_min_flips": 5,
"release_shear_oscillation_amplitude": 0.3,
"release_shear_oscillation_confirm_samples": 4,
"release_side_shear_enabled": True,
"release_side_normal_enabled": True,
"release_shear_onset_enabled": True,
"release_normal_onset_enabled": True,
"rearm_seconds": 0.9,
"recover_stable_seconds": 0.9,
}
# Roughly ~10x harder to trip open than sensitive; intentional open needs clear force.
_FIRM_PROFILE = {
"release_open_speed": 35,
"release_open_accel": 50,
"release_open_decel": 50,
"release_arm_delay_seconds": 1.2,
"release_min_hold_seconds": 3.0,
"release_settle_seconds": 0.5,
"release_min_change": 0.8,
"release_touch_window_seconds": 0.35,
"release_side_touch_change": 1.2,
"release_confirm_window_seconds": 0.5,
"release_sample_timeout_seconds": 0.4,
"release_confirm_samples": 12,
"release_slow_confirm_samples": 18,
"release_normal_rate": 20.0,
"release_shear_rate": 18.0,
"release_slow_rate": 0.08,
"release_normal_change": 4.0,
"release_shear_change": 3.5,
"release_curve_history": 60,
"release_slope_window_seconds": 2.0,
"release_slope_settle_rate": 0.06,
"release_slope_min_points": 20,
"release_require_slope_settle": True,
"release_slope_hold_seconds": 2.0,
"release_settle_normal_only": True,
# Keep path available, but require large rub/twist evidence.
"release_shear_oscillation_open": True,
"release_shear_oscillation_window_seconds": 0.8,
"release_shear_oscillation_min_flips": 6,
"release_shear_oscillation_amplitude": 1.5,
"release_shear_oscillation_confirm_samples": 6,
"release_side_shear_enabled": True,
"release_side_normal_enabled": True,
"release_shear_onset_enabled": True,
"release_normal_onset_enabled": True,
"rearm_seconds": 1.5,
"recover_stable_seconds": 1.5,
}
GRIP_BEHAVIOR_PROFILES = {
"rapid": {
"filter_alpha": 0.72,
"shear_filter_alpha": 0.78,
"open_wait_stable_samples": 3,
"open_wait_stable_range_n": 0.04,
"open_wait_stable_trend_n": 0.03,
"grip_settle_seconds": 0.05,
"force_ramp_step": 3,
"force_ramp_interval": 0.04,
"adaptive_grip_enabled": False,
"adaptive_force_interval": 0.05,
"release_open_speed": 85,
"release_open_accel": 90,
"release_open_decel": 90,
# Almost no normal-contact lock; rely on curve/side-touch rejection.
"release_arm_delay_seconds": 0.04,
"release_min_hold_seconds": 0.0,
"release_settle_seconds": 0.03,
"release_min_change": 0.03,
"release_touch_window_seconds": 0.28,
"release_side_touch_change": 0.04,
"release_confirm_window_seconds": 0.28,
"release_sample_timeout_seconds": 0.25,
"release_confirm_samples": 2,
"release_slow_confirm_samples": 3,
"release_normal_rate": 1.0,
"release_shear_rate": 1.0,
"release_slow_rate": 0.55,
"release_normal_change": 0.14,
"release_shear_change": 0.14,
"release_curve_history": 6,
"release_slope_window_seconds": 0.35,
"release_slope_settle_rate": 0.8,
"release_slope_min_points": 4,
"release_require_slope_settle": False,
"release_slope_hold_seconds": 0.0,
"release_settle_normal_only": False,
"release_side_shear_enabled": True,
"release_side_normal_enabled": True,
"release_shear_onset_enabled": True,
"release_normal_onset_enabled": True,
"rearm_seconds": 0.12,
"recover_stable_seconds": 0.12,
},
"stable": {
"filter_alpha": 0.42,
"shear_filter_alpha": 0.52,
"open_wait_stable_samples": 8,
"open_wait_stable_range_n": 0.025,
"open_wait_stable_trend_n": 0.015,
"grip_settle_seconds": 0.2,
"force_ramp_step": 1,
"force_ramp_interval": 0.1,
"adaptive_grip_enabled": True,
"adaptive_force_interval": 0.08,
"release_open_speed": 55,
"release_open_accel": 70,
"release_open_decel": 70,
"release_arm_delay_seconds": 0.28,
"release_min_hold_seconds": 0.65,
"release_settle_seconds": 0.12,
"release_min_change": 0.08,
"release_touch_window_seconds": 0.14,
"release_side_touch_change": 0.12,
"release_confirm_window_seconds": 0.14,
"release_sample_timeout_seconds": 0.2,
"release_confirm_samples": 4,
"release_slow_confirm_samples": 8,
"release_normal_rate": 2.6,
"release_shear_rate": 2.4,
"release_slow_rate": 0.22,
"release_normal_change": 0.42,
"release_shear_change": 0.35,
# Clean split: settle on normal plateau; open only via shear.
"release_curve_history": 30,
"release_slope_window_seconds": 0.8,
"release_slope_settle_rate": 0.22,
"release_slope_min_points": 10,
"release_require_slope_settle": True,
"release_slope_hold_seconds": 0.45,
"release_settle_normal_only": True,
"release_side_shear_enabled": True,
"release_side_normal_enabled": False,
"release_shear_onset_enabled": True,
"release_normal_onset_enabled": False,
"rearm_seconds": 0.55,
"recover_stable_seconds": 0.55,
},
"sensitive": _SENSITIVE_PROFILE,
"stable": _STABLE_PROFILE,
"firm": _FIRM_PROFILE,
}
def normalize_grip_behavior_mode(mode) -> str:
"""Resolve aliases to sensitive / stable / firm; default sensitive."""
text = str(mode or "").strip().lower()
aliases = {
"fast": "rapid",
"quick": "rapid",
"snap": "rapid",
"迅速": "rapid",
"迅速开合": "rapid",
"hold": "stable",
"firm": "stable",
"secure": "stable",
"fast": "sensitive",
"quick": "sensitive",
"snap": "sensitive",
"rapid": "sensitive",
"responsive": "sensitive",
"灵敏": "sensitive",
"灵敏开合": "sensitive",
"迅速": "sensitive",
"迅速开合": "sensitive",
"sensitive": "sensitive",
"稳定": "stable",
"较稳定": "stable",
"夹持稳定": "stable",
"stable": "stable",
"hold": "firm",
"firm": "firm",
"secure": "firm",
"very_stable": "firm",
"verystable": "firm",
"十分稳定": "firm",
"极度稳定": "firm",
}
text = aliases.get(text, text)
if text in aliases:
return aliases[text]
if text not in GRIP_BEHAVIOR_PROFILES:
raise ValueError(
f"unsupported grip_behavior_mode: {mode!r}; "
f"expected one of {list(GRIP_BEHAVIOR_PROFILES)}"
)
return "sensitive"
return text
def grip_behavior_profile(mode: str) -> dict:
def _merged_profile(mode: str) -> dict:
"""Release/open params from *mode*, trigger params always from sensitive."""
mode = normalize_grip_behavior_mode(mode)
return copy.deepcopy(GRIP_BEHAVIOR_PROFILES[mode])
profile = {
key: copy.deepcopy(GRIP_BEHAVIOR_PROFILES[mode][key])
for key in GRIP_BEHAVIOR_KEYS
if key in GRIP_BEHAVIOR_PROFILES[mode]
}
for key in GRIP_TRIGGER_KEYS:
profile[key] = copy.deepcopy(_SENSITIVE_TRIGGER[key])
return profile
def apply_grip_behavior_profile(target, mode: str) -> str:
"""Write profile fields onto a config object or dict. Returns normalized mode."""
def grip_behavior_profile(mode: str = "sensitive") -> dict:
return _merged_profile(mode)
def apply_grip_behavior_profile(target, mode: str = "sensitive") -> str:
mode = normalize_grip_behavior_mode(mode)
profile = GRIP_BEHAVIOR_PROFILES[mode]
profile = _merged_profile(mode)
if isinstance(target, dict):
target["grip_behavior_mode"] = mode
for key, value in profile.items():
target[key] = copy.deepcopy(value)
return mode
setattr(target, "grip_behavior_mode", mode)
for key, value in profile.items():
if hasattr(target, key):
+26 -2
View File
@@ -68,6 +68,8 @@ class Demo02Config:
baseline_seconds: float = 1.0
filter_alpha: float = 0.55
shear_filter_alpha: float = 0.65
# Shear magnitude used for trigger/adaptive/release: "xy" | "x" | "y".
shear_control_axes: str = "y"
trigger_normal_force: float = 0.04
trigger_shear_force: float = 0.04
@@ -82,7 +84,7 @@ class Demo02Config:
force_ramp_step: int = 1
force_ramp_interval: float = 0.1
grip_settle_seconds: float = 0.1
grip_behavior_mode: str = "stable"
grip_behavior_mode: str = "sensitive"
adaptive_grip_enabled: bool = True
adaptive_shear_start_force: float = 0.10
adaptive_shear_full_force: float = 0.60
@@ -112,6 +114,11 @@ class Demo02Config:
release_require_slope_settle: bool = False
release_slope_hold_seconds: float = 0.0
release_settle_normal_only: bool = True
release_shear_oscillation_open: bool = False
release_shear_oscillation_window_seconds: float = 0.4
release_shear_oscillation_min_flips: int = 3
release_shear_oscillation_amplitude: float = 0.15
release_shear_oscillation_confirm_samples: int = 2
release_side_shear_enabled: bool = True
release_side_normal_enabled: bool = True
release_shear_onset_enabled: bool = True
@@ -184,6 +191,12 @@ class Demo02Config:
add("--baseline-seconds", type=float, default=get("baseline_seconds", cls.baseline_seconds))
add("--filter-alpha", type=float, default=get("filter_alpha", cls.filter_alpha))
add("--shear-filter-alpha", type=float, default=get("shear_filter_alpha", cls.shear_filter_alpha))
add(
"--shear-control-axes",
type=str,
choices=["xy", "x", "y"],
default=str(get("shear_control_axes", cls.shear_control_axes)).strip().lower() or "y",
)
add("--trigger-normal-force", type=float, default=get("trigger_normal_force", cls.trigger_normal_force))
add("--trigger-shear-force", type=float, default=get("trigger_shear_force", cls.trigger_shear_force))
@@ -202,7 +215,7 @@ class Demo02Config:
add(
"--grip-behavior-mode",
type=str,
choices=["rapid", "stable"],
choices=["sensitive", "stable", "firm"],
default=normalize_grip_behavior_mode(
get("grip_behavior_mode", cls.grip_behavior_mode)
),
@@ -241,6 +254,17 @@ class Demo02Config:
add("--release-settle-normal-only", dest="release_settle_normal_only", action="store_true",
default=get("release_settle_normal_only", cls.release_settle_normal_only))
add("--release-settle-all-channels", dest="release_settle_normal_only", action="store_false")
add("--release-shear-oscillation-open", dest="release_shear_oscillation_open", action="store_true",
default=get("release_shear_oscillation_open", cls.release_shear_oscillation_open))
add("--no-release-shear-oscillation-open", dest="release_shear_oscillation_open", action="store_false")
add("--release-shear-oscillation-window-seconds", type=float,
default=get("release_shear_oscillation_window_seconds", cls.release_shear_oscillation_window_seconds))
add("--release-shear-oscillation-min-flips", type=int,
default=get("release_shear_oscillation_min_flips", cls.release_shear_oscillation_min_flips))
add("--release-shear-oscillation-amplitude", type=float,
default=get("release_shear_oscillation_amplitude", cls.release_shear_oscillation_amplitude))
add("--release-shear-oscillation-confirm-samples", type=int,
default=get("release_shear_oscillation_confirm_samples", cls.release_shear_oscillation_confirm_samples))
add("--release-side-shear-enabled", dest="release_side_shear_enabled", action="store_true",
default=get("release_side_shear_enabled", cls.release_side_shear_enabled))
add("--no-release-side-shear", dest="release_side_shear_enabled", action="store_false")