mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 06:00:09 +02:00
Hands: park hand tracking behind ft-handsctl
Hand tracking no longer starts with SteamVR: hands/run.sh install leaves the units disabled and links hands/ft-handsctl into ~/.local/bin, which turns it on and off (on | off | status | log | cutouts on|off | gestures). With it on, hands show through the screens; pinches and grips move the pointer only with POINTER_HANDS=1, now off by default. ft-camd's service runs the mono cameras only: while the headset is worn, the colour module writes just a half-size image into the top-left quarter of its buffers, which ft-camd can't use yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5ca4160a7e
commit
c2739cb0df
5 files changed
+63
-12
No files matched your search
@@ -10,9 +10,10 @@ PartOf=steamvr.service
|
|||||||
# On the host: the dev container can't reach XRService. Its file capabilities (set by
|
# On the host: the dev container can't reach XRService. Its file capabilities (set by
|
||||||
# hands/run.sh install) let it borrow the buffers; it drops them once set up. It exits when
|
# hands/run.sh install) let it borrow the buffers; it drops them once set up. It exits when
|
||||||
# XRService restarts, and comes back to attach to the new one.
|
# XRService restarts, and comes back to attach to the new one.
|
||||||
# The colour cameras run at 2 fps (their brightness picks the tracking cameras) until
|
# Mono cameras only: while the headset is worn the colour module writes only a half-size
|
||||||
# ft-hands asks for more; HANDS_CAMERAS=mono leaves them out.
|
# image into the top-left quarter of its buffers (2026-09-30), which ft-camd can't use yet.
|
||||||
ExecStart=@REPO@/hands/build/ft-camd --with-color --status 60
|
# Add --with-color to try the colour cameras (ft-hands then picks them by the light).
|
||||||
|
ExecStart=@REPO@/hands/build/ft-camd --status 60
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
TimeoutStopSec=5
|
TimeoutStopSec=5
|
||||||
|
|||||||
Executable
+48
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ft-handsctl: turn hand tracking on and off by hand, on the Frame. With it on, your hands show
|
||||||
|
# through Frametop's screens (the hand cutouts); pinches and grips only move the pointer with
|
||||||
|
# POINTER_HANDS=1. It doesn't start with SteamVR (hands/run.sh install leaves it off), and it
|
||||||
|
# stops when SteamVR does.
|
||||||
|
#
|
||||||
|
# ft-handsctl on | off | status | log [lines]
|
||||||
|
# ft-handsctl cutouts on|off|state ft-screens' hand cutouts, without stopping tracking
|
||||||
|
# ft-handsctl gestures watch pinches and grips live (Ctrl+C to stop)
|
||||||
|
#
|
||||||
|
# Needs the services installed once: hands/run.sh install (it sets ft-camd's capabilities).
|
||||||
|
set -euo pipefail
|
||||||
|
here=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
|
||||||
|
units="frametop-camd.service frametop-hands.service"
|
||||||
|
|
||||||
|
ask_screens() { # a command to ft-screens' control socket, and its reply
|
||||||
|
python3 - "$1" <<'EOF'
|
||||||
|
import socket, sys
|
||||||
|
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||||
|
s.bind("")
|
||||||
|
s.settimeout(2)
|
||||||
|
try:
|
||||||
|
s.sendto(sys.argv[1].encode(), "\0ft_screens")
|
||||||
|
print(s.recv(512).decode())
|
||||||
|
except OSError as e:
|
||||||
|
sys.exit("ft-screens didn't answer (is the Frametop desktop running?): %s" % e)
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
case ${1:-status} in
|
||||||
|
on)
|
||||||
|
systemctl --user -q is-active steamvr.service || { echo "SteamVR isn't running" >&2; exit 1; }
|
||||||
|
systemctl --user start $units
|
||||||
|
sleep 4
|
||||||
|
"$0" status ;;
|
||||||
|
off)
|
||||||
|
systemctl --user stop $units
|
||||||
|
echo "hand tracking off" ;;
|
||||||
|
status)
|
||||||
|
for u in $units; do echo "$u: $(systemctl --user is-active $u || true)"; done
|
||||||
|
journalctl --user -u frametop-hands.service --no-pager -o cat -n 40 |
|
||||||
|
grep -E '^ *[0-9.]+s |sets with a hand|cameras:' | tail -2 | cut -c1-160 || true ;;
|
||||||
|
log) journalctl --user -u frametop-camd.service -u frametop-hands.service --no-pager -o short -n "${2:-30}" ;;
|
||||||
|
cutouts)
|
||||||
|
ask_screens "cutouts ${2:-state}" ;;
|
||||||
|
gestures) exec python3 "$here/tools/watch_gestures.py" --distance ;;
|
||||||
|
*) sed -n '2,11p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||||
|
esac
|
||||||
+6
-4
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Install, start, stop, or inspect hand tracking on the Frame: ft-camd (the camera broker) and
|
# Install, start, stop, or inspect hand tracking on the Frame: ft-camd (the camera broker) and
|
||||||
# ft-hands (the tracker), user services that start and stop with SteamVR.
|
# ft-hands (the tracker), user services that stop with SteamVR. They don't start with it:
|
||||||
|
# ft-handsctl on|off (on the Frame) or hands/run.sh start|stop.
|
||||||
# Usage: hands/run.sh install|uninstall
|
# Usage: hands/run.sh install|uninstall
|
||||||
# hands/run.sh caps # give ft-camd its capabilities again (a rebuild clears them)
|
# hands/run.sh caps # give ft-camd its capabilities again (a rebuild clears them)
|
||||||
# hands/run.sh start|stop|restart|status|log [lines]
|
# hands/run.sh start|stop|restart|status|log [lines]
|
||||||
@@ -47,9 +48,10 @@ case ${1:-status} in
|
|||||||
for u in $units; do
|
for u in $units; do
|
||||||
fill_template "$root/hands/$u" | on_frame "mkdir -p ~/.config/systemd/user && cat > ~/.config/systemd/user/$u"
|
fill_template "$root/hands/$u" | on_frame "mkdir -p ~/.config/systemd/user && cat > ~/.config/systemd/user/$u"
|
||||||
done
|
done
|
||||||
"$frame" --host "set -e; systemctl --user daemon-reload; systemctl --user enable $units
|
# Installed but not started with SteamVR: ft-handsctl on|off (linked into ~/.local/bin).
|
||||||
if systemctl --user -q is-active steamvr.service; then systemctl --user restart $units; sleep 5; fi
|
"$frame" --host "set -e; systemctl --user daemon-reload; systemctl --user disable $units 2>/dev/null || true
|
||||||
$states" ;;
|
mkdir -p ~/.local/bin && ln -sfn $(printf %q "$FRAME_REPO/hands/ft-handsctl") ~/.local/bin/ft-handsctl
|
||||||
|
$states; echo 'start it with: ft-handsctl on'" ;;
|
||||||
caps) set_caps ;;
|
caps) set_caps ;;
|
||||||
uninstall) "$frame" --host "systemctl --user disable --now $units 2>/dev/null
|
uninstall) "$frame" --host "systemctl --user disable --now $units 2>/dev/null
|
||||||
for u in $units; do rm -f ~/.config/systemd/user/\$u; done; systemctl --user daemon-reload; echo removed" ;;
|
for u in $units; do rm -f ~/.config/systemd/user/\$u; done; systemctl --user daemon-reload; echo removed" ;;
|
||||||
|
|||||||
@@ -163,7 +163,7 @@
|
|||||||
// press dragged onto the target is the same: from the raw gaze at the press to the release. With no
|
// press dragged onto the target is the same: from the raw gaze at the press to the release. With no
|
||||||
// fresh gaze (a blink, the service stopped, the headset off), the pointer stays put.
|
// fresh gaze (a blink, the service stopped, the headset off), the pointer stays put.
|
||||||
//
|
//
|
||||||
// Hands (POINTER_HANDS, on by default; needs hand tracking, hands/): ft-hands publishes
|
// Hands (POINTER_HANDS, off by default; needs hand tracking, hands/): ft-hands publishes
|
||||||
// pinches and grips (hands/include/fh_gestures.h), read here every frame.
|
// pinches and grips (hands/include/fh_gestures.h), read here every frame.
|
||||||
// A pinch works like gaze mode's mouse press: the pointer stops (where the gaze put it), and
|
// A pinch works like gaze mode's mouse press: the pointer stops (where the gaze put it), and
|
||||||
// the click comes when the pinch opens, where the pointer is then. A quick tap clicks where
|
// the click comes when the pinch opens, where the pointer is then. A quick tap clicks where
|
||||||
@@ -238,7 +238,7 @@
|
|||||||
// (5 deg), POINTER_GAZE_NUDGE_MAX (8 deg), POINTER_GAZE_HOLD (0.5 s), POINTER_GAZE_SHOW (1 s):
|
// (5 deg), POINTER_GAZE_NUDGE_MAX (8 deg), POINTER_GAZE_HOLD (0.5 s), POINTER_GAZE_SHOW (1 s):
|
||||||
// gaze mode, above. POINTER_CONTROLLER_PICKUP (1, 0.5 to 5): how hard a controller must
|
// gaze mode, above. POINTER_CONTROLLER_PICKUP (1, 0.5 to 5): how hard a controller must
|
||||||
// move to take the laser back, above. POINTER_IGNORE (empty): ignored panels, above.
|
// move to take the laser back, above. POINTER_IGNORE (empty): ignored panels, above.
|
||||||
// POINTER_HANDS (1), POINTER_PINCH_GAIN (0.5), POINTER_PINCH_DEADZONE (1.5 deg),
|
// POINTER_HANDS (0), POINTER_PINCH_GAIN (0.5), POINTER_PINCH_DEADZONE (1.5 deg),
|
||||||
// POINTER_GRIP_GAIN (1), POINTER_GRIP_BELOW (0.35 m), POINTER_PINCH_TYPING (1 s): hands, above.
|
// POINTER_GRIP_GAIN (1), POINTER_GRIP_BELOW (0.35 m), POINTER_PINCH_TYPING (1 s): hands, above.
|
||||||
#include <openvr.h>
|
#include <openvr.h>
|
||||||
|
|
||||||
@@ -648,7 +648,7 @@ int main() {
|
|||||||
bool gazeOn = false, gazeConf = false;
|
bool gazeOn = false, gazeConf = false;
|
||||||
double gazeRetake = 5, gazeNudgeMax = 8, gazeHold = 0.5, gazeShow = 1;
|
double gazeRetake = 5, gazeNudgeMax = 8, gazeHold = 0.5, gazeShow = 1;
|
||||||
// Hands (see the top): POINTER_HANDS, POINTER_PINCH_GAIN, POINTER_PINCH_DEADZONE, POINTER_GRIP_GAIN.
|
// Hands (see the top): POINTER_HANDS, POINTER_PINCH_GAIN, POINTER_PINCH_DEADZONE, POINTER_GRIP_GAIN.
|
||||||
bool handsOn = true;
|
bool handsOn = false;
|
||||||
double pinchGain = 0.5, pinchDeadzone = 1.5, gripGain = 1.0, handBelow = 0.35, typingHold = 1.0;
|
double pinchGain = 0.5, pinchDeadzone = 1.5, gripGain = 1.0, handBelow = 0.35, typingHold = 1.0;
|
||||||
double pickupScale = 1; // POINTER_CONTROLLER_PICKUP: scales the controller-moved limits
|
double pickupScale = 1; // POINTER_CONTROLLER_PICKUP: scales the controller-moved limits
|
||||||
std::vector<std::string> ignore; // POINTER_IGNORE (see ParseIgnore)
|
std::vector<std::string> ignore; // POINTER_IGNORE (see ParseIgnore)
|
||||||
@@ -674,7 +674,7 @@ int main() {
|
|||||||
gazeShow = std::clamp(ConfDouble(conf, "POINTER_GAZE_SHOW", 1), 0.0, 30.0);
|
gazeShow = std::clamp(ConfDouble(conf, "POINTER_GAZE_SHOW", 1), 0.0, 30.0);
|
||||||
const bool wantGaze = ConfDouble(conf, "POINTER_GAZE", 0) != 0;
|
const bool wantGaze = ConfDouble(conf, "POINTER_GAZE", 0) != 0;
|
||||||
if (wantGaze != gazeConf) gazeOn = gazeConf = wantGaze;
|
if (wantGaze != gazeConf) gazeOn = gazeConf = wantGaze;
|
||||||
handsOn = ConfDouble(conf, "POINTER_HANDS", 1) != 0;
|
handsOn = ConfDouble(conf, "POINTER_HANDS", 0) != 0;
|
||||||
pinchGain = std::clamp(ConfDouble(conf, "POINTER_PINCH_GAIN", 0.5), 0.05, 3.0);
|
pinchGain = std::clamp(ConfDouble(conf, "POINTER_PINCH_GAIN", 0.5), 0.05, 3.0);
|
||||||
pinchDeadzone = std::clamp(ConfDouble(conf, "POINTER_PINCH_DEADZONE", 1.5), 0.0, 10.0);
|
pinchDeadzone = std::clamp(ConfDouble(conf, "POINTER_PINCH_DEADZONE", 1.5), 0.0, 10.0);
|
||||||
gripGain = std::clamp(ConfDouble(conf, "POINTER_GRIP_GAIN", 1.0), 0.05, 3.0);
|
gripGain = std::clamp(ConfDouble(conf, "POINTER_GRIP_GAIN", 1.0), 0.05, 3.0);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ HANDS_BRIGHT_ON=40 # hand tracking, auto: the colour frames' mean bright
|
|||||||
HANDS_BRIGHT_OFF=25 # ... and back to the mono cameras under this
|
HANDS_BRIGHT_OFF=25 # ... and back to the mono cameras under this
|
||||||
HANDS_COLOR_LEFT=color_video0 # hand tracking: which colour camera is passthrough_left (hands/tools/check_color.py tells)
|
HANDS_COLOR_LEFT=color_video0 # hand tracking: which colour camera is passthrough_left (hands/tools/check_color.py tells)
|
||||||
HANDS_COLOR_CROP=subtract # hand tracking: how the colour calibration's crop applies, subtract | none (check_color.py tells)
|
HANDS_COLOR_CROP=subtract # hand tracking: how the colour calibration's crop applies, subtract | none (check_color.py tells)
|
||||||
POINTER_HANDS=1 # 1 = pinches click (on the release; hold and move to nudge the pointer first) and grips press and drag, with hand tracking
|
POINTER_HANDS=0 # 1 = pinches click (on the release; hold and move to nudge the pointer first) and grips press and drag, with hand tracking
|
||||||
POINTER_PINCH_GAIN=0.5 # hands: the pointer moves this times the pinching hand's angle (precision)
|
POINTER_PINCH_GAIN=0.5 # hands: the pointer moves this times the pinching hand's angle (precision)
|
||||||
POINTER_PINCH_DEADZONE=1.5 # hands: degrees the pinching hand moves before the pointer does (a tap's jitter)
|
POINTER_PINCH_DEADZONE=1.5 # hands: degrees the pinching hand moves before the pointer does (a tap's jitter)
|
||||||
POINTER_GRIP_GAIN=1 # hands: the pointer moves this times the gripping hand's angle
|
POINTER_GRIP_GAIN=1 # hands: the pointer moves this times the gripping hand's angle
|
||||||
|
|||||||
Reference in new issue
Block a user