Skip to content

Repository files navigation

Русский · English · Deutsch · Español · 中文

Тепловизор UNI-T UTi260M на macOS

Простое приложение, которое даёт работать с тепловизором UNI-T UTi260M на маке. Модуль продаётся как приставка к Android-телефону (разъём Type-C), но это обычная USB-камера, и на macOS её тоже можно заставить показывать температуры.

Характеристика Значение
Разрешение сенсора 256 x 192
Диапазон температур -20 ... 550 °C
Поле зрения (FOV) 56° x 42°
Разъём USB Type-C (штатно для Android)
Как виден системе USB Camera, VID 0x0BDA, PID 0x5830

UNI-T UTi260M

macOS считает такой модуль обычной веб-камерой, поэтому Photo Booth показывает серый силуэт вообще без температур. Это приложение читает сырые тепловые данные и строит из них калиброванную температурную карту со шкалой °C, маркерами горячей и холодной точек и замером по клику.

скриншот

Скачать готовое приложение

Собранное Thermal.app лежит в релизах - собирать ничего не надо, python и все библиотеки уже внутри.

  • macOS 13 или новее, только Apple Silicon (M1 и новее)
  • скачать архив, распаковать, перетащить Thermal.app в «Программы»
  • первый запуск - правой кнопкой по значку и «Открыть», иначе macOS откажется его запускать: подпись ad-hoc, без платного сертификата Apple. Спрашивается один раз.

То же самое из терминала:

xattr -dr com.apple.quarantine /Applications/Thermal.app

Для Intel-мака или своей сборки есть build_app.sh, см. ниже.

Проверено только на UTi260M

Всё написано и отлажено на одном экземпляре UNI-T UTi260M. Другие модули на том же ядре (серия UNI-T UTi, InfiRay P2 Pro, Topdon TC001) устроены похоже и, скорее всего, заработают, но не проверялись - если у вас такой, отпишитесь в Issues, добавлю в список.

Почему обычные программы тут не работают

Камера отдаёт два UVC-режима:

Режим Что внутри
256x192 yuvs только картинка сенсора после AGC, серый силуэт, без температур
256x384 yuvs картинка плюс сырая температурная карта

В режиме 256x384 кадр склеен из двух блоков:

  • строки 0..191 - 8-битная картинка сенсора (YUYV, яркость в чётных байтах);
  • строки 192..383 - сырые 16 бит little-endian на пиксель.

Температура: T(°C) = raw / 64 - 273.15

Photo Booth и FaceTime выбирают режим 256x192, отсюда и силуэт без цифр.

Готовые скрипты вроде PyThermalCamera или P2Pro-Viewer опираются на OpenCV. На macOS cv2.VideoCapture идёт через AVFoundation, который молча конвертирует YUYV в RGB: сырые 16 бит теряются безвозвратно, остаются только красивые цвета без привязки к температуре. Здесь это обходится открытием AVCaptureVideoDataOutput с явно запрошенным пиксельным форматом 'yuvs' (0x79757673) и чтением байтов CVPixelBuffer как есть, с учётом bytesPerRow.

Требования

  • macOS 13 или новее
  • Python 3 сборки python.org (macosx-бэкенду matplotlib нужна именно framework-сборка)
  • Сам тепловизор UTi260M

Запуск

git clone https://github.com/mamapapa1/uti260m-macos.git
cd uti260m-macos
python3 -m venv venv
./venv/bin/pip install -r requirements.txt
./venv/bin/python -m unithermal

Собрать приложение, запускаемое двойным кликом:

./build_app.sh              # → ~/Desktop/Thermal.app
./build_app.sh /Applications

При первом запуске macOS спросит доступ к камере, надо разрешить. Приложение запрашивает его явно: без этого собранный бандл не получает ни кадров, ни диалога.

Посмотреть на приложение можно и вовсе без модуля:

./venv/bin/python -m unithermal --demo

Демо-режим рисует синтетические кадры, но собирает их в том же проводном формате и пропускает через тот же разбор, что и настоящие, так что работает вся программа целиком, а не урезанная ветка.

Сам тепловизор при запуске не обязателен: окно открывается сразу и пишет, что ждёт модуль. Воткнули - картинка появляется сама; выдернули - окно не закрывается, а снова ждёт. Захват переподнимается сам, так что USB можно передёргивать сколько угодно.

Снимки по s кладутся в ~/Pictures/Thermal/, если запуск был из бандла (рабочий каталог там /), иначе в текущий каталог. Каждый снимок - это PNG плюс CSV с полной матрицей 192x256 в градусах Цельсия.

Клавиши

r / R поворот на 90° по / против часовой стрелки
m зеркало по горизонтали
пробел стоп-кадр: заморозить / отпустить картинку
f во весь экран (Esc - выйти)
p следующая палитра
a авто / фиксированный диапазон температур
c температуры / собственная картинка сенсора
клик поставить точку замера
s сохранить PNG + CSV
l следующий язык интерфейса
h показать / скрыть панель справки
q выход

Внизу окна всегда висит строка-подсказка: при запуске из бандла терминала нет, поэтому список клавиш должен жить прямо в окне.

Раскладки клавиатуры

Работает любая раскладка. matplotlib отдаёт в событии символ, а не клавишу, поэтому при русской раскладке физическая R приходит как к. Приложение спрашивает текущую раскладку у самой системы (Text Input Services) и через UCKeyTranslate выясняет, какой символ даёт каждая физическая клавиша, после чего переводит нажатие обратно по позиции клавиши. Таблиц под каждый язык нет: так же работают украинская, греческая, армянская, иврит и любая другая раскладка, а переключение прямо во время работы подхватывается за полсекунды.

Регистр сохраняется, он различает направление поворота, и модификаторы тоже (ctrl+кctrl+r). Если системный API недоступен, остаётся встроенная таблица ЙЦУКЕН. Латинским раскладкам вроде AZERTY или QWERTZ перевод не нужен: буквы там те же самые, просто на других местах.

Язык интерфейса

Интерфейс следует языку macOS и переведён на русский, английский, немецкий, испанский, французский и китайский. Переопределяется ключом --lang:

./venv/bin/python -m unithermal --lang zh

Прямо в окне язык выбирается кликом по кнопке с кодом языка в левом верхнем углу: она раскрывает список, где языки написаны на себе самих. Клавиша l листает их по кругу. Выбор запоминается: он лежит в ~/Library/Application Support/Thermal/settings.json и подхватывается при следующем запуске.

Для китайского приложение переключается на CJK-шрифт (PingFang SC и запасные): шрифт matplotlib по умолчанию, DejaVu Sans, не содержит иероглифов и рисовал бы пустые квадраты, не выдавая при этом никакой ошибки.

Командная строка

--snapshot PATH    сохранить один кадр в PNG и выйти
--offset DELTA_C   поправка в °C ко всем значениям (калибровка по эталону)
--device UNIQUE_ID выбрать камеру, если их несколько
--list             показать подходящие камеры и выйти
--demo             работать на синтетических кадрах, без камеры
--lang CODE        en, ru, de, es, fr, zh

Точность

  • Разрешение по температуре 1/64 K, но абсолютная точность таких модулей порядка ±2 °C, а заводская калибровка рассчитана на коэффициент излучения ε ≈ 0.95.
  • Блестящий металл, стекло и полированный пластик сильно занижают показания, по ним меряется отражённое излучение, а не сама поверхность. Обычное решение - матовая изолента на участок замера.
  • Сенсор периодически делает FFC (щелчок затвора, кадр на мгновение замирает). Это норма, без неё картинка «плывёт».
  • --offset даёт линейную поправку, если сверять с эталонным термометром.

Камера ищется по наличию режима 256x384, а не по uniqueID, потому что последний меняется в зависимости от USB-порта.

Грабли упаковки в .app

Четыре ловушки стоили здесь заметного времени; все они закрыты в build_app.sh и в коде. Первые две дают одинаковый симптом: двойной клик не делает ничего, ошибки нет, crash report не создаётся, а запуск того же лаунчера из терминала при этом работает.

  1. Симлинк за пределы бандла. python -m venv делает bin/python3.x симлинком на /Library/Frameworks/.... Подпись кода запрещает симлинки наружу (invalid destination for symbolic link in bundle), подпись получается битой, и Gatekeeper блокирует запуск. Лечится копированием реального бинаря внутрь.
  2. Rosetta. Главный исполняемый файл бандла - shell-скрипт, у которого нет архитектуры, поэтому LaunchServices запускает процесс как x86_64 под Rosetta, а колёса numpy/pyobjc собраны только под arm64. Закрыто тремя способами сразу: lipo -thin для интерпретатора, LSArchitecturePriority в Info.plist и exec /usr/bin/arch в лаунчере.
  3. Доступ к камере. Собранное приложение не получает диалог автоматически, сессия просто не отдаёт кадры. Нужно явно вызывать AVCaptureDevice.requestAccessForMediaType_completionHandler_.
  4. Компоновка. colorbar(ax=...) и set_title забирают место у осей, ужимая кадр примерно до двух третей окна; в полноэкранном режиме картинка занимала лишь 77% высоты экрана. Теперь оси растянуты на весь холст, а все подписи сделаны оверлеями, и получается 100%.

Чтобы отладить бандл, который молча умирает, перенаправьте вывод лаунчера в файл (exec >> ~/thermal-launch.log 2>&1 первой строкой), иначе traceback уходит в никуда.

spctl -a показывает rejected для ad-hoc подписи. Это ожидаемо без нотаризации и запуску не мешает: у локально собранного бандла нет атрибута карантина.

Если что-то пошло не так

  • «Тепловизор не найден» → модуль занят другим приложением (Photo Booth, FaceTime, Zoom, старая копия этого же приложения). Закрыть их и проверить --list.
  • Окно открылось, но кадров нет → не выдан доступ к камере: Системные настройки → Конфиденциальность и безопасность → Камера, включить для «Thermal».
  • Вместо иероглифов пустые квадраты → в системе нет ни одного CJK-шрифта из списка, поставить PingFang SC или запустить с --lang en.
  • Двойной клик по .app не делает ничего → смотреть раздел про грабли упаковки выше.

Тесты

./venv/bin/pip install pytest
./venv/bin/python -m pytest tests -q

Набор покрывает перевод раскладок (включая все горячие клавиши по-русски), полноту переводов и подстановки для всех шести языков, покрытие глифов выбранным шрифтом, геометрию поворота (соотношение сторон пикселя должно оставаться ровно 1.0 во всех четырёх ориентациях), долю экрана, занимаемую кадром в полноэкранном режиме, стоп-кадр, работу без подключённого модуля с переподключением на ходу и переключение языка в окне.

Дисклеймер

Любительский проект, не аффилирован с UNI-T. Приложение только читает то, что отдаёт модуль, и не является измерительным прибором: показания зависят от коэффициента излучения поверхности, расстояния и прогрева камеры. Для ответственных измерений нужен поверенный прибор.

Лицензия

GNU GPL v3 © 2026 Alexey Verhogladov.

💚 Поддержать

Проект бесплатный и сделан на энтузиазме. Если он пригодился - спасибо за поддержку (USDT):

Сеть Адрес
USDT · TRC20 TYLmBvdxL8t9ziyZiFp3jcHwQbAsZR5haZ
USDT · TON UQCnVND-uBgkWIAD1UP14tsN8239KE5BTOfKSJmg-0XqrN-k
USDT · ERC20 0xa61dcA98A86D84883Ddb3d62aA7F008f157c8eFF

About

Тепловизор UNI-T UTi260M на macOS: сырые температуры вместо серого силуэта

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages