Kontynuacja projektu nexarm_block_following (ten sam Jetson, ta sama
kamera Aurora930, to samo ramię NexArm), ale jako osobne repozytorium:
zamiast śledzić kolorowy klocek, ramię ma śledzić dłoń, wykrywaną siecią
YOLO, z realnym pomiarem odległości z kamery głębi (zamiast proxy z pola
powierzchni bboxa). Pełny plan i uzasadnienie decyzji: dokument planu w
projekcie na claude.ai ("NexArm — plan: śledzenie dłoni (YOLO) + realny
odczyt głębi").
Stage 1 (kamera + detekcja dłoni + odległość z głębi, bez ramienia) i stage 2 (manualne sterowanie ramieniem + chwytakiem z panelu WWW) — potwierdzone działające na sprzęcie.
Stage 3 (ten etap) dodaje autonomiczne śledzenie dłoni: regulator P
(app/servoing.py, HandFollower) czyta gotową detekcję + realny dystans
z ProcessingLoop (bez ponownego odpalania YOLO — patrz komentarz w
servoing.py) i sam wysyła mikroruchy do ramienia, próbując wyśrodkować
dłoń w kadrze i utrzymać stałą odległość (target_distance_mm). Pan/tilt
przeniesione ze wzoru BlockFollower z nexarm_block_following; regulator
dystansu przeliczony pod realną głębię w mm zamiast proxy z pola
powierzchni bboxa.
Bezpiecznik: śledzenie startuje WYŁĄCZONE przy każdym uruchomieniu
main.py, niezależnie od arm_backend. Włączasz je świadomie przyciskiem
"Śledzenie" w panelu WWW (obok podglądu, przy sterowaniu ręcznym). Każdy
ręczny jog albo powrót do pozycji bazowej automatycznie wyłącza śledzenie
z powrotem — ręczne i automatyczne komendy nigdy nie idą jednocześnie.
Detektor (app/detector.py) używa YOLOv8n przez ultralytics
(models/hand_yolov8n.pt, Bingsu/adetailer na Hugging Face) — zgodnie z
planem i celem "wykorzystać wydajność Nvidii na maksa". Pierwszy szkic
używał tymczasowo YOLOv4-tiny/Darknet przez cv2.dnn
(cross-hands-yolov4-tiny, projekt
cansik/yolo-hand-detection),
bo z sandboxa, w którym ten szkic powstał, nie dało się pobrać wag z
Hugging Face — te pliki (models/cross-hands-yolov4-tiny.*) zostały w
repo jako fallback/porównanie, ale nie są już domyślnie używane.
Do zrobienia przed pierwszym uruchomieniem tej wersji:
- Pobierz
hand_yolov8n.ptz https://huggingface.co/Bingsu/adetailer/blob/main/hand_yolov8n.pt (z Twojej sieci powinno być dostępne bez problemu) i zapisz jakomodels/hand_yolov8n.pt. - Zainstaluj/zaktualizuj zależności (
ultralyticsjest już wrequirements.txt) — koniecznie przeczytaj sekcję "Instalacja na Jetsonie" niżej, punkt o torch/CUDA, bo samopip install ultralyticsNIE da Ci przyspieszenia GPU. - Eksport do TensorRT (opcjonalnie, po potwierdzeniu że detekcja działa
na
.pt):model.export(format="engine")na samym Jetsonie — silnik TensorRT jest specyficzny dla konkretnego sprzętu/wersji TensorRT, więc eksportuj bezpośrednio na docelowym Jetsonie, nie gdzie indziej. Potem podmieńmodel_pathwconfig.yamlna wygenerowany plik.engine.
Ten sam co w nexarm_block_following:
- NVIDIA Jetson Orin Nano,
192.168.2.75. Potwierdzone na sprzęcie:/etc/nv_tegra_release→R39 (release), REVISION: 2.1= L4T R39.2.1 = JetPack 7.2.1, Ubuntu 24.04, Python 3.12.3. To bardzo świeży JetPack (Orin Nano + AGX Thor na wspólnej bazie L4T R39, premiera ok. połowy 2026) — nie standardowy JetPack 6.x (L4T 36.x, Ubuntu 22.04/Python 3.10), z którym większość poradników w sieci wciąż jest pisana. To ma znaczenie przy instalacji torch/CUDA, patrz niżej. - Kamera Hiwonder Aurora930 (Deptrum, structured light), eye-in-hand.
- Ramię Hiwonder NexArm — sterowanie manualne z panelu WWW od stage 2
(patrz "Sterowanie ramieniem" niżej). SDK (
app/nexarm_sdk.py) przeniesiony bez zmian znexarm_block_following(to samo ramię, protokół UART,/dev/ttyCH341USB0, 1 000 000 bps).
Aurora930 --(Deptrum Stream SDK)--> aurora_bridge (podproces C++) --(stdout: ramki RGB + głębi)-->
app/camera.py --(BGR + mapa głębi mm)--> app/detector.py (YOLO) --(bbox dłoni + pewność)-->
app/web_server.py (Flask: pomiar odległości pod bboxem, MJPEG + status) --> panel WWW
aurora_bridge.cc to rozszerzona wersja mostka z projektu klocka: oprócz
strumienia RGB otwiera (jeśli sprzęt to wspiera w wybranym trybie) strumień
głębi wyrównanej do RGB (Aurora900::SwitchAlignedMode(true)), żeby bbox
dłoni policzony na obrazie RGB można było bezpośrednio nałożyć na mapę
głębi. Kod obsługuje trzy warianty w zależności od tego, co kamera
faktycznie zgłosi na tym sprzęcie (nieznane, dopóki nie uruchomisz
--list-modes — patrz niżej): jeden połączony strumień kRgbd, dwa
oddzielne strumienie kRgb+kDepth, albo — jeśli głębia w ogóle
niedostępna w żadnym trybie — fallback do samego RGB (wtedy panel WWW po
prostu nie pokaże odległości, reszta działa normalnie).
Nie jest częścią repo (licencja) — skopiuj z projektu klocka, patrz
aurora_bridge/SDK_DEPTRUM.md.
sudo apt-get update && sudo apt-get install -y python3-pip
pip3 install --break-system-packages -r requirements.txt(Ubuntu 24.04 wymaga --break-system-packages — PEP 668. Na Ubuntu 22.04
zwykle nie jest to konieczne.)
To zainstaluje ultralytics, który po drodze pociągnie zwykły torch z
PyPI — a ten na Jetsonie nie ma wsparcia CUDA (inna architektura niż
desktop/serwer x86, Jetson ma własny stos CUDA/cuDNN/TensorRT wbudowany w
L4T/JetPack). Bez kolejnego kroku detektor będzie działał, ale wyłącznie
na CPU — czyli dokładnie NIE to, po co kupiłeś Jetsona zamiast RPI5.
Krok 2b — podmień torch na wersję z CUDA dla Jetsona (JetPack 7.2.1 / L4T R39.2.1, Python 3.12, CUDA 13.2 — potwierdzone na tym sprzęcie):
JetPack 7 jest na tyle świeży, że nie ma jeszcze dojrzałych, powszechnie
sprawdzonych paczek pod Jetsona z tej wersji (stare wheele
Ultralytics/Jetson AI Lab dla JetPack 6 są zbudowane pod Python 3.10 —
cp310 — i nie zainstalują się pod Twoim 3.12/cp312). Do wypróbowania,
w tej kolejności:
- Oficjalny indeks PyTorcha, prerelease dla CUDA 13.2 (najbardziej
wiarygodne źródło, bo to sam PyTorch, nie build community):
pip3 install --break-system-packages --pre torch torchvision \ --extra-index-url https://download.pytorch.org/whl/cu132
- Jeśli krok 1 nie znajdzie pasującego wheela (
cp312+aarch64+CUDA 13.2 bywa wąskim przekrojem) — alternatywa to nowy, ujednolicony indeks Jetson AI Lab dla architektury SBSA (od Thor Jetsony korzystają ze standardowego ARM Server-Base-System-Architecture, więc wheele nie są już per-JetPack tylko wspólne):Może wymagać doinstalowaniapip3 install --break-system-packages torch torchvision \ --extra-index-url https://pypi.jetson-ai-lab.io/sbsa/cu130
nvpl-blas,nvpl-lapack,nvidia-cudss-cu13jako dodatkowych zależności, jeśli import torch się wywali na brakującej bibliotece. - Zweryfikuj, że CUDA faktycznie widoczna:
Musi wypisać
python3 -c "import torch; print(torch.__version__, torch.cuda.is_available())"True. JeśliFalsemimo wykonania kroku 1/2, albo żaden z wheeli się nie zainstaluje — wklej mi pełny błąd, to bardzo świeży teren (JetPack 7 miał premierę dopiero w 2026) i część powyższego może wymagać korekty pod Twoją dokładną kombinację wersji CUDA/sterowników. main.pyi tak wystartuje nawet jeśli ten krok pominiesz albo się nie uda —app/detector.pyprzy starcie loguje, na jakim urządzeniu faktycznie pracuje ("CPU" albo numer GPU), więc zawsze widać stan faktyczny bez zgadywania.dusty-nv/jetson-containers(Docker, alternatywne podejście) na razie nie wspiera jeszcze JetPack 7 — to nie jest opcja awaryjna na dziś.
cd aurora_bridge
bash build.sh./build/aurora_bridge --list-modesTo pokaże listę trybów (rgb=... depth=...) oraz surowe wartości
StreamType, jakie zgłasza kamera — to jest kluczowy, jeszcze
niezweryfikowany punkt tego etapu: czy kRgbd (połączony strumień) jest
wspierany, czy trzeba dwóch oddzielnych strumieni, czy któryś tryb w ogóle
ma depth != invalid. Jeśli żaden tryb nie ma głębi, aplikacja i tak
zadziała (samo RGB + detekcja), tylko bez odczytu odległości — do
zdiagnozowania dopiero na sprzęcie.
python3 main.pyOtwórz http://192.168.2.75:8080 z dowolnego urządzenia w tej samej sieci.
Zobaczysz: podgląd kamery z bboxem dłoni (jeśli wykryta), pewność detekcji
w %, oraz odległość w mm pod bboxem (jeśli strumień głębi zadziałał).
config.yaml → arm.arm_backend ma dwie wartości:
mock— nic fizycznie się nie rusza. Panel WWW, wszystkie przyciski/suwaki i telemetria działają normalnie, ale sterują tylko symulacją w pamięci (MockArmController). Użyj tego trybu, żeby sprawdzić cały panel (layout, suwaki, odczyty) zanim podłączysz prawdziwe ramię.hiwonder(domyślnie od momentu potwierdzenia ręcznego sterowania na sprzęcie) — prawdziwe sterowanie przez UART (app/nexarm_sdk.py,HiwonderArmController). Jeśli kiedyś znów potrzebujesz bezpiecznego trybu bez fizycznego ruchu (np. testując nowy kod zdalnie), przełącz z powrotem namockwconfig.yaml.
Wartości axis_sign/claw_sign w config.yaml są przeniesione z już
fizycznie zweryfikowanych wartości w nexarm_block_following (to samo
ramię/Jetson), więc powinny być od razu poprawne — ale to nowy kod
(nowa ścieżka sterowania), nie ten sam plik co tam. Przy pierwszym teście
po przejściu na hiwonder:
- Zacznij od małych kroków — suwak "krok ręcznego ruchu" (
move_duration_msw panelu "Ramię — kalibracja") na niskiej wartości. - Miej rękę w pobliżu wyłącznika/zasilania ramienia.
- Sprawdź najpierw pojedyncze naciśnięcia pada pan/tilt/dystans osobno, zanim użyjesz suwaka pozycji chwytaka.
- Jeśli ramię rusza się w złą stronę po naciśnięciu konkretnego
kierunku — odwróć znak (
1.0↔-1.0) odpowiedniej osi waxis_sign/claw_signwconfig.yamli zrestartujmain.py.
Zakres chwytaka (claw_min/claw_max w config.yaml, domyślnie
−150…150) NIE jest fizycznie zweryfikowany w tym projekcie (manual
Hiwondera nie podaje liczbowego zakresu dla naszego SDK) — testuj suwak
absolutnej pozycji chwytaka ostrożnie, małymi krokami, obserwując ramię.
Duży czerwony przycisk na samej górze panelu (przyklejony do góry strony, zawsze widoczny) — kliknięcie ALBO wciśnięcie spacji (działa niezależnie od tego, co jest aktualnie w fokusie na stronie) natychmiast:
- wyłącza autonomiczne śledzenie, jeśli było włączone,
- blokuje ramię na WSZYSTKIE kolejne komendy ruchu (ręczne i automatyczne) — dopóki świadomie nie klikniesz "Wznów pracę ramienia", które się wtedy pojawia obok.
Ważne ograniczenie, żeby nie było złudzeń: to NIE jest twardy,
natychmiastowy stop w trakcie już wykonywanego ruchu — SDK NexArm po UART
nie ma komendy przerywającej wysłaną już komendę set_pose/
move_increment. Pojedynczy mikroruch (domyślnie move_duration_ms =
200 ms) dokończy się na ESP32, zanim zadziała blokada. W praktyce, przy
ruchu ciągłym (śledzenie automatyczne albo trzymanie przycisku jog), to
efektywnie natychmiastowe zatrzymanie — kolejny mikroruch po prostu nigdy
nie zostanie wysłany. Jeśli kiedykolwiek potrzebujesz GWARANTOWANEGO
zatrzymania w trakcie pojedynczego ruchu — to wymaga fizycznego
wyłącznika zasilania ramienia, nie da się tego zrobić samym UART z tym
SDK. Szczegóły implementacji: docstring app/arm_controller.py, sekcja
"EMERGENCY STOP".
Layout: przycisk EMERGENCY STOP na samej górze (przyklejony do góry strony), poniżej obiekt kontroli ramienia obok okna podglądu (pad pan/tilt, przyciski dystansu, powrót do home, przełącznik śledzenia, wskazówka trybu backendu), poniżej reszta paneli w trzech kolumnach (jedna kolumna na szerokich ekranach poniżej 900px):
- Status ogólny — połączenie kamery/ramienia, wiek ostatniej klatki, FPS przetwarzania, czy dłoń wykryta.
- Detekcja dłoni (YOLOv8n) —
confidence_threshold/nms_thresholdjako suwaki, zmiana na żywo bez restartu. - Kamera / głębia (debug) — rozdzielczość RGB, czy strumień głębi
dostępny, jego wiek,
mode_index,swap_rb,flip_horizontal. - Ramię — telemetria — bieżąca pozycja (x/y/z/pitch/roll/claw), surowe pozycje serw, wersja firmware, błąd ostatniego polecenia, przycisk odczytu napięcia baterii (na żądanie, nie w każdym cyklu odświeżania — dodatkowy round-trip po porcie szeregowym).
- Ramię — kalibracja — podgląd
axis_map/axis_sign/claw_signzconfig.yaml(tylko odczyt — do zmiany trzeba edytować plik i zrestartować), suwakmove_duration_ms(czas trwania mikroruchu, edytowalny na żywo). - Chwytak (dłoń robota) — przyciski otwórz/zamknij (ruch względny,
jak pad pan/tilt) ORAZ suwak pozycji absolutnej (wysyła bezpośrednio
wartość docelową, nie deltę) w zakresie
claw_min…claw_max.
POST /api/arm/home— powrót dohome_posezconfig.yaml.POST /api/arm/jog— ruch względny:{dpan, dtilt, ddist, dclaw}(wszystko delty, jak stary panel klocka).POST /api/arm/claw— pozycja chwytaka ABSOLUTNA:{"value": ...}. W przeciwieństwie dodclaww/api/arm/jog, to nie jest delta — SDK nie ma osobnej komendy "tylko chwytak", więcset_claw()liczy różnicę względem wewnętrznie śledzonej pozycji chwytaka i wysyła ją jakomove_increment()z zerową deltą na x/y/z/pitch/roll (patrz docstringapp/arm_controller.py— wcześniejsza wersja przezset_pose()z odczytaną pozycją potrafiła nieoczekiwanie ruszyć całym ramieniem, jeśli firmware nie zwrócił pitch/roll w odpowiedzi).POST /api/arm/battery— odczyt napięcia baterii na żądanie.POST /api/tracking— włącz/wyłącz autonomiczne śledzenie dłoni:{"enabled": true|false}(patrz sekcja "Autonomiczne śledzenie dłoni" niżej).POST /api/arm/jog,POST /api/arm/home— jak wyżej, ale teraz też automatycznie wyłączają śledzenie, jeśli było włączone (odpowiedź niesietracking_disabled: true/false, UI synchronizuje po tym przełącznik).GET/POST /api/params— rozszerzone oarm.move_duration_msi sekcjęservoing(parametry regulatora P śledzenia) obok istniejących progów detekcji.POST /api/emergency_stop— EMERGENCY STOP (patrz sekcja niżej): wyłącza śledzenie i blokuje ramię na kolejne komendy ruchu.POST /api/emergency_clear— świadome wznowienie po emergency stop./api/statusniesie stanarm.estopped(true/false).
Regulator P (app/servoing.py, HandFollower) — pan/tilt centruje dłoń w
kadrze, regulator dystansu utrzymuje target_distance_mm (realny pomiar z
kamery głębi, nie proxy z pola bboxa jak w nexarm_block_following).
Panel WWW: przycisk "Śledzenie" obok sterowania ręcznego (włącza/wyłącza),
oraz panel "Śledzenie dłoni (auto)" w trzech kolumnach z podglądem stanu
(dystans surowy i wygładzony, błąd dystansu, ostatni wysłany mikroruch) i
suwakami do strojenia na żywo: target_distance_mm, distance_deadband_mm,
center_deadband_px, kp_pan, kp_tilt, kp_distance, max_step_mm,
distance_smoothing_alpha (wygładzanie EMA sygnału dystansu — patrz sekcja
niżej "Śledzenie dystansu 'nie reaguje'..."), min_safe_distance_mm i
max_plausible_jump_mm (bezpiecznik minimalnego dystansu — patrz sekcja
niżej "
Bezpiecznik: śledzenie startuje WYŁĄCZONE przy każdym uruchomieniu
main.py — włączasz świadomie przyciskiem w panelu, PO sprawdzeniu, że
ręczne sterowanie działa poprawnie (dobre znaki osi, sensowny zakres
chwytaka). Każdy ręczny jog/home automatycznie je wyłącza z powrotem.
Naprawiony błąd: dystans nie reagował wcale, pan/tilt tak. Przyczyna:
SDK koduje ruch jako całe milimetry (round() przed wysłaniem po UART w
nexarm_sdk.py). Pan/tilt praktycznie zawsze saturuje do max_step_mm
(≥1 mm po zaokrągleniu), więc zawsze coś realnie leci po drucie. Dystans, z
mniejszym kp_distance, potrafił wyliczyć np. 0.375 mm dla błędu tuż za
martwą strefą — round(0.375) = 0, czyli ramię dostawało komendę
zerowego ruchu i efektywnie nic nie robiło na tej osi, mimo że regulator
"chciał" się ruszyć. Naprawione: app/servoing.py gwarantuje teraz co
najmniej 1 mm (najmniejszy krok, jaki protokół w ogóle umie przekazać) za
każdym razem, gdy regulator jest poza martwą strefą, niezależnie od tego,
jak mały wyszedłby wynik z samego mnożenia przez kp_distance. Przy okazji
podniosłem domyślne kp_distance z 0.015 na 0.02, żeby średnie błędy nie
były zawsze przypięte do tego minimum.
Strojenie na pierwszy test:
- Zacznij z domyślnymi wzmocnieniami z
config.yaml(servoing:) — przeniesione strukturalnie znexarm_block_following, alekp_distancejest NOWĄ wartością (przeliczoną pod mm zamiast pod px² pola powierzchni) — nie ma gwarancji, że są od razu dobrze dobrane na Twoim sprzęcie. - Włącz śledzenie z dłonią w spokojnej, znanej pozycji, obserwuj —
jeśli ramię "goni" zbyt wolno, zwiększaj
kp_pan/kp_tilt/kp_distancestopniowo; jeśli oscyluje/drga, zmniejszaj. max_step_mmto twardy limit pojedynczego mikroruchu — trzymaj nisko (domyślnie 3 mm) podczas pierwszych testów, niezależnie od wzmocnień.target_distance_mmustaw na wygodny dla Ciebie dystans trzymania ręki od kamery — regulator będzie do niego "dociągał" ramię.
Zgłoszony objaw: niezależnie od kp_distance (nawet na maksa), regulator
dystansu w większości przypadków się nie rusza, a pole "Błąd dystansu" w
panelu potrafi skoczyć do ok. ±1000mm, mimo że "Odległość" w panelu "Status
ogólny" wygląda stabilnie. To NIE był problem strojenia kp — dwie
osobne przyczyny, obie naprawione w kodzie:
- "Zamrożony" odczyt błędu dystansu (
app/servoing.py) — gdy dłoń na chwilę znika z kadru albo śledzenie jest wyłączone, pole "Ostatni ruch" zerowało się poprawnie, ale "Błąd dystansu" zostawało zamrożone na wartości z ostatniej udanej detekcji — czyli mogło pokazywać liczbę policzoną dla zupełnie innej pozycji dłoni niż ta widoczna właśnie na podglądzie. Teraz oba pola resetują się razem. - Pojedyncze zaszumione klatki pomiaru głębi trafiały SUROWE do
regulatora. "Odległość" w panelu odświeża się szybko (~9 klatek/s),
więc pojedynczy skok jest dla oka praktycznie niewidoczny — ale wpadał
nieprzetworzony do wzoru błędu, dając skokowe
ddist(stąd wrażenie "nie reaguje": błąd regulatora migał między dodatnim a ujemnym zamiast płynnie przechodzić przez martwą strefę). Dwie niezależne warstwy naprawy:app/camera.py,get_depth_roi_mean(): dłoń nie jest prostokątem, więc narożniki bboxa z YOLO regularnie łapały kawałek tła za dłonią — teraz próbkujemy tylko wyśrodkowany fragment bboxa (60%) i liczymy MEDIANĘ zamiast średniej (odporna na mniejszościowe wartości odstające, w przeciwieństwie do średniej, którą potrafi zepsuć nawet kilka pikseli tła).app/servoing.py: nowy parametrdistance_smoothing_alpha(config.yaml→servoing:, domyślnie 0.3) wygładza dystans wykładniczo (EMA) PRZED policzeniem błędu regulatora — surowy odczyt nadal widać osobno w "Odległość" (bez zmian), a nowe pole "Dystans (wygładzony)" w panelu "Śledzenie dłoni (auto)" pokazuje, co faktycznie widzi regulator. 1.0 = brak wygładzania (zachowanie jak przed tą zmianą); jeśli po tej zmianie regulator dalej wydaje się "szarpać", obniż to jeszcze (np. do 0.15–0.2) suwakiem w panelu — działa na żywo, bez restartu.
Ważne zastrzeżenie: te poprawki są oparte na analizie kodu (nie mam
dostępu do Twojego sprzętu, żeby to zweryfikować na żywo) — są to
principled fixes pod konkretne, prawdopodobne przyczyny zgłoszonych
objawów, nie potwierdzony na 100% root cause. Po zaktualizowaniu, obserwuj
nowe pole "Dystans (wygładzony)" obok "Błąd dystansu": jeśli po tej
zmianie nadal widzisz nagłe skoki rzędu setek/tysięcy mm w "Odległość"
(surowej, nie wygładzonej) — to znak, że sam pomiar głębi z kamery
(aurora_bridge) miewa poważniejsze, jednorazowe błędy odczytu, nie tylko
kontaminację tła w rogach bboxa, i wymaga dalszej diagnozy (np. logowanie
histogramu wartości w ROI). Daj znać, jak zachowa się po tej zmianie.
Zgłoszony objaw: przy bardzo bliskiej dłoni i wysoko ustawionym
target_distance_mm, ramię czasem jechało do przodu i dotykało ręki —
nie za każdym razem, więc niereprodukowalne w 100%. To realny problem
bezpieczeństwa, więc potraktowałem go priorytetowo.
Sprawdziłem cały łańcuch znaków algebraicznie (błąd regulatora →
ddist → axis_sign → dx wysyłane po UART) i wychodzi on spójny:
dla dłoni bliżej niż target_distance_mm, regulator poprawnie liczy
ujemne ddist, co przy niezmienionym axis_sign["distance"]
(przeniesionym z już fizycznie zweryfikowanego nexarm_block_following)
powinno oznaczać cofanie, nie dojazd do przodu. Innymi słowy — nie
znalazłem błędu w samej matematyce regulatora P, co zgadza się z Twoją
obserwacją, że samo dokręcanie/luzowanie kp czy wygładzanie nic tu nie
zmienia. Najbardziej prawdopodobne (ale NIEPOTWIERDZONE bez testu na
sprzęcie) wytłumaczenie: kamery structured-light typu Aurora930 mają
fizyczne minimum zasięgu — poniżej niego promiennik IR i odbiornik "widzą"
inny fragment sceny (okluzja paralaksy) i pomiar potrafi się zachować
nieprzewidywalnie, nie tylko zwracając 0 (już filtrowane), ale czasem
zwracając wiarygodnie wyglądającą, lecz BŁĘDNĄ dużą wartość, jakby dłoń
była daleko. To pasowałoby zarówno do "nie zawsze się powtarza" (zależy od
kąta/odbicia dłoni z bliska), jak i do Twojej własnej diagnozy ("coś nie
tak z przekazywaniem/konwersją danych o odległości").
Niezależnie od dokładnej przyczyny, dodałem twardy bezpiecznik
niezależny od reszty regulatora (app/servoing.py, docstring modułu,
punkt 5):
min_safe_distance_mm(domyślnie 150mm,config.yaml→servoing:) — jeśli SUROWY zmierzony dystans spadnie poniżej tej wartości, regulator ignorujetarget_distance_mmi cały P-controller i wymusza pełny krok cofnięcia w każdym cyklu, dopóki dłoń się nie oddali. Panel WWW pokazuje to na żywo jako "Bezpiecznik dystansu: AKTYWNY" (czerwony badge) w panelu "Śledzenie dłoni (auto)".max_plausible_jump_mm(domyślnie 150mm) — jeśli surowy dystans skoczy w górę (dalej) o więcej niż tyle w jednym cyklu (fizycznie niemożliwe dla ręki w ~100ms), a regulator na tej podstawie chciałby jechać do przodu — ta klatka jest ignorowana. Celowo asymetryczne: skok w dół (bliżej) nigdy nie jest filtrowany, bo cofanie jest zawsze bezpieczne.
min_safe_distance_mm=150mm JEST NIEZWERYFIKOWANYM
ZGADYWANIEM — nie znam offsetu kamera→chwytak na Twoim montażu
(eye-in-hand). Zanim zaufasz temu bezpiecznikowi blisko dłoni:
- Trzymaj rękę blisko wyłącznika zasilania ramienia.
- Powoli przybliżaj dłoń do ramienia (ręcznie, bez włączonego śledzenia automatycznego), obserwując w panelu "Status ogólny" pole "Odległość".
- Zanotuj, przy jakiej wartości chwytak faktycznie dotyka/prawie dotyka dłoni.
- Ustaw
min_safe_distance_mmw panelu (suwak w "Śledzenie dłoni (auto)") albo wconfig.yamlna liczbę wyraźnie wyższą niż to, z zapasem bezpieczeństwa (np. 1.5–2×).
To jest DODATKOWA warstwa NAD enabled/EMERGENCY STOP, nie zamiast nich —
oba nadal w pełni obowiązują i mają pierwszeństwo (patrz sekcja "Przycisk
EMERGENCY STOP" wyżej, łącznie z jej ograniczeniem co do już wysłanej
komendy).
Większość błędów SDK/USB/ABI (crash przy starcie, Device::Open()
zwraca ret=1, błędy pip3) jest identyczna jak w nexarm_block_following
— patrz tamto README, sekcja "Rozwiązywanie problemów", te same przyczyny i
poprawki dotyczą tego projektu.
Nowe, specyficzne dla tego etapu:
- Brak odczytu odległości / głębi — na tym sprzęcie pole
depthw trybach z--list-modeszawsze pokazujeinvalid, mimo że kamera faktycznie wspiera głębię (GetSupportedStreamTypeto potwierdza) — to już uwzględnione, kod nie blokuje się na tym polu. Były już dwie poprawki waurora_bridge.cc: (1)SwitchAlignedMode(true)musi być wywołane PO udanymSetMode(), nie zaraz poOpen()(SDK tego pilnuje i inaczej zwraca błąd), (2) wybór trybu/strumienia nie patrzy już na niewiarygodne poledepthzGetSupportedFrameMode(). Jeśli mimo to nadal brak odległości — upewnij się najpierw, żeaurora_bridgezostał PRZEBUDOWANY po tych zmianach (cd aurora_bridge && bash build.sh— sama podmiana pliku.ccnic nie da bez rebuildu), a potem sprawdź na stderrpython3 main.py(linie z prefiksemaurora_bridge:) który wariant strumienia został wybrany — "POLACZONY strumień kRgbd", "DWA oddzielne strumienie" czy fallback do samego RGB — oraz czy w logu Pythona pojawia się ostrzeżenie "Rozmiar ramki głębi... nie zgadza się" (oznaczałoby to, że payload głębi ma inny format niż zakładane 16-bit/piksel w mm). - Rozmiar ramki głębi nie zgadza się z
cols*rows*2(log wapp/camera.py: "Rozmiar ramki głębi... nie zgadza się") — założenie o 16-bit/piksel w milimetrach z nagłówków SDK może nie pasować do tego, co faktycznie wysyła ten sprzęt/tryb; wymaga sprawdzenia realnego rozmiaru payloadu na żywo (dodaj tymczasowy log w_decode_depth) i poprawki interpretacji formatu. - Detekcja dłoni słaba/niestabilna (pewność, rozmiar bboxa z bliska) —
te objawy były zaobserwowane z poprzednim modelem (YOLOv4-tiny). Nowy
hand_yolov8n.ptma inną architekturę (anchor-free head), która generalnie lepiej radzi sobie ze zmienną skalą obiektu (dłoń wypełniająca cały kadr z bliska) — ale to trzeba zweryfikować na sprzęcie, nie zakładać z góry. Jeśli po przejściu na YOLOv8n nadal słabo:- Dostrój
confidence_threshold/nms_thresholdwconfig.yaml(start: 0.5/0.3) — obniż próg pewności, jeśli detektor jest zbyt "nieśmiały" i traci dłoń, podnieś jeśli łapie fałszywe wykrycia. - Sprawdź, czy detektor faktycznie pracuje na GPU (log przy starcie
main.py) — CPU nie tylko wolniej liczy, ale u niektórych buildów ultralytics/torch bywa mniej dokładny numerycznie niż ścieżka CUDA (rzadkie, ale warto to wykluczyć, jeśli coś jest nie tak). - Wyższa rozdzielczość kamery — spróbuj
mode_index: 1albo2wconfig.yaml(sprawdź dokładne rozdzielczości przez--list-modesna Jetsonie). Więcej pikseli źródłowych na dłoni przed przeskalowaniem do wejścia sieci (640×640) może pomóc, bez gwarancji.
- Dostrój
Projekt jest w repozytorium git (lokalnie zainicjowanym w tym katalogu).
Duże pliki binarne (wagi modeli models/*.pt / models/*.weights,
zbudowany aurora_bridge/build/, itp.) są celowo wykluczone przez
.gitignore — pobierz je ręcznie wg sekcji "Instalacja na Jetsonie"
wyżej.
To repozytorium powstało lokalnie (na tym Macu, nie w chmurowej
sandboksie Claude) — założenie konta zdalnego i pierwszy push trzeba
wykonać z własnego terminala, tam gdzie masz zalogowane gh/klucze SSH:
cd ~/PycharmProjects/nexarm_hand_tracking
gh repo create radoslawo81/nexarm-hand-tracking --public --source=. --remote=origin --push
git push origin v1.0.0 # wypchnij też tag wydania(Konwencja nazewnictwa i konto — radoslawo81 — jak w siostrzanym
repozytorium nexarm-block-following. Zmień --public na --private,
jeśli wolisz repo prywatne.)
scripts/make_release.sh buduje archiwum .tar.gz z aktualnego stanu
repozytorium przez git archive — czyli tylko pliki śledzone przez
git, automatycznie z pominięciem wszystkiego z .gitignore (modele,
__pycache__, .venv, dist/ itd.). Wynik trafia do dist/ (katalog
sam jest zignorowany przez git, więc nie zaśmieca repo).
# Zbuduj paczkę dla wskazanej wersji (z aktualnego HEAD):
scripts/make_release.sh v1.0.0
# Albo dla konkretnego tagu/gałęzi:
scripts/make_release.sh v1.0.0 v1.0.0Wynik: dist/nexarm-hand-tracking-v1.0.0.tar.gz + suma kontrolna
.sha256 obok. Skrypt na końcu wypisuje gotowe polecenia do otagowania
i utworzenia wydania na GitHubie.
# Jeśli tag jeszcze nie istnieje:
git tag -a v1.0.0 -m "Wydanie v1.0.0"
git push origin v1.0.0
# Zbuduj paczkę (patrz wyżej), a potem:
gh release create v1.0.0 dist/nexarm-hand-tracking-v1.0.0.tar.gz \
--title "v1.0.0" --generate-notesAktualny stan repozytorium (ten commit) jest już otagowany jako
v1.0.0 — to punkt zamknięcia etapu 3 (kamera + detekcja + głębia,
sterowanie ręczne, autonomiczne śledzenie P, EMERGENCY STOP, bezpiecznik
minimalnego dystansu). Kolejne wydania: podnieś numer wersji (np.
v1.1.0 dla nowych funkcji, v1.0.1 dla poprawek) i powtórz powyższe
kroki.
- Eksport
hand_yolov8n.ptdo TensorRT (.engine) na Jetsonie, po potwierdzeniu że detekcja na.ptdziała dobrze (patrz wyżej) — nieblokujące, można zrobić w dowolnym momencie. - Strojenie regulatora P śledzenia na sprzęcie (patrz sekcja
"Autonomiczne śledzenie dłoni" wyżej) — wartości
kp_pan/kp_tilt/kp_distance/target_distance_mmwconfig.yamlsą startowe, niezweryfikowane fizycznie na tym konkretnym ramieniu/kamerze. - Jeśli po strojeniu regulator P nadal "goni się w kółko" albo za bardzo
spóźnia za szybkim ruchem dłoni — naturalny następny krok to dodanie
członu D (na pochodną błędu) do
HandFollower, tak jak było zasygnalizowane już w oryginalnymBlockFollower.