Русский
Русский
English
Статистика
Реклама

Пульсометр

Определяем пульс по вебкамере в 50 строчек кода

01.09.2020 22:17:56 | Автор: admin

Привет Хабр.

Однажды мне попалось описание приложения для Android, которое определяло пульс по камере телефона, просто по общей картинке. Камера не прикладывалась к пальцу, не просвечивалась светодиодом и пр. Интересный момент был в том, что ревьюеры не поверили в возможность такого определения пульса, и приложение было отклонено. Чем дело кончилось у автора программы, не знаю, но стало интересно проверить, возможно ли это.

Для тех кому интересно что получилось, продолжение под катом.

Разумеется, я не буду делать приложение под Android, гораздо проще проверить идею на языке Python.

Получаем данные с камеры

Сначала мы должны получить поток с вебкамеры, для чего воспользуемся OpenCV. Код является кроссплатформенным, и может работать как под Windows, так и под Linux/OSX.

import cv2import ioimport timecap = cv2.VideoCapture(0)cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)cap.set(cv2.CAP_PROP_FPS, 30)while(True):    ret, frame = cap.read()    # Our operations on the frame come here    img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)    # Display the frame    cv2.imshow('Crop', crop_img)    if cv2.waitKey(1) & 0xFF == ord('q'):        breakcap.release()cv2.destroyAllWindows()

Идея определения пульса состоит в том, что оттенок кожи слабо меняется из-за протекания крови в сосудах, поэтому нам понадобится кроп картинки, на котором будет только фрагмент кожи.

x, y, w, h = 800, 500, 100, 100crop_img = img[y:y + h, x:x + w]cv2.imshow('Crop', crop_img)

Если все было сделано правильно, при запуске программы мы должны получить примерно такую картинку с камеры (заблюрено из соображений приватности) и кропа:

Обработка

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

heartbeat_count = 128heartbeat_values = [0]*heartbeat_countheartbeat_times = [time.time()]*heartbeat_countwhile True:    ...    # Update the list    heartbeat_values = heartbeat_values[1:] + [np.average(crop_img)]    heartbeat_times = heartbeat_times[1:] + [time.time()]

Функция numpy.average вычисляет среднее из двухмерного массива, на выходе мы получаем число, которое и является усредненной яркостью.

Остается вывести график на экран в реальном времени:

fig = plt.figure()ax = fig.add_subplot(111)while(True):    ...    ax.plot(heartbeat_times, heartbeat_values)    fig.canvas.draw()    plot_img_np = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')    plot_img_np = plot_img_np.reshape(fig.canvas.get_width_height()[::-1] + (3,))    plt.cla()        cv2.imshow('Graph', plot_img_np)

Тут есть небольшая тонкость: OpenCV работает с изображениями в формате numpy, поэтому мы должны получить из matplotlib график в виде массива, для чего используется функция numpy.fromstring.

Собственно и все.

Запускаем программу, подбираем такое положение, чтобы в кропе с камеры был только фрагмент кожи, принимаем "позу мыслителя", подперев голову рукой - изображение должно быть максимально неподвижно. И вуаля - это действительно работает!

Возможно, из заголовка не совсем очевидно, но камера не прикладывается к коже, мы просто анализируем общую картинку с человеком на экране. И удивительно, что даже на таком расстоянии изменение оттенка кожи вполне уверенно фиксируется камерой! Разумеется, по клеточкам считать не точно, примерный пульс получился около 75bpm. Для сравнения, результат с поверенного китайскими мастерами пульсоксиметра:

Заключение

Как ни странно, но это действительно работает. Если честно, в результате я был не уверен. Разумеется, для реального использования нужно сначала найти лицо на изображении, но встроенная функция поиска лиц в OpenCV также есть. И конечно, нужна несложная математика для выделения периода из достаточно шумных данных.

И раз уж мы анализируем видеопоток, может возникнуть отдельный вопрос - работает ли это со сжатыми данными, можно ли увидеть пульс у актера кино или диктора на телевидении? Ответа я не знаю, желающие могут попробовать самостоятельно.

Для желающих поэкспериментировать, исходный код целиком под спойлером.

Spoiler
import numpy as npfrom matplotlib import pyplot as pltimport cv2import ioimport timecap = cv2.VideoCapture(0)cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1280)cap.set(cv2.CAP_PROP_FPS, 30)# Image cropx, y, w, h = 800, 500, 100, 100heartbeat_count = 128heartbeat_values = [0]*heartbeat_countheartbeat_times = [time.time()]*heartbeat_count# Matplotlib graph surfacefig = plt.figure()ax = fig.add_subplot(111)while(True):    # Capture frame-by-frame    ret, frame = cap.read()    # Our operations on the frame come here    img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)    crop_img = img[y:y + h, x:x + w]    # Update the data    heartbeat_values = heartbeat_values[1:] + [np.average(crop_img)]    heartbeat_times = heartbeat_times[1:] + [time.time()]    # Draw matplotlib graph to numpy array    ax.plot(heartbeat_times, heartbeat_values)    fig.canvas.draw()    plot_img_np = np.fromstring(fig.canvas.tostring_rgb(), dtype=np.uint8, sep='')    plot_img_np = plot_img_np.reshape(fig.canvas.get_width_height()[::-1] + (3,))    plt.cla()    # Display the frames    cv2.imshow('Crop', crop_img)    cv2.imshow('Graph', plot_img_np)    if cv2.waitKey(1) & 0xFF == ord('q'):        breakcap.release()cv2.destroyAllWindows()

И как обычно, всем удачных экспериментов

Подробнее..

Сенсорика для медицины и Умного дома лекция Станислава Полонского

04.12.2020 16:09:39 | Автор: admin

Предлагаем вашему вниманию текстовую версию видео лекции Станислава Полонского, посвященная сенсорике в Интернете вещей. Станислав - физик, кандидат физико-математических наук, начальник управления перспективных исследований и разработок в Исследовательском центре Samsung. Много лет жил и работал в США.

Это лекция вводного уровня, рекомендуется студентам и начинающим инженерам и программистам. В ней будет обзор существующих сенсоров и технологий Интернета вещей в приложениях Умного дома и персонального здоровья, а также размышления автора о том, что интересует лично его в этой сфере, и советы на будущее тем, кто еще только задумывается о создании своего устройства. Будем обсуждать, как измерять пульс, вариабельность сердечных сокращений, насыщенность крови кислородом - и можно ли превратить собственную квартиру в измеряющую среду.

Добрый день, меня зовут Стас Полонский, я представляю российское подразделение Samsung, управление перспективных исследований и разработок (Samsung Advanced Institute of Technology).

Сперва я хотел бы обратить внимание, что прежде чем говорить о сенсорах, науке, технологиях, необходимо найти потребность. Нужна ли ваша идея, нужно ли ваше приложение? Как выйти на потребителя, на людей, которым потенциально ваша идея нужна, и с чем выйти? Есть термин минимальный жизнеспособный продукт (MVP). Какие минимальные движения вы должны предпринять, чтобы проверить на практике, нужна ли ваша идея?

Моя лекция из четырех частей:

1. Потребности. Я расскажу, как мы представляем эти потребности без проверки рынков, исключительно исходя из того, что описывается в прессе. Посмотрим, как так называемая кривая шумихи Гартнера видит использование Интернета вещей. Поговорим о том, чего москвичи ожидают от Интернета вещей, более точно - от Умного города как его части. Оказывается, что людей очень сильно волнует Интернет вещей в применении к здоровью

2. Сенсоры в медицине. Я приведу примеры сенсоров, которые могут быть полезны для Интернета вещей в области персонального здоровья: как измерить пульс, как измерить электрическое сопротивление нашего организма - называется импеданс - и для чего это нужно, и, наконец, как при помощи носимых сенсоров измерить электрокардиограмму, электроэнцефалограмму.

3. Сенсоры для Умного дома. Мы поговорим о газах, о запахах, которые бывают в доме, бывают неприятными и опасными. Еще одна интересная тема - мы постоянно теряем вещи, и мне кажется, было бы абсолютно классно, если бы мы могли эти вещи находить при помощи Интернета вещей.

4. Пожелания. Мое личное мнение, какие проекты было бы классно сделать. Может быть, у них есть шанс найти своего потребителя.

Потребности

Итак, начинаем. Что такое кривая шумихи Гартнера? По оси X время слева направо, и по оси Y - ожидания.

График Гартнера. Источник - ВикипедияГрафик Гартнера. Источник - Википедия

В самом начале кривой возникают новые красивые идеи. Они плохо разработаны и пока плохо понятны. Вначале мы всегда встречаем их с энтузиазмом, и наши ожидания от технологии быстро возрастают, соответственно финансирование этих идей улучшается. Мы лучше понимаем эти идеи, и через какое-то время становится ясно, что не все из них сработают, не все из них нужны. Что начинается после достижения максимума ожиданий? Мы видим так называемую Долину смерти. Многие технологии здесь погибнут и никогда не выживут. Только часть, пройдя Долину смерти - здесь это даже не показано, потому что их так мало - в какой-то момент очень маленькая часть технологий доходит до промышленности.

График Гартнера за 2020 год. Источник: GartnerГрафик Гартнера за 2020 год. Источник: Gartner

Почему я показываю эту кривую? Здесь достаточно много технологий, связанных с Интернетом вещей. Я выделил их красным:

  • Паспорт здоровья: здесь, мы ожидаем, Интернет вещей может помочь нам.

  • Цифровой двойник человека. Очень похожая вещь. Компьютерная программа, структура данных, которая более-менее описывает, что происходит с человеком, но на каком-то примитивном уровне, скорее всего, на уровне физиологии. Необязательно таскать человека к врачам, вдруг наши алгоритмы помогут нам. Я надеюсь без врачей понять, что же происходит сейчас с человеком.

  • Технология социального дистанцирования. Очень модная, и может быть, она окажется полезной. Есть очевидный запрос и очевидно простая реализация.

Ключевые идеи, которые хотелось бы здесь донести:

  1. Интернет вещей пока еще не взлетел, пока что это много надежд, небольшие приложения, но повсеместного использования нет.

  2. Мы говорим о сенсорике Интернета вещей. Сенсорика - эта физика, и за двести-триста лет развития физики мы изобрели массу сенсоров. По всей видимости, особой нужды изобретать новые сенсоры у нас нет. Но важно посмотреть, как существующие сенсоры могут быть вписаны в новые модели, сценарии использования, которые позволят продавать. Я напоминаю: как инженеры, мы хотим не только делать вещи, но и продавать их. Этим занимается не только отдел продаж, инженеры тоже должны об этом беспокоиться.

Итак, давайте двигаться дальше. Что думает Москва? Это слайд из стратегии Москва - Умный город 2030, и здесь показаны проценты по опросам, какие технологии больше всего нас волнуют. Это на 64% персональная медицина, и на втором месте всего на два процента меньше, это Умный дом. Другие технологии в принципе не так сильно отстают по процентам.

Источник: mos.ruИсточник: mos.ru

Понятно, москвичам хочется всего и сразу, но тем не менее на первом месте это персональная медицина и умный дом. Поэтому мы и сосредоточимся на этих двух темах. Москвичи, и скорее всего все россияне и весь мир, возможно, действительно хотят иметь Интернет вещей, помогающий персональной медицине или Умному дому, но для меня пока совершенно не очевидно, что люди готовы платить за это.

Сенсоры в медицине

Сенсор пульсовой волны

Какие существующие сенсоры могут быть использованы для персональной медицины? Например, это сенсор пульсовой волны. Научное название - фотоплетизмография, часто используется аббревиатура PPG. Картинка выглядит сложно, но на самом деле это просто сечение пальца.

Источник: how2electronics.comИсточник: how2electronics.com

В зависимости от того, как сокращается сердце, наполняемость кровью в пальце может быть большой или маленькой, и это показано двух смежных картинках слева и справа. Почему это важно: когда мы пропускаем свет через палец, наполненность кровью пальца измеряется оптически, исходя из того, как свет проходит сквозь палец. Это необязательно должен быть палец, это может быть ухо или другие части тела. Мы можем вытащить пульсовую волну, она показана здесь внизу со своими характерными частями.

Источник: EuropePMCИсточник: EuropePMC

Да, это не просто синус, это сложная форма, она часто говорит врачам о том, что с нами происходит. Причем анализировать волну могут и умные алгоритмы. Что конкретно мы при помощи этого датчика измеряем? Прежде всего, пульс. Достаточно ли пульса? Не всегда. Например, можно посмотреть, насколько регулярен пульс, как часто меняется скорость сокращений сердца, это называется вариабельность (heart rate variability, HRV). Еще можно измерить насыщенность крови кислородом, эта переменная называется SpO2. Это, пожалуй, основное.

Небольшое лирическое отступление: мечта разработчиков оптических сенсоров - измерить сахар в крови. Это важно для диабетиков, это важно для контроля, как и что мы едим, но, насколько я понимаю, это очень сложная задача, и сейчас она еще не решена. Так что давайте думать больше про то, как часто сокращается наше сердце - это пульс, как меняется частота сердечных сокращений и насколько наша кровь насыщена кислородом.

Это была теория, а теперь перейдем к практике. Что можно сделать на основе сенсоров пульсовой волны? В медицине они широко используются. В поликлинике на палец надевают оптическую прищепку, и она измеряет сокращение сердечной мышцы. Но всё-таки это узкоспециализированные вещи.

Источник: stern.deИсточник: stern.de

Можно ли вытащить эти сенсоры и проникнуть на потребительский рынок? Это делается, к примеру, в носимых устройствах. Например, на корпусе часов Samsung Gear S3 Frontier есть небольшое окошечко, там стоят оптические элементы, которые светят на руку и вытаскивают эти данные.

Samsung Gear S3 FrontierSamsung Gear S3 Frontier

Я не думаю, что это очень важно абсолютно всем. Может быть, важно для атлетов. Но мы экспериментируем, смотрим, что конкретно можно сделать, какие проекты возможны. Например, в медицине пока что большинство датчиков с проводом - неприятно, неудобно, если вы лежите в кровати и повернулись на другой бок. Все ли можно сделать беспроводным через WiFi и Bluetooth? В принципе, была хорошая идея и даже есть попытки стандартизации - американское министерство здравоохранения говорит на эту тему уже несколько лет.

Что можно сделать для потребительского рынка: любопытно, что стресс и вариабельность сердечных сокращений связаны между собой, и можно оценивать стресс. Более того, существует биологическая обратная связь - знание этих характеристик может оказать влияние на наше поведение. Можем ли мы из этого сделать какую-то увлекательную игру, конечной целью которой будет, естественно, управление нашим стрессом? Мне кажется: почему бы не попытаться встроить все эти датчики в мебель, в окружающую среду, стулья, столы, это было бы интересно.

И еще чуть-чуть о возможных используемых технологиях. Частоту сердечных сокращений не обязательно смотреть измерительным прибором, прикладывая его к руке, пальцам. Видеокамера, наблюдая нас, видит, как микроскопически меняется цвет лица в зависимости от сокращений сердца. Эта информация вытаскивается, и она не такая точная, как носимый сенсор, но тем не менее может оказаться важной для отдельных приложений. Сейчас радары используются не только у военных и на аэродромах. Создаются микросхемы, которые при помощи радара могут измерять частоту сокращений. XETHRU - пример компании, которая занимается такими радарными микросхемами.

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

Измеряем сопротивление

Теперь рассмотрим сенсор электросопротивления организма. По-научному это называется биоимпедансометрия. Импеданс - термин из электроники, связывает токи и напряжения. Био, потому что мы не кусок металла, не графитовый стержень - мы сложный объект, и наше сопротивление зависит от ряда факторов: насколько мы полные или тощие, сколько в нас воды; более того, на разных частотах наше сопротивления отличается, не константа.

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

Источник: AVM Active SportИсточник: AVM Active Sport

Вот пример умных часов, которые измеряют ваш импеданс.

Источник: TomTomИсточник: TomTom

Если не вдаваться в подробные объяснения, то вот эта чашка на картинке ниже - это клетка нашего организма, и на схеме показано, что даже на уровне элементарной биологической клетки в нас есть сопротивление и емкость. Клеточная мембрана - это кожура, это стенки чашки, они фактически непроницаемы для электрического тока. С точки зрения электроники они представляют собой емкость в зависимости от того, в каком состоянии клетка принимает и выпускает ионы. Когда мы измеряем сопротивление, можно сказать многое о том, какие процессы происходят внутри нас.

Источник: EMBИсточник: EMB

Эта тема имеет много приложений: можно ли предсказать появление и развитие болезней, и так называемый основной обмен веществ, то есть сколько калорий мы сжигаем. Мы ведь сжигаем их не только, когда упражняемся в тренажерном зале, но и когда сидим, и это тоже важно знать. Как мы потребляем наши калории? Всё это широко используется для спорта, либо врачами-диетологами. Это интересные темы, они есть на рынке. Насколько они удачны или нет, тяжело судить. Но кто сказал, что у вас не будет своих хороших идей, которые бы вы смогли довести до рынка?

Я хотел бы предложить идею. Как правило, мы говорим либо о специализированном оборудовании для импедансометрии, либо про умные часыносимые устройства. А можно ли те же самые измерения проводить в окружающей нас среде? Можно ли сделать умный стол, кресло, кровать, либо даже пол, который бы проводил эти измерения? Я к этому вернусь позже, но сейчас я отмечу одну вещь. Когда мы говорим про Интернет вещей, то самое последнее, что я хотел бы сделать как инженер, это заставлять человека активно использовать датчики, обвешаться ими, надеть на шею, на уши, на руки. Не думаю, что это сработает. Мне ближе идея прозрачности, когда среда, окружающий вас мир не вызывает у вас негативных эффектов, и не заставляет вас думать - как, к чему подключиться. Мне кажется, это одно из требований к технологиям будущего. Они должны быть неинвазивными и прозрачными для нас как пользователей.

Измеряем электроактивность

Давайте перейдем к другим датчикам, связанным с нашим здоровьем: сенсор электроактивности организма. Это может быть электрокардиография: мы смотрим, как сердце ведет себя электрически, либо электроэнцефалография - это про наш мозг. Здесь очень простая теория: подключить электроды к поверхности нашего тела и смотреть, какие токи и напряжения у нас возникают. Можно измерить электрическое расстояние между различными частями тела, и чем больше измерительных точек мы используем, тем точнее будут наши измерения.

Источник: MedicalFuturistИсточник: MedicalFuturist

Тут я снова призываю помнить о том, что в быту это вряд ли сработает. Если вы в госпитале, либо вы профессиональный атлет, желающий улучшить свои показатели, то может быть, вы этим воспользуетесь. Но для большинства из нас обвешаться датчиками сложно, и вряд ли кто-то будет это делать в повседневности. Лучшее приложение, по всей видимости, будет наименее инвазивным, чтобы не не мучить пользователя бесконечным измерениями.

Давайте посмотрим, какие приложения датчиков электроактивности организма есть. Скорее всего, вы хотя бы раз в поликлинике снимали электрокардиограмму.

Источник: ECG MedicalИсточник: ECG Medical

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

Умный дом

Итак, мы обсудили три сенсора для персонального здоровья, и сейчас мы переходим к Умному дому.

Газовые сенсоры

Мне бы хотелось выделить для начала газовый сенсор. Какие газы нас волнуют? CO2 - углекислый газ. Мы не хотим находиться в помещении, где много углекислого газа. Если у нас газовая плита, то если в комнате появляется CH4 - метан - это проблема, может быть взрыв. Другие взрывчатые вещества тоже хотелось бы определять. Как это делается? Хорошо известен оптический способ, или, я бы даже сказал, спектроскопический, когда поглощение молекулами газа на определенной длине волны связано с концентрацией этого газа.

Источник: researchgate.netИсточник: researchgate.net

Бывают датчики электрохимические: взаимодействие молекул газа с поверхностью нашего электрического прибора может быть зарегистрировано электрическим способом.

Источник: OrientJChemИсточник: OrientJChem

Наиболее футуристический, и мне безумно интересен, третий вариант, когда используется система обоняния человека, насекомых, животных - биологический. Тут важно сказать, что в случае первых двух сенсоров мы можем определять только простые вещи типа углекислого газа. Клетка же, в отличие от других сенсоров, чувствительна к запахам, которые важны для людей: запах протухшего мяса, запах духов - это реально важные для нас запахи. Использовать генетически модифицированную обонятельную клетку - очень интересная тема, в ближайшем будущем возможно будут большие прорывы.

Источник: Tokyo Institute of TechnologyИсточник: Tokyo Institute of Technology

Я хотел бы обратить внимание вот на что: посмотрите, компьютеры распознают речь, зрение, это все подвластно анализу инженеров, но с запахами ситуация совсем пока не блестящая. Нам по-прежнему нужны люди-эксперты, если мы говорим про духи, никакой автоматизации распознавания запахов пока что нет. Тема исключительно интересная!

Вот идеи для студенческих проектов: отслеживание качества воздуха,углекислоты и утечки опасных газов. Еще один пример - контроль качества продуктов. У вас в холодильнике прокисло молоко, как об этом сообщить пользователю? Т.е. важно применение сенсора в неком устройстве, а не сенсор сам по себе.

Из примеров устройств. Как насчет рекуперации воздуха в помещении? Если концентрация углекислоты поднялась, не пора ли автоматически проветрить помещение? Если такие продукты на рынке есть, то насколько они идеальные, насколько они встраиваются в существующую инфраструктуру - большой вопрос. Повторюсь, важно не что-то потрясающее и новое, а что-то, что продается.

Поиск предметов

Не знаю, как вы, а я регулярно теряю вещи, поэтому любая автоматизация поиска потерянных вещей - ключей, книжек, компьютеров, собак, кошек - очень интересная тема. Опять же, нельзя сказать, что на рынке этого нет. Мы можем искать что-то вблизи, в масштабах нескольких метров, но если кошка или собака убежала, нам уже нужны километры. Вся эта тема, мне кажется, готова для коммерциализации.

Если начать с самых простых решений, то стоит сказать о пассивных RFID-метках. Это просто небольшая антенна, связанная с микросхемой, наверняка вы видели их на товарах в магазинах.

Источник: all-electronics.deИсточник: all-electronics.de

Другое решение - поставить GPS-сенсор, прикрепить его на машину или на кошку, и узнать, где она, даже во многих километрах от нас.

Источник: gps-tracking.com.uaИсточник: gps-tracking.com.ua

А если необходимо будет искать потерянные вещи на больших расстояниях? Скорее всего нам понадобятся батарейки. Хорошо, если батарейки хватит на несколько месяцев, но, например, в случае GPS-геолокации, она может служить только пару дней. И это плохая новость для нас, инженеров. Для такого кейса неприемлемо, чтобы пользователь часто менял или заряжал батарейку, и это нерешенная проблема. Если вначале я говорил, что для сенсоров все технологии известны и нужно найти лишь приложение для них, то здесь я бы сказал, что есть место для хорошей физики и радиоэлектроники.

Какие еще идеи для проектов я бы хотел предложить? Может быть, это звучит футуристически, но если будет решена проблема с батарейками и мы можем пометить вещи, то возможно компьютерная система сможет навести порядок, если вы не способны сделать это сами? Может ли система запомнить, что такое порядок, когда вещи находятся на своих местах, и если вдруг вещь окажется не на своем месте, система вежливо нас попросит положить эту вещь на место? Это реально моя любимая идея, мне кажется, здесь очень большие приложения, потому что мы не самый аккуратный биологический вид на этой планете, и мы постоянно боремся с бардаком в доме и на работе. Если мы сможем электронным способом навести порядок, это было бы классно.

Заключение

Итак, давайте посмотрим, о чем мы сегодня говорили. Мы поговорили о том, какие направления Интернета вещей нам интересны: это персональное здоровье, это Умный дом. Мы коротко обсудили, какие сенсоры для этого используются и дали примеры, как эти сенсоры могли бы быть встроены в приложения, которые потенциально могут найти своих потребителей.

Я хотел бы дать вам несколько рекомендаций. Прежде всего - ориентироваться на коммерческий успех. Если вы инженер, я бы не рекомендовал слишком сосредотачиваться на науке, на формулах. Начните с покупателей ваших идей. Инженерия - это не просто создание вещей, она начинается с продаж. По моему мнению, инженер, не думающий о продажах, по всей видимости, не самый лучший инженер.

Далее, при создании ваших решений Интернета вещей, я рекомендую руководствоваться следующими принципами.

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

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

  2. Защита данных. Используйте криптографию с самого начала; что бы вы ни делали, ваши данные должны быть защищены! Тем более, если мы говорим о таких чувствительных вещах, как персональная медицина.

  3. Эффективное энергопотребление. Как я уже говорил, если вы хотите использовать батарейки, подумайте, как часто потребитель захочет менять их? Ваш продукт должен быть настолько энергоэффективным, чтобы он работал на батарейке многие годы, либо вы могли использовать методы передачи энергии на расстоянии.

Я хотел бы завершить лекцию, с моей точки зрения, идеальным устройством - Умной розеткой! Реально, когда я думаю про умную розетку, она удовлетворяет всем принципам, о которых я только что говорил: прозрачность и беспроводность. По определению, в розетке всегда есть электричество, вам не нужно думать о батарейках. Вы не добавляете ничего нового, вы не навешиваете ничего на себя. И если в этой розетке сделать модульную систему и добавить разъемы для новых расширений, мне кажется, это могло бы быть удачной идеей для стартапа.

Повторяю, это не новая вещь. Но умные розетки есть пока что не в каждом доме - в ваших руках сделать так, чтобы умные розетки были везде. Какие расширения этой розетки можно предложить? Из примеров по Умного дома: это датчики газов. Можно встроить поиск предметов в розетку. Чтобы найти положение вещей, нужно делать триангуляцию, то есть посмотреть расстояние до разных розеток, и узнать, где находится эта вещь, и мне кажется, в случае розеток это абсолютно возможно.

Источник: astra-media.byИсточник: astra-media.by

Если есть вопросы, пишите в комментариях. Я и мои коллеги в Samsung Research Russia всегда рады пообщаться с талантливыми интересующимися людьми.

Видео лекции

Авторы

Станислав Полонский Начальник управления перспективных исследований и разработок Исследовательского центра Samsung

Татьяна Волкова Автор учебной программы трека по Интернету вещей IT Академии Samsung, специалист по программам корпоративной социальной ответственности Исследовательского центра Samsung

Ссылки

Если вам понравился материал, то ознакомьтесь и с другими статьями тех же авторов:

  1. Статья Татьяны Волковой График Гартнера 2019: о чём все эти модные слова?

  2. Статья Станислава Полонского 5G - где и кому он нужен?

Подробнее..

Подключаем нагрудный датчик пульса по Bluetooth на Swift

02.04.2021 14:12:19 | Автор: admin

С чего все началось?

Около года назад я приобрел данный девайс для контроля частоты сердечных сокращений (далее - ЧСС) во время тренировок. Датчик отлично подсоединяется к телефону, умным часам по Bluetooth, но обычно, фитнесс-приложения, анализирующие подобного рода данные требуют либо подписки, либо нагружены излишне сложными аналитиками, которые мне, как рядовому пользователю не очень интересны. Поэтому у меня родилась идея написать свое приложение для контроля ЧСС во время тренировок для IOS на Swift.

Немного теории о технологии Bluetooth LE

Bluetooth Low Energy - очень популярный и распространённый протокол обмена данными, который мы используем повсеместно и который становится все популярнее с каждым днем. У меня даже чайник на кухне управляется дистанционно через BLE. Low energy, кстати, гораздо сниженное энергопотребление в отличие от "голого" Bluetooth, настолько сниженное, что устройство готово общаться по данному протоколу на одной батарейке несколько месяцев, а то и лет.

Конечно, цитировать и переписывать спецификацию протокола BLE 5.2 нет никакого смысла, поэтому ограничимся основными понятиями.

Центральное и периферийное устройство

В зависимости от использования и назначения, устройство Bluetooth может быть:

  • Центральным (главным) - получает данные от периферийного устройства (наш телефон)

  • Периферийным - устройство, которое отправляет данные на центральное устройство (датчик ЧСС)

Рекламные пакеты данных протокола

Рекламные или оповещательные данные отправляются с периферийного устройства в виде пакетов, которые содержат в себе основную информацию об устройстве: его название, а также его функциональные возможности. Задача центрального устройства, получить их, прочитать, а после выделить из списка периферии необходимый для подключения девайс.

Объем рекламных пакетов не очень большой и вместить всю информацию об устройстве не способен. Чтобы получить доступ ко всем возможностям и характеристикам устройства, необходимо выполнить подключение, после чего считать его данные, которые, в свою очередь, могут быть предоставлены в виде:

  • Сервиса (услуг) - набор данных, описывающих функции устройства. В нашем случае мы увидим службу получения ЧСС.

  • Характеристик - дополнительных описаний сервисов устройства. Например характеристика изменения сердечного ритма в секунду, а также положения датчика на теле.

Переходя к абстракциям, сервисом является некий шкаф, в котором много ящиков - характеристик. Причем каждый сервис уникален и представлен идентификатором UUID, который может быть 16-битным или 128-битным, в зависимости от типов сервисов.

Перейдем к написанию кода

Создадим проект в Xcode с одноимённым названием, после чего добавим несколько необходимых Label в Main.storyboard и перетянем outlets этих labels во View Controller, закрепим их с помощью constraints, а также скроем их для первоначального изображения в методе viewDidLoad, как я сделал это на изображении:

Я создал outlets для текстовых значений "121" и "грудь", другие же текстовые значения просто закрепил на view, так как изменений в них делать мы не планируем.

Отладку и демонстрацию работы необходимо совершать на реальном устройстве, так как симулятор не поддерживает возможность работы по протоколу Bluetooth.

В файле Info.plist проекта необходимо добавить свойство: Bluetooth Always Usage Description и прикрепить к нему описание, чтобы уведомить пользователя об использовании данных по Bluetooth при первом запуске приложения. Если данное свойство не добавить в список, то приложение "упадет" с одноименной ошибкой. Не забывайте про это!

Подключаем библиотеку Bluetooth

Тут все просто, для подключения библиотеки воспользуемся следующей строчкой:

import CoreBluetooth

Вспомним, что по протоколу у нас существуют так называемые центральные и периферийные устройства, логично предположить, что основной функционал работы данного протокола будет исполнен методами делегатов централи и периферии.

Для начала создадим переменную центрального (главного) устройства в проекте рядом с объявлением других переменных:

var centralManager: CBCentralManager!

Теперь, чтобы получить доступ к методам необходимо назначить ViewController делегатом, но предварительно подпишем его под протокол CBCentralManagerDelegate. Сделать это предлагаю в extension ViewController, так будет рациональнее.

extension ViewController: CBCentralManagerDelegate {}

Xcode на такое пользовательское действие отреагирует ошибкой: "Type 'ViewController' does not conform to protocol 'CBCentralManagerDelegate'", оповещая, что данный протокол требует обязательную реализацию метода: "func centralManagerDidUpdateState(_ central: CBCentralManager)". Нажмем "fix", добавив этот метод в проект. Данный метод нужен для автоматической проверки состояния центрального менеджера, которого мы создали ранее.

Чтобы отобразить все состояния центрального менеджера, в теле метода "func centralManagerDidUpdateState(_ central: CBCentralManager)" напишем:

 func centralManagerDidUpdateState(_ central: CBCentralManager) {        switch central.state {        }

Xcode автоматически предложит вставить все возможные состояния данного условия, соглашаемся с ним. А в каждом из состояний напишем функцию print("это состояние"):

   extension ViewController: CBCentralManagerDelegate {    func centralManagerDidUpdateState(_ central: CBCentralManager) {        switch central.state {        case .unknown:            print ("central.state is unknown")        case .resetting:            print ("central.state is resetting")        case .unsupported:            print ("central.state is unsupported")        case .unauthorized:            print ("central.state is unauthorized")        case .poweredOff:            print ("central.state is poweredOff")        case .poweredOn:            print ("central.state is poweredOn")        @unknown default:            break        }    }}

Теперь нам осталось проинициализировать переменную "centralManager" и задать ей делегирование. Сделаем это в методе "viewDidLoad", а в качестве параметра очереди напишем "nil", определяя всю работу про Bluetooth в главной очереди.

override func viewDidLoad() {        super.viewDidLoad()        centralManager = CBCentralManager(delegate: self, queue: nil)        heartRateLabel.isHidden = true        bodyLocationLabel.isHidden = true    }

Собираем проект, запускаем на устройстве с включенным Bluetooth, видим системный запрос за его использование, соглашаемся и получаем в консоль заветное сообщение "central.state is poweredOn", которое сигнализирует нам о том, что центральный менеджер готов к работе. Если выключить Bluetooth на телефоне, то в консоли появится логичное "central.state is poweredOff".

Поиск Bluetooth устройств

Центральный менеджер ждет дальнейших указаний, и сейчас он их получит. Для этого в методе "centralManagerDidUpdateState" в случае ".poweredOn" после метода "print" пишем:

centralManager.scanForPeripherals(withServices: nil)

Менеджер начнет сканировать все доступные вокруг устройства, а чтобы мы смогли увидеть их в консоли приложения, необходимо реализовать метод делегата в extension ViewController ниже метода "centralManagerDidUpdateState" следующим образом:

 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {        print(peripheral)    }

Запускаем приложение... И теперь в консоли мы можем увидеть множество всех доступных для подключения устройств. Замечательно! Одним из них и является необходимый для подключения пульсометр. Но чтобы упростить поиск пульсометра, можно воспользоваться некоторой хитростью, которую я сейчас покажу.

Идентификатор служб UUID

Я ранее упомянул наличие данного идентификатора в протоколе Bluetooth как уникальную характеристику для различных устройств, поэтому могу сказать вам, что пульсометры обладают таким уникальным UUID для своей непосредственной службы измерения ЧСС. Список всех UUID можно также найти в спецификации, из которой я нашел нужный: "0x180D". Добавим новую константу в проект над объявленными ранее outlets:

let heartRateUUID = CBUUID(string: "0x180D")

Также обновим метод "centralManager.scanForPeripherals(withServices: nil)" добавив в него вышенаписанный идентификатор пульсометра:

case .poweredOn:            print ("central.state is poweredOn")            centralManager.scanForPeripherals(withServices: [heartRateUUID] )

Теперь центральный менеджер находится в поиске устройств с данным UUID, и после некоторого времени в консоли появиться заветное устройство:

<CBPeripheral: 0x280214000, identifier = D5A5CD3E-33AC-7245-4294-4FFB9B986DFC, name = COOSPO H6 0062870, state = disconnected>

Теперь необходимо создать переменную в проекте, с которой мы сможем связать данное устройство, для этого рядом с "var centralManager: CBCentralManager!" напишем:

var heartRatePeripheral: CBPeripheral!

А в методе "didDiscover peripheral" свяжем найденное устройство с вышеобъявленной переменной и прекратим поиск новых устройств с помощью метода:

 func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {        print(peripheral)        heartRatePeripheral = peripheral        centralManager.stopScan()    }

Подключаемся к пульсометру

Для этого напишем под строкой "centralManager.stopScan()":

centralManager.connect(heartRatePeripheral, options: nil)

Нам уже удалось подключиться к пульсометру, но чтобы это действительно увидеть, необходимо реализовать еще один метод делегата "didConnect peripheral" ниже метода "didDiscover peripheral", который автоматически вызывается при подключении нового устройства:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {        print("Соединение установлено")    }

Собираем проект, запускаем на устройстве и видим в консоле заветное "Соединение установлено". Хороший результат, теперь двигаемся дальше.

Получаем список сервисов с пульсометра

После того, как соединение установлено, необходимо сделать запрос об услугах (сервисах), которые данный пульсометр готов предоставить. Для этого после установки соединения вызовем метод "heartRatePeripheral.discoverServices()" в методе "didConnect", который примет следующий вид:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {        print("Соединение установлено")        heartRatePeripheral.discoverServices(nil)    }

Запрос на получение сервисов сделан, а чтобы их увидеть и начать с ними работать, необходимо расширить класс протоколом "CBPeripheralDelegate" в самом низу нашего проекта и вызвать метод "peripheral(_:didDiscoverServices:)" следующим образом:

extension ViewController: CBPeripheralDelegate {        func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {        guard let services = peripheral.services else { return }        for service in services {            print(service)        }    }}

Метод получает сервисы, сообщает об этом центральному менеджеру и выводит их в консоль. В данный момент консоль будет пуста, так как необходимо делегировать данный протокол периферийному устройству "heartRatePeripheral". Сделаем это после инициализации периферийного устройства в следующем методе:

  func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {        print(peripheral)        heartRatePeripheral = peripheral                heartRatePeripheral.delegate = self                centralManager.stopScan()        centralManager.connect(heartRatePeripheral, options: nil)    }

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

<CBService: 0x2824b4340, isPrimary = YES, UUID = Heart Rate>

<CBService: 0x2824b4240, isPrimary = YES, UUID = Battery>

<CBService: 0x2824b4280, isPrimary = YES, UUID = Device Information>

<CBService: 0x2824b4200, isPrimary = YES, UUID = 8FC3FD00-F21D-11E3-976C-0002A5D5C51B>

Не все сервисы нам интересны и оставить необходимо лишь первый. Для этого можно провести так называемую фильтрацию с помощью идентификатора UUID в методе "heartRatePeripheral.discoverServices()"

heartRatePeripheral.discoverServices([heartRateUUID])

Вот теперь список служб отобразится в виде "<CBService: 0x2824b4340, isPrimary = YES, UUID = Heart Rate>", из которой мы сможем извлечь нужные нам характеристики - ящики ( шкафа мы уже получили).

Достаем характеристики из шкафа

Шкаф-сервис нам известен, осталось посмотреть, что он предлагает и получить это. Сделаем запрос на получение характеристик, для этого в теле метода "didDiscoverServices - peripheral" реализуем метод - поиск:

extension ViewController: CBPeripheralDelegate {        func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {        guard let services = peripheral.services else { return }        for service in services {            peripheral.discoverCharacteristics(nil, for: service)        }    }}

Теперь доступный сервис будет посылать свои характеристики, а увидеть мы их сможем в самостоятельном методе делегата "CBPeripheralDelegate" под названием "didDiscoverCharacteristicsFor". Реализуем его и выведем в консоль все доступные характеристики:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {        guard let characteristics = service.characteristics else { return }        for characteristic in characteristics {            print(characteristic)        }    }

Запускаем программу, видим, что характеристики получены, а консоль заполнилась следующими строками:

<CBCharacteristic: 0x28024c120, UUID = 2A37, properties = 0x10, value = {length = 2, bytes = 0x0469}, notifying = NO>

<CBCharacteristic: 0x28024c180, UUID = 2A38, properties = 0x2, value = {length = 1, bytes = 0x01}, notifying = NO>

Видно, что у данной службы две характеристики, имеющие два уникальных идентификатора. Из спецификации на Bluetooth узнаем, что UUID = 2A37 отвечает за измерение ЧСС, а UUID = 2A38 за положение датчика на теле. Положение датчика на теле не самая интересная характеристика в данной теме, но будет полезно считать и ее.

Для удобства добавим в проект два уникальных идентификатора данных характеристик следующим образом:

 let heartRateUUID = CBUUID(string: "0x180D") let heartRateCharacteristicCBUUID = CBUUID(string: "2A37") let bodyLocationCharacteristicCBUUID = CBUUID(string: "2A38")

Характеристики отличаются друг от друга типами свойств. Например, характеристика ЧСС имеет свойство ".notify" т.е. она уведомляет об изменении значения ЧСС, а характеристика положения на теле имеет свойство ".read", т.е. может быть считана напрямую. Данное пояснение необходимо, чтобы правильно получить значения из них.

Положение пульсометра на теле

Характеристика выведена консоль, теперь нужно лишь реализовать метода считывая значений из нее. Для этого напишем запрос на чтение значений "peripheral.readValue(for: characteristic)"

 func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {        guard let characteristics = service.characteristics else { return }        for characteristic in characteristics {            peripheral.readValue(for: characteristic)        }    }

Запрос написан, как вы догадываетесь, нужно реализовать еще один метод "peripheral(_:didUpdateValueFor:error:)" делегата "CBPeripheralDelegate", который будет в асинхронном режиме получать ответ с данного запроса, причем в данном методе напишем конструкцию "switch - case", чтобы была возможность разделить характеристики по уникальному идентификатору:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,                error: Error?) {  switch characteristic.uuid {    case bodySensorLocationCharacteristicCBUUID:      print(characteristic.value ?? "no value")    default:      print("Unhandled Characteristic UUID: \(characteristic.uuid)")  }}

В консоли после выполнения данной программы появится строка "1 bytes". Это нужный результат, потому что мы пытались вывести объект типа "data".

Чтобы "распасить" данный байт, необходимо снова прибегнуть к спецификации, из которой мы сможем понять, как данный байт заполнен. Ускоряя результат, реализуем ниже функцию получения строки положения на теле датчика ЧСС из этого байта:

      private func bodyLocation(from characteristic: CBCharacteristic) -> String {        guard let characteristicData = characteristic.value,              let byte = characteristicData.first else { return "Error" }        switch byte {        case 0: return "Другое"        case 1: return "Грудь"        case 2: return "Запястье"        case 3: return "Палец"        case 4: return "Ладонь"        case 5: return "Мочка уха"        case 6: return "Нога"        default:            return "Резерв"        }    }

И теперь вызовем данную функцию в методе "didUpdateValueFor characteristic", одновременно выводя результат на экран телефона (не забудем показать скрытый label для положения датчика):

   func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,                    error: Error?) {                switch characteristic.uuid {                case bodyLocationCharacteristicCBUUID:            let bodySensorLocation = bodyLocation(from: characteristic)            bodyLocationLabel.text = bodySensorLocation            bodyLocationLabel.isHidden = false                  default:          print("Unhandled Characteristic UUID: \(characteristic.uuid)")      }            }

Ура! Характеристика успешно получена, прочитана и выведена на экран!

Не совсем ясно, где еще можно носить данный пульсометр, поэтому существует данная характеристика :)


Получение ЧСС и вывод на экран пользователя

Осталось совсем немного, и теперь нужно получить значения из характеристики ЧСС. Как мы помним, у нее тип значения ".notify", поэтому нам нужно как бы "подписаться на нее", чтобы она присылала обновленные значения ЧСС. Для этого нужно выполнить метод "peripheral.setNotifyValue(true, for: characteristic)" в функции "didDiscoverCharacteristicsFor service:

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {        guard let characteristics = service.characteristics else { return }        for characteristic in characteristics {            peripheral.readValue(for: characteristic)            peripheral.setNotifyValue(true, for: characteristic)        }    }

Если запустить приложение, то в консоли появятся стоки:

Unhandled Characteristic UUID: 2A37

Unhandled Characteristic UUID: 2A37

Unhandled Characteristic UUID: 2A37

Именно в этой характеристики и лежат данные о ЧСС. Теперь необходимо провернуть такую же развертку этих данных, обращаясь к спецификации. В некоторых моделях данные могут быть представлены либо 1 либо 2 байтами. Чтобы не получить конфуз, реализуем метод для "парсинга" этих данных в нужном порядке в протоколе "CBPeripheralDelegate".

  private func heartRate(from characteristic: CBCharacteristic) -> Int {        guard let characteristicData = characteristic.value else { return -1 }        let byteArray = [UInt8](characteristicData)                let firstBitValue = byteArray[0] & 0x01        if firstBitValue == 0 {            return Int(byteArray[1])        } else {            return (Int(byteArray[1]) << 8) + Int(byteArray[2])        }    }

И, наконец, добавим еще один case в методе "peripheral(_:didUpdateValueFor:error:)", в котором получим ЧСС, а также обновим и покажем label пользовательского интерфейса:

   func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic,                    error: Error?) {                switch characteristic.uuid {                case bodyLocationCharacteristicCBUUID:            let bodySensorLocation = bodyLocation(from: characteristic)            bodyLocationLabel.text = bodySensorLocation            bodyLocationLabel.isHidden = false                    case heartRateCharacteristicCBUUID:            let bpm = heartRate(from: characteristic)            heartRateLabel.text = String(bpm)            heartRateLabel.isHidden = false                    default:          print("Unhandled Characteristic UUID: \(characteristic.uuid)")      }    }

Поздравляю!

Теперь данные с пульсометра выводятся на экран телефона. Я даже слегка нервничаю :)


Итоги

В целом гайд по использованию Bluetooth для подключения датчика ЧСС вышел немного большим и местами сложным, надеюсь, что основной смысл мне удалось донести. Конечно, есть еще несколько нереализованных методов, которые можно было бы добавить (например, метод переподключения при обрыве соединения), но я посчитал этого набора достаточным, чтобы в меру оценить лаконичность и удобность библиотеки на swift CoreBluetooth.

Всем успехов и спасибо!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru