mirror of
https://github.com/DeeJanuz/frametop.git
synced 2026-10-06 05:00:08 +02:00
Hands: ft-cutouts, the hand cutouts without pinches and grips
hands/ft-cutouts on|off|status starts ft-camd and ft-hands as transient user units with ft-hands' new --no-gestures: hands are published for ft-screens' cutouts, but no pinch or grip is detected, so nothing clicks or drags and a closing hand doesn't raise the tracking rate. It needs a build and ft-camd's capabilities, not hands/run.sh install. Its units conflict with ft-handsctl's, so each stops the other, and they stop with SteamVR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c377133859
commit
b417534425
3 files changed
+93
-20
No files matched your search
@@ -14,6 +14,8 @@ Two programs, each a user service that stops when SteamVR does:
|
||||
|
||||
They don't start with SteamVR. `hands/run.sh install` builds them, gives ft-camd its capabilities, installs both services disabled, and links `hands/ft-handsctl` into `~/.local/bin`. Then `ft-handsctl on` starts hand tracking and `ft-handsctl off` stops it. `install.sh` doesn't install it.
|
||||
|
||||
For the cutouts alone, `hands/ft-cutouts on` starts the same two programs with ft-hands' `--no-gestures`: your hands show through the screens, and no pinch or grip is detected, so nothing clicks or drags. It runs this checkout's build as transient user units, so it needs `hands/build.sh` and ft-camd's capabilities (`hands/run.sh caps`) but not `hands/run.sh install`. It and `ft-handsctl on` stop each other's services, and it stops with SteamVR too.
|
||||
|
||||
```
|
||||
ft-handsctl on | off # on the Frame: start or stop hand tracking (SteamVR must be running)
|
||||
ft-handsctl status # the services, and ft-hands' last status lines
|
||||
@@ -21,6 +23,8 @@ ft-handsctl log [lines]
|
||||
ft-handsctl cutouts on|off|state # ft-screens' hand cutouts, without stopping tracking
|
||||
ft-handsctl gestures # pinches and grips, live (tools/watch_gestures.py --distance)
|
||||
|
||||
hands/ft-cutouts on | off | status # the cutouts only: no pinches or grips
|
||||
|
||||
hands/run.sh install # build, give ft-camd its capabilities (sudo, once per build), install disabled
|
||||
hands/run.sh start|stop # start or stop the services
|
||||
hands/run.sh restart # after changing a setting
|
||||
@@ -104,6 +108,7 @@ Options:
|
||||
- `--models DIR`: where the models are.
|
||||
- `--nice N`: niceness. Default 5, so the VR stack wins contested CPUs.
|
||||
- `--no-publish`: don't write the hands and gestures files.
|
||||
- `--no-gestures`: hands for the cutouts only. No pinch or grip detection, so nothing reaches the pointer and a closing hand doesn't raise the rate to 30 Hz. The gestures file is removed at start. `ft-cutouts` runs it this way.
|
||||
- `--record DIR`, `--record-for S`: save every frame set for S seconds (default 120) to `DIR/sets.bin`. That's about 80 MB/s. Sending the tracker SIGUSR1 (`pkill -USR1 -x ft-hands`) starts a recording in `~/.local/share/frametop/hands/rec-<time>` without a restart. Recordings are images of your hands and room: they stay on the headset unless you move them.
|
||||
- `--record-only`: record without tracking or publishing, so it can run beside the live tracker. Give it `--record DIR`, since SIGUSR1 would reach both trackers. With `ft-camd --with-dark`, recordings also hold each camera's newest dark frame as `<name>_dk`, which doubles the rate. With `--with-color`, each colour camera's newest frame is saved with every set, as `color_video<N>`, which adds about 70 MB/s. Run the recorder at normal I/O priority: idle I/O priority stalled a 165 MB/s recording.
|
||||
- `--keep-presence P`: the landmark presence a tracked view needs to stay tracked. New views always need 0.5. Default 0.5. Lowering it to 0.2 barely helped in the bright recording, because lost hands drop to near-zero presence.
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
# ft-cutouts: the hand cutouts without pinches and grips. Your hands show through Frametop's
|
||||
# screens, but ft-hands runs with --no-gestures, so nothing clicks or drags. ft-handsctl on is
|
||||
# the full version; each stops the other. Like it, this doesn't start with SteamVR, and it
|
||||
# stops when SteamVR does. On the Frame.
|
||||
#
|
||||
# ft-cutouts on | off | status
|
||||
#
|
||||
# Runs this checkout's hands/build as transient user units, so it doesn't need hands/run.sh
|
||||
# install: only a build (hands/build.sh) and ft-camd's capabilities (hands/run.sh caps).
|
||||
set -euo pipefail
|
||||
here=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)
|
||||
camd=frametop-cutouts-camd.service hands=frametop-cutouts-hands.service
|
||||
|
||||
die() { echo "$*" >&2; exit 1; }
|
||||
|
||||
run_unit() { # unit, the ft-handsctl unit it replaces, description, then the command
|
||||
local unit=$1 conflicts=$2 what=$3
|
||||
shift 3
|
||||
systemctl --user -q is-active "$unit" && return
|
||||
systemd-run --user --quiet --collect --unit="$unit" --description="Frametop hand cutouts: $what" \
|
||||
-p PartOf=steamvr.service -p After=steamvr.service -p Conflicts="$conflicts" \
|
||||
-p Restart=always -p RestartSec=5 -p TimeoutStopSec=5 "$@"
|
||||
}
|
||||
|
||||
case ${1:-status} in
|
||||
on)
|
||||
systemctl --user -q is-active steamvr.service || die "SteamVR isn't running"
|
||||
[ -x "$here/build/ft-camd" ] && [ -x "$here/build/ft-hands" ] || die "not built: hands/build.sh"
|
||||
grep -qa -- --no-gestures "$here/build/ft-hands" || die "ft-hands predates --no-gestures: hands/build.sh"
|
||||
getcap "$here/build/ft-camd" | grep -q cap_sys_ptrace ||
|
||||
die "ft-camd needs its capabilities: hands/run.sh caps (asks for sudo)"
|
||||
if systemctl --user -q is-active frametop-camd.service frametop-hands.service; then
|
||||
echo "stopping ft-handsctl's hand tracking (it has pinches and grips)"
|
||||
fi
|
||||
run_unit $camd frametop-camd.service "the camera broker" "$here/build/ft-camd" --status 60
|
||||
if ! systemctl --user -q is-active $hands; then
|
||||
"$here/../scripts/container-up.sh" # so stopping the unit can't take the container down
|
||||
pkill -x ft-hands || true
|
||||
fi
|
||||
run_unit $hands frametop-hands.service "hand tracking, no gestures" "$HOME/.local/bin/distrobox" enter dev -- \
|
||||
"$here/build/ft-hands" --status 60 --no-gestures
|
||||
"$here/ft-handsctl" cutouts on >/dev/null || true
|
||||
sleep 4
|
||||
"$0" status ;;
|
||||
off)
|
||||
systemctl --user stop $hands $camd 2>/dev/null || true
|
||||
systemctl --user -q is-active frametop-hands.service || pkill -x ft-hands || true
|
||||
echo "hand cutouts off" ;;
|
||||
status)
|
||||
for u in $camd $hands; do echo "$u: $(systemctl --user is-active $u || true)"; done
|
||||
echo "ft-screens cutouts: $("$here/ft-handsctl" cutouts state 2>&1)"
|
||||
journalctl --user -u $hands --no-pager -o cat -n 40 |
|
||||
grep -E '^ *[0-9.]+s |sets with a hand|cameras:' | tail -2 | cut -c1-160 || true ;;
|
||||
*) sed -n '2,10p' "$0" | sed 's/^# \{0,1\}//'; exit 2 ;;
|
||||
esac
|
||||
+32
-20
@@ -3,9 +3,12 @@
|
||||
// Python prototype: the same scheduling, with the models on a few threads.
|
||||
//
|
||||
// ft-hands [--seconds N] [--threads N] [--int8] [--status S] [--models DIR] [--nice N]
|
||||
// [--no-publish] [--record DIR] [--swap-sides] [--cams auto|mono|color|all] ...
|
||||
// [--no-publish] [--no-gestures] [--record DIR] [--swap-sides] [--cams auto|mono|color|all] ...
|
||||
// (--help lists them all)
|
||||
//
|
||||
// --no-gestures: hands for the cutouts only. No pinch or grip detection, so nothing reaches
|
||||
// the pointer and a closing hand doesn't raise the rate; the gestures file is removed.
|
||||
//
|
||||
// Which cameras (--cams, HANDS_CAMERAS): the four mono IR cameras light the hands with their
|
||||
// own IR and track well in dim rooms, but in bright light (a sunny room, a window behind the
|
||||
// hands) they expose for the room and the hands come out dark. The two Arcturus colour
|
||||
@@ -150,7 +153,7 @@ struct Lighting {
|
||||
int main(int argc, char **argv) {
|
||||
double seconds = 0, status = 5;
|
||||
int threads = 3, niceness = 5;
|
||||
bool int8 = false, publish = true, track = true, swap_sides = false;
|
||||
bool int8 = false, publish = true, track = true, swap_sides = false, gestures_on = true;
|
||||
std::string models = std::string(argv[0]).substr(0, std::string(argv[0]).rfind('/') + 1) + "../models/ncnn";
|
||||
std::string record, ring_path = "/run/user/" + std::to_string(getuid()) + "/" FH_RING_NAME;
|
||||
// SteamOS starts user processes on CPUs 0-4 and keeps 5-7 (two A720s and the X4) for
|
||||
@@ -189,6 +192,7 @@ int main(int argc, char **argv) {
|
||||
else if (a == "--nice" && more) niceness = std::atoi(argv[++i]);
|
||||
else if (a == "--int8") int8 = true;
|
||||
else if (a == "--no-publish") publish = false;
|
||||
else if (a == "--no-gestures") gestures_on = false;
|
||||
else if (a == "--pinch-begin" && more) pinch_params.begin_m = std::atof(argv[++i]);
|
||||
else if (a == "--pinch-end" && more) pinch_params.end_m = std::atof(argv[++i]);
|
||||
else if (a == "--pinch-triangulated") pinch_params.triangulated = true;
|
||||
@@ -217,6 +221,7 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
else {
|
||||
std::printf("usage: %s [--seconds N] [--threads N] [--int8] [--status S] [--models DIR] [--nice N] [--no-publish]\n"
|
||||
" [--no-gestures] (hands for the cutouts only: no pinches or grips)\n"
|
||||
" [--record DIR] [--record-for S] [--record-only] [--cpus 5,6,7] [--swap-sides]\n"
|
||||
" [--keep-presence P] (0.5) [--ring PATH] (ft-camd's, or ft-ringplay's)\n"
|
||||
" [--cams auto|mono|color|all] (auto) [--bright all|color] (all) [--bright-on L] (40) [--bright-off L] (25)\n"
|
||||
@@ -265,12 +270,14 @@ int main(int argc, char **argv) {
|
||||
return true;
|
||||
};
|
||||
if (!load_calibration(calib, err) || !ring.open(ring_path.c_str(), err) || !nets.load(models, int8, err) ||
|
||||
(publish && (!pub.open(err) || !gestures.open(pinch, grip, err))) ||
|
||||
(publish && (!pub.open(err) || (gestures_on && !gestures.open(pinch, grip, err)))) ||
|
||||
(!record.empty() && !start_recording(record, err))) {
|
||||
std::fprintf(stderr, "%s\n", err.c_str());
|
||||
return 1;
|
||||
}
|
||||
if (!ring.alive()) return std::fprintf(stderr, "ft-camd isn't running (no heartbeat)\n"), 1;
|
||||
// so a reader can't take an earlier run's file for this one's
|
||||
if (publish && !gestures_on) unlink((run_dir() + "/gestures").c_str());
|
||||
|
||||
std::map<std::string, int> index; // mono calibration name -> ring camera
|
||||
std::map<std::string, int> color; // colour calibration name (color_video<N>) -> ring camera
|
||||
@@ -541,9 +548,11 @@ int main(int argc, char **argv) {
|
||||
const auto hands = tracker.step(images, int64_t(tmin));
|
||||
const uint64_t capture = uint64_t(int64_t(tmin) - raw_off); // CLOCK_MONOTONIC
|
||||
const std::vector<Seen> views = tracker.views_now();
|
||||
grip.update(hands, views, int64_t(capture));
|
||||
pinch.update(hands, views, int64_t(capture), grip.gripping());
|
||||
if (gesture_log && capture - t_glog >= 100'000'000) {
|
||||
if (gestures_on) {
|
||||
grip.update(hands, views, int64_t(capture));
|
||||
pinch.update(hands, views, int64_t(capture), grip.gripping());
|
||||
}
|
||||
if (gestures_on && gesture_log && capture - t_glog >= 100'000'000) {
|
||||
t_glog = capture;
|
||||
for (int k = 0; k < 2; ++k)
|
||||
if (pinch.world_d[k] >= 0)
|
||||
@@ -554,7 +563,8 @@ int main(int argc, char **argv) {
|
||||
// a gesture down or closing gets the full rate, even while the palm holds still
|
||||
next_ns = tmin + uint64_t((std::min(tracker.interval(), pinch.engaged() || grip.engaged() ? 1 / 30.0 : 1.0) -
|
||||
0.005) * 1e9);
|
||||
if (publish) pub.write(hands, capture), gestures.write(pinch, grip, capture);
|
||||
if (publish) pub.write(hands, capture);
|
||||
if (publish && gestures_on) gestures.write(pinch, grip, capture);
|
||||
for (const Pinch::Event &e : grip.events)
|
||||
std::printf("grip %s %-5s curl %.2f at %+.3f %+.3f %+.3f\n", e.side ? "right" : "left ", e.what, e.distance,
|
||||
e.point[0], e.point[1], e.point[2]);
|
||||
@@ -593,17 +603,19 @@ int main(int argc, char **argv) {
|
||||
s.handoff_miss, s.dups, s.splits, s.created, s.merged, s.forgotten,
|
||||
!rec ? "" : (" recorded " + std::to_string(rec->written()) + " dropped " +
|
||||
std::to_string(rec->dropped())).c_str());
|
||||
std::printf(" pinches: left %u right %u (held back, palm down: %d %d) grips: left %u right %u",
|
||||
pinch.side(0).begins, pinch.side(1).begins, pinch.held_back[0], pinch.held_back[1],
|
||||
grip.side(0).begins, grip.side(1).begins);
|
||||
for (int k = 0; k < 2; ++k)
|
||||
if (pinch.side(k).flags & FH_PINCH_TRACKED)
|
||||
std::printf(" %s %s d %.3f curl %.2f", k ? "right" : "left",
|
||||
grip.side(k).flags & FH_PINCH_DOWN ? "GRIP"
|
||||
: pinch.side(k).flags & FH_PINCH_DOWN ? "PINCH"
|
||||
: "open",
|
||||
pinch.side(k).distance, grip.curl[k]);
|
||||
std::printf("\n");
|
||||
if (gestures_on) {
|
||||
std::printf(" pinches: left %u right %u (held back, palm down: %d %d) grips: left %u right %u",
|
||||
pinch.side(0).begins, pinch.side(1).begins, pinch.held_back[0], pinch.held_back[1],
|
||||
grip.side(0).begins, grip.side(1).begins);
|
||||
for (int k = 0; k < 2; ++k)
|
||||
if (pinch.side(k).flags & FH_PINCH_TRACKED)
|
||||
std::printf(" %s %s d %.3f curl %.2f", k ? "right" : "left",
|
||||
grip.side(k).flags & FH_PINCH_DOWN ? "GRIP"
|
||||
: pinch.side(k).flags & FH_PINCH_DOWN ? "PINCH"
|
||||
: "open",
|
||||
pinch.side(k).distance, grip.curl[k]);
|
||||
std::printf("\n");
|
||||
}
|
||||
for (const Hand *h : hands)
|
||||
std::printf(" hand %d %-5s views %d wrist %+.3f %+.3f %+.3f m scale %.2f speed %.2f m/s\n", h->id,
|
||||
h->right() ? "right" : "left", h->nviews, h->pts[0][0], h->pts[0][1], h->pts[0][2], h->scale,
|
||||
@@ -616,8 +628,8 @@ int main(int argc, char **argv) {
|
||||
}
|
||||
}
|
||||
if (!color.empty()) unlink(want_file.c_str());
|
||||
if (publish) {
|
||||
pub.write({}, mono_ns());
|
||||
if (publish) pub.write({}, mono_ns());
|
||||
if (publish && gestures_on) {
|
||||
pinch.release(int64_t(mono_ns())); // a drag in progress ends, as lost
|
||||
grip.release(int64_t(mono_ns()));
|
||||
gestures.write(pinch, grip, mono_ns());
|
||||
|
||||
Reference in new issue
Block a user