稳定度配置条与自定义挡位、独立速度调节;状态文字中文简写与平滑;灵敏挡切向振荡判据收紧;新增重置底图按钮。

- 设置框新增 0~100 稳定度配置条(灵敏 0 / 较稳定 50 / 十分稳定 100 为内置挡位,中间按比例插值),支持滚轮/左键拖动,可另存/删除自定义挡位;速度改为独立滑块。
- 状态栏改为中文简写(悬停显示原文);夹持保护期文字按挡位区分;合并同义子状态并加最短停留,避免逐帧跳动。
- 灵敏挡切向振荡:0.5 s 内换向 4 次,并须持续至少 0.15 s 才张开(新参数 release_shear_oscillation_min_seconds)。
- 传感器控制区新增「重置底图」:左右传感器一起用当前画面更换光流参考底图(orisys Sensor.reset),不断开相机。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cheng
2026-10-08 13:34:30 +08:00
parent 3507a2d313
commit 33578fa83e
7 changed files with 842 additions and 70 deletions
+102 -2
View File
@@ -8,6 +8,7 @@ import copy
# sensitive = 灵敏:跟手释放,自适应夹持力
# stable = 较稳定:接触即 70% 恒力夹持,2 s 保护期,Y 拟合偏移 0.6 N 张开
# firm = 十分稳定:接触即 100% 恒力夹持,2.5 s 保护期,Y 拟合偏移 0.8 N 张开
# 稳定度 0..100:三挡分别位于 0 / 50 / 100,中间值按比例插值(见 grip_behavior_level_profile)。
GRIP_BEHAVIOR_MODES = ("sensitive", "stable", "firm")
GRIP_BEHAVIOR_LABELS = {
@@ -74,6 +75,7 @@ GRIP_BEHAVIOR_KEYS = (
"release_shear_oscillation_min_flips",
"release_shear_oscillation_amplitude",
"release_shear_oscillation_confirm_samples",
"release_shear_oscillation_min_seconds",
"release_side_shear_enabled",
"release_side_normal_enabled",
"release_shear_onset_enabled",
@@ -157,10 +159,11 @@ _SENSITIVE_PROFILE = {
"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_window_seconds": 0.5,
"release_shear_oscillation_min_flips": 4,
"release_shear_oscillation_amplitude": 0.15,
"release_shear_oscillation_confirm_samples": 2,
"release_shear_oscillation_min_seconds": 0.15,
"release_side_shear_enabled": True,
"release_side_normal_enabled": True,
"release_shear_onset_enabled": True,
@@ -288,6 +291,103 @@ def grip_behavior_profile(mode: str = "sensitive") -> dict:
return _merged_profile(mode)
# Stability level 0..100: the three built-in modes sit at fixed anchors.
# Below the stable anchor the sensitive algorithm runs with its numeric params
# scaled toward the stable values; from the stable anchor on, the held
# algorithm runs, scaled from stable to firm.
GRIP_BEHAVIOR_LEVEL_MIN = 0
GRIP_BEHAVIOR_LEVEL_MAX = 100
GRIP_BEHAVIOR_LEVELS = {"sensitive": 0, "stable": 50, "firm": 100}
# Only read by the held (stable/firm) release; stays off on the sensitive side.
_HELD_ONLY_KEYS = ("release_shear_displacement",)
def clamp_grip_behavior_level(level) -> int:
try:
value = int(round(float(level)))
except (TypeError, ValueError):
value = GRIP_BEHAVIOR_LEVEL_MIN
return max(GRIP_BEHAVIOR_LEVEL_MIN, min(GRIP_BEHAVIOR_LEVEL_MAX, value))
def grip_behavior_mode_for_level(level) -> str:
level = clamp_grip_behavior_level(level)
if level >= GRIP_BEHAVIOR_LEVELS["firm"]:
return "firm"
if level >= GRIP_BEHAVIOR_LEVELS["stable"]:
return "stable"
return "sensitive"
def default_grip_behavior_level(mode) -> int:
return GRIP_BEHAVIOR_LEVELS[normalize_grip_behavior_mode(mode)]
def builtin_grip_behavior_presets() -> list:
return [
{
"name": GRIP_BEHAVIOR_LABELS[mode],
"level": GRIP_BEHAVIOR_LEVELS[mode],
"speed": GRIP_BEHAVIOR_PROFILES[mode]["speed"],
"builtin": True,
}
for mode in GRIP_BEHAVIOR_MODES
]
def normalize_grip_behavior_presets(presets) -> list:
"""User presets as [{name, level[, speed]}], without duplicates of built-in names."""
reserved = {GRIP_BEHAVIOR_LABELS[mode] for mode in GRIP_BEHAVIOR_MODES}
result = []
seen = set()
for item in presets if isinstance(presets, list) else []:
if not isinstance(item, dict):
continue
name = str(item.get("name") or "").strip()
if not name or name in reserved or name in seen:
continue
seen.add(name)
preset = {"name": name, "level": clamp_grip_behavior_level(item.get("level"))}
if item.get("speed") is not None:
preset["speed"] = clamp_grip_behavior_level(item.get("speed"))
result.append(preset)
return result
def _lerp_value(a, b, t):
if isinstance(a, bool) or isinstance(b, bool):
return copy.deepcopy(a)
if not isinstance(a, (int, float)) or not isinstance(b, (int, float)):
return copy.deepcopy(a)
value = a + (b - a) * t
if isinstance(a, int) and isinstance(b, int):
return int(round(value))
return round(float(value), 4)
def grip_behavior_level_profile(level) -> dict:
"""Merged params for stability *level*; anchors equal the built-in modes."""
level = clamp_grip_behavior_level(level)
for mode, anchor in GRIP_BEHAVIOR_LEVELS.items():
if level == anchor:
return _merged_profile(mode)
stable_at = GRIP_BEHAVIOR_LEVELS["stable"]
if level < stable_at:
low, high = _merged_profile("sensitive"), _merged_profile("stable")
t = (level - GRIP_BEHAVIOR_LEVELS["sensitive"]) / float(stable_at - GRIP_BEHAVIOR_LEVELS["sensitive"])
else:
low, high = _merged_profile("stable"), _merged_profile("firm")
t = (level - stable_at) / float(GRIP_BEHAVIOR_LEVELS["firm"] - stable_at)
fixed = GRIP_TRIGGER_KEYS + (_HELD_ONLY_KEYS if level < stable_at else ())
profile = {}
for key, value in low.items():
if key in fixed:
profile[key] = copy.deepcopy(value)
else:
profile[key] = _lerp_value(value, high.get(key, value), t)
return profile
def apply_grip_behavior_profile(target, mode: str = "sensitive") -> str:
mode = normalize_grip_behavior_mode(mode)
profile = _merged_profile(mode)
+4
View File
@@ -122,6 +122,8 @@ class Demo02Config:
release_shear_oscillation_min_flips: int = 3
release_shear_oscillation_amplitude: float = 0.15
release_shear_oscillation_confirm_samples: int = 2
# Oscillation must persist this long (in addition to confirm samples) before opening.
release_shear_oscillation_min_seconds: float = 0.0
# Block oscillation/side-touch release during contact hold and servo motion.
release_guard_fast_paths: bool = False
# Shear axes for release only: "" follows shear_control_axes; "y" | "x" | "xy".
@@ -281,6 +283,8 @@ class Demo02Config:
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-shear-oscillation-min-seconds", type=float,
default=get("release_shear_oscillation_min_seconds", cls.release_shear_oscillation_min_seconds))
add("--release-guard-fast-paths", dest="release_guard_fast_paths", action="store_true",
default=get("release_guard_fast_paths", cls.release_guard_fast_paths))
add("--no-release-guard-fast-paths", dest="release_guard_fast_paths", action="store_false")