Skip to content

Commit f5c7402

Browse files
authored
This PR adds debouncing to poses. (#8)
1 parent ef1f39e commit f5c7402

18 files changed

+89
-40
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ New hand poses can be made by creating new Hand Pose Resource instances.
6767
Hand Pose Resources consist of:
6868
* A Pose Name (reported in the pose detector signals)
6969
* A Threshold (a minimal fitness threshold to report the pose)
70+
* A Hold Time (a debounce time necessary to register the pose)
71+
* A Release Time (a debounce time necessary to release the pose)
7072
* A set of fitness functions to apply to each pose component
7173

7274

@@ -106,7 +108,7 @@ The Range function provides non-zero values in a finite range.
106108

107109
## Designing and Tuning
108110

109-
The inspect scene provided in the demo project can be used to inspect the flexion, curl, abduction, and tip-distance of a hand, and can also inspect a selected hand pose to diagnose the fitness of the rules.
111+
The inspect scene provided in the demo project can be used to inspect the flexion, curl, abduction, and tip-distance of a hand, and can also inspect a selected hand pose to diagnose the fitness of each component.
110112

111113
![Inspect Scene](/docs/inspect_scene.png)
112114

VERSIONS.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
# 1.2.0
1+
# 1.2.0 (in progress)
22
- Added hand pose controller capable of generating XR input actions
3+
- Added debounce hold and release times for poses
34

45
# 1.1.0
56
- Moved fitness function to separate resource

addons/hand_pose_detector/hand_pose.gd

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ extends Resource
1515
## Detection threshold
1616
@export_range(0.0, 1.0) var threshold : float = 0.5
1717

18+
## Hold time before pose is detected
19+
@export_range(0.01, 1.0) var hold_time : float = 0.2
20+
21+
## Release time before pose is lost
22+
@export_range(0.01, 1.0) var release_time : float = 0.2
23+
1824
# Flexion group
1925
@export_group("Flexion", "flexion_")
2026

addons/hand_pose_detector/hand_pose_controller.gd

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func _get_configuration_warnings() -> PackedStringArray:
9999

100100

101101
# Handle start of pose
102-
func _pose_started(p_name : String, _fitness : float) -> void:
102+
func _pose_started(p_name : String) -> void:
103103
# Skip if no tracker or action set
104104
if not tracker or not action_set:
105105
return

addons/hand_pose_detector/hand_pose_detector.gd

+56-22
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ extends Node
99

1010

1111
## Signal reported when a hand pose starts
12-
signal pose_started(p_name : String, p_fitness : float)
13-
14-
## Signal reported while a hand pose is in effect
15-
signal pose_update(p_name : String, p_fitness : float)
12+
signal pose_started(p_name : String)
1613

1714
## Signal reported when a hand pose ends
1815
signal pose_ended(p_name : String)
@@ -31,7 +28,16 @@ var tracker : XRHandTracker
3128
var _current_data : HandPoseData = HandPoseData.new()
3229

3330
# Current hand pose
34-
var _current_pose : String = ""
31+
var _current_pose : HandPose
32+
33+
# Current pose hold
34+
var _current_hold : float = 0.0
35+
36+
# New hand pose
37+
var _new_pose : HandPose
38+
39+
# New pose hold
40+
var _new_hold : float = 0.0
3541

3642

3743
# Customize the properties
@@ -42,7 +48,7 @@ func _validate_property(property: Dictionary) -> void:
4248

4349

4450
# Called every frame. 'delta' is the elapsed time since the previous frame.
45-
func _process(_delta: float) -> void:
51+
func _process(delta: float) -> void:
4652
# Skip when running in the engine
4753
if Engine.is_editor_hint():
4854
return
@@ -57,24 +63,52 @@ func _process(_delta: float) -> void:
5763
if not tracker:
5864
return
5965

66+
# Save the active pose before updates (to report changes)
67+
var active_pos := _current_pose
68+
6069
# Find the pose
6170
_current_data.update(tracker)
6271
var pose_match := hand_pose_set.find_pose(_current_data)
63-
var pose : String = pose_match.pose
72+
var pose : HandPose = pose_match.pose
6473
var fitness : float = pose_match.fitness
6574

66-
# Handle pose changing
67-
if pose != _current_pose:
68-
# Report end of the current pose
69-
if _current_pose != "":
70-
pose_ended.emit(_current_pose)
71-
72-
# Report start of the new pose
73-
if pose != "":
74-
pose_started.emit(pose, fitness)
75-
76-
# Save the updated pose
77-
_current_pose = pose
78-
elif _current_pose != "":
79-
# Report update of existing pose
80-
pose_update.emit(_current_pose, fitness)
75+
# Manage current pose
76+
if _current_pose:
77+
# Test if we detect the current pose
78+
if pose == _current_pose:
79+
# Restore hold on current pose
80+
_current_hold = 1.0
81+
else:
82+
# Decay hold on current pose
83+
_current_hold -= delta / _current_pose.release_time
84+
if _current_hold <= 0.0:
85+
# Current pose lost
86+
_current_hold = 0.0
87+
_current_pose = null
88+
89+
# Handle ramp of new pose
90+
if pose != _new_pose:
91+
# New pose detected
92+
_new_pose = pose
93+
_new_hold = 0.0
94+
elif _new_pose:
95+
# Ramp hold on new pose
96+
_new_hold += delta / _new_pose.hold_time
97+
if _new_hold >= 1.0:
98+
# New pose "ready"
99+
_new_hold = 1.0
100+
if not _current_pose:
101+
# No current pose, transition to new pose
102+
_current_pose = _new_pose
103+
_current_hold = 1.0
104+
105+
# Detect change in active pose
106+
if _current_pose != active_pos:
107+
# Report loss of old pose
108+
if active_pos:
109+
pose_ended.emit(active_pos.pose_name)
110+
111+
# Report start of new pose
112+
active_pos = _current_pose
113+
if active_pos:
114+
pose_started.emit(active_pos.pose_name)

addons/hand_pose_detector/hand_pose_set.gd

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ extends Resource
1616
## Returns the best pose for the specified [param hand].
1717
func find_pose(hand : HandPoseData) -> Dictionary:
1818
# Search for the best pose
19-
var best_pose := ""
20-
var best_fitness := 0.0
19+
var best_pose : HandPose = null
20+
var best_fitness : float = 0.0
2121
for p in poses:
2222
var f := p.get_fitness(hand)
2323
if f > best_fitness:
24+
best_pose = p
2425
best_fitness = f
25-
best_pose = p.pose_name
2626

2727
# Return the best pose
2828
return {

addons/hand_pose_detector/poses/fist.tres

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ max = 0.0
7979
script = ExtResource("2_ykawg")
8080
pose_name = "Fist"
8181
threshold = 0.3
82+
hold_time = 0.2
83+
release_time = 0.2
8284
flexion_thumb = SubResource("Resource_nb4va")
8385
flexion_index = SubResource("Resource_j5qa6")
8486
flexion_middle = SubResource("Resource_rmm3v")

addons/hand_pose_detector/poses/metal.tres

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ max = 0.0
6363
script = ExtResource("1_xil5d")
6464
pose_name = "Metal"
6565
threshold = 0.5
66+
hold_time = 0.2
67+
release_time = 0.2
6668
flexion_thumb = SubResource("Resource_h0roc")
6769
flexion_index = SubResource("Resource_gw741")
6870
flexion_middle = SubResource("Resource_pi16g")

addons/hand_pose_detector/poses/peace_sign.tres

+2
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ max = 0.0
7171
script = ExtResource("2_k3pua")
7272
pose_name = "Peace Sign"
7373
threshold = 0.3
74+
hold_time = 0.2
75+
release_time = 0.2
7476
flexion_thumb = SubResource("Resource_21fsw")
7577
flexion_index = SubResource("Resource_0wa5f")
7678
flexion_middle = SubResource("Resource_dj0e2")

addons/hand_pose_detector/poses/point.tres

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ max = 0.0
4747
script = ExtResource("2_fvjog")
4848
pose_name = "Point"
4949
threshold = 0.3
50+
hold_time = 0.2
51+
release_time = 0.2
5052
flexion_index = SubResource("Resource_3xngl")
5153
curl_index = SubResource("Resource_5lwnk")
5254
curl_middle = SubResource("Resource_ni4th")

addons/hand_pose_detector/poses/point_thumb_up.tres

+2
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ max = 0.0
6363
script = ExtResource("2_vgihw")
6464
pose_name = "Point Thumb Up"
6565
threshold = 0.3
66+
hold_time = 0.2
67+
release_time = 0.2
6668
flexion_thumb = SubResource("Resource_iey1r")
6769
flexion_index = SubResource("Resource_yunfv")
6870
curl_index = SubResource("Resource_ja5by")

addons/hand_pose_detector/poses/spock.tres

+2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ max = 0.0
111111
script = ExtResource("2_agcwt")
112112
pose_name = "Spock"
113113
threshold = 0.3
114+
hold_time = 0.2
115+
release_time = 0.2
114116
flexion_thumb = SubResource("Resource_o7ite")
115117
flexion_index = SubResource("Resource_vih3q")
116118
flexion_middle = SubResource("Resource_s4kcy")

addons/hand_pose_detector/poses/thumbs_up.tres

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ max = 0.0
5555
script = ExtResource("2_khw5p")
5656
pose_name = "ThumbsUp"
5757
threshold = 0.3
58+
hold_time = 0.2
59+
release_time = 0.2
5860
flexion_thumb = SubResource("Resource_p5lrn")
5961
curl_thumb = SubResource("Resource_tv1jx")
6062
curl_index = SubResource("Resource_p1kvp")

demo.gd

+4-10
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,14 @@ func _ready() -> void:
88
get_viewport().use_xr = true
99

1010

11-
func _on_left_hand_pose_started(p_name: String, p_fitness: float) -> void:
12-
$LeftHandLabel.text = "Left\n\n%s\n%0.2f" % [p_name, p_fitness]
13-
14-
func _on_left_hand_pose_update(p_name: String, p_fitness: float) -> void:
15-
$LeftHandLabel.text = "Left\n\n%s\n%0.2f" % [p_name, p_fitness]
11+
func _on_left_hand_pose_started(p_name: String) -> void:
12+
$LeftHandLabel.text = "Left\n\n%s" % p_name
1613

1714
func _on_left_hand_pose_ended(_p_name: String) -> void:
1815
$LeftHandLabel.text = "Left"
1916

20-
func _on_right_hand_pose_started(p_name: String, p_fitness: float) -> void:
21-
$RightHandLabel.text = "Right\n\n%s\n%0.2f" % [p_name, p_fitness]
22-
23-
func _on_right_hand_pose_update(p_name: String, p_fitness: float) -> void:
24-
$RightHandLabel.text = "Right\n\n%s\n%0.2f" % [p_name, p_fitness]
17+
func _on_right_hand_pose_started(p_name: String) -> void:
18+
$RightHandLabel.text = "Right\n\n%s" % p_name
2519

2620
func _on_right_hand_pose_ended(_p_name: String) -> void:
2721
$RightHandLabel.text = "Right"

demo.tscn

-2
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,8 @@ vertical_alignment = 0
7272

7373
[connection signal="pose_ended" from="XROrigin3D/LeftTrackedHand/LeftHandPose" to="." method="_on_left_hand_pose_ended"]
7474
[connection signal="pose_started" from="XROrigin3D/LeftTrackedHand/LeftHandPose" to="." method="_on_left_hand_pose_started"]
75-
[connection signal="pose_update" from="XROrigin3D/LeftTrackedHand/LeftHandPose" to="." method="_on_left_hand_pose_update"]
7675
[connection signal="pose_ended" from="XROrigin3D/RightTrackedHand/RightHandPose" to="." method="_on_right_hand_pose_ended"]
7776
[connection signal="pose_started" from="XROrigin3D/RightTrackedHand/RightHandPose" to="." method="_on_right_hand_pose_started"]
78-
[connection signal="pose_update" from="XROrigin3D/RightTrackedHand/RightHandPose" to="." method="_on_right_hand_pose_update"]
7977

8078
[editable path="XROrigin3D/LeftTrackedHand/LeftHandHumanoid"]
8179
[editable path="XROrigin3D/RightTrackedHand/RightHandHumanoid"]

docs/hand_pose_detector_signals.png

-8.64 KB
Loading

docs/hand_pose_resource.png

11.4 KB
Loading

docs/hand_pose_rule.png

-24.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)