Эта статья является логичным продолжением одной из предыдущих, в которой показывалось применение бытового тепловизора в связке с квадрокоптером. Многие подробности реализации описаны там, здесь будет крупноузловая сборка и особенности применения.
Итак, раз на выходе "малины" у нас аналоговый видеосигнал, то ничто не мешает убрать находящееся между тепловизором и человеком звено в виде радиоканала и передавать картинку с датчика прямо в шлем/очки.
В данном случае для сборки потребовались уже знакомый тепловизор Infiray P2 Pro, одноплатный компьютер Raspberry Pi Zero 2W, а также бюджетный шлем Eachine EV800DM. Его часто советуют новичкам и за свои деньги он справляется с задачей на 5+. Огромным преимуществом именно шлема в сравнении с очками является наличие встроенного питания.
Общая схема предельно проста:

Запитать одноплатник можно прямо от шлема (разумеется, через преобразователь), внутри которого обычная литий-полимерная батарея на 3,7 В.
Разборка
Чтобы получить доступ к батарее и плате нужно выкрутить четыре винта, скрепляющих держатель линзы:

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

Кстати, сама линза даёт очень хорошее увеличение, поэтому вместе с держателем её можно использовать для пайки и мелкого ремонта просто надев на голову. Так что не спешите выбрасывать сломанный шлем, он ещё пригодится ;)

Но нас интересует не она, а плата:


Недолго думая, ко входу питания были припаяны выводы для подключения распространённого повышающего преобразователя HW-106:


И тут случилось непредвиденное: мощности преобразователя оказалось недостаточно для "малины" и в процессе загрузки одноплатник просто выключался. Потребовался более мощный и стабильный источник питания и выбор пал на U3V40F5 от Pololu, отдающий до 4 ампер (в пределе "малина" потребляет 2,5 А) при входном напряжении от 1,3 В, и имеющий КПД свыше 90%:

С ним всё завелось.
Подключение к очкам
Следующий участок работы – передача сигнала. В коробке со шлемом шел простой переходник для аналогового видео:

Для наших целей он едва ли применим, но службу сослужит. С помощью кнопки SRC/Menu переключим шлем в режим аналогового входа (его признаком кроме OSD-надписи "AV" является синий экран смерти):

Теперь, вставив переходник в гнездо шлема V-in и просто коснувшись его выводов проводами, припаянными к выходам TV и GND "малины", мы увидим в шлеме картинку:

Это доказывает работоспособность схемы.
Последняя часть – обычный четырёхвыводной TRRS-штекер (3,5"):

Прозвоном переходника определяем его распайку:

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

Теперь вставим в шлем карту памяти, примотаем всю конструкцию изолентой, и можно начинать испытания:
Вероятно, вы заметили, что переключение между палитрами выполняется с клавиатуры. Сделано это с помощью удалённого рабочего стола и подключения к "малине" по WiFi (подробности в прошлой статье). В моём случае это не особо нужно, но в общем система позволяет передавать картинку из очков на монитор компьютера, что может быть полезно для группового наблюдения.
Изображение получилось довольно шумным, чему виной два фактора:
работа импульсного преобразователя
включенный на одноплатнике вай-фай, рабочая частота которого (5,8 ГГц) совпадает с частотой приёмников в шлеме
Теперь займёмся подавлением помех.
Шумоподавление
Перво-наперво отключаем вай-фай. Сделать это можно как с помощью иконки в правом верхнем углу рабочего стола "малины", так и программно. Для надёжности в starter.sh пропишем
sudo rfkill block wifi
Из документации производителя следует, что частота импульсов ШИМ в преобразователе под нагрузкой достигает 600 кГц. Следствием этого становится рябь даже при выключенном вай-фае:
Картинка без фильтра
Первый и самый простой шумодав – это конденсатор на выходе преобразователя. У меня в запасе было несколько керамических конденсаторов поверхностного монтажа на 10 мкФ (GRM31CR71C106KA12L), поэтому начал с них:

Картинка стала чуть лучше
Горизонтальные полосы почти исчезли, время от времени вылезает непонятная сетчатая рябь вокруг руки.
Второе решение – полноценный LC-фильтр вслед за конденсатором. Для убойности я взял дроссель SRP1265A-4R7M (4,7 мкГн) и электролитический конденсатор с низким ЭПС (470 мкФ):

Расчёт по известной формуле показывает, что частота отсечки такого фильтра составляет около 3,39 кГц, что значительно ниже частоты ШИМ преобразователя и должно хорошо сглаживать пульсации напряжения.
Проверим:
К своему удивлению, я обнаружил зависимость помех от положения фильтра в пространстве: иногда они полностью отсутствуют, но если повернуть его чуть в сторону, то снова появляется рябь.
Вариант с LC-фильтром оказался крайне неудобным, т. к. добавляет в конструкцию ещё одну свисающую часть и лишние провода:

Третий – использование интегрального стабилизатора напряжения, например, LD29150. Его предельный ток составляет лишь 1,5 А, и, хотя по спецификации "малине" требуется 2,5 А, в моём случае она потребляет значительно меньше и полутора ампер хватает. Ещё один плюс интегрального стабилизатора – это простота и небольшие размеры, что позволило припаять его к обратной стороне платы:

Испытываем:
Если честно, качество меня разочаровало, я ожидал более чистой картинки.
Сравнение картинки без фильтрации, с конденсатором, LC-фильтром и интегральным стабилизатором напряжения (слева сверху – отсутствие фильтрации, справа – конденсатор, внизу слева – LC-фильтр, справа – интегральный стабилизатор):
В итоге я остановился на фильтре из двух параллельных конденсаторов: 10 мкФ (GRM31CR71C106KA12L) и 0,1 мкФ (104).
Маска Хищника:
Ожидание

И суровая действительность ?


Код
Код на питоне не претерпел особых изменений, единственное, что я добавил, – это запись картинки в файл (строки 116-118 и 276/279).
Код целиком
print('Les Wright 21 June 2023') print('https://youtube.com/leslaboratory') print('A Python program to read, parse and display thermal data from the Topdon TC001, Infiray P2 Pro and other compatible USB Thermal Cameras!') print('') print('THIS IS A PATCHED VERSION; patch created by CATCH22MANIA \'07/24 - https://www.youtube.com/@catch22mania') print('') print('Key bindings:') print('') print('c x: cycle through colormaps - 0 = raw (no hud)') print('f w: fullscreen, windowed') print('h : toggle HUD') print('q : quit') print('') print('1 : show highest temperature') print('2 : show centre crosshairs temperature') print('3 4: change scale - in fullscreen: zoom in/out font') print('5 6: +/- contrast') print('7 8: +/- blur') # print('9 0: floating high and low temp label threshold') print('r t: Record and Stop') # print('p : Snapshot') print('') print('check out the command line options > python ~/testing/ptc-patched-c22-014-test1.py -h') print('') import cv2 import numpy as np #print(np.__version__) import argparse import time import io #256x192 General settings width = 256 #Sensor; width for output window size (* scale) height = 192 #sensor; height for output windows size (* scale) scale = 3 #scale multiplier alpha = 1.0 # Contrast control (1.0-3.0) colormap = 0 font=cv2.FONT_HERSHEY_SIMPLEX dispFullscreen = False rad = 0 #blur radius threshold = 2 hud = False recording = False showcrosshairs = False showtemps = False elapsed = "00:00:00" snaptime = "None" #We need to know if we are running on the Pi, because openCV behaves a little oddly on all the builds! #https://raspberrypi.stackexchange.com/questions/5100/detect-that-a-python-program-is-running-on-the-pi def is_raspberrypi(): try: with io.open('/sys/firmware/devicetree/base/model', 'r') as m: if 'raspberry pi' in m.read().lower(): return True except Exception: pass return False isPi = is_raspberrypi() parser = argparse.ArgumentParser() parser.add_argument("--device", type=int, default=0, help="video device number; e.g. 0, use v4l2-ctl --list-devices") parser.add_argument("--width", type=int, default=0, help="video output width; e.g. 720, scale to width (scale factor = 1)") parser.add_argument("--height", type=int, default=0, help="video output height; e.g. 576, scale to height (scale factor = 1)") parser.add_argument("--fullscreen", dest="fullscreen",action='store_true', help="start in fullscreen; no argument") parser.add_argument("--colormap", type=int, default=3, help="colormap; range: 1-22; 0 = no colormap (raw); default: 3") parser.add_argument("--crosshairs", dest="crosshairs", action='store_true', help="show crosshairs; no argument") parser.add_argument("--temps", dest="temps", action='store_true', help="show temperatures; no argument") parser.add_argument("--blur", type=int, default=0, help="blur radius; default: 0") parser.add_argument("--contrast", type=float, default=1.0, help="contrast; default: 1.0") args = parser.parse_args() if args.device: dev = args.device else: dev = 0 if args.width: scale = 1 width = args.width if args.height: scale = 1 height = args.height if args.fullscreen: dispFullscreen = args.fullscreen if args.colormap: colormap = args.colormap if args.blur: rad = args.blur if args.contrast: alpha = args.contrast if args.crosshairs: showcrosshairs = args.crosshairs if args.temps: showtemps = args.temps newWidth = width*scale newHeight = height*scale cap = cv2.VideoCapture('/dev/video'+str(dev), cv2.CAP_V4L) #pull in the video but do NOT automatically convert to RGB, else it breaks the temperature data! #https://stackoverflow.com/questions/63108721/opencv-setting-videocap-property-to-cap-prop-convert-rgb-generates-weird-boolean if isPi == True: cap.set(cv2.CAP_PROP_CONVERT_RGB, 0.0) else: cap.set(cv2.CAP_PROP_CONVERT_RGB, False) cv2.namedWindow('ThermalWindow',cv2.WND_PROP_FULLSCREEN) cv2.setWindowProperty('ThermalWindow',cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN) if dispFullscreen == False: cv2.resizeWindow('ThermalWindow', newWidth,newHeight) current_time_str = time.strftime("%Y%m%d--%H%M%S") fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter(f"thermal_output-{current_time_str}.avi", fourcc, 25, (newWidth, newHeight)) while(cap.isOpened()): # Capture frame-by-frame ret, frame = cap.read() if ret == True: imdata,thdata = np.array_split(frame, 2) hi = thdata[96][128][0] lo = thdata[96][128][1] lo = lo*256 rawtemp = hi+lo temp = (rawtemp/64)-273.15 temp = round(temp) #find the max temperature in the frame wat = np.add(thdata[...,0], np.multiply(thdata[...,1], 256)) maxtemp = wat[...].max() posmax = wat[...].argmax() mcol, mrow = divmod(posmax, 256) maxtemp = (maxtemp/64) - 273.15 maxtemp = round(maxtemp) # hotspot: quick n dirty for testing - scaling width, height mcol2 = mcol / (192 / height) mrow2 = mrow / (256 / width) mcol = round (mcol2) mrow = round (mrow2) bgr = cv2.cvtColor(imdata, cv2.COLOR_YUV2BGR_YUYV) bgr = cv2.resize(bgr,(newWidth,newHeight),interpolation=cv2.INTER_CUBIC)#Scale up! if colormap != 0: # 0 = no colormap, no blur, no contrast # contrast if alpha> 1.0: bgr = cv2.convertScaleAbs(bgr, alpha=alpha)#Contrast # blur if rad>0: bgr = cv2.blur(bgr,(rad,rad)) # apply colormap if colormap == 1: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_AUTUMN) cmapText = 'autumn' if colormap == 2: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_BONE) cmapText = 'bone' if colormap == 3: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_JET) cmapText = 'jet' if colormap == 4: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_WINTER) cmapText = 'winter' if colormap == 5: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_RAINBOW) cmapText = 'rainbow' if colormap == 6: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_OCEAN) cmapText = 'ocean' if colormap == 7: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_SUMMER) cmapText = 'summer' if colormap == 8: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_SPRING) cmapText = 'spring' if colormap == 9: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_COOL) cmapText = 'cool' if colormap == 10: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_HSV) cmapText = 'HSV' if colormap == 11: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_PINK) cmapText = 'pink' if colormap == 12: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_HOT) cmapText = 'hot' if colormap == 13: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_PARULA) cmapText = 'parula' if colormap == 14: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_MAGMA) cmapText = 'magma' if colormap == 15: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_INFERNO) cmapText = 'inferno' if colormap == 16: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_PLASMA) cmapText = 'plasma' if colormap == 17: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_VIRIDIS) cmapText = 'viridis' if colormap == 18: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_CIVIDIS) cmapText = 'cividis' if colormap == 19: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_TWILIGHT) cmapText = 'twilight' if colormap == 20: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_TWILIGHT_SHIFTED) cmapText = 'twilight s.' if colormap == 21: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_TURBO) cmapText = 'turbo' if colormap == 22: heatmap = cv2.applyColorMap(bgr, cv2.COLORMAP_DEEPGREEN) cmapText = 'deepgreen' if showcrosshairs == True: # draw crosshairs cv2.line(heatmap,(int(newWidth/2),int(newHeight/2)+20),\ (int(newWidth/2),int(newHeight/2)+5),(255,255,255),1) #vline 1 cv2.line(heatmap,(int(newWidth/2),int(newHeight/2)-5),\ (int(newWidth/2),int(newHeight/2)-20),(255,255,255),1) #vline 2 cv2.line(heatmap,(int(newWidth/2)-5,int(newHeight/2)),\ (int(newWidth/2)-20,int(newHeight/2)),(255,255,255),1) #hline 1 cv2.line(heatmap,(int(newWidth/2)+5,int(newHeight/2)),\ (int(newWidth/2)+20,int(newHeight/2)),(255,255,255),1) #hline 2 cv2.putText(heatmap,str(temp)+" C", (int(newWidth/2)+10, int(newHeight/2)-10),\ cv2.FONT_HERSHEY_SIMPLEX, 0.55,(0, 0, 0), 2, cv2.LINE_AA) cv2.putText(heatmap,str(temp)+" C", (int(newWidth/2)+10, int(newHeight/2)-10),\ cv2.FONT_HERSHEY_SIMPLEX, 0.55,(0, 255, 255), 1, cv2.LINE_AA) if hud==True: # display black box for our data cv2.rectangle(heatmap, (0, 0),(180, 55), (0,0,0), -1) # put text in the box cv2.putText(heatmap,'Colormap: '+str(colormap)+' ('+cmapText+')', (10, 16),\ cv2.FONT_HERSHEY_SIMPLEX, 0.4,(0, 255, 255), 1, cv2.LINE_AA) cv2.putText(heatmap,'Blur: '+str(rad)+' ', (10, 30),\ cv2.FONT_HERSHEY_SIMPLEX, 0.4,(0, 255, 255), 1, cv2.LINE_AA) cv2.putText(heatmap,'Contrast: '+str(alpha)+' ', (10, 44),\ cv2.FONT_HERSHEY_SIMPLEX, 0.4,(0, 255, 255), 1, cv2.LINE_AA) if showtemps == True: cv2.circle(heatmap, (mrow*scale, mcol*scale), 5, (0,0,0), 1) cv2.putText(heatmap,str(maxtemp)+' C', ((mrow*scale)+10, (mcol*scale)-10),\ cv2.FONT_HERSHEY_SIMPLEX, 0.55,(0,0,0), 2, cv2.LINE_AA) cv2.putText(heatmap,str(maxtemp)+' C', ((mrow*scale)+10, (mcol*scale)-10),\ cv2.FONT_HERSHEY_SIMPLEX, 0.55,(0, 255, 255), 1, cv2.LINE_AA) cv2.line(heatmap,(int(mrow*scale),int(mcol*scale)+20),\ (int(mrow*scale),int(mcol*scale)+5),(255,255,255),1) #vline 1 cv2.line(heatmap,(int(mrow*scale),int(mcol*scale)-5),\ (int(mrow*scale),int(mcol*scale)-20),(255,255,255),1) #vline 2 cv2.line(heatmap,(int(mrow*scale)-5,int(mcol*scale)),\ (int(mrow*scale)-20,int(mcol*scale)),(255,255,255),1) #hline 1 cv2.line(heatmap,(int(mrow*scale)+5,int(mcol*scale)),\ (int(mrow*scale)+20,int(mcol*scale)),(255,255,255),1) #hline 2 #display image cv2.imshow('ThermalWindow',heatmap) # rendered image out.write(heatmap) else: cv2.imshow('ThermalWindow',bgr) # "raw" frame out.write(bgr) keyPress = cv2.waitKey(1) if keyPress == ord('7'): #Increase blur radius rad += 1 if keyPress == ord('8'): #Decrease blur radius rad -= 1 if rad <= 0: rad = 0 if keyPress == ord('3'): #Increase scale scale += 1 if scale >=5: scale = 5 newWidth = width*scale newHeight = height*scale if dispFullscreen == False and isPi == False: cv2.resizeWindow('ThermalWindow', newWidth,newHeight) if keyPress == ord('4'): #Decrease scale scale -= 1 if scale <= 1: scale = 1 newWidth = width*scale newHeight = height*scale if dispFullscreen == False and isPi == False: cv2.resizeWindow('ThermalWindow', newWidth,newHeight) if keyPress == ord('f'): #enable fullscreen dispFullscreen = True cv2.namedWindow('ThermalWindow',cv2.WND_PROP_FULLSCREEN) cv2.setWindowProperty('ThermalWindow',cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN) if keyPress == ord('w'): #disable fullscreen dispFullscreen = False cv2.namedWindow('ThermalWindow',cv2.WINDOW_GUI_NORMAL) cv2.setWindowProperty('ThermalWindow',cv2.WND_PROP_AUTOSIZE,cv2.WINDOW_GUI_NORMAL) cv2.resizeWindow('ThermalWindow', newWidth,newHeight) if keyPress == ord('5'): #contrast+ alpha += 0.1 alpha = round(alpha,1)#fix round error if alpha >= 3.0: alpha=3.0 if keyPress == ord('6'): #contrast- alpha -= 0.1 alpha = round(alpha,1)#fix round error if alpha<=0: alpha = 0.0 if keyPress == ord('h'): if hud==True: hud=False elif hud==False: hud=True if keyPress == ord('c'): #c to cycle through colormaps colormap += 1 if colormap > 22: colormap = 0 if keyPress == ord('x'): #x to cycle through colormaps colormap -= 1 if colormap < 0: colormap = 22 if keyPress == ord('q'): break out.release() capture.release() cv2.destroyAllWindows() exit(0) if keyPress == ord('1'): if showtemps==True: showtemps=False elif showtemps==False: showtemps=True if keyPress == ord('2'): if showcrosshairs==True: showcrosshairs=False elif showcrosshairs==False: showcrosshairs=True
Чтобы получить выделение точки с наивысшей температурой в скрипте ~/starter.sh к аргументам команды дописываем --temps:
#!/bin/bash sudo rfkill block wifi python /home/admin/testing/ptc-patched-c22-014.py --temps --fullscreen --colormap 20 & # 256x192; options see ~/starter.sh.ptc.examples
После каждого запуска скрипта в ~ появляется файл вида thermal_output-YYYYMMDD--HHmmSS.avi, содержащий изображение с датчика. Давайте сравним его с DVR (картинка, пишущаяся на карту памяти шлема), что даст представление о влиянии помех и аналоговой передачи на первоисточник:
Здесь слева аналоговый DVR, справа – созданное скриптом изображение (первоисточник), записанное в файл. Разница видна невооруженным глазом, особенно если обратить внимание на отрисовку прицела. Небольшое отличие в высоте видео объясняется тем, что разрешение картинки слева равно разрешению рабочего стола "малины" (720х576), в то время как справа оно равно разрешению кадра (768х576).
Причёсывание
В завершение, чтобы хоть как-то облагородить внешний вид, я заказал корпус, объединив две модели:
Итогом стали две накладки из мягкого пластика, которые идеально подошли под мои задачи. К шлему их всё ещё нужно приклеивать (подойдёт двусторонний скотч или обычная изолента).
Трёхмерная модель




Готовая сборка
"Циклоп", прошу любить и жаловать





В идеале неплохо смотрелся бы зацеп, но я не стал заморачиваться и посадил всё на двусторонний скотч.
Испытание зимой
Для начала сравним картинку с InfiRay P2 Pro, полученную с помощью приложения c картинкой, созданной скриптом (в данном случае я беру по 1 кадру):
Приложение P2 Pro

Малина

Не знаю, как это работает, но за счёт более узкого угла обзора картинка на глаз выглядит резче и чётче. Секрета здесь нет: андроид-приложение растягивает картинку до 1080х810, в то время как "малина" честно умножает 256х192 датчика на 3 (в нашем коде scale = 3), отдавая в итоге 768х576 (что близко к разрешению шлема).
На качество картинки сильно влияет влажность (в идеале воздух должен быть сухим), поэтому не пугайтесь, если вдруг она станет размытой.
Неожиданный результат дало подключение приставки к очкам Skyzone 02O: я обнаружил почти полное отсутствие помех, что объясняется более дорогим и качественным приёмником, системой шумоподавления и т. п. OLED также даёт о себе знать. DVR напротив разочаровал, картинка выглядит сильно менее резкой, что объясняется более низким разрешением очков – 640x480 против 720х576 у EV800DM:
Единственное преимущество Skyzone в данном случае заключается в наличии цифрового HDMI-входа. Картинка будет лучше, правда, начисто пропадает возможность писать DVR и требуется переделка питания (очки требуют 7,4-22,2 В, т. е. батарею 2S-6S), следовательно, для "малины" нужен уже понижающий преобразователь напряжения.
Можно попробовать совместить лучшее из двух миров и использовать тепловизионную приставку вместо со шлемом Skyzone Cobra, работающим от одного элемента 18650, и оборудованным mini-HDMI входом.
Ну а пока вернёмся к собранным очкам. Ниже будет несколько видео в разных условиях:
На чердаке
Вертолёт
Зимний лес
Наблюдаем за людьми и машинами:
Что можно сказать насчёт использования датчика зимой? С одной стороны, низкая и однородная температура в сочетании с хорошей чувствительностью датчика (≤ 40 мК) способствуют обнаружению всех хоть немного тёплых предметов. С другой детализация оставляет желать лучшего.
Испытание летом
Включение в жаркий летний день, используется палитра Viridis (оттенки зелёного и жёлтого), на мой взгляд, одна из наиболее удобных:
Теперь попробуем более однородную в цветовом отношении (инферно) ночью:
Летом картинка получается более подробной, особенно на закате/рассвете, когда происходит охлаждение/нагрев предметов и за счёт его неравномерности у разных материалов их температура отличается сильнее, чем днём/ночью.
Таким образом, данная система позволяет организовать наблюдение в тепловизионном диапазоне, сделать трансляцию на удалённый рабочий стол, записывать видео в память "малины" и очков. Качество пока так себе, ибо разрешение датчика тепловизионных приставок для смартфонов всего 256х192 при широком угле обзора.
Альтернатива
Более простым решением является использование тепловизионных камер серии AxisFlight, предоставляющих выход CVBS (аналоговое видео), что позволяет передавать тепловизионную картинку напрямую на вход очков/шлема, что показано в видео ниже:
Преимущества:
- простота
- намного меньшее потребление мощности и большее время работы
- в случае использования SkyZone Cobra можно снять крышку, извлечь приёмник и вставить в гнездо обычный 8-штыревой разъём с шагом 2,54 мм (разводка показана в видео), что облегчает установку, улучшает прочность и делает всю сборку более компактной и модульной
- разрешение до 640х512!
Недостатки:
- цена (и шлем, и камера дороже)
- нет программных плюшек вроде прицела, смены палитр, шкалы температур и т. п.
- на данно этапе нельзя передавать картинку одновременно и в шлем, и во вай-фаю
- запись только в DVR шлема
- нет HDMI
- суженный угол обзора
На сегодня всё, спасибо за внимание и до новых встреч!