В этой подробной статье мы рассмотрим технологию сверхширокополосной связи с использованием модуля ESP32 UWB Pro. Этот модуль оснащен чипом DW1000 UWB, а также чипом усилителя мощности. Хотя чип DW1000 UWB имеет собственный диапазон обнаружения около 40 метров, добавление чипа усилителя позволяет увеличить расстояние обнаружения до 200 метров .
Сверхширокополосный (UWB) — это протокол беспроводной связи малого радиуса действия, который использует радиоволны для безопасного, надежного определения дальности и точного зондирования, обеспечивая расширенный пространственный контекст для беспроводных устройств.
В этом руководстве мы рассмотрим конструкцию платы, технические характеристики и различные приложения модуля ESP32 UWB Pro. Кроме того, мы узнаем, как использовать эту плату для высокоточного определения дальности и локализации, включая тестирование расстояний.
Чтобы получить более полное представление о технологии UWB , мы рекомендуем вам ознакомиться с нашими предыдущими статьями, посвященными чипам DW1000 и DW3000, прежде чем приступать к изучению этого руководства:
- сверхширокополосная связь и определение расстояний с модулем ESP32 DW1000 UWB;
- измерение дальности и местоположения с помощью ESP32 UWB DW3000.
Необходимые компоненты
- Плата ESP32 UWB Pro.
- Плата ESP32 UWB Pro с дисплеем (купить на AliExpress).
- USB-кабель типа C.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
ESP32 DW1000 UWB (сверхширокополосная) профессиональная плата
Сверхширокополосная связь (Ultra-wideband, UWB) — это протокол беспроводной связи малого радиуса действия, работающий посредством радиоволн и обеспечивающий надежное и точное измерение дальности, создавая новое измерение пространственного контекста для беспроводных устройств.
Плата ESP32 DW1000 UWB (Ultra Wideband) Pro производится компанией Makerfabs. Она использует чип DW1000 UWB Pro для сверхширокополосной связи.
В частности, ESP32 UWB и UWB DW3000, которые предлагают максимальное расстояние 40 метров, могут быть недостаточными для определенных приложений позиционирования на открытом воздухе или позиционирования больших хранилищ. Для решения этой проблемы после обширных испытаний была разработана высокомощная версия трансивера UWB.
С версией высокой мощности максимальное расстояние, которого можно достичь, составляет до 200 метров. Этот увеличенный диапазон делает его более подходящим для приложений, где требуются большие расстояния, например, для позиционирования на открытом воздухе или позиционирования на больших складах. Эта разработка демонстрирует универсальность технологии UWB и ее потенциал для удовлетворения широкого спектра требований беспроводной связи.
Особенности DW1000 UWB Pro
Основные характеристики DW1000 UWB Pro включают в себя:
- Технология UWB: DW1000 использует технологию UWB, которая работает в диапазоне частот 3,5–6,5 ГГц, обеспечивая высокоточную связь с низким энергопотреблением.
- Точность: чип может достигать сантиметровой точности определения местоположения и дальности, измеряя время, необходимое для прохождения сигнала между передатчиком и приемником.
- Скорость передачи данных: DW1000 поддерживает несколько скоростей передачи данных, включая 110 кбит/с, 850 кбит/с и 6,8 Мбит/с.
- Поддержка каналов: устройство поддерживает 6 различных каналов, что обеспечивает большую гибкость при проектировании и эксплуатации системы.
- Низкое энергопотребление: DW1000 разработан для низкого энергопотребления, что делает его пригодным для устройств с питанием от батарей и энергосберегающих систем.
- Интеграция: чип можно легко интегрировать в различные устройства и системы, включая устройства Интернета вещей, робототехнику и промышленную автоматику.
- Безопасность: DW1000 имеет встроенные функции безопасности, такие как шифрование данных и протоколы безопасного диапазона, для обеспечения безопасной связи.
- Масштабируемость: технология UWB, используемая в DW1000, позволяет разрабатывать крупномасштабные сети с тысячами узлов.
Основные характеристики платы ESP32 UWB Pro
- Модуль ESP32 для быстрых и мощных приложений.
- Поддержка Wi-Fi, Bluetooth.
- Совместимость с Arduino IDE.
- USB-разъем типа C.
- Диапазон напряжения питания USB-платы: 4,8~5,5 В, типичное значение 5,0 В.
- Встроенный OLED-дисплей размером 1,3 дюйма и разрешением 128*64 позволяет напрямую отображать результаты измерения расстояния.
- Зарядное устройство и разъем для LiPo-аккумулятора, поэтому этот модуль может работать отдельно с аккумуляторами.
- Обновите положение UWB, чтобы модуль можно было напрямую установить в корпус.
Диапазон DW1000 UWB Pro
Дальность действия приемопередатчика Decawave DW1000 UWB зависит от различных факторов, таких как окружающая среда, конструкция антенны и настройки устройства. Однако в типичных условиях эксплуатации DW1000 может достигать дальности связи в пределах прямой видимости до 290 метров (приблизительно 950 футов). На эту дальность могут влиять такие факторы как сила сигнала, помехи и препятствия на пути связи.
Для достижения желаемого диапазона действия в конкретных приложениях крайне важно оптимизировать конструкцию антенны, настройки устройства и факторы окружающей среды.
Исходный код программы и библиотеки
Давайте посмотрим код для модуля ESP32 UWB Pro с дисплеем. ESP32 UWB Pro имеет чип DW1000 с усилителем. Следовательно, ему требуется модифицированная библиотека DW1000. Помимо библиотеки DW1000, ему также требуется библиотека для OLED-дисплея.
- Модифицированная библиотека DW1000: Загрузить
- Библиотека Adafruit GFX: Загрузить
- Библиотека Adafruit SSD1306: Загрузить
После установки библиотек нам нужно загрузить код в пару модулей ESP32 UWB Pro. Один из модулей будет выступать в качестве якоря (Anchor), а другой — в качестве тега (Tag).
Подготовьте модуль и подключите его к ПК с помощью кабеля Type-C. Выберите плату разработки «ESP32 Dev Module» и порт.
Код якоря
Загрузите следующий скетч на плату ESP32 UWB Pro. Этот модуль может быть якорным портом для получения сигнала другого устройства 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 |
#include <SPI.h> #include "DW1000Ranging.h" #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define ANCHOR_ADD "86:17:5B:D5:A9:9A:E2:9C" #define SPI_SCK 18 #define SPI_MISO 19 #define SPI_MOSI 23 #define UWB_RST 27 // reset pin #define UWB_IRQ 34 // irq pin #define UWB_SS 21 // spi select pin #define I2C_SDA 4 #define I2C_SCL 5 Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { Serial.begin(115200); Wire.begin(I2C_SDA, I2C_SCL); delay(1000); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.clearDisplay(); logoshow(); // init the configuration SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI); DW1000Ranging.initCommunication(UWB_RST, UWB_SS, UWB_IRQ); // Reset, CS, IRQ pin DW1000Ranging.attachNewRange(newRange); DW1000Ranging.attachBlinkDevice(newBlink); DW1000Ranging.attachInactiveDevice(inactiveDevice); DW1000Ranging.startAsAnchor(ANCHOR_ADD, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false); } void loop() { DW1000Ranging.loop(); } void newRange() { Serial.print("from: "); Serial.print(DW1000Ranging.getDistantDevice()->getShortAddress(), HEX); Serial.print("\t Range: "); Serial.print(DW1000Ranging.getDistantDevice()->getRange()); Serial.print(" m"); Serial.print("\t RX power: "); Serial.print(DW1000Ranging.getDistantDevice()->getRXPower()); Serial.println(" dBm"); } void newBlink(DW1000Device *device) { Serial.print("blink; 1 device added ! -> "); Serial.print(" short:"); Serial.println(device->getShortAddress(), HEX); } void inactiveDevice(DW1000Device *device) { Serial.print("delete inactive device: "); Serial.println(device->getShortAddress(), HEX); } void logoshow(void) { display.clearDisplay(); display.setTextSize(2); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.println(F("Makerfabs")); display.println(F("UWB Anchor")); display.setTextSize(1); display.setCursor(0, 40); // Start at top-left corner display.println(ANCHOR_ADD); display.display(); } |
Код тега
Загрузите следующий скетч на плату ESP32 UWB Pro. Этот модуль может быть портом тега для отправки сигнала на другую плату 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
#include <SPI.h> #include "DW1000Ranging.h" #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define TAG_ADDR "7D:00:22:EA:82:60:3B:9B" // #define DEBUG #define SPI_SCK 18 #define SPI_MISO 19 #define SPI_MOSI 23 #define UWB_RST 27 // reset pin #define UWB_IRQ 34 // irq pin #define UWB_SS 21 // spi select pin #define I2C_SDA 4 #define I2C_SCL 5 struct Link { uint16_t anchor_addr; float range; float dbm; struct Link *next; }; struct Link *uwb_data; Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { Serial.begin(115200); Wire.begin(I2C_SDA, I2C_SCL); delay(1000); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.clearDisplay(); logoshow(); // init the configuration SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI); DW1000Ranging.initCommunication(UWB_RST, UWB_SS, UWB_IRQ); // Reset, CS, IRQ pin // define the sketch as anchor. It will be great to dynamically change the type of module DW1000Ranging.attachNewRange(newRange); DW1000Ranging.attachNewDevice(newDevice); DW1000Ranging.attachInactiveDevice(inactiveDevice); // Enable the filter to smooth the distance // DW1000Ranging.useRangeFilter(true); // we start the module as a tag DW1000Ranging.startAsTag(TAG_ADDR, DW1000.MODE_LONGDATA_RANGE_LOWPOWER); // DW1000Ranging.startAsTag(TAG_ADDR, DW1000.MODE_SHORTDATA_FAST_LOWPOWER); // DW1000Ranging.startAsTag(TAG_ADDR, DW1000.MODE_LONGDATA_FAST_LOWPOWER); // DW1000Ranging.startAsTag(TAG_ADDR, DW1000.MODE_SHORTDATA_FAST_ACCURACY); // DW1000Ranging.startAsTag(TAG_ADDR, DW1000.MODE_LONGDATA_FAST_ACCURACY); // DW1000Ranging.startAsTag(TAG_ADDR, DW1000.MODE_LONGDATA_RANGE_ACCURACY); uwb_data = init_link(); } long int runtime = 0; void loop() { DW1000Ranging.loop(); if ((millis() - runtime) > 1000) { display_uwb(uwb_data); runtime = millis(); } } void newRange() { Serial.print("from: "); Serial.print(DW1000Ranging.getDistantDevice()->getShortAddress(), HEX); Serial.print("\t Range: "); Serial.print(DW1000Ranging.getDistantDevice()->getRange()); Serial.print(" m"); Serial.print("\t RX power: "); Serial.print(DW1000Ranging.getDistantDevice()->getRXPower()); Serial.println(" dBm"); fresh_link(uwb_data, DW1000Ranging.getDistantDevice()->getShortAddress(), DW1000Ranging.getDistantDevice()->getRange(), DW1000Ranging.getDistantDevice()->getRXPower()); // print_link(uwb_data); } void newDevice(DW1000Device *device) { Serial.print("ranging init; 1 device added ! -> "); Serial.print(" short:"); Serial.println(device->getShortAddress(), HEX); add_link(uwb_data, device->getShortAddress()); } void inactiveDevice(DW1000Device *device) { Serial.print("delete inactive device: "); Serial.println(device->getShortAddress(), HEX); delete_link(uwb_data, device->getShortAddress()); } // Data Link struct Link *init_link() { #ifdef DEBUG Serial.println("init_link"); #endif struct Link *p = (struct Link *)malloc(sizeof(struct Link)); p->next = NULL; p->anchor_addr = 0; p->range = 0.0; return p; } void add_link(struct Link *p, uint16_t addr) { #ifdef DEBUG Serial.println("add_link"); #endif struct Link *temp = p; // Find struct Link end while (temp->next != NULL) { temp = temp->next; } Serial.println("add_link:find struct Link end"); // Create a anchor struct Link *a = (struct Link *)malloc(sizeof(struct Link)); a->anchor_addr = addr; a->range = 0.0; a->dbm = 0.0; a->next = NULL; // Add anchor to end of struct Link temp->next = a; return; } struct Link *find_link(struct Link *p, uint16_t addr) { #ifdef DEBUG Serial.println("find_link"); #endif if (addr == 0) { Serial.println("find_link:Input addr is 0"); return NULL; } if (p->next == NULL) { Serial.println("find_link:Link is empty"); return NULL; } struct Link *temp = p; // Find target struct Link or struct Link end while (temp->next != NULL) { temp = temp->next; if (temp->anchor_addr == addr) { // Serial.println("find_link:Find addr"); return temp; } } Serial.println("find_link:Can't find addr"); return NULL; } void fresh_link(struct Link *p, uint16_t addr, float range, float dbm) { #ifdef DEBUG Serial.println("fresh_link"); #endif struct Link *temp = find_link(p, addr); if (temp != NULL) { temp->range = range; temp->dbm = dbm; return; } else { Serial.println("fresh_link:Fresh fail"); return; } } void print_link(struct Link *p) { #ifdef DEBUG Serial.println("print_link"); #endif struct Link *temp = p; while (temp->next != NULL) { // Serial.println("Dev %d:%d m", temp->next->anchor_addr, temp->next->range); Serial.println(temp->next->anchor_addr, HEX); Serial.println(temp->next->range); Serial.println(temp->next->dbm); temp = temp->next; } return; } void delete_link(struct Link *p, uint16_t addr) { #ifdef DEBUG Serial.println("delete_link"); #endif if (addr == 0) return; struct Link *temp = p; while (temp->next != NULL) { if (temp->next->anchor_addr == addr) { struct Link *del = temp->next; temp->next = del->next; free(del); return; } temp = temp->next; } return; } // SSD1306 void logoshow(void) { display.clearDisplay(); display.setTextSize(2); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.println(F("Makerfabs")); display.setTextSize(1); display.setCursor(0, 20); // Start at top-left corner display.println(F("DW1000 DEMO")); display.display(); delay(2000); } void display_uwb(struct Link *p) { struct Link *temp = p; int row = 0; display.clearDisplay(); display.setTextColor(SSD1306_WHITE); if (temp->next == NULL) { display.setTextSize(2); display.setCursor(0, 0); display.println("No Anchor"); display.display(); return; } while (temp->next != NULL) { temp = temp->next; // Serial.println("Dev %d:%d m", temp->next->anchor_addr, temp->next->range); Serial.println(temp->anchor_addr, HEX); Serial.println(temp->range); char c[30]; // sprintf(c, "%X:%.1f m %.1f", temp->anchor_addr, temp->range, temp->dbm); // sprintf(c, "%X:%.1f m", temp->anchor_addr, temp->range); sprintf(c, "%.1f m", temp->range); display.setTextSize(2); display.setCursor(0, row++ * 32); // Start at top-left corner display.println(c); display.println(""); sprintf(c, "%.2f dbm", temp->dbm); display.setTextSize(2); display.println(c); if (row >= 1) { break; } } delay(100); display.display(); return; } |
Демонстрация и тестирование на полигоне
После загрузки кода на платы якоря и тега вы можете начать процесс тестирования. Для приложения батареи я отключил код OLED для платы якоря.
Откройте последовательный монитор для платы якоря и тега.
Расположите платы якоря и тега рядом друг с другом и проверьте минимальное расстояние.
Так как эта плата предназначена для тестирования на больших расстояниях, минимальное расстояние, которое она показывает, немного некорректно. Она показывала расстояние 0,5 м, когда была размещена на расстоянии 0,2 м друг от друга. Поэтому она показывает дополнительное расстояние в 300 см.
Тестирование на открытом воздухе
По данным производителя, дальность действия платы составляет 200 метров. Поэтому мы решили вынести его на улицу для проверки дальности действия.
Я запитал якорь от аккумулятора и установил его в неподвижном положении на скамье.
Тогда метка была подвижной частью в этом тестировании. OLED-дисплей показывает измеренное расстояние в метрах, а мощность сигнала — дБм.
После перемещения на определенное расстояние около 25 метров от якоря OLED-дисплей показал следующее расстояние и уровень сигнала.
При дальнейшем движении OLED продолжал непрерывно отображать расстояние, и сигнал был правильным. Но сила сигнала начала уменьшаться.
Когда мы приблизились на расстояние 100 метров, уровень сигнала еще больше снизился.
На расстоянии 142 метров сигнал был четким и ясным.
На расстоянии около 150 метров уровень сигнала начал резко ослабевать, а иногда OLED-дисплей не показывал якорь, поскольку иногда пропускал сигнал.
После того, как мы продвинулись дальше, сигнал был потерян, и тег принимал сигнал только иногда от якоря. Большую часть времени сигнал был потерян. Но нам удалось преодолеть расстояние в 176 метров.
В дальнейшем мы прекратили тестирование, поскольку уровень сигнала был слишком слабым, и метка редко могла его принять.
Заключение
В заключение, тестирование диапазона на открытом воздухе показало, что производительность платы не соответствует заявленному производителем диапазону в 200 метров. Это могло произойти из-за наличия препятствий, таких как деревья, и отсутствия прямой видимости во время тестирования. Вы можете провести надлежащее тестирование еще раз, если хотите получить показания на большем расстоянии.
Сигнал был четким и правильным до расстояния 142 метров. Однако за пределами этой точки сила сигнала начала значительно ослабевать, и соединение стало прерывистым на расстоянии 150 метров. В конечном итоге максимальное зарегистрированное расстояние составило 176 метров, но сигнал был слишком слабым и редко принимался меткой в этой точке.
Следовательно, для практического применения целесообразно считать, что эффективный радиус действия устройства составляет менее заявленных 200 метров.