Для создания системы позиционирования внутри помещений с использованием ESP32 требуются технологии, выходящие за рамки GPS. В то время как GPS-трекеры отлично подходят для использования на открытом воздухе, система позиционирования внутри помещений требует технологии сверхширокополосной связи (UWB) для достижения точности на уровне сантиметров внутри зданий. В этом руководстве показано, как создать полноценную систему позиционирования внутри помещений на основе UWB-модуля Qorvo DWM3000 с ESP32, способную отслеживать устройства с точностью до 10 см для отслеживания активов по технологии UWB и приложений определения местоположения в реальном времени.
![]()
Если вы когда-либо пытались отслеживать устройство в помещении с помощью GPS, вы знаете его ограничения. Спутники GPS, находящиеся на расстоянии 20 000 км, не могут эффективно проникать сквозь бетон, сталь и стекло. Даже в полузакрытых помещениях, таких как склады или стадионы, точность GPS снижается до нескольких метров. Этого достаточно для идентификации зданий, но недостаточно для точной навигации внутри помещений или отслеживания активов. Если вам не требуется точное отслеживание внутри помещений и вы хотите использовать GPS, тогда вам можно ознакомиться с платформой отслеживания GPS GeoLinker — бесплатным инструментом с открытым исходным кодом от сервиса CircuitDigest, который позволяет вам беспрепятственно создавать, тестировать и развертывать проекты GPS.
Более современные приложения, такие как складская робототехника, робототехника на заводах, системы определения местоположения в реальном времени на основе UWB , навигация/отслеживание в аэропортах и дополненная/виртуальная реальность, требуют быстрых и точных систем позиционирования внутри помещений. Традиционные технологии, такие как Bluetooth и Wi-Fi, не могут обеспечить точность до долей метра с постоянной стабильностью; именно поэтому данный проект по созданию системы позиционирования UWB на базе ESP32 сосредоточен на модуле позиционирования DWM3000 UWB и проверенной методологии. В этом проекте по созданию системы UWB своими руками мы покажем вам, как настроить систему позиционирования внутри помещений на базе ESP32 с использованием модуля DWM3000 и визуализации в реальном времени.

Вот тут-то и пригодится сверхширокополосная связь (UWB). В отличие от GPS, UWB не зависит от спутников. Вместо этого она измеряет время, необходимое радиоимпульсам для распространения между устройствами, обеспечивая точность позиционирования внутри помещений до 10 см , что позволяет точно знать, на каком стуле вы сидите, а не просто в какой комнате.
Сравнение: UWB против традиционных технологий позиционирования внутри помещений
Данное сравнение вы можете посмотреть в следующей таблице.
Также основные принципы рассматриваемого проекта вы можете посмотреть в следующем видео.
Qorvo DWM3000: ядро вашей системы позиционирования внутри помещений на базе UWB с использованием ESP32
Qorvo DWM3000 — это компактный, полностью интегрированный сверхширокополосный (UWB) приемопередающий модуль, предназначенный для высокоточных задач определения местоположения и расстояния в системах позиционирования UWB. Созданный на основе микросхемы Qorvo DW3110, он соответствует стандарту IEEE 802.15.4z и полностью совместим со спецификациями FiRa Consortium PHY/MAC, что означает возможность его работы с другими UWB-устройствами в экосистеме.
![]()
В отличие от микросхем без корпуса, DWM3000 поставляется в виде готового к использованию радиочастотного модуля, а это значит, что вам не нужно быть радиочастотным инженером, чтобы подключить его к вашей системе позиционирования UWB. Он уже включает в себя высокопроизводительную керамическую UWB-антенну с всенаправленными характеристиками. На борту имеются все пассивные радиочастотные компоненты, кварцевый генератор на 38,4 МГц и полная система управления питанием.

Такая конструкция, работающая по принципу «подключи и работай», особенно привлекательна для разработчиков, которым нужны передовые технологии определения местоположения без необходимости тратить месяцы на проектирование радиочастотных систем.
Как работает система позиционирования внутри помещений на базе UWB с использованием ESP32
Создание системы позиционирования внутри помещений с использованием технологии UWB включает в себя следующие ключевые этапы:
1. Размещение якорей: Разместите 3 или более UWB-якорей в известных местах (координатах) в вашей среде для наилучшего покрытия в системе определения местоположения в реальном времени (RTLS) на основе UWB. Размещение якорей, прошивка метки и логика трилатерации направлены на обеспечение надежного позиционирования ESP32 внутри помещений с точностью до сантиметра в сложных внутренних пространствах.
2. Инициализация метки: Мобильное устройство позиционирования UWB (метка) инициирует перемещение якорей.
3. Двустороннее определение расстояния: Метки и якоря используют метод измерения времени пролета с точностью до одной наносекунды, отправляя и принимая точные UWB-импульсы.
4. Расчет расстояний: Использование двустороннего TWR на метках позволяет рассчитать расстояние между меткой и опорными точками для отслеживания местоположения по UWB.
5. Трилатерация: Скрипт на Python собирает данные о расстояниях от 3 или более опорных точек и вычисляет точные координаты (x,y).
6. Визуализация в реальном времени: Данные о местоположении с метки передаются по Wi-Fi для отображения отслеживания в режиме реального времени на плане помещения для отслеживания активов по UWB.
Возможности DWM3000 для систем позиционирования внутри помещений
Представлены в следующей таблице.
Почему технология UWB превосходно подходит для систем позиционирования внутри помещений
Ключевое преимущество UWB заключается в его способности измерять время распространения сигнала с исключительной точностью. Поскольку DWM3000 разработан для стандарта 802.15.4z, он передает очень короткие импульсы (порядка наносекунд) в широком диапазоне частот. Это делает систему позиционирования UWB устойчивой к многолучевым помехам, распространенной проблеме в помещениях, где стены, потолки и металлические поверхности вызывают отражения.
При позиционировании внутри помещений многолучевое распространение сигнала может значительно снизить точность Wi-Fi или Bluetooth, но технология UWB, особенно DWM3000, способна отфильтровывать эти отражения и фокусироваться на прямом сигнале, обеспечивая точность до сантиметра даже в загроможденных помещениях.
Таким образом, система позиционирования UWB устойчива к многолучевым помехам, которые часто возникают в помещениях из-за отражения от стен, потолков и металлических поверхностей.
В системах локализации UWB внутри помещений сигнал, распространяющийся по более длинному пути в результате многолучевого распространения, может привести к крайне неточным результатам Wi-Fi или Bluetooth, в то время как UWB позволяет отбрасывать нежелательные отражения и фокусироваться исключительно на прямом сигнале, особенно это касается модуля позиционирования DWM3000 UWB. Системы отслеживания местоположения UWB отлично подходят для закрытых помещений, где часто встречаются отражения от металлических полок, а отслеживание объектов имеет важное значение в промышленных условиях. Поскольку традиционные системы Bluetooth или Wi-Fi испытывают трудности с отражениями и многолучевым распространением внутри помещений, данный подход использует UWB для обеспечения точности, что позволяет создать полноценное решение для позиционирования внутри помещений на базе ESP32, а не просто осуществлять грубое зональное отслеживание.
Другие модули Qorvo UWB для систем позиционирования внутри помещений
Компания Qorvo предлагает несколько UWB-модулей со схожими форм-факторами, но разными чипсетами, частотными характеристиками и соответствием стандартам, подходящих для различных реализаций UWB-систем позиционирования. Их краткое сравнение представлено в следующей таблице.
Принципы работы нашей системы позиционирования внутри помещений на базе UWB
В своей системе отслеживания местоположения внутри помещений с использованием UWB-технологии я не использовал все функции DWM3000; я сосредоточился на возможностях, необходимых для системы отслеживания местоположения в реальном времени в 2D-пространстве внутри помещений, чтобы создать систему позиционирования внутри помещений с использованием UWB-технологии на базе ESP32:
- Режим двустороннего измерения расстояния (DS-TWR) позволяет точно измерять расстояния между меткой и якорем.
- Работа на канале 5 (6,5 ГГц), обеспечивающая стабильную работу моей системы позиционирования UWB внутри помещений с использованием ESP32. Поддерживает каналы UWB 5 (6,5 ГГц) и 9 (8 ГГц), оба подходят для глобального использования в приложениях UWB RTLS.
- SPI-связь с хост-микроконтроллером ESP32.
- Стандартная калибровка задержки антенны для дальнейшей оптимизации точности определения местоположения внутри помещений с использованием сверхширокополосной связи (UWB).
- Высокая скорость передачи данных (6,8 Мбит/с) позволяет проводить измерения и ускорять циклы измерений для отслеживания местоположения в режиме реального времени с помощью UWB-технологии.
Кроме отслеживания активов по протоколу UWB, DWM3000 также поддерживает такие опции, как TDoA, режимы работы с низким энергопотреблением меток и многоканальную работу, если в дальнейшем потребуется масштабировать систему для отслеживания нескольких меток или использовать ее в больших открытых пространствах в качестве комплексной системы UWB RTLS.
| Двусторонняя двухсторонняя система измерения расстояния (DS-TWR) | режим для измерения расстояний от метки до якоря. |
| 5 канал | работа на частоте 6,5 ГГц для стабильной работы. |
| SPI | связь с главным микроконтроллером ESP32. |
| Стандартная калибровка задержки антенны | для повышения точности. |
| Высокая скорость передачи данных (6,8 Мбит/с) | для обеспечения более быстрых циклов измерений. |
Помимо отслеживания активов по протоколу UWB, доступны такие функции, как TDoA, режимы работы с низким энергопотреблением меток и многоканальная работа, если в дальнейшем потребуется масштабировать систему для отслеживания нескольких меток или для больших помещений.
Компоненты, необходимые для создания системы позиционирования
Для создания этой системы позиционирования внутри помещений по протоколу UWB с использованием ESP32 вам потребуется следующее оборудование:
| Компонент | Количество | Примечания |
| Модуль Qorvo DWM3000 UWB (купить на AliExpress, есть значительно более дешевые аналоги этого модуля, но их нужно будет немного адаптировать к данному проекту) | 4 | Один в качестве метки, три в качестве якоря |
| Плата разработки ESP32-WROOM (купить на AliExpress) | 4 | Каждый DWM3000 оснащен собственным модулем ESP32-WROOM для связи и обработки данных по протоколу SPI |
| Кабель Micro USB | 4 | Для программирования и питания каждого ESP32-WROOM |
| Макетная плата / Изготовление креплений для печатных плат на заказ | 4 | Для установки и подключения DWM3000 к ESP32 |
| Источник питания 5 В USB / Power Bank | 4 | Для питания каждого модуля ESP32 |
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Настройка оборудования и подключение контактов
Для того чтобы модули DWM3000 работали в качестве меток и якорей в этом проекте внутренней системы позиционирования UWB, мы подключаем их к платам ESP32 через интерфейс SPI. Каждая пара ESP32-DWM3000 будет иметь одинаковую схему подключения, но каждому якорю будет присвоен уникальный ANCHOR_ID в коде. ESP32 метки программируется прошивкой метки, а якоря — прошивкой якоря .

Схема подключения модуля DWM3000 к плате ESP32 для создания нашей системы позиционирования представлена на следующем рисунке.
| Контакт DWM3000 | Контакт ESP32 | Функция |
| VCC | 3.3V | Источник питания |
| GND | GND | Земля |
| SCK | GPIO18 | Тактовый сигнал SPI |
| MOSI | GPIO23 | Данные SPI (ESP32 → DWM3000) |
| MISO | GPIO19 | Данные SPI (DWM3000 → ESP32) |
| CS (Chip Select) | GPIO4 | Выбор микросхемы SPI |
| RST (Reset) | GPIO27 | Аппаратный сброс |
| IRQ | GPIO34 | Прерывание от DWM3000 (опционально для обработки событий) |
Внимание! Модуль DWM3000 работает только при напряжении 3,3 В. Подача более высокого напряжения может повредить его. Убедитесь, что источник питания чистый и имеет минимальный уровень помех для стабильной работы.


Расположение крепежных элементов в помещении для установки UWB RTLS
Для трилатерализации в вашей системе отслеживания местоположения на основе сверхширокополосной связи мы рекомендуем разместить три опорных точки в фиксированных, известных положениях.
![]()
| Якорь 1 | Нижний левый угол комнаты (например, координаты (15, 5) см) |
| Якорь 2 | Нижний правый угол комнаты (290, 5) см |
| Якорь 3 | Верхняя центральная часть (165, 625) см |
Примечание!: Эти координаты должны совпадать с массивом ANCHOR_POSITIONS в вашем скрипте просмотра экрана на Python.
Как метка взаимодействует с якорными точками в системе позиционирования UWB внутри помещений
В нашей системе определения местоположения внутри помещений с использованием UWB-технологии метка выступает в роли инициатора, а опорные точки — в роли ответчиков. Положение каждой опорной точки фиксировано и известно заранее. Задача метки — измерить расстояние до каждой опорной точки, используя для этого точный обмен UWB-сообщениями. Этот процесс называется двусторонним определением расстояния (Two-Way Ranging, TWR), и для достижения максимальной точности в нашей системе позиционирования UWB мы используем версию, называемую двусторонним определением расстояния (Double-Sided TWR, DS-TWR) .
Фиксация точных моментов времени для отслеживания местоположения с помощью UWB-технологии
Каждый раз, когда DWM3000 передает или принимает UWB-кадр, он записывает аппаратную метку времени — сверхточное значение счетчика, получаемое непосредственно от внутренних часов радиомодуля. Эти метки времени имеют точность до долей наносекунды и являются секретом сантиметровой точности отслеживания объектов в UWB-среде. ESP32 никогда не использует для этого собственные часы процессора; он просто считывает метки времени радиомодуля, используя функции API, такие как readtxtimestamp() и readrxtimestamp().
Трехпоточный обмен сообщениями в системе позиционирования внутри помещений на базе UWB с использованием ESP32
Вот что происходит во время сеанса измерения расстояния с использованием одного якорного датчика в нашей системе UWB RTLS:
- Опрос: Метка отправляет сообщение «Опрос» в момент времени T1 и записывает метку времени передачи.
- Ответ: Якорь получает запрос на этапе T2, ожидает известную задержку ответа, затем передает «Ответ» на этапе T3. Якорь записывает данные как на этапе T2, так и на этапе T3.
- Финал: Метка получает ответ на этапе T4, а затем отправляет сообщение «Финал», которое включает в себя собственные записанные метки времени, чтобы якорный узел мог рассчитать время полета (Time of Flight, ToF).
Этот трехэтапный обмен позволяет обеим сторонам обмениваться информацией о времени и устраняет необходимость в синхронизированных часах в процессе определения местоположения внутри помещений с использованием технологии UWB.
Асимметричный двусторонний алгоритм определения расстояния (двустороннее измерение расстояния)
В асимметричном двустороннем измерении расстояния время ожидания отсутствует. Когда якорь получает сообщение, он отвечает немедленно. Поскольку скорость обработки кода варьируется, время ответа метки (Treply1) не будет совпадать со временем ответа якоря (Treply2). Система использует измерение дрейфа тактовой частоты для математической коррекции ошибок, вызванных этим неравным временем ответа. Для расчета используются четыре временных интервала, измеренных в аппаратных единицах такта DWM3000:
- Tround1 = T 4 - T 1 (количество циклов обмена метками при первом обмене)
- Treply1 = T 3 - T 2 (задержка ответа якоря после получения опроса)
- Tround2 = T 6 - T 3 ( время кругового пути якоря для второго обмена)
- Treply2 = T 5 - T 4 (задержка ответа тега после получения ответа)
Эти четыре измеренных числа являются входными данными для алгебры TWR, используемой в системе позиционирования UWB.

Затем время прохождения сигнала (ToF) вычисляется следующим образом:
Получив значение ToF в тиках, мы преобразуем его в секунды, используя период тика DWM3000, а затем в расстояние, используя скорость света для точной локализации внутри помещений с помощью UWB-технологии:

Что мы использовали в нашем проекте
В нашей реализации кода мы добавили дополнительную передачу пакетов от якоря к метке после окончательной передачи, чтобы отправить Treply1 и Tround2 на сторону метки, поскольку в нашем проекте мы используем метку для вычисления расстояния между каждым якорем, для чего нам нужны эти временные интервалы от точки якоря.

Задержка и калибровка антенны для системы позиционирования внутри помещений на базе UWB
Внутренний сигнальный тракт и антенна DWM3000 вносят небольшую фиксированную задержку как при передаче, так и при приеме. Если мы не компенсируем её, погрешность в определении расстояния в системе UWB RTLS всегда будет составлять несколько сантиметров. Модуль позиционирования UWB позволяет устанавливать задержки антенн TX и RX в тактах устройства после калибровки. Это делается один раз для каждого модуля, а затем сохраняется в микропрограмме.
Обеспечение надежности измерений
Технология UWB обладает высокой устойчивостью к многолучевому распространению, однако плохая геометрия или препятствия все еще могут вызывать ошибки. Для смягчения этой проблемы в нашем проекте системы позиционирования UWB внутри помещений микропрограмма применяет простое усреднение для фильтрации шумовых измерений, а значения RSSI отслеживаются для обнаружения потенциальных условий отсутствия прямой видимости (NLOS), которые могут повлиять на точность отслеживания местоположения с помощью UWB.
От данных в формате JSON к определению местоположения в реальном времени в системах отслеживания активов UWB
После того, как метка рассчитает расстояние до всех трех опорных точек, следующим шагом будет отправка этой информации на ПК, чтобы он мог определить координаты метки и отобразить их в реальном времени. Для этого мы используем JSON — легковесный текстовый формат, который легко обрабатывается как ESP32, так и Python.
Формат JSON для данных UWB RTLS
Вот пример того, что отправляет метка после каждого цикла измерения в системе отслеживания местоположения UWB:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
{ "tag_id": 10, "anchors": { "A1": { "distance": 116.82, "raw": 116.82, "rssi": -62.23, "fp_rssi": -63.10, "round_time": 66071380, "reply_time": 81007788, "clock_offset": -0.000004 }, "A2": { "distance": 112.60, "raw": 115.42, "rssi": -68.32, "fp_rssi": -73.36, "round_time": 68508406, "reply_time": 79276298, "clock_offset": -0.000001 }, "A3": { "distance": 123.86, "raw": 124.33, "rssi": -68.28, "fp_rssi": -69.81, "round_time": 68377096, "reply_time": 79564280, "clock_offset": -0.000001 } } } |
Каждый якорь имеет метку (A1, A2, A3), и внутри каждой записи мы храним:
- tag_id: Числовой идентификатор тега, полезный, если вы отслеживаете несколько тегов одновременно в вашей системе UWB RTLS.
Внутри каждой якорной записи (A1, A2, A3): - Расстояние: Окончательное, откалиброванное расстояние в сантиметрах после применения компенсации задержки антенны и любой фильтрации для определения местоположения внутри помещений с использованием сверхширокополосной связи . Это значение используется для трилатерации.
- raw: Исходное некалиброванное расстояние в сантиметрах, полученное непосредственно из расчетов DS-TWR до применения поправок.
- rssi: Расчетная мощность сигнала принятого пакета в дБм.
- fp_rssi: Уровень сигнала, измеренный на первом пути, который помогает определить, слабее ли прямой путь, чем отражения (полезно для обнаружения отсутствия прямой видимости, отслеживания объектов UWB).
- round_time: Общее время обмена сообщениями в обоих направлениях, выраженное в тактах часов устройства.
- reply_time: Время, затраченное отвечающим (якорным) устройством на ответ, в тактах устройства.
- clock_offset: Доля дрейфа тактовой частоты между меткой и якорным датчиком, полезная для диагностики и повышения точности измерения расстояния.
ESP32 отправляет эту JSON-строку по Wi-Fi на ПК, где наш скрипт на Python готов обработать ее для визуализации отслеживания местоположения в реальном времени с помощью UWB-технологии.
Приём и анализ данных в Python для системы позиционирования внутри помещений UWB с использованием ESP32
На компьютере скрипт на Python:
- Обрабатывает входящие TCP-данные от метки в системе позиционирования UWB.
- Функция считывает строку JSON и преобразует её в словарь Python с помощью метода json.loads().
- Извлекает расстояния и значения RSSI для каждой опорной точки для обработки данных по локализации внутри помещений с использованием UWB-технологии.
Для настройки UWB RTLS скрипт уже знает фиксированные координаты каждого якорного узла в помещении, например:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
{ "tag_id": 10, "anchors": { "A1": { "distance": 116.82, "raw": 116.82, "rssi": -62.23, "fp_rssi": -63.10, "round_time": 66071380, "reply_time": 81007788, "clock_offset": -0.000004 }, "A2": { "distance": 112.60, "raw": 115.42, "rssi": -68.32, "fp_rssi": -73.36, "round_time": 68508406, "reply_time": 79276298, "clock_offset": -0.000001 }, "A3": { "distance": 123.86, "raw": 124.33, "rssi": -68.28, "fp_rssi": -69.81, "round_time": 68377096, "reply_time": 79564280, "clock_offset": -0.000001 } } } |
Скрипт уже знает фиксированные координаты каждого якоря в комнате, например:
|
1 2 3 4 5 |
anchors = { "A1": (15.0, 5.0), "A2": (300.0, 5.0), "A3": (165.0, 625.0) } |
Математика трилатерации в системе позиционирования внутри помещений на базе UWB
Если нанести точки привязки на график и обвести каждую из них кругом радиусом, равным измеренному расстоянию, то метка должна находиться в точке пересечения кругов. Это основной принцип определения местоположения внутри помещений с помощью UWB.
Математически каждая точка привязки дает одно уравнение в системе позиционирования UWB:

Где (x1 ,y1 ), (x2 ,y2 ), (x3, y3 ) — опорные точки, а d1, d2, d3 — измеренные расстояния от JSON-пакета в нашей системе отслеживания местоположения UWB.
![]()
Обработка шумов и погрешностей измерений в UWB RTLS
На практике шум и небольшие погрешности измерения расстояния означают, что вероятность пересечения окружностей в одной точке в реальной системе позиционирования внутри помещений с использованием UWB крайне мала. Именно поэтому скрипт использует оптимизацию методом наименьших квадратов для точного отслеживания объектов с помощью UWB.
В библиотеке SciPy есть функция least_squares(), которая автоматически выполняет оптимизацию для определения наиболее подходящего положения для локализации UWB внутри помещений.
В действительности, из-за шума и небольших погрешностей измерения расстояния эти окружности редко пересекаются ровно в одной точке. Именно поэтому в скрипте используется оптимизация методом наименьших квадратов:

Это делается с помощью функции least_squares() из библиотеки SciPy, которая автоматически выполняет оптимизацию.
Построение графиков в реальном времени для отслеживания местоположения с помощью UWB-технологии
После нахождения оптимального положения (x,y) выполняется следующий скрипт:
- Отмечает опорные точки как фиксированные.
- Отображает текущее местоположение метки в виде движущейся точки.
- Над каждым якорным устройством отображается значение RSSI для мониторинга уровня сигнала в реальном времени в системе UWB RTLS.
График непрерывно обновляется по мере поступления новых JSON-пакетов, обеспечивая плавное отображение перемещения метки в реальном времени для эффективного отслеживания активов с помощью UWB-технологии.

Настройка прошивки метки ESP32 для системы позиционирования внутри помещений
|
1 2 3 4 5 6 7 8 9 10 |
#include <SPI.h> #include <WiFi.h> #include <WiFiClient.h> // WiFi Configuration const char *ssid = "Semicon Media"; const char *password = "xxxxxxxxx"; const char *host = "192.168.xx.xx"; // Your PC's IP address const int port = 7007; // Choose a port number WiFiClient client; bool wifiConnected = false; |
Задайте базовый идентификационный номер привязки, что позволит легко добавлять новые привязки в дальнейшем без необходимости переделывать весь код для развертывания UWB RTLS.
В прошивку входят основные библиотеки: SPI.h для высокоскоростной связи с модулем позиционирования DWM3000 UWB и WiFi.h/WiFiClient.h для подключения к сети. Здесь жестко закодированы SSID, пароль и данные сервера, чтобы ESP32 мог немедленно подключиться к вашей локальной сети и установить TCP-соединение с ПК, на котором запущен скрипт визуализации UWB для определения местоположения внутри помещений на языке Python.
Инициализация системы и настройка UWB-тегов
|
1 2 3 4 5 6 7 |
// SPI Setup #define RST_PIN 27 #define CHIP_SELECT_PIN 4 // Scalable Anchor Configuration #define NUM_ANCHORS 3 // Change this to scale the system #define TAG_ID 10 #define FIRST_ANCHOR_ID 1 // Starting ID for anchors (1, 2, 3, ...) |
Контакты управления SPI определены для системы позиционирования UWB внутри помещений с использованием ESP32. RST_PIN позволяет ESP32 перезагружать DWM3000, а CHIP_SELECT_PIN управляет тем, какое устройство SPI активно. Блок конфигурации якорей задает общее количество якорей, присваивает уникальный идентификатор тега и устанавливает базовый идентификационный номер якоря, что упрощает добавление дополнительных якорей в дальнейшем без переделки всего кода для вашей системы UWB RTLS.
|
1 2 3 4 |
// Ranging Configuration #define FILTER_SIZE 50 // For median filter #define MIN_DISTANCE 0 #define MAX_DISTANCE 10000.0 |
Параметры определения расстояния управляют количеством сохраняемых для фильтрации выборок расстояний (FILTER_SIZE) и тем, какие значения расстояния считаются допустимыми (MIN_DISTANCE и MAX_DISTANCE). Эти проверки предотвращают попадание недопустимых значений в расчет положения.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
// UWB Configuration #define LEN_RX_CAL_CONF 4 #define LEN_TX_FCTRL_CONF 6 #define LEN_AON_DIG_CFG_CONF 3 #define PMSC_STATE_IDLE 0x3 #define FCS_LEN 2 #define STDRD_SYS_CONFIG 0x188 #define DTUNE0_CONFIG 0x0F #define SYS_STATUS_FRAME_RX_SUCC 0x2000 #define SYS_STATUS_RX_ERR 0x4279000 #define SYS_STATUS_FRAME_TX_SUCC 0x80 #define PREAMBLE_32 4 #define PREAMBLE_64 8 #define PREAMBLE_128 5 #define PREAMBLE_256 9 #define PREAMBLE_512 11 #define PREAMBLE_1024 2 #define PREAMBLE_2048 10 #define PREAMBLE_4096 3 #define PREAMBLE_1536 6 #define CHANNEL_5 0x0 #define CHANNEL_9 0x1 #define PAC4 0x03 #define PAC8 0x00 #define PAC16 0x01 #define PAC32 0x02 #define DATARATE_6_8MB 0x1 #define DATARATE_850KB 0x0 #define PHR_MODE_STANDARD 0x0 #define PHR_MODE_LONG 0x1 #define PHR_RATE_6_8MB 0x1 #define PHR_RATE_850KB 0x0 #define SPIRDY_MASK 0x80 #define RCINIT_MASK 0x100 #define BIAS_CTRL_BIAS_MASK 0x1F #define GEN_CFG_AES_LOW_REG 0x00 #define GEN_CFG_AES_HIGH_REG 0x01 #define STS_CFG_REG 0x2 #define RX_TUNE_REG 0x3 #define EXT_SYNC_REG 0x4 #define GPIO_CTRL_REG 0x5 #define DRX_REG 0x6 #define RF_CONF_REG 0x7 #define RF_CAL_REG 0x8 #define FS_CTRL_REG 0x9 #define AON_REG 0xA #define OTP_IF_REG 0xB #define CIA_REG1 0xC #define CIA_REG2 0xD #define CIA_REG3 0xE #define DIG_DIAG_REG 0xF #define PMSC_REG 0x11 #define RX_BUFFER_0_REG 0x12 #define RX_BUFFER_1_REG 0x13 #define TX_BUFFER_REG 0x14 #define ACC_MEM_REG 0x15 #define SCRATCH_RAM_REG 0x16 #define AES_RAM_REG 0x17 #define SET_1_2_REG 0x18 #define INDIRECT_PTR_A_REG 0x1D #define INDIRECT_PTR_B_REG 0x1E #define IN_PTR_CFG_REG 0x1F #define TRANSMIT_DELAY 0x3B9ACA00 #define TRANSMIT_DIFF 0x1FF #define NS_UNIT 4.0064102564102564 // ns #define PS_UNIT 15.6500400641025641 // ps #define SPEED_OF_LIGHT 0.029979245800 // in centimetres per picosecond #define CLOCK_OFFSET_CHAN_5_CONSTANT -0.5731e-3f #define CLOCK_OFFSET_CHAN_9_CONSTANT -0.1252e-3f #define NO_OFFSET 0x0 #define DEBUG_OUTPUT 0 static int ANTENNA_DELAY = 16350; |
Этот большой блок определений напрямую соответствует регистрам конфигурации DWM3000, константам и значениям настройки. Он включает номера каналов UWB, длину преамбулы, размеры PAC, скорость передачи данных, режимы PHR (Power Headroom Report), адреса регистров и константы компенсации смещения тактовой частоты для различных каналов. Параметр ANTENNA_DELAY точно настраивает время прохождения сигнала через радиочастотный вход, повышая точность измерения расстояния до сантиметрового уровня.
|
1 2 3 4 5 6 7 8 9 10 |
// Initial Radio Configuration int config[] = { CHANNEL_5, // Channel PREAMBLE_128, // Preamble Length 9, // Preamble Code (Same for RX and TX!) PAC8, // PAC DATARATE_6_8MB, // Datarate PHR_MODE_STANDARD, // PHR Mode PHR_RATE_850KB // PHR Rate }; |
Массив config[] объединяет наиболее важные начальные настройки радиосвязи в одном месте. В этом проекте используется канал 5, преамбула из 128 символов с кодом 9, длина PAC 8, максимальная скорость передачи данных 6,8 Мбит/с и стандартный режим PHR. Эти настройки обеспечивают баланс между производительностью и надежностью для определения расстояния внутри помещений.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// Anchor data structure struct AnchorData { int anchor_id; // Anchor ID // Timing measurements int t_roundA = 0; int t_replyA = 0; long long rx = 0; long long tx = 0; int clock_offset = 0; // Distance measurements float distance = 0; float distance_history[FILTER_SIZE] = {0}; int history_index = 0; float filtered_distance = 0; // Signal quality metrics float signal_strength = 0; // RSSI in dBm float fp_signal_strength = 0; // First Path RSSI in dBm }; |
Структура AnchorData содержит всю необходимую информацию для каждого якорного датчика: измерения времени из обмена данными DS-TWR, исходные и отфильтрованные расстояния, скользящую историю для медианной фильтрации и показатели качества сигнала (RSSI первого пути). Это позволяет упорядочить все данные для каждого якорного датчика и обеспечить к ним легкий доступ во время измерения расстояния и построения JSON-файлов.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// Dynamic array of anchor data AnchorData anchors[NUM_ANCHORS]; // Helper functions for anchor management void initializeAnchors() { for (int i = 0; i < NUM_ANCHORS; i++) { anchors[i].anchor_id = FIRST_ANCHOR_ID + i; // Initialize all other fields to zero (default constructor handles this) } } AnchorData *getCurrentAnchor() { return &anchors[current_anchor_index]; } int getCurrentAnchorId() { return anchors[current_anchor_index].anchor_id; } void switchToNextAnchor() { current_anchor_index = (current_anchor_index + 1) % NUM_ANCHORS; } bool allAnchorsHaveValidData() { for (int i = 0; i < NUM_ANCHORS; i++) { if (anchors[i].filtered_distance <= 0) { return false; } } return true; } |
Наконец, набор вспомогательных функций управляет якорями в памяти. Функция initializeAnchors() присваивает идентификаторы, функции getCurrentAnchor() и getCurrentAnchorId() обеспечивают быстрый доступ к активному якорю, функция switchToNextAnchor() последовательно переключает якоря, а функция allAnchorsHaveValidData() подтверждает, что каждый якорь имеет допустимые показания, прежде чем пытаться вычислить его положение.
Класс для работы с DWM3000
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
class DWM3000Class { public: // Chip Setup static void spiSelect(uint8_t cs); static void begin(); static void init(); static void writeSysConfig(); static void configureAsTX(); static void setupGPIO(); // Double-Sided Ranging static void ds_sendFrame(int stage); static void ds_sendRTInfo(int t_roundB, int t_replyB); static int ds_processRTInfo(int t_roundA, int t_replyA, int t_roundB, int t_replyB, int clock_offset); static int ds_getStage(); static bool ds_isErrorFrame(); static void ds_sendErrorFrame(); // Radio Settings static void setChannel(uint8_t data); static void setPreambleLength(uint8_t data); static void setPreambleCode(uint8_t data); static void setPACSize(uint8_t data); static void setDatarate(uint8_t data); static void setPHRMode(uint8_t data); static void setPHRRate(uint8_t data); // Protocol Settings static void setMode(int mode); static void setTXFrame(unsigned long long frame_data); static void setFrameLength(int frame_len); static void setTXAntennaDelay(int delay); static void setSenderID(int senderID); static void setDestinationID(int destID); // Status Checks static int receivedFrameSucc(); static int sentFrameSucc(); static int getSenderID(); static int getDestinationID(); static bool checkForIDLE(); static bool checkSPI(); // Radio Analytics static double getSignalStrength(); static double getFirstPathSignalStrength(); static int getTXAntennaDelay(); static long double getClockOffset(); static long double getClockOffset(int32_t ext_clock_offset); static int getRawClockOffset(); static float getTempInC(); static unsigned long long readRXTimestamp(); static unsigned long long readTXTimestamp(); // Chip Interaction static uint32_t write(int base, int sub, uint32_t data, int data_len); static uint32_t write(int base, int sub, uint32_t data); static uint32_t read(int base, int sub); static uint8_t read8bit(int base, int sub); static uint32_t readOTP(uint8_t addr); // Delayed Sending Settings static void writeTXDelay(uint32_t delay); static void prepareDelayedTX(); // Radio Stage Settings / Transfer and Receive Modes static void delayedTXThenRX(); static void delayedTX(); static void standardTX(); static void standardRX(); static void TXInstantRX(); // DWM3000 Firmware Interaction static void softReset(); static void hardReset(); static void clearSystemStatus(); // Hardware Status Information static void pullLEDHigh(int led); static void pullLEDLow(int led); // Calculation and Conversion static double convertToCM(int DWM3000_ps_units); static void calculateTXRXdiff(); // Printing static void printRoundTripInformation(); static void printDouble(double val, unsigned int precision, bool linebreak); private: // Single Bit Settings static void setBit(int reg_addr, int sub_addr, int shift, bool b); static void setBitLow(int reg_addr, int sub_addr, int shift); static void setBitHigh(int reg_addr, int sub_addr, int shift); // Fast Commands static void writeFastCommand(int cmd); // SPI Interaction static uint32_t readOrWriteFullAddress(uint32_t base, uint32_t sub, uint32_t data, uint32_t data_len, uint32_t readWriteBit); static uint32_t sendBytes(int b[], int lenB, int recLen); // Soft Reset Helper Method static void clearAONConfig(); // Other Helper Methods static unsigned int countBits(unsigned int number); static int checkForDevID(); }; DWM3000Class DWM3000; |
В этом разделе описывается класс DWM3000Class, пользовательская оболочка драйвера, которая обрабатывает всю прямую связь с трансивером DWM3000 UWB. Она абстрагирует сложные операции с регистрами, управление синхронизацией и этапы протокола определения расстояния, предоставляя чистый, многократно используемый API для остальной части прошивки.
Класс разделен на функциональные группы:
- Методы настройки микросхемы отвечают за выбор интерфейса SPI, инициализацию и конфигурацию GPIO.
- Методы двустороннего определения расстояния реализуют протокол DS-TWR, от отправки и приема кадров до вычисления конечного расстояния.
- Настройки радио и протокола позволяют быстро переключать канал, преамбулу, скорость передачи данных и адресацию.
- Функции «Состояние и аналитика» считывают уровень сигнала, временные метки и смещение тактовой частоты, что крайне важно как для точности измерения расстояния, так и для отладки.
- Методы взаимодействия с оборудованием выполняют сбросы, управление светодиодами и планирование отложенной передачи.
- Подпрограммы низкоуровневого доступа считывают и записывают данные непосредственно в регистры DWM3000, что позволяет при необходимости выполнять расширенную настройку параметров.
Благодаря тому, что все эти функции выделены в отдельный класс, остальная часть прошивки метки может выполнять сложные операции измерения расстояния и диагностики по протоколу UWB всего несколькими высокоуровневыми вызовами, сохраняя основной код чистым и читаемым, при этом обеспечивая полный контроль над оборудованием.
Wi-Fi-связь и обработка данных в формате JSON
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// Send ranging data to host void sendDataOverWiFi() { ... } // Validate a measurement bool isValidDistance(float distance) { ... } // Apply median filtering float calculateMedian(float arr[], int size) { ... } // Update anchor’s filtered distance void updateFilteredDistance(AnchorData &data) { ... } // Print debug info for one anchor void printDebugInfo(int anchor, long long rx, long long tx, int t_round, int t_reply, int clock_offset) { ... } // Print all anchors’ filtered distances void printAllDistances() { ... } |
Этот раздел прошивки отвечает за все сетевые подключения и форматирование данных. Метка сначала подключается к Wi-Fi с помощью функции connectToWiFi() и поддерживает соединение. В каждом цикле измерения sendDataOverWiFi() формирует структурированную полезную нагрузку JSON, содержащую отфильтрованное расстояние каждого якорного датчика, исходное измерение, RSSI, RSSI первого пути, значения синхронизации и смещение тактовой частоты, а затем отправляет ее по TCP на хост-скрипт Python. Для обеспечения стабильности функции isValidDistance() и calculateMedian() фильтруют зашумленные показания перед передачей. Функция updateFilteredDistance() поддерживает скользящую историю измерений каждого якорного датчика, а функции printDebugInfo() и printAllDistances() обеспечивают быстрый мониторинг на устройстве для устранения неполадок. Вместе эти функции образуют мост между низкоуровневыми измерениями UWB и построением графика положения в реальном времени на вашем компьютере.
Инициализация системы и настройка UWB-тегов
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
void setup() { Serial.begin(115200); // Initialize anchor array initializeAnchors(); Serial.print("Initialized "); Serial.print(NUM_ANCHORS); Serial.println(" anchors:"); for (int i = 0; i < NUM_ANCHORS; i++) { Serial.print(" Anchor "); Serial.print(i); Serial.print(" - ID: "); Serial.println(anchors[i].anchor_id); } // Connect to WiFi first connectToWiFi(); // Initialize UWB DWM3000.begin(); DWM3000.hardReset(); delay(200); if (!DWM3000.checkSPI()) { Serial.println("[ERROR] Could not establish SPI Connection to DWM3000!"); while (1) ; } while (!DWM3000.checkForIDLE()) { Serial.println("[ERROR] IDLE1 FAILED\r"); delay(1000); } DWM3000.softReset(); delay(200); if (!DWM3000.checkForIDLE()) { Serial.println("[ERROR] IDLE2 FAILED\r"); while (1) ; } DWM3000.init(); DWM3000.setupGPIO(); DWM3000.setTXAntennaDelay(16350); DWM3000.setSenderID(TAG_ID); Serial.println("> TAG - Three Anchor Ranging System <"); Serial.println("> With WiFi Communication <\n"); Serial.println("[INFO] Setup is finished."); Serial.print("Antenna delay set to: "); Serial.println(DWM3000.getTXAntennaDelay()); DWM3000.configureAsTX(); DWM3000.clearSystemStatus(); } |
Функция setup() запускается один раз при старте системы и подготавливает всю систему позиционирования UWB внутри помещений с использованием ESP32. Сначала она открывает последовательное соединение для отладки и инициализирует список опорных точек с уникальными идентификаторами. Затем она подключает ESP32 к сети Wi-Fi, чтобы потоковая передача данных была готова с самого начала для отслеживания местоположения по протоколу UWB. После этого трансивер DWM3000 UWB включается, выполняется аппаратный сброс и проверяется корректность связи по SPI. Прошивка гарантирует, что микросхема находится в режиме ожидания (IDLE) перед применением программного сброса и продолжением инициализации. Настраиваются выводы GPIO, устанавливается задержка антенны для точной синхронизации, и присваивается уникальный идентификатор метки. Наконец, DWM3000 переводится в режим передачи, и его регистры состояния системы очищаются, оставляя метку готовой к началу цикла измерения расстояния.
Основной контур измерения расстояния для отслеживания местоположения по технологии UWB
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
void loop() { AnchorData* currentAnchor = getCurrentAnchor(); int currentAnchorId = getCurrentAnchorId(); switch (curr_stage) { case 0: // Start ranging with current target // Reset timing measurements for current anchor currentAnchor->t_roundA = 0; currentAnchor->t_replyA = 0; DWM3000.setDestinationID(currentAnchorId); DWM3000.ds_sendFrame(1); currentAnchor->tx = DWM3000.readTXTimestamp(); curr_stage = 1; break; case 1: // Await first response if (rx_status = DWM3000.receivedFrameSucc()) { DWM3000.clearSystemStatus(); if (rx_status == 1) { if (DWM3000.ds_isErrorFrame()) { Serial.print("[WARNING] Error frame from Anchor "); Serial.print(currentAnchorId); Serial.print("! Signal strength: "); Serial.print(DWM3000.getSignalStrength()); Serial.println(" dBm"); curr_stage = 0; } else if (DWM3000.ds_getStage() != 2) { Serial.print("[WARNING] Unexpected stage from Anchor "); Serial.print(currentAnchorId); Serial.print(": "); Serial.println(DWM3000.ds_getStage()); DWM3000.ds_sendErrorFrame(); curr_stage = 0; } else { curr_stage = 2; } } else { Serial.print("[ERROR] Receiver Error from Anchor "); Serial.println(currentAnchorId); DWM3000.clearSystemStatus(); } } break; case 2: // Response received. Send second ranging currentAnchor->rx = DWM3000.readRXTimestamp(); DWM3000.ds_sendFrame(3); currentAnchor->t_roundA = currentAnchor->rx - currentAnchor->tx; currentAnchor->tx = DWM3000.readTXTimestamp(); currentAnchor->t_replyA = currentAnchor->tx - currentAnchor->rx; curr_stage = 3; break; case 3: // Await second response if (rx_status = DWM3000.receivedFrameSucc()) { DWM3000.clearSystemStatus(); if (rx_status == 1) { if (DWM3000.ds_isErrorFrame()) { Serial.print("[WARNING] Error frame from Anchor "); Serial.println(currentAnchorId); curr_stage = 0; } else { currentAnchor->clock_offset = DWM3000.getRawClockOffset(); curr_stage = 4; } } else { Serial.print("[ERROR] Receiver Error from Anchor "); Serial.println(currentAnchorId); DWM3000.clearSystemStatus(); } } break; case 4: // Response received. Calculating results { int ranging_time = DWM3000.ds_processRTInfo( currentAnchor->t_roundA, currentAnchor->t_replyA, DWM3000.read(0x12, 0x04), DWM3000.read(0x12, 0x08), currentAnchor->clock_offset); currentAnchor->distance = DWM3000.convertToCM(ranging_time); currentAnchor->signal_strength = DWM3000.getSignalStrength(); currentAnchor->fp_signal_strength = DWM3000.getFirstPathSignalStrength(); updateFilteredDistance(*currentAnchor); } // Print current distances printAllDistances(); // Send data over WiFi if all anchors have valid data if (allAnchorsHaveValidData()) { sendDataOverWiFi(); } // Switch to next anchor switchToNextAnchor(); curr_stage = 0; break; default: Serial.print("Entered stage ("); Serial.print(curr_stage); Serial.println("). Reverting back to stage 0"); curr_stage = 0; break; } } |
Функция loop() управляет процессом двустороннего измерения расстояния (DS-TWR) с использованием системы позиционирования UWB внутри помещений на базе ESP32 для каждого якорного датчика по очереди. Она работает как небольшой конечный автомат (curr_stage), который пошагово проходит через обмен данными о расстоянии для обеспечения точной локализации UWB внутри помещений. Этап 0 отправляет первый кадр текущему якорному датчику и записывает метку времени передачи. Этап 1 ожидает корректного ответа, проверяя наличие ошибочных кадров или неожиданных событий. Этап 2 отправляет второй кадр и регистрирует данные о времени (t_roundA и t_replyA). Этап 3 ожидает окончательного ответа от якорного датчика, считывая смещение его тактовой частоты для компенсации дрейфа. Этап 4 обрабатывает все измерения времени с помощью функции ds_processRTInfo(), преобразует результат в сантиметры, сохраняет метрики уровня сигнала и обновляет расстояние, отфильтрованное медианой. Если все якорные датчики имеют корректные расстояния, метка отправляет полное обновление JSON на хост через Wi-Fi. Затем цикл переключается на следующий якорный датчик и повторяется. Этот непрерывный цикл постоянно обновляет данные о расстоянии на хосте для позиционирования в реальном времени. Цикл непрерывно проходит через все точки привязки в вашей системе UWB RTLS, поддерживая измерения расстояния в реальном времени для точного отслеживания объектов UWB и расчета их местоположения.
Разбор процесса настройки прошивки якоря (Anchor)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
#include <Arduino.h> #include <SPI.h> // SPI Setup #define RST_PIN 27 #define CHIP_SELECT_PIN 4 // Set to 1 for Anchor 1, 2 for Anchor 2 #define ANCHOR_ID 1 #define RESPONSE_TIMEOUT_MS 10 // Maximum time to wait for a response unsigned long last_ranging_time = 0; #define MAX_RETRIES 3 int retry_count = 0; static int rx_status; static int tx_status; /* valid stages: 0 - default stage; await ranging 1 - ranging received; sending response 2 - response sent; await second response 3 - second response received; sending information frame 4 - information frame sent */ static int curr_stage = 0; static int t_roundB = 0; static int t_replyB = 0; static long long rx = 0; static long long tx = 0; #define LEN_RX_CAL_CONF 4 #define LEN_TX_FCTRL_CONF 6 #define LEN_AON_DIG_CFG_CONF 3 #define PMSC_STATE_IDLE 0x3 ... #define TRANSMIT_DIFF 0x1FF #define NS_UNIT 4.0064102564102564 // ns #define PS_UNIT 15.6500400641025641 // ps #define SPEED_OF_LIGHT 0.029979245800 // in centimetres per picosecond #define CLOCK_OFFSET_CHAN_5_CONSTANT -0.5731e-3f #define CLOCK_OFFSET_CHAN_9_CONSTANT -0.1252e-3f // Offsets #define NO_OFFSET 0x0 #define DEBUG_OUTPUT 0 // Turn to 1 to get all reads, writes, etc. as info in the console static int ANTENNA_DELAY = 16350; int led_status = 0; int destination = 0x0; // Default Values for Destination and Sender IDs int sender = 0x0; |
На стороне якоря микропрограмма начинает с настройки аппаратного интерфейса для DWM3000, включая контакты SPI (RST_PIN, CHIP_SELECT_PIN) и уникальный ANCHOR_ID, который отличает каждый якорь в сети. Она также определяет параметры синхронизации, такие как RESPONSE_TIMEOUT_MS, определяющие время ожидания якорем передачи метки, и константы для максимального количества повторных попыток.
Остальные параметры определяют настройки параметров связи UWB: длины преамбулы, каналов, размеров PAC-пакетов, скорости передачи данных и различных адресов/масок регистров, используемых внутри микросхемы DWM3000. Эти константы гарантируют, что якорный датчик настроен на те же параметры радиочастоты и протокола, что и метка, обеспечивая точное двустороннее измерение расстояния (DS-TWR). Параметр ANTENNA_DELAY калибруется для компенсации аппаратной задержки распространения сигнала, а константа SPEED_OF_LIGHT в сантиметрах в пикосекунду используется в дальнейшем при расчете расстояния.
Загружая все эти параметры в начало файла, код якоря может оставаться чистым и полагаться на эти предопределенные константы в процессе определения расстояния и получения ответа, гарантируя, что каждый якорь работает идентично, за исключением своего уникального идентификатора.
DWM3000Class — основной интерфейс управления UWB
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class DWM3000Class { public: // Hardware initialization static void begin(); static void init(); static void configureAsTX(); ... // Double-Sided Ranging control static void ds_sendFrame(int stage); static int ds_processRTInfo(...); ... // Radio settings static void setChannel(uint8_t data); static void setPreambleLength(uint8_t data); ... // Measurement analytics static double getSignalStrength(); static double getFirstPathSignalStrength(); ... // Conversions static double convertToCM(int DWM3000_ps_units); ... }; |
Этот класс является основой как прошивки метки, так и прошивки якорного устройства. Он служит уровнем аппаратной абстракции (HAL) для модуля Qorvo DWM3000, что означает, что все низкоуровневые команды SPI, запись в регистры и конфигурация радиомодуля заключены в легко вызываемые функции.
С помощью DWM3000Class прошивка может:
- Инициализация UWB-радио (begin(), init(), configureAsTX() )
- Запустите двустороннюю двухстороннюю проверку дальности (DS-TWR) с помощью таких функций, как ds_sendFrame() и ds_processRTInfo().
- Настройте параметры радиочастотного сигнала, такие как канал, длина преамбулы, размер PAC-пакета и скорость передачи данных, в соответствии с требованиями системы.
- Получите данные измерений, такие как RSSI, мощность первого пути, смещения тактовой частоты и точные метки времени передачи/приема.
- Выполнение преобразований из внутренних единиц времени (пикосекунд) в реальные расстояния в сантиметрах (convertToCM()).
- Управление состояниями микросхемы, включая сброс, управление GPIO и очистку состояния системы.
Благодаря изоляции всей логики на уровне микросхемы, основной цикл приложения остается чистым и сосредоточенным на процедуре определения расстояния, в то время как класс обрабатывает всю связь с оборудованием DWM3000. Такая модульность позволяет легко адаптировать прошивку для новых вариантов использования или будущих модулей UWB с минимальными изменениями.
Настройка якоря — инициализация DWM3000 для определения расстояния
|
1 2 3 4 5 6 7 8 9 10 11 |
void setup() { Serial.begin(921600); // High-speed serial debug DWM3000.begin(); DWM3000.hardReset(); ... DWM3000.setTXAntennaDelay(16350); DWM3000.setSenderID(ANCHOR_ID); ... DWM3000.configureAsTX(); DWM3000.standardRX(); } |
Эта функция настройки включает питание якоря, проверяет его SPI-соединение с DWM3000 и гарантирует, что модуль находится в стабильном состоянии ожидания (IDLE) перед продолжением. После программного сброса и настройки GPIO она:
- Выполняет калибровку задержки антенны (setTXAntennaDelay()), что крайне важно для точных измерений времени пролета.
- Присваивает уникальный идентификатор якорной точки, чтобы метки могли определить, относительно какой якорной точки они отслеживают.
- Настраивает радиостанцию в качестве передатчика для выполнения ею функции двустороннего измерения расстояния.
- Переключается в режим приема (standardRX()), чтобы ожидать запросов на определение расстояния от метки.
К концу функции setup() якорь полностью готов к участию в цикле определения расстояния, прослушивает входящие UWB-пакеты и готов отвечать данными о времени.
Цикл якоря — ответ на запросы определения расстояния до метки
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
void loop() { if (DWM3000.receivedFrameSucc() == 1 && DWM3000.ds_getStage() == 1 && DWM3000.getDestinationID() == ANCHOR_ID) { if (curr_stage != 0) { // New ranging request resets session Serial.println("[INFO] New request - resetting session"); curr_stage = 0; t_roundB = 0; t_replyB = 0; } } switch (curr_stage) { case 0: // Await first ranging packet ... break; case 1: // Send first response DWM3000.ds_sendFrame(2); rx = DWM3000.readRXTimestamp(); tx = DWM3000.readTXTimestamp(); t_replyB = tx - rx; curr_stage = 2; break; case 2: // Await second ranging packet ... break; case 3: // Send timing info rx = DWM3000.readRXTimestamp(); t_roundB = rx - tx; DWM3000.ds_sendRTInfo(t_roundB, t_replyB); curr_stage = 0; break; default: ... break; } } |
В этом цикле якорный модуль непрерывно отслеживает входящие UWB-пакеты от метки. На этапе 0 он прослушивает первый пакет определения расстояния, проверяет, адресован ли он ему, и переходит к этапу 1.
- На первом этапе якорь отправляет свой первый ответ обратно на метку и записывает точные метки времени передачи (tx) и приема (rx) для расчета времени ответа t_replyB.
- На втором этапе якорь ожидает второй пакет данных о расстоянии от метки. После его получения он переходит к третьему этапу.
- На третьем этапе вычисляется время кругового пути t_roundB для сигнала метки, и это значение, вместе с t_replyB, отправляется обратно метке в «информационном кадре», чтобы метка могла вычислить окончательное расстояние.
Для обеспечения надежности цикла измерения расстояния предусмотрены тайм-ауты, повторные попытки и обработка ошибок; процесс перезапускается, если ожидаемые пакеты не поступают или обнаруживаются ошибки.
Визуализация отслеживания объектов в помещении в реальном времени с помощью Python
Этот скрипт на Python предоставляет слой визуализации для вашей системы позиционирования UWB внутри помещений с использованием ESP32, принимая данные в формате JSON по протоколу TCP и отображая положение метки в реальном времени на плане помещения с помощью библиотеки matplotlib.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import socket import threading import matplotlib.pyplot as plt import matplotlib.animation as animation import matplotlib.image as mpimg import numpy as np from scipy.optimize import least_squares import json # ---------------- CONFIGURATION ---------------- # HOST = "192.xxx.xx.xx" # Your PC's IP address PORT = 7007 # Must match ESP32 port ANCHOR_POSITIONS = np.array( [[15, 5], [290, 5], [165, 625]] # Anchor 1 # Anchor 2 # Anchor 3 ) MARGIN = 20 # For dynamic zoom (not needed for fixed view) ROOM_WIDTH = 480 # cm ROOM_HEIGHT = 650 # cm IMAGE_FILE = "floorplan.png" # Background image of your room # ------------------------------------------------ # # Global variables latest_data = None latest_signal_strengths = None # New variable for signal strengths data_lock = threading.Lock() server_running = True buffer = "" |
Начнем с импорта модулей Python для работы с сетью, многопоточностью, построением графиков и численными вычислениями. В разделе конфигурации задаются IP-адрес ПК, TCP-порт, координаты привязки, размеры помещения и изображение плана этажа. Эти параметры должны соответствовать настройкам Wi-Fi-клиента ESP32 и вашей физической конфигурации помещения. Скрипт Python предоставляет слой визуализации для вашей системы позиционирования UWB внутри помещений с использованием ESP32, принимая данные JSON по TCP и отображая положение меток в реальном времени на плане этажа с помощью matplotlib.
Функция трилатерации для определения местоположения внутри помещений с использованием UWB-технологии
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
def trilaterate(distances, anchor_positions): """Calculate tag position using trilateration with 3 anchors.""" def equations(p): x, y = p return [ np.sqrt( (x - anchor_positions[0][0]) ** 2 + (y - anchor_positions[0][1]) ** 2 ) - distances[0], np.sqrt( (x - anchor_positions[1][0]) ** 2 + (y - anchor_positions[1][1]) ** 2 ) - distances[1], np.sqrt( (x - anchor_positions[2][0]) ** 2 + (y - anchor_positions[2][1]) ** 2 ) - distances[2], ] initial_guess = np.mean(anchor_positions, axis=0) try: result = least_squares(equations, initial_guess, method="lm") return result.x if result.success else None except Exception as e: print(f"Trilateration error: {e}") return None |
Функция trilateration() вычисляет положение метки, используя расстояния от трех известных опорных точек в системе позиционирования UWB. Она использует метод наименьших квадратов для минимизации разницы между измеренными расстояниями и расстояниями, вычисленными на основе предполагаемого положения (x, y), для точного отслеживания местоположения в системе UWB.
Сервер Wi-Fi TCP
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
def wifi_server(): """TCP server to receive data from ESP32""" global latest_data, latest_signal_strengths, server_running, buffer with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST, PORT)) s.listen() print(f"Server listening on {HOST}:{PORT}") while server_running: try: conn, addr = s.accept() with conn: print(f"Connected by {addr}") while server_running: try: data = conn.recv(1024) if not data: break decoded = data.decode("utf-8") buffer += decoded while "\n" in buffer: line, buffer = buffer.split("\n", 1) line = line.strip() if line: try: # Parse JSON data json_data = json.loads(line) anchors = json_data["anchors"] # Extract the anchors dictionary d1 = float(anchors["A1"]["distance"]) d2 = float(anchors["A2"]["distance"]) d3 = float(anchors["A3"]["distance"]) s1 = float(anchors["A1"]["rssi"]) s2 = float(anchors["A2"]["rssi"]) s3 = float(anchors["A3"]["rssi"]) with data_lock: latest_data = (d1, d2, d3) latest_signal_strengths = (s1, s2, s3) print(f"Tag ID: {json_data['tag_id']}") print(f"Received data: d1={d1:.2f} cm, d2={d2:.2f} cm, d3={d3:.2f} cm") print(f"Signal strengths: s1={s1:.2f} dBm, s2={s2:.2f} dBm, s3={s3:.2f} dBm") except ( ValueError, KeyError, json.JSONDecodeError, ) as e: print(f"Invalid data: {line} - Error: {e}") |
Функция wifi_server() прослушивает входящие TCP-соединения от метки ESP32. Когда данные поступают, они буферизуются, разбиваются на строки, анализируются как JSON и сохраняются в общих переменных для использования функцией построения графиков. Для каждой опорной точки извлекаются расстояния и значения RSSI.
Инициализация графика
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
fig, ax = plt.subplots(figsize=(10, 8)) # Load and display room background image bg_img = mpimg.imread(IMAGE_FILE) img_extent = [0, ROOM_WIDTH, 0, ROOM_HEIGHT] ax.imshow(bg_img, extent=img_extent, origin="lower", alpha=0.6, zorder=-1) # Plot anchors and create text annotations for signal strength anchor_texts = [] for i, (x, y) in enumerate(ANCHOR_POSITIONS): color = ["g", "b", "m"][i] ax.plot(x, y, f"{color}^", markersize=12, label=f"Anchor {i + 1}") # Create text object for signal strength, positioned above each anchor txt = ax.text(x, y + 20, "", color=color, ha="center", va="bottom") anchor_texts.append(txt) # Tag and path (tag_dot,) = ax.plot([], [], "ro", markersize=10, label="Tag Position") (path_line,) = ax.plot([], [], "b-", alpha=0.5, linewidth=1, label="Tag Path") path_x, path_y = [], [] # Set fixed room size view ax.set_xlim(0, ROOM_WIDTH) ax.set_ylim(0, ROOM_HEIGHT) ax.set_aspect("equal") ax.grid(True, linestyle="--", alpha=0.7) ax.legend(loc="upper right") ax.set_title("Real-time UWB Tag Position Tracking (3 Anchors)", pad=40) ax.set_xlabel("X Position (cm)", labelpad=10) ax.set_ylabel("Y Position (cm)", labelpad=10) |
Здесь Matplotlib настраивает область построения графика, загружает изображение плана помещения и помечает опорные точки цветными треугольниками. Над каждой опорной точкой также отображается текстовая метка для отображения значений RSSI в реальном времени.
Обновление анимации в реальном времени
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
def update(frame): global latest_data, latest_signal_strengths, path_x, path_y with data_lock: if latest_data and latest_signal_strengths: d1, d2, d3 = latest_data s1, s2, s3 = latest_signal_strengths # Update signal strength texts for i, (txt, sig) in enumerate(zip(anchor_texts, (s1, s2, s3))): txt.set_text(f"{sig:.1f} dBm") pos = trilaterate([d1, d2, d3], ANCHOR_POSITIONS) if pos is not None: x_cm, y_cm = pos print(f"Tag position: x={x_cm:.1f} cm, y={y_cm:.1f} cm") tag_dot.set_data([x_cm], [y_cm]) path_x.append(x_cm) path_y.append(y_cm) if len(path_x) > 100: path_x.pop(0) path_y.pop(0) path_line.set_data(path_x, path_y) return tag_dot, path_line, *anchor_texts |
Функция update() запускается каждые 100 мс, получая последние данные и обновляя график. Она обновляет метки RSSI, вычисляет положение метки с помощью трилатерации и отображает как текущее положение, так и траекторию движения.
Основной цикл
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
if __name__ == "__main__": server_thread = threading.Thread(target=wifi_server, daemon=True) server_thread.start() ani = animation.FuncAnimation( fig, update, interval=100, cache_frame_data=False, blit=False ) try: plt.tight_layout() plt.show() except KeyboardInterrupt: print("\nShutting down server...") finally: server_running = False plt.close() |
В заключение, скрипт запускает TCP-сервер в фоновом потоке, настраивает цикл анимации Matplotlib и отображает представление отслеживания в реальном времени.
Тестирование работы проекта: проверка точности UWB
Для демонстрации три якоря размещены в фиксированных, известных координатах в комнате, соответствующих положениям, заданным в скрипте Python для системы позиционирования внутри помещений UWB с использованием ESP32:
| Якорь 1 | Нижний левый угол (15 см, 5 см) |
| Якорь 2 | Нижний правый угол (290 см, 5 см) |
| Якорь 3 | Верхняя центральная часть (165 см, 625 см) |
Метка перемещается по пространству, непрерывно выполняя двустороннее измерение расстояния (DS-TWR) с каждым якорным узлом. ESP32, установленный на метке, собирает данные о расстояниях от всех трех якорных узлов и передает их по Wi-Fi на ПК, на котором запущен инструмент визуализации пола на Python для отслеживания местоположения в реальном времени с помощью UWB-технологии.
Графический интерфейс пользователя на Python отображает план помещения в качестве фона и накладывает на него положение метки в реальном времени, представленное красной точкой. По мере перемещения метки ее траектория визуализируется синей линией, а показания RSSI в реальном времени от каждой опорной точки непрерывно обновляются и отображаются рядом в этой демонстрации UWB RTLS.
Эта конфигурация позволяет визуально проверять точность системы позиционирования UWB и контролировать уровень сигнала для эффективного отслеживания объектов с помощью UWB и определения местоположения внутри помещений с помощью UWB.


Расширение возможностей системы позиционирования внутри помещений: будущие обновления
В будущем эту систему позиционирования внутри помещений на базе UWB с использованием ESP32 можно будет расширить за счет ряда усовершенствований:
- Четвертый якорь: введение четвертого якоря для повышения избыточности и стабильности при многолучевых отражениях, что улучшает надежность позиционирования внутри помещений с использованием UWB-технологии на базе ESP32.
- Внедрение метода учета разницы во времени прибытия: добавление метода учета разницы во времени прибытия (TDoA) может позволить одновременно отслеживать несколько меток без необходимости планирования временных интервалов, что значительно повысит эффективность отслеживания активов с использованием технологии UWB.
- Веб-панель управления: возможность улучшить вашу систему заключается в использовании веб-панели управления вместо стандартного представления Python для удаленного мониторинга вашей системы UWB RTLS.
- 3D-позиционирование: внедрение 3D-позиционирования путем добавления опорных точек на разной высоте потенциально откроет для вашего приложения новые возможности отслеживания местоположения с помощью UWB-технологии.
- Снижение проблем, связанных с отсутствием прямой видимости: добавление данных RSSI в сеть приводит к улучшению производительности при использовании UWB-измерений для определения местоположения, что может помочь уменьшить проблемы, связанные с отсутствием прямой видимости, при более надежном позиционировании с помощью UWB.
- В рамках проекта было проведено углубленное исследование методов измерения расстояния в сверхширокополосном диапазоне (UWB) и отслеживания объектов внутри помещений в режиме реального времени.
- Использование плат ESP32-WROOM и модулей Qorvo DWM3000, а также нескольких сотен строк кода на Arduino и Python позволило создать функциональную систему позиционирования, способную точно визуализировать движение.
- Несмотря на такие сложности, как калибровка задержки антенны и отражения сигнала, результаты многообещающие.
- Сочетание сверхширокополосной точности и возможности подключения по Wi-Fi открывает захватывающие возможности для навигации роботов, отслеживания грузов на складах и определения местоположения внутри помещений в виртуальной и дополненной реальности.
- Разработчикам рекомендуется делиться вопросами, предложениями и опытом в рамках сообщества для совместного улучшения системы.
- В рамках проекта успешно продемонстрирована полноценная система позиционирования внутри помещений в режиме реального времени с использованием модулей Qorvo DWM3000 UWB в паре с платами ESP32 для автономной работы.
- Благодаря двустороннему измерению расстояния между меткой и тремя опорными точками, а также передаче данных по Wi-Fi и трилатерации на основе Python, система может точно определять и визуализировать положение метки на плане помещения.
- В этом проекте демонстрируется, как сверхширокополосная система в сочетании с интегрированным оборудованием, программным обеспечением и средствами визуализации позволяет создать масштабируемое решение для определения местоположения внутри помещений, подходящее для различных реальных задач.
Репозиторий GitHub с кодом и схемой проекта
Часто задаваемые вопросы о системах позиционирования UWB внутри помещений
⇥ Каков радиус действия модуля позиционирования DWM3000 UWB?
Модуль позиционирования Qorvo DWM3000 UWB обеспечивает хорошую дальность действия до 200 метров на открытом воздухе. В системах позиционирования внутри помещений, где есть стены и препятствия, полезная дальность действия обычно составляет 50-100 метров с точностью до 10 см. Дальность действия зависит от препятствий, помех и ориентации антенны в вашем решении для определения местоположения внутри помещений с использованием UWB.
⇥ Чем отличается от GPS-отслеживания отслеживание объектов с помощью UWB-технологии?
UWB-отслеживание объектов работает внутри помещений, где GPS не работает, с точностью до 10 см, в то время как точность GPS на открытом воздухе составляет приблизительно 5-10 м. GPS использует спутники, в то время как локальные точки привязки и двусторонняя связь измеряют расстояния для систем позиционирования UWB внутри помещений. Отслеживание местоположения с помощью UWB необходимо предприятиям на складах, в больницах и на производственных предприятиях, где требуется точное отслеживание внутри помещений.
⇥ Безопасна ли технология позиционирования UWB?
Да, более новые модули позиционирования UWB, такие как DWM3000, поддерживают безопасность IEEE 802.15.4z с использованием зашифрованных последовательностей временных меток (STS) для защиты от подделки и повторных атак. Технология UWB RTLS имеет все шансы стать безопасной и востребованной в приложениях отслеживания активов с использованием UWB, где целостность и безопасность данных о местоположении являются первостепенными требованиями.
⇥ Возможно ли сосуществование нескольких меток в системе позиционирования UWB внутри помещений?
Да, UWB-локализация внутри помещений может поддерживать несколько меток, используя методы временного разделения или TDoA (разница времени прихода сигнала). Хотя в этом проекте показано отслеживание местоположения с помощью одной метки UWB, архитектура DWM3000 может быть расширена до десятков меток в сети UWB RTLS при правильной настройке прошивки для предотвращения столкновений и планирования в вашей системе позиционирования UWB.
⇥ Каковы основные области применения отслеживания местоположения по технологии UWB?
Отслеживание местоположения по технологии UWB превосходно подходит для автоматизации складов (навигация автоматизированных транспортных средств, отслеживание запасов), здравоохранения (отслеживание активов с помощью UWB, мониторинг пациентов), производства (местоположение инструментов, зоны безопасности), розничной торговли (аналитика клиентов) и спорта (отслеживание игроков). Любой сценарий использования, требующий точной системы позиционирования внутри помещений, улучшается благодаря технологии позиционирования внутри помещений UWB и инфраструктуре UWB RTLS.
⇥ Как откалибровать систему позиционирования UWB внутри помещений для достижения максимальной точности?
Откалибруйте систему позиционирования UWB, используя:
1) Определить точные координаты опорных точек,
2) Настроить параметры задержки антенны (обычно 16350 для модуля позиционирования DWM3000 UWB),
3) Провести тестовые измерения в известных точках,
4) Скорректировать систематические ошибки и
5) Применить медианную фильтрацию для исключения выбросов.
Правильная калибровка повышает точность определения местоположения внутри помещений с помощью UWB-технологии с 30 см до 10 см, обеспечивая точное отслеживание местоположения.
104 просмотров









Спасибо огромное. Очень интересный проект
Да, мне тоже так показалось. Но при этом сравнительно непростой. Надеюсь, вам удастся его повторить
За что спасибо? Это корявый перевод. Переводивший или совсем не читал, что переводчик выдал или совершенно не соображает по теме. Пользы больше было бы от ссылки на оригинал.
Ну переведите сами лучше если можете, опубликуйте в сети для всеобщего доступа