feat(pose): pinch engage debounce
This commit is contained in:
@@ -133,11 +133,12 @@ class PinchEvent:
|
||||
|
||||
|
||||
class _PinchState:
|
||||
__slots__ = ("engaged", "last_t")
|
||||
__slots__ = ("engaged", "last_t", "qual")
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.engaged: bool = False
|
||||
self.last_t: float = -1e9
|
||||
self.qual: int = 0 # consecutive qualifying frames (debounce)
|
||||
|
||||
|
||||
class PinchDetector:
|
||||
@@ -217,13 +218,22 @@ class PinchDetector:
|
||||
st.engaged = False
|
||||
events.append(PinchEvent(hand=slot, finger=i + 1, state=0))
|
||||
elif i == winner and (t_now - st.last_t) >= self.refractory_s:
|
||||
st.engaged = True
|
||||
st.last_t = t_now
|
||||
events.append(PinchEvent(hand=slot, finger=i + 1, state=1))
|
||||
# engage only after debounce_frames consecutive qualifying
|
||||
# frames; release below stays edge-immediate.
|
||||
st.qual += 1
|
||||
if st.qual >= self.debounce_frames:
|
||||
st.engaged = True
|
||||
st.last_t = t_now
|
||||
st.qual = 0
|
||||
events.append(PinchEvent(hand=slot, finger=i + 1, state=1))
|
||||
else:
|
||||
st.qual = 0
|
||||
for slot in range(len(self._state)):
|
||||
if slot not in present:
|
||||
for i in range(4):
|
||||
if self._state[slot][i].engaged:
|
||||
self._state[slot][i].engaged = False
|
||||
st = self._state[slot][i]
|
||||
st.qual = 0
|
||||
if st.engaged:
|
||||
st.engaged = False
|
||||
events.append(PinchEvent(hand=slot, finger=i + 1, state=0))
|
||||
return events
|
||||
|
||||
@@ -221,3 +221,36 @@ def test_extension_gate_defeat_reproduces_old_behavior():
|
||||
ext_min=0)
|
||||
ev = det.step([_relaxed_hand()], 0.1)
|
||||
assert len(ev) == 1 and ev[0].state == 1
|
||||
|
||||
|
||||
def test_debounce_delays_engage_to_nth_frame():
|
||||
det = PinchDetector(ratio_on=0.45, ratio_off=0.65, refractory_ms=0,
|
||||
debounce_frames=3)
|
||||
det.step([_pinch_hand(*_OPEN)], 0.00)
|
||||
a = det.step([_pinch_hand(*_PINCH)], 0.10) # qualifying 1
|
||||
b = det.step([_pinch_hand(*_PINCH)], 0.13) # qualifying 2
|
||||
c = det.step([_pinch_hand(*_PINCH)], 0.16) # qualifying 3 -> engage
|
||||
assert a == [] and b == []
|
||||
assert len(c) == 1 and c[0].state == 1 and c[0].finger == 1
|
||||
|
||||
|
||||
def test_debounce_jitter_resets_counter():
|
||||
det = PinchDetector(ratio_on=0.45, ratio_off=0.65, refractory_ms=0,
|
||||
debounce_frames=3)
|
||||
det.step([_pinch_hand(*_PINCH)], 0.00) # qualifying 1
|
||||
det.step([_pinch_hand(*_PINCH)], 0.03) # qualifying 2
|
||||
det.step([_pinch_hand(*_OPEN)], 0.06) # jitter -> counter resets
|
||||
a = det.step([_pinch_hand(*_PINCH)], 0.09) # qualifying 1 again
|
||||
b = det.step([_pinch_hand(*_PINCH)], 0.12) # qualifying 2
|
||||
assert a == [] and b == []
|
||||
|
||||
|
||||
def test_release_immediate_after_debounced_engage():
|
||||
det = PinchDetector(ratio_on=0.45, ratio_off=0.65, refractory_ms=0,
|
||||
debounce_frames=3)
|
||||
det.step([_pinch_hand(*_PINCH)], 0.00)
|
||||
det.step([_pinch_hand(*_PINCH)], 0.03)
|
||||
eng = det.step([_pinch_hand(*_PINCH)], 0.06) # 3rd frame -> engage
|
||||
rel = det.step([_pinch_hand(*_OPEN)], 0.09) # very next frame
|
||||
assert len(eng) == 1 and eng[0].state == 1
|
||||
assert len(rel) == 1 and rel[0].state == 0
|
||||
|
||||
Reference in New Issue
Block a user