Oct 27 13:11 · AI content
Pico 4 Pro и Lighthouse без трекера на голове: порт QuestLHSync
Всё что ниже - это обработанный пересказ утренних изысканий относительно возможности избавиться от трекера на голове для синхронизации с Lighthouse девайсами в частности - базовыми станциями. Спасибо технологиям что позволяют набросать прототипы за пару часов, а не 1-2 недели плотных задротских технических вечеров. Спасибо.
Кто играет в VRChat с фулбоди на Lighthouse и шлемом не от Valve, знает эту боль: пространство трекеров и пространство шлема живут отдельно, и их приходится постоянно сводить. Обычно это SpaceCalibrator плюс трекер на голове или перекалибровка каждые полчаса. Для Quest уже есть QuestLHSync от CreoleVR: шлем сам видит базовые станции своими камерами и держит всё выровненным, без трекера на голове. Для Pico такого не было. Теперь есть: я портировал QuestLHSync на Pico 4 Pro. Ниже история целиком: как это устроено, на какие грабли наступил и как поставить себе.
⚠️ Сначала о рисках и ограничениях
- Нужен root (Magisk) на шлеме. Как я его получал, есть в прошлом посте про раздельные глаза: разблокировка загрузчика стирает все данные в шлеме.
- Порт экспериментальный. Проверен на одном шлеме: Pico 4 Pro, PICO OS 5.13.3S, стриминг через Virtual Desktop, три станции 2.0. На обычном Pico 4 и других прошивках не проверялся.
- Готовых сборок пока нет, собирать из исходников. Это несложно, команды ниже.
- Модуль на шлеме ничего не прошивает и не меняет в системе. Удаляется из Magisk одной кнопкой.
Как работает QuestLHSync
Базовые станции 2.0 крутят лазерные плоскости. Если в момент короткой экспозиции камеры шлема через неё проходит лазер, в кадре вспыхивает яркая точка. Направление на неё - это луч из камеры на станцию. Шлем знает свою позу, SteamVR знает, где стоят станции. Если набрать достаточно таких лучей, можно найти поворот и сдвиг, при которых все лучи попадают в станции. Это и есть выравнивание двух пространств, и драйвер применяет его ко всем Lighthouse-устройствам прямо на лету.
Устроено из двух частей:
- на шлеме: модуль Magisk. Читает кадры камер трекинга, ищет яркие точки и отдаёт их по сети (TCP 47280);
- на ПК: драйвер SteamVR. Принимает точки, решает задачу выравнивания и добавляет свою страницу в дашборд SteamVR.
Чтобы запустить это на Pico, нужно было переписать часть на шлеме под его камеры и научить драйвер понимать калибровку Pico. Дальше самое интересное.
Шаг 1. Найти камеры
У Pico 4 Pro четыре камеры трекинга OV9282: глобальный затвор, 640×480, 10 бит. Все кадры живут в системном процессе pvrtrackingservice, в кольце из 32 буферов dmabuf, по 8 на камеру. Камеры снимают 50 кадров в секунду, чередуя длинную и короткую экспозицию. Лазеры видны только в коротких.
Грабля №1: буферы не читаются снаружи. Даже из-под root /proc/pid/mem на этих областях отвечает ошибкой ввода-вывода. Пришлось делать как в оригинале для Quest: внедрять Frida прямо в процесс трекинга и искать точки там, на маленьком C-коде, скомпилированном на лету.
Грабля №2: двойные кадры. Сначала я видел около 80 кадров в секунду вместо 50: один и тот же кадр иногда засчитывался дважды. Оказалось, что в конце каждого буфера есть служебный «хвост» с номером кадра и точным временем экспозиции в наносекундах. По нему кадры идут ровно 50,0 в секунду. Заодно время каждого кадра теперь точное, без задержки опроса.
Грабля №3: какая камера какая. Калибровку Pico хранит в /mnt/vendor/persist/pvr/mav0/cam0..3/sensor.yaml (формат Kalibr). Но в каком порядке в памяти лежат буферы камер, неизвестно. Выяснял дедовским способом: закрывал камеры пальцами по одной и смотрел, какой поток темнеет. Порядок по ходу перепутал, но по логам всё равно восстановилось: 3,2,1,0.
Калибровку Pico в модели omni я пересчитываю в модель Fisheye62, которую понимает драйвер, с точностью 0,03–0,07 пикселя.
Шаг 2. Первый запуск и... «чёт жиденько вышло»
Всё подключилось: шлем нашёлся, четыре камеры отдают кадры, ярких точек больше тысячи в минуту. А захвата нет вообще. Драйвер записал сессию, и я разобрал её офлайн.
Грабля №4: 98% точек - это лампы. У меня на потолке два трековых LED-светильника, и короткие кадры Pico пересвечивают их так же, как лазер. Драйвер раз за разом принимал светильник за станцию. Отличить по размеру не получилось: лазерная вспышка у Pico крупная, 18-52 пикселя, но и лампа вблизи бывает такой же. Отличает другое: лампа видна почти в каждом кадре, а лазер мелькает в одиночных. Точки, которые держатся несколько кадров подряд, теперь считаются лампами.
Грабля №5: лазер ловится редко. Короткая экспозиция у Pico около 40 микросекунд, так что лазер попадает в неё лишь иногда: несколько раз в минуту на станцию. У Quest намного чаще. Пришлось подстроить драйвер:
- копить данные 5 минут вместо 1;
- использовать все четыре камеры, а не только верхние: нижние дают около 40% попаданий;
- принимать засветки и при быстрых поворотах головы: время кадров точное, так что это безопасно.
После этого захват пошёл: 1-2 минуты при выключенных лампах.
Шаг 3. Лампы наносят ответный удар
С включёнными светильниками в одной из сессий драйвер «захватил» неверное решение: поставил две станции на два светильника и несколько минут держал трекеры не там. Пара ламповых точек всё же просачивалась, когда светильник пропадал на кадр, а потом появлялся снова.
Лечение из двух частей:
- драйвер смотрит на два кадра вперёд: точка, которая начинает серию, тоже считается лампой;
- лампы больше не выбрасываются, а запоминаются. Если решение ставит станцию туда, куда указывают десятки ламповых лучей, такое решение отбрасывается.
Тонкость: у станции на канале 1 лазер иногда виден в соседних кадрах, так что у настоящей станции тоже бывают «постоянные» точки. Но их не больше чем в 1,6 раза больше, чем обычных, а у ламп - в 7-20 раз. На этом разрыве и держится проверка.
Шаг 4. Докручиваем точность
Когда всё заработало, осталась средняя ошибка около 0,5°. По семи записанным сессиям нашлись две систематические поправки, одинаковые для всех четырёх камер:
- ИК-лазер изображается на 0,58% ближе к центру кадра, чем предсказывает заводская калибровка. Скорее всего, камеры калибровали в другом свете, а линза преломляет инфракрасный чуть иначе. Похожая поправка в оригинале есть для боковых камер Quest.
- Картинка сдвинута ровно на одну строку относительно калибровки.
С ними средняя ошибка упала до 0,2-0,3°. Чтобы не обмануть себя подгонкой, я подбирал поправки по трём записям и проверял на четвёртой: каждый раз становилось лучше.
Что в итоге
- Захват станций: 1-3 минуты с нуля, около 3 минут при включённых лампах.
- После захвата выравнивание держится 10+ минут, гуляет в пределах ±1 см и ±0,15°.
- При перезапуске SteamVR сохранённое выравнивание подхватывается за секунды.
- Проверял в зеркале VRChat, в том числе в странных позах: ноги, таз и руки на месте, без трекера на голове и без SpaceCalibrator.
- Все семь записанных сессий проигрываются офлайн без единого ложного захвата.
Что понадобится
- Pico 4 Pro с root (Magisk);
- базовые станции SteamVR и хотя бы один включённый Lighthouse-трекер или контроллер (без них SteamVR не показывает станции);
- ПК с Windows и SteamVR, шлем стримится любым способом (у меня Virtual Desktop), шлем и ПК в одной сети;
- для сборки модуля: Python 3 и pip install ziglang;
- для сборки драйвера: Visual Studio 2022 или новее с C++;
- Git и adb.
Установка
Совет по PowerShell: у меня при вставке команд из браузера терялись заглавные буквы и знак $. Если команда странно падает, наберите её руками. Регистр в путях Windows неважен, можно всё маленькими.
1. Скачать исходники
git clone https://github.com/UndefinedSignal/QuestLHSync-pico4pro
cd QuestLHSync-pico4pro
git checkout pico-4-pro
2. Модуль для шлема
pip install ziglang
python pico/build_module.py
При первой сборке скрипт сам скачает Frida (около 50 МБ). Получится out/QuestLHSync-pico-module-1.26-pico4.zip. Ставим через Magisk (Модули → Установить из хранилища) или командами:
adb push out/QuestLHSync-pico-module-1.26-pico4.zip /sdcard/Download/
adb shell su -c "magisk --install-module /sdcard/Download/QuestLHSync-pico-module-1.26-pico4.zip"
adb reboot
Установщик сам проверит, что это Pico 4 Pro и что калибровка камер на месте.
3. Драйвер для ПК
SteamVR должен быть закрыт. В той же папке:
.\build.bat
powershell -executionpolicy bypass -file pico\register_driver.ps1
Скрипт регистрации сам найдёт SteamVR, в какой бы библиотеке Steam он ни стоял. Удалить драйвер: та же команда с ключом -remove. Для обновления потом хватает git pull и .\build.bat.
4. Выключить SpaceCalibrator
И вообще всё, что двигает Lighthouse-устройства. Два инструмента, которые правят одно и то же, будут драться.
Первый запуск
- Запускаем SteamVR со шлемом. На странице QuestLHSync в дашборде должен появиться шлем и его камеры.
- Надеваем трекеры: по ним драйвер отличает правильное решение от зеркального.
- Стоим посреди комнаты и медленно смотрим на каждую станцию по очереди, по 20-30 секунд, иногда делая шаг. Не замирать: засветки при неподвижной голове отбрасываются.
- Когда в логе %LOCALAPPDATA%\QuestLHSync\questlhsync.log появится строка acquired: ..., станции захвачены.
- Если на потолке яркие LED-лампы, захват пойдёт медленнее. Можно на минуту их выключить.
Выравнивание сохраняется для конкретного шлема, так что в следующий раз всё подхватится сразу. Если после перезагрузки шлема или пересоздания границы трекеры встали криво, нажмите в дашборде Force recalibrate.
Если не работает
- Шлем не находится: он должен быть в той же сети и проснуться. Если роутер режет широковещательные пакеты, укажите IP шлема в настройке host. Лог на шлеме: adb shell su -c cat /dev/.questlhsync/lhsyncd.log
- Долго нет захвата: смотрите в логе строку «acquiring, last 60 s: ...». Если там в основном steady lights, мешают лампы. Если while it turned fast или while the headset was still, двигайтесь медленнее, но двигайтесь.
- Что-то странное: в дашборде есть кнопка Record session. Запись можно прогнать через драйвер офлайн и посмотреть, что пошло не так. Учтите, что в ней есть серийные номера ваших станций и трекеров.
Что дальше
- готовые сборки в Releases, чтобы не собирать руками;
- подстройка поправок калибровки на лету: я подбирал их на одном шлеме, у другого экземпляра Pico они могут немного отличаться;
- проверка на других прошивках и на обычном Pico 4.
Если повторите у себя, напишите в комментариях, как прошло, особенно если у вас другая прошивка или модель. Это сильно поможет.
Спасибо
- CreoleVR: QuestLHSync. Весь драйвер и сама идея — их, я только добавил сторону Pico;
- chaixshot: more-picohaxx-tool, без которого не было бы рута;
- авторам Frida, Magisk и Zig.
Ссылки
- Мой форк с портом (ветка pico-4-pro): https://github.com/UndefinedSignal/QuestLHSync-pico4pro
- Оригинальный QuestLHSync: https://github.com/CreoleVR/QuestLHSync
- more-picohaxx-tool: https://github.com/chaixshot/more-picohaxx-tool
гайд
vr
pico 4 pro
root
magisk
vrchat
steamvr
lighthouse
questlhsync
Ar'Vi
Джип-штука +- доведена до ума, но основные тесты под нагрузкой постоянного движения и перекрытия пройдут в выходные.
Oct 28 08:34