Getting Inaacurate antenna delays for ESP32 UWBPRO (DW1000) module

Good day all,
Currently i am using the anchor autocalibration code, courtesy of @jremington
(GitHub - jremington/UWB-Indoor-Localization_Arduino: Open source Indoor localization using Arduino and ESP32_UWB tags + anchors), but getting grossly inaccurate antenna delay (Adelay) values using the ESP32 UWB pro module by makerfabs. I get these inaccurate values in both my 1 and 7 meter anchor autocalibration. in my trials i have Gotten Final Adelay values ranging from 20k-60k. Currently i have both modules on a table 1 meter apart as im testing. Any guidance will be greatly appreciated thank you.

Anchor code:

// This program calibrates an ESP32_UWB module intended for use as a fixed anchor point
// uses binary search to find anchor antenna delay to calibrate against a known distance
//
// modified version of Thomas Trojer's DW1000 library is required!

// Remote tag (at origin) must be set up with default antenna delay (library default = 16384)

// user input required, possibly unique to each tag:
// 1) accurately measured distance from anchor to tag
// 2) address of anchor
//
// output: antenna delay parameter for use in final anchor setup.
// S. James Remington 2/20/2022

#include <SPI.h>
#include "DW1000Ranging.h"
#include "DW1000.h"

// ESP32_UWB pin definitions

#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
#define DW_CS 4

// connection pins
const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4;   // spi select pin


char this_anchor_addr[] = "84:00:22:EA:82:60:3B:9C";
float this_anchor_target_distance = 1; //measured distance to anchor in m

uint16_t this_anchor_Adelay = 16600; //starting value
uint16_t Adelay_delta = 100; //initial binary search step size


void setup()
{
  Serial.begin(115200);
  while (!Serial);
  //init the configuration
  SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin


  Serial.print("Starting Adelay "); Serial.println(this_anchor_Adelay);
  Serial.print("Measured distance "); Serial.println(this_anchor_target_distance);
  
  DW1000.setAntennaDelay(this_anchor_Adelay);

  DW1000Ranging.attachNewRange(newRange);
  DW1000Ranging.attachNewDevice(newDevice);
  DW1000Ranging.attachInactiveDevice(inactiveDevice);
  //Enable the filter to smooth the distance
  //DW1000Ranging.useRangeFilter(true);

  //start the module as anchor, don't assign random short address
  DW1000Ranging.startAsAnchor(this_anchor_addr, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false);

}

void loop()
{
  DW1000Ranging.loop();
}

void newRange()
{
  static float last_delta = 0.0;
  Serial.print(DW1000Ranging.getDistantDevice()->getShortAddress(), DEC);

    float dist = DW1000Ranging.getDistantDevice()->getRange();

  Serial.print(",");
  Serial.print(dist); 
  if (Adelay_delta < 3) {
    Serial.print(", final Adelay ");
    Serial.println(this_anchor_Adelay);
//    Serial.print("Check: stored Adelay = ");
//    Serial.println(DW1000.getAntennaDelay());
    while(1);  //done calibrating
  }

  float this_delta = dist - this_anchor_target_distance;  //error in measured distance

  if ( this_delta * last_delta < 0.0) Adelay_delta = Adelay_delta / 2; //sign changed, reduce step size
    last_delta = this_delta;
  
  if (this_delta > 0.0 ) this_anchor_Adelay += Adelay_delta; //new trial Adelay
  else this_anchor_Adelay -= Adelay_delta;
  
  Serial.print(", Adelay = ");
  Serial.println (this_anchor_Adelay);
//  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin
  DW1000.setAntennaDelay(this_anchor_Adelay);
}

void newDevice(DW1000Device *device)
{
  Serial.print("Device added: ");
  Serial.println(device->getShortAddress(), HEX);
}

void inactiveDevice(DW1000Device *device)
{
  Serial.print("delete inactive device: ");
  Serial.println(device->getShortAddress(), HEX);
}

Tag code:

// currently tag is module #5
// The purpose of this code is to set the tag address and antenna delay to default.
// this tag will be used for calibrating the anchors.

#include <SPI.h>
#include "DW1000Ranging.h"
#include "DW1000.h"

#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
#define DW_CS 4

// connection pins
const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4;   // spi select pin

// TAG antenna delay defaults to 16384
// leftmost two bytes below will become the "short address"
char tag_addr[] = "7D:00:22:EA:82:60:3B:9C";

void setup()
{
  Serial.begin(115200);
  delay(1000);

  //init the configuration
  SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin

  DW1000Ranging.attachNewRange(newRange);
  DW1000Ranging.attachNewDevice(newDevice);
  DW1000Ranging.attachInactiveDevice(inactiveDevice);

// start as tag, do not assign random short address

  DW1000Ranging.startAsTag(tag_addr, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false);
}

void loop()
{
  DW1000Ranging.loop();
}

void newRange()
{
  Serial.print(DW1000Ranging.getDistantDevice()->getShortAddress(), HEX);
  Serial.print(",");
  Serial.println(DW1000Ranging.getDistantDevice()->getRange());
}

void newDevice(DW1000Device *device)
{
  Serial.print("Device added: ");
  Serial.println(device->getShortAddress(), HEX);
}

void inactiveDevice(DW1000Device *device)
{
  Serial.print("delete inactive device: ");
  Serial.println(device->getShortAddress(), HEX);
}

I suggest to move the modules farther apart (say, 5m), change the target distance in the code to 5, and run the programs again.

Please post a printout of the binary search output.

It would be a very good idea just to check the basic functionality of tag and anchor modules, using the default antenna delay, and see if they return reasonably accurate distance values for a range of trial distances.

Setting "Adelay" is just fine tuning, and it can be done by hand, using trial and error, combined with distance checks as suggested above.

What is the achievable range or distance with the DW1000 modules ?

Screenshot - 15_04_2024 , 09_46_44

Doen some Googling and circa 50M seems possible, but then there is comment about high power options.

Good day, thank you for your reply. Ive been trying to gather the data to send back to you, but what the serial monitor shows it it looping through Adelay values (1-60k) for 30 minutes then settling on an arbitrary value. I did try to write code to loop through the 16550-16650 values for the adelay and output which had the most consistent values that fell in the range of +-10cm, but didnt have much success. It has been proven difficult to copy paste the 30 minutes of binary values for you to see, but today looking back on your link you did describe having his problem due to some misconfiguration between trojer's library and arduino modules. I knew i had downloaded your dw1000 module version but when i looked back on the ide it seemed to have recognized the 2 dw1000 libraries, but only used Trojer's version, which was the cause of my issue and i now can get an accurate Adelay.

A question of Clarification however, if i am to use the tag 2D_3A code for testing the anchor code im supposed to use is the "set_up anchor" , and just change "char anchor_addr[] = "84:00:5B:D5:A9:9A:E2:9C" to "char anchor_addr[] = "84:00:5B:D5:A9:9A:E2:80", 81 ,82... and so on?

but only used Trojer's version, which was the cause of my issue and i now can get an accurate Adelay.

Trojer's library had to be modified to make setting Adelay possible. The settings were just being ignored in your first experiment.

"char anchor_addr[] = "84:00:5B:D5:A9:9A:E2:80", 81 ,82... and so on?

Change the leftmost byte. Little Endian.

Thank you, now changed.

Is the tag supposed to output the values obtained by the three anchors in some order of fashion? Currently is does recognize device 80-82 in the serial montor but, shows nothing else, even with this uncommented.
#define DEBUG_TRILAT
#define DEBUG_DIST



// currently tag is module #5
// this version is 2D (X,Y) only, 3 anchors.
// The Z coordinates of anchors and tag are all assumed to be zero, so for highest accuracy
// the anchors should be approximately in the same horizontal plane as the tag.
// S. James Remington 1/2022

// This code does not average multiple position measurements!

#include <SPI.h>
#include "DW1000Ranging.h"
#include "DW1000.h"

#define DEBUG_TRILAT   //prints in trilateration code
#define DEBUG_DIST     //print anchor distances

#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
#define DW_CS 4

// connection pins
const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4;   // spi select pin

// TAG antenna delay defaults to 16384
// leftmost two bytes below will become the "short address"
char tag_addr[] = "7D:00:22:EA:82:60:3B:9C";

// variables for position determination
#define N_ANCHORS 3
#define ANCHOR_DISTANCE_EXPIRED 5000   //measurements older than this are ignore (milliseconds)

// global variables, input and output

float anchor_matrix[N_ANCHORS][3] = { //list of anchor coordinates, relative to chosen origin.
  {0.0, 0.0, 0.97},  //Anchor labeled #1
  {3.99, 5.44, 1.14},//Anchor labeled #2
  {3.71, -0.3, 0.6}, //Anchor labeled #3
};  //Z values are ignored in this code

uint32_t last_anchor_update[N_ANCHORS] = {0}; //millis() value last time anchor was seen
float last_anchor_distance[N_ANCHORS] = {0.0}; //most recent distance reports

float current_tag_position[2] = {0.0, 0.0}; //global current position (meters with respect to anchor origin)
float current_distance_rmse = 0.0;  //rms error in distance calc => crude measure of position error (meters).  Needs to be better characterized

void setup()
{
  Serial.begin(115200);
  delay(1000);

  //initialize configuration
  SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin

  DW1000Ranging.attachNewRange(newRange);
  DW1000Ranging.attachNewDevice(newDevice);
  DW1000Ranging.attachInactiveDevice(inactiveDevice);

  // start as tag, do not assign random short address

  DW1000Ranging.startAsTag(tag_addr, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false);
}

void loop()
{
  DW1000Ranging.loop();
}

// collect distance data from anchors, presently configured for 4 anchors
// solve for position if all four current

void newRange()
{
  int i; //indices, expecting values 1 to 4
  //index of this anchor, expecting values 1,2,3
  int index = DW1000Ranging.getDistantDevice()->getShortAddress() & 0x03; 
  
  if (index > 0) {
    last_anchor_update[index - 1] = millis();  //decrement for array index
    float range = DW1000Ranging.getDistantDevice()->getRange();
    last_anchor_distance[index-1] = range;
    if (range < 0.0 || range > 30.0)     last_anchor_update[index - 1] = 0;  //sanity check, ignore this measurement
  }
  
  int detected = 0;

  //reject old measurements
  for (i = 0; i < N_ANCHORS; i++) {
    if (millis() - last_anchor_update[i] > ANCHOR_DISTANCE_EXPIRED) last_anchor_update[i] = 0; //not from this one
    if (last_anchor_update[i] > 0) detected++;
  }

  if ( detected == 3) { //three measurements TODO: check millis() wrap

#ifdef DEBUG_DIST
    // print distance and age of measurement
    uint32_t current_time = millis();
    for (i = 0; i < N_ANCHORS; i++) {
      Serial.print(last_anchor_distance[i]);
      Serial.print("\t");
      Serial.println(current_time - last_anchor_update[i]); //age in millis
    }
#endif

    trilat2D_3A();

    //output the values (X, Y and error estimate)
    Serial.print("P= ");
    Serial.print(current_tag_position[0]);
    Serial.write(',');
    Serial.print(current_tag_position[1]);
    Serial.write(',');
    Serial.println(current_distance_rmse);
  }
}  //end newRange

void newDevice(DW1000Device *device)
{
  Serial.print("Device added: ");
  Serial.println(device->getShortAddress(), HEX);
}

void inactiveDevice(DW1000Device *device)
{
  Serial.print("delete inactive device: ");
  Serial.println(device->getShortAddress(), HEX);
}

int trilat2D_3A(void) {

  // for method see technical paper at
  // https://www.th-luebeck.de/fileadmin/media_cosa/Dateien/Veroeffentlichungen/Sammlung/TR-2-2015-least-sqaures-with-ToA.pdf
  // S. James Remington 1/2022
  //
  // A nice feature of this method is that the normal matrix depends only on the anchor arrangement 
  // and needs to be inverted only once. Hence, the position calculation should be robust.
  //
  static bool first = true;  //first time through, some preliminary work
  float b[N_ANCHORS], d[N_ANCHORS]; //temp vector, distances from anchors

  static float Ainv[2][2], k[N_ANCHORS]; //these are calculated only once

  int i;
  // copy distances to local storage
  for (i = 0; i < N_ANCHORS; i++) d[i] = last_anchor_distance[i];

#ifdef DEBUG_TRILAT
  char line[60];
  snprintf(line, sizeof line, "d: %6.2f %6.2f %6.2f", d[0], d[1], d[2]);
  Serial.println(line);
#endif

  if (first) {  //intermediate fixed vectors
    first = false;

    float x[N_ANCHORS], y[N_ANCHORS]; //intermediate vectors
    float A[2][2];  //the A matrix for system of equations to solve

    for (i = 0; i < N_ANCHORS; i++) {
      x[i] = anchor_matrix[i][0];
      y[i] = anchor_matrix[i][1];
      k[i] = x[i] * x[i] + y[i] * y[i];
    }

    // set up least squares equation

    for (i = 1; i < N_ANCHORS; i++) {
      A[i - 1][0] = x[i] - x[0];
      A[i - 1][1] = y[i] - y[0];
#ifdef DEBUG_TRILAT
      snprintf(line, sizeof line, "A  %5.2f %5.2f \n", A[i - 1][0], A[i - 1][1]);
      Serial.println(line);
#endif
    }
    //invert A
    float det = A[0][0] * A[1][1] - A[1][0] * A[0][1];
    if (fabs(det) < 1.0E-4) {
      Serial.println("***Singular matrix, check anchor coordinates***");
      while (1) delay(1); //hang
    }

#ifdef DEBUG_TRILAT
    snprintf(line, sizeof line, "det A %8.3e\n", det);
    Serial.println(line);
#endif

    det = 1.0 / det;
    //scale adjoint
    Ainv[0][0] =  det * A[1][1];
    Ainv[0][1] = -det * A[0][1];
    Ainv[1][0] = -det * A[1][0];
    Ainv[1][1] =  det * A[0][0];
  } //end if (first);

  for (i = 1; i < N_ANCHORS; i++) {
    b[i - 1] = d[0] * d[0] - d[i] * d[i] + k[i] - k[0];
  }

  //least squares solution for position
  //solve:  2 A rc = b

  current_tag_position[0] = 0.5 * (Ainv[0][0] * b[0] + Ainv[0][1] * b[1]);
  current_tag_position[1] = 0.5 * (Ainv[1][0] * b[0] + Ainv[1][1] * b[1]);

  // calculate rms error for distances
  float rmse = 0.0, dc0 = 0.0, dc1 = 0.0;
  for (i = 0; i < N_ANCHORS; i++) {
    dc0 = current_tag_position[0] - anchor_matrix[i][0];
    dc1 = current_tag_position[1] - anchor_matrix[i][1];
    dc0 = d[i] - sqrt(dc0 * dc0 + dc1 * dc1);
    rmse += dc0 * dc0;
  }
  current_distance_rmse = sqrt(rmse / ((float)N_ANCHORS));

  return 1;
}  //end trilat2D_3A

Post an example of the output.

if ( detected == 3) { //three measurements TODO: check millis() wrap
Note that If three anchors total are not detected, nothing is printed.

Put in a serial print to see what is happening in newRange(), e.g.:

  if (index > 0) {
    last_anchor_update[index - 1] = millis();  //decrement for array index
    float range = DW1000Ranging.getDistantDevice()->getRange();
    last_anchor_distance[index-1] = range;
   Serial.print(index);
   Serial.print(", ");
   Serial.println(range);
    if (range < 0.0 || range > 30.0)     last_anchor_update[index - 1] = 0;  //sanity check, ignore this measurement
  }

This is how i adjusted the code:



// currently tag is module #5
// this version is 2D (X,Y) only, 3 anchors.
// The Z coordinates of anchors and tag are all assumed to be zero, so for highest accuracy
// the anchors should be approximately in the same horizontal plane as the tag.
// S. James Remington 1/2022

// This code does not average multiple position measurements!

#include <SPI.h>
#include "DW1000Ranging.h"
#include "DW1000.h"

#define DEBUG_TRILAT   //prints in trilateration code
#define DEBUG_DIST     //print anchor distances

#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
#define DW_CS 4

// connection pins
const uint8_t PIN_RST = 27; // reset pin
const uint8_t PIN_IRQ = 34; // irq pin
const uint8_t PIN_SS = 4;   // spi select pin

// TAG antenna delay defaults to 16384
// leftmost two bytes below will become the "short address"
char tag_addr[] = "7D:00:22:EA:82:60:3B:9C";

// variables for position determination
#define N_ANCHORS 3
#define ANCHOR_DISTANCE_EXPIRED 5000   //measurements older than this are ignore (milliseconds)

// global variables, input and output

float anchor_matrix[N_ANCHORS][3] = { //list of anchor coordinates, relative to chosen origin.
  {0.0, 0.0, 0.97},  //Anchor labeled #1
  {3.99, 5.44, 1.14},//Anchor labeled #2
  {3.71, -0.3, 0.6}, //Anchor labeled #3
};  //Z values are ignored in this code

uint32_t last_anchor_update[N_ANCHORS] = {0}; //millis() value last time anchor was seen
float last_anchor_distance[N_ANCHORS] = {0.0}; //most recent distance reports

float current_tag_position[2] = {0.0, 0.0}; //global current position (meters with respect to anchor origin)
float current_distance_rmse = 0.0;  //rms error in distance calc => crude measure of position error (meters).  Needs to be better characterized

void setup()
{
  Serial.begin(115200);
  delay(1000);

  //initialize configuration
  SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin

  DW1000Ranging.attachNewRange(newRange);
  DW1000Ranging.attachNewDevice(newDevice);
  DW1000Ranging.attachInactiveDevice(inactiveDevice);

  // start as tag, do not assign random short address

  DW1000Ranging.startAsTag(tag_addr, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false);
}

void loop()
{
  DW1000Ranging.loop();
}

// collect distance data from anchors, presently configured for 4 anchors
// solve for position if all four current

void newRange()
{
  int i; //indices, expecting values 1 to 4
  //index of this anchor, expecting values 1,2,3
  int index = DW1000Ranging.getDistantDevice()->getShortAddress() & 0x03; 
  
  if (index > 0) {
   last_anchor_update[index - 1] = millis();  //decrement for array index
    float range = DW1000Ranging.getDistantDevice()->getRange();
    last_anchor_distance[index-1] = range;
   Serial.print(index);
   Serial.print(", ");
   Serial.println(range);
    if (range < 0.0 || range > 30.0)     last_anchor_update[index - 1] = 0;  //sanity check, ignore this measurement
  }
  
  int detected = 0;

  //reject old measurements
  for (i = 0; i < N_ANCHORS; i++) {
    if (millis() - last_anchor_update[i] > ANCHOR_DISTANCE_EXPIRED) last_anchor_update[i] = 0; //not from this one
    if (last_anchor_update[i] > 0) detected++;
  }

  if ( detected == 3) { //three measurements TODO: check millis() wrap

#ifdef DEBUG_DIST
    // print distance and age of measurement
    uint32_t current_time = millis();
    for (i = 0; i < N_ANCHORS; i++) {
      Serial.print(last_anchor_distance[i]);
      Serial.print("\t");
      Serial.println(current_time - last_anchor_update[i]); //age in millis
    }
#endif

    trilat2D_3A();

    //output the values (X, Y and error estimate)
    Serial.print("P= ");
    Serial.print(current_tag_position[0]);
    Serial.write(',');
    Serial.print(current_tag_position[1]);
    Serial.write(',');
    Serial.println(current_distance_rmse);
  }
}  //end newRange

void newDevice(DW1000Device *device)
{
  Serial.print("Device added: ");
  Serial.println(device->getShortAddress(), HEX);
}

void inactiveDevice(DW1000Device *device)
{
  Serial.print("delete inactive device: ");
  Serial.println(device->getShortAddress(), HEX);
}

int trilat2D_3A(void) {

  // for method see technical paper at
  // https://www.th-luebeck.de/fileadmin/media_cosa/Dateien/Veroeffentlichungen/Sammlung/TR-2-2015-least-sqaures-with-ToA.pdf
  // S. James Remington 1/2022
  //
  // A nice feature of this method is that the normal matrix depends only on the anchor arrangement 
  // and needs to be inverted only once. Hence, the position calculation should be robust.
  //
  static bool first = true;  //first time through, some preliminary work
  float b[N_ANCHORS], d[N_ANCHORS]; //temp vector, distances from anchors

  static float Ainv[2][2], k[N_ANCHORS]; //these are calculated only once

  int i;
  // copy distances to local storage
  for (i = 0; i < N_ANCHORS; i++) d[i] = last_anchor_distance[i];

#ifdef DEBUG_TRILAT
  char line[60];
  snprintf(line, sizeof line, "d: %6.2f %6.2f %6.2f", d[0], d[1], d[2]);
  Serial.println(line);
#endif

  if (first) {  //intermediate fixed vectors
    first = false;

    float x[N_ANCHORS], y[N_ANCHORS]; //intermediate vectors
    float A[2][2];  //the A matrix for system of equations to solve

    for (i = 0; i < N_ANCHORS; i++) {
      x[i] = anchor_matrix[i][0];
      y[i] = anchor_matrix[i][1];
      k[i] = x[i] * x[i] + y[i] * y[i];
    }

    // set up least squares equation

    for (i = 1; i < N_ANCHORS; i++) {
      A[i - 1][0] = x[i] - x[0];
      A[i - 1][1] = y[i] - y[0];
#ifdef DEBUG_TRILAT
      snprintf(line, sizeof line, "A  %5.2f %5.2f \n", A[i - 1][0], A[i - 1][1]);
      Serial.println(line);
#endif
    }
    //invert A
    float det = A[0][0] * A[1][1] - A[1][0] * A[0][1];
    if (fabs(det) < 1.0E-4) {
      Serial.println("***Singular matrix, check anchor coordinates***");
      while (1) delay(1); //hang
    }

#ifdef DEBUG_TRILAT
    snprintf(line, sizeof line, "det A %8.3e\n", det);
    Serial.println(line);
#endif

    det = 1.0 / det;
    //scale adjoint
    Ainv[0][0] =  det * A[1][1];
    Ainv[0][1] = -det * A[0][1];
    Ainv[1][0] = -det * A[1][0];
    Ainv[1][1] =  det * A[0][0];
  } //end if (first);

  for (i = 1; i < N_ANCHORS; i++) {
    b[i - 1] = d[0] * d[0] - d[i] * d[i] + k[i] - k[0];
  }

  //least squares solution for position
  //solve:  2 A rc = b

  current_tag_position[0] = 0.5 * (Ainv[0][0] * b[0] + Ainv[0][1] * b[1]);
  current_tag_position[1] = 0.5 * (Ainv[1][0] * b[0] + Ainv[1][1] * b[1]);

  // calculate rms error for distances
  float rmse = 0.0, dc0 = 0.0, dc1 = 0.0;
  for (i = 0; i < N_ANCHORS; i++) {
    dc0 = current_tag_position[0] - anchor_matrix[i][0];
    dc1 = current_tag_position[1] - anchor_matrix[i][1];
    dc0 = d[i] - sqrt(dc0 * dc0 + dc1 * dc1);
    rmse += dc0 * dc0;
  }
  current_distance_rmse = sqrt(rmse / ((float)N_ANCHORS));

  return 1;
}  //end trilat2D_3A

These is what's being printed on the serial monitor now:

Device added: 81

1, 0.43

1, 0.44

1, 0.49

1, 0.57

1, 0.52

1, 0.45

1, 0.46

1, 0.38

1, 0.42

1, 0.44

1, 0.52

1, 0.48

Device added: 82

1, 0.46

2, 0.33

1, 0.64

2, 0.33

1, 0.68

2, 0.30

1, 0.60

2, 0.31

1, 0.61

2, 0.31

1, 0.59

Device added: 80

1, 0.50

2, 0.33

1, 0.51

2, 0.29

1, 0.53

2, 0.32

1, 0.51

It sees only two anchors. Three have to be detected in order to calculation a position.

Device added: 80

Name that anchor "83".

Are these the correct coordinates for the anchors? The distances seem too short.

  {0.0, 0.0, 0.97},  //Anchor labeled #1
  {3.99, 5.44, 1.14},//Anchor labeled #2
  {3.71, -0.3, 0.6}, //Anchor labeled #3

after the device anchor from 80 to 83, it currently work thank you. Some results seen below:

1, 0.77
d: 0.77 0.63 0.70
P= 2.10,2.66,2.68
3, 0.68
d: 0.77 0.63 0.68
P= 2.10,2.66,2.68
2, 0.68
d: 0.77 0.68 0.68
P= 2.10,2.66,2.66

Regarding your second question i do currently have them relatively close to each other (plugged into my computer) for ease of testing, so they are incorrect. I do plan to however use these in a building about 35-40 meters. I had planned to make a corner of the building be the origin point and make a triangle with the 3 anchors and measure the distance using tape measure and input the distance in meters as anchor 1,2 and 3 in the code. If my logic is flawed please let me know. A picture as an example is shown. Secondly in the tag code where the coordinates are, is anchor 1, 2 and 3, linked to a specific id ? eg anchor with address 83 is anchor 3 in tag code, or does that aspect not matter and i should just make sure they all have different coordinates?

Thanks again for your help.

image

Yes, "81" = anchor #1, "82" = anchor #2, etc. The coordinates you enter must match the ID and be in that order in the position array, as commented below:

If anchor #1 is at (0, 6.13, Z) then the first line of that array could look like this

float anchor_matrix[N_ANCHORS][3] = { //list of anchor coordinates, relative to chosen origin
  {0.0, 6.13, 0.0 },  //Anchor labeled #1 (Z value?)
...

Noted. Also The P value in my last messasge ,the first 2 values assuming its the current x and y value of the tag's position based on its coordinates of the anchors and the origin , what is the third value?

code below:

    trilat2D_3A();

    //output the values (X, Y and error estimate)
    Serial.print("P= ");
    Serial.print(current_tag_position[0]);
    Serial.write(',');
    Serial.print(current_tag_position[1]);
    Serial.write(',');
    Serial.println(current_distance_rmse);
  }
}  //end newRange

3rd value of P:

Serial.println(current_distance_rmse);

Estimated RMS error in the distance measurements, which is large because you have not entered the correct anchor coordinates.

From the code:

  // calculate rms error for distances
  float rmse = 0.0, dc0 = 0.0, dc1 = 0.0;
  for (i = 0; i < N_ANCHORS; i++) {
    dc0 = current_tag_position[0] - anchor_matrix[i][0];
    dc1 = current_tag_position[1] - anchor_matrix[i][1];
    dc0 = d[i] - sqrt(dc0 * dc0 + dc1 * dc1);
    rmse += dc0 * dc0;
  }
  current_distance_rmse = sqrt(rmse / ((float)N_ANCHORS));

Noted, Thank you

Hey, i am trying to recreate the tag as a project on a pcb using the dwm1000 whilst still using the uwbpro as anchors, im not getting an readings from the tag after testing and was hoping if you could point out any issues that i may have missed in my pcb creation
UWBwho2.zip (48.3 KB)

i used this code just to ascertain if it was working, any input is appreciated thank you

// currently tag is module #5
// The purpose of this code is to set the tag address and antenna delay to default.
// this tag will be used for calibrating the anchors.

#include <SPI.h>
#include "DW1000Ranging.h"
#include "DW1000.h"

#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
#define DW_CS 4

// connection pins
const uint8_t PIN_RST = 34; // reset pin
const uint8_t PIN_IRQ = 35; // irq pin
const uint8_t PIN_SS = 4;   // spi select pin

// TAG antenna delay defaults to 16384
// leftmost two bytes below will become the "short address"
char tag_addr[] = "7D:00:22:EA:82:60:3B:9C";

void setup()
{
  Serial.begin(115200);
  delay(1000);

  //init the configuration
  SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin

  DW1000Ranging.attachNewRange(newRange);
  DW1000Ranging.attachNewDevice(newDevice);
  DW1000Ranging.attachInactiveDevice(inactiveDevice);

// start as tag, do not assign random short address

  DW1000Ranging.startAsTag(tag_addr, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false);
}

void loop()
{
  DW1000Ranging.loop();
}

void newRange()
{
  Serial.print(DW1000Ranging.getDistantDevice()->getShortAddress(), HEX);
  Serial.print(",");
  Serial.println(DW1000Ranging.getDistantDevice()->getRange());
}

void newDevice(DW1000Device *device)
{
  Serial.print("Device added: ");
  Serial.println(device->getShortAddress(), HEX);
}

void inactiveDevice(DW1000Device *device)
{
  Serial.print("delete inactive device: ");
  Serial.println(device->getShortAddress(), HEX);
}

UWBwho2 (2).zip (14.6 KB)
this is the layout of the pcb i used

never mind lol, had a panic attack, my pcb is fine, i just defined one of the pins incorrectly in the code. I double checked for continuity between all connections, checked the voltage values for everything then after cross referencing the code i realized it.

@jremington i made some adjustments to the code to facilitate sending the infomation to a mysql database for storage to be represented to a real time map. It worked well when i tested it yesterday... however its giving me insane values in for coordinates and distances currently. Im doing the test on a table less than 1.5 meters long, and have an if statement to filter infrequent inccorect distance and coordinates that appear outside the range and dimension of the bounds set by the anchors. Below is some of the more recent results i have regarding this issue, any insight is appreciated

from serial monitor

d: 50.00 -248.56 -101.61

P= 25227.00,-14840.52,29368.51

Coordinates out of valid range, data not sent.

1, 0.51

0.51 0

-248.56 273

-101.61 14

d: 0.51 -248.56 -101.61

P= 26290.72,-13990.26,29897.80

Coordinates out of valid range, data not sent.

2, 0.29

0.51 14

0.29 0

-101.61 28

d: 0.51 0.29 -101.61

P= 0.51,7024.02,7057.30

Coordinates out of valid range, data not sent.

3, 0.37

0.51 130

0.29 116

0.37 0

d: 0.51 0.29 0.37

P= 0.51,0.58,0.21

HTTP Response code: 200

Response:

New record created successfully

3, -2.24

0.51 318

0.29 304

-2.24 0

d: 0.51 0.29 -2.24

P= 0.51,3.90,4.23

Coordinates out of valid range, data not sent.

1, -258.72

-258.72 0

0.29 318

-2.24 14

d: -258.72 0.29 -2.24

P= -28482.80,-22763.37,36549.36

HTTP Response code: 200

Response:

New record created successfully

3, 283.34

-258.72 207

0.29 525

283.34 0

d: -258.72 0.29 283.34

P= -28482.80,31846.51,42717.82

Coordinates out of valid range, data not sent.

1, 113.44

113.44 0

0.29 539

283.34 14

d: 113.44 0.29 283.34

P= -5475.39,50236.79,50401.65

Coordinates out of valid range, data not sent.

2, 283.92

113.44 14

283.92 0

283.34 28

d: 113.44 283.92 283.34

P= 28826.95,22818.26,36537.43

Coordinates out of valid range, data not sent.


#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>
#include "DW1000Ranging.h"
#include "DW1000.h"

#define DEBUG_TRILAT   //prints in trilateration code
#define DEBUG_DIST     //print anchor distances

#define SPI_SCK 18
#define SPI_MISO 19
#define SPI_MOSI 23
#define DW_CS 2

// connection pins
const uint8_t PIN_RST = 34; // reset pin
const uint8_t PIN_IRQ = 35; // irq pin
const uint8_t PIN_SS = 2;   // spi select pin
// TAG antenna delay defaults to 16384
// leftmost two bytes below will become the "short address"
char tag_addr[] = "7D:00:22:EA:82:60:3B:9C";

// variables for position determination
#define N_ANCHORS 3
#define ANCHOR_DISTANCE_EXPIRED 5000   //measurements older than this are ignore (milliseconds)


const char* ssid = "CWC-7053359";
const char* password = "rt6xzTfrLnc6";
const char* serverName = "http://localhost/UWBDATABASE/insert_distance1.inc.php";


// global variables, input and output

float anchor_matrix[N_ANCHORS][3] = { //list of anchor coordinates, relative to chosen origin.
  {1.175, 0.735, 0.0},  //Anchor labeled #1
  {0.00, 0.735, 0.0},//Anchor labeled #2
  {0.5875, 0.0, 0.0}, //Anchor labeled #3
};  //Z values are ignored in this code

uint32_t last_anchor_update[N_ANCHORS] = {0}; //millis() value last time anchor was seen
float last_anchor_distance[N_ANCHORS] = {0.0}; //most recent distance reports

float current_tag_position[2] = {0.0, 0.0}; //global current position (meters with respect to anchor origin)
float current_distance_rmse = 0.0;

void setup()
{
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("\nWiFi connected.");
  Serial.println("IP address: " + WiFi.localIP().toString());



  //delay(1000);

  //initialize configuration
  SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI);
  DW1000Ranging.initCommunication(PIN_RST, PIN_SS, PIN_IRQ); //Reset, CS, IRQ pin

  DW1000Ranging.attachNewRange(newRange);
  DW1000Ranging.attachNewDevice(newDevice);
  DW1000Ranging.attachInactiveDevice(inactiveDevice);

  // start as tag, do not assign random short address

  DW1000Ranging.startAsTag(tag_addr, DW1000.MODE_LONGDATA_RANGE_LOWPOWER, false);
}

void loop()
{
  DW1000Ranging.loop();
}

// collect distance data from anchors, presently configured for 4 anchors
// solve for position if all four current

void newRange()
{
  int i; //indices, expecting values 1 to 4
  //index of this anchor, expecting values 1,2,3
  int index = DW1000Ranging.getDistantDevice()->getShortAddress() & 0x03; 
  
  if (index > 0) {
    last_anchor_update[index - 1] = millis();  //decrement for array index
    float range = DW1000Ranging.getDistantDevice()->getRange();
    last_anchor_distance[index-1] = range;
   Serial.print(index);
   Serial.print(", ");
   Serial.println(range);
    
  }

  int detected = 0;

  //reject old measurements
  for (i = 0; i < N_ANCHORS; i++) {

    //checks the status of each anchor in the system.
    //This condition checks if the time difference
    // between the current time (millis()) and the last update time for the current anchor
    // (last_anchor_update[i]) is greater than a threshold value (ANCHOR_DISTANCE_EXPIRED) 5000MS
    //iftrue sets last anchor update to zero and discards value, false its now detected
    if (millis() - last_anchor_update[i] > ANCHOR_DISTANCE_EXPIRED) last_anchor_update[i] = 0; //not from this one
    if (last_anchor_update[i] > 0) detected++;
  }

  if ( detected == 3) { //three measurements TODO: check millis() wrap

#ifdef DEBUG_DIST
    // print distance and age of measurement
    uint32_t current_time = millis();
    for (i = 0; i < N_ANCHORS; i++) {
      Serial.print(last_anchor_distance[i]);
      Serial.print("\t");
      Serial.println(current_time - last_anchor_update[i]); //age in millis
    }
#endif

    trilat2D_3A();

    //output the values (X, Y and error estimate)
    Serial.print("P= ");
    Serial.print(current_tag_position[0]);
    Serial.write(',');
    Serial.print(current_tag_position[1]);
    Serial.write(',');
    Serial.println(current_distance_rmse);
  }
  
if ( detected == 3){
if (current_tag_position[0] <= 1.175 && current_tag_position[1] <= 0.735 && last_anchor_distance[i] <1.5 ) {


  // Create HTTP client to send the data
  HTTPClient http;
  http.begin(serverName);
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");

  String httpRequestData = "distance1=" + String(last_anchor_distance[0]) +
                           "&distance2=" + String(last_anchor_distance[1]) +
                           "&distance3=" + String(last_anchor_distance[2]) +
                           "&coordinateX=" + String(current_tag_position[0]) +
                           "&coordinateY=" + String(current_tag_position[1]);

  int httpResponseCode = http.POST(httpRequestData);

  if (httpResponseCode > 0) {
    String response = http.getString();
    Serial.println("HTTP Response code: " + String(httpResponseCode));
    Serial.println("Response: " + response);
  } else {
    Serial.println("Error on sending POST: " + String(httpResponseCode));
  }

  http.end();


} else{
  Serial.println("Coordinates out of valid range, data not sent.");

}

}

}  //end newRange

void newDevice(DW1000Device *device)
{
  Serial.print("Device added: ");
  Serial.println(device->getShortAddress(), HEX);
}

void inactiveDevice(DW1000Device *device)
{
  Serial.print("delete inactive device: ");
  Serial.println(device->getShortAddress(), HEX);
}

int trilat2D_3A(void) {

  // keeps track of whether its the first time the function is being called
  static bool first = true;  //first time through, some preliminary work

  //stores temporary values,b for immediate caluclatoin, d for distances from anchors

  float b[N_ANCHORS], d[N_ANCHORS]; //temp vector, distances from anchors

  //float anchor_matrix[N_ANCHORS][3]
  //last_anchor_distance


  //least squares formula
  //2*A*X=B
  //X= 0.5 * Ainv * b
  // A (Ainv) is an inverted 2x2 matrix representing the anchor arrangement
  //x (current_tag_position[0,1])is a 2x1 vector representing unknown tag coordinates x and y
  //B is a 2x1 vector intermediate values calculated based 
  //on the measured distances (d) and anchor locations (k)

//Ainv stores the inverted anchor matrix(for solving the linear system)
//k stores the pre calculated values based on anchor coordinates(to simplify calc)
  static float Ainv[2][2], k[N_ANCHORS]; //these are calculated only once

  int i;
  // copy latest distances to local d array variable
  for (i = 0; i < N_ANCHORS; i++) d[i] = last_anchor_distance[i];

#ifdef DEBUG_TRILAT
  char line[60];
  snprintf(line, sizeof line, "d: %6.2f %6.2f %6.2f", d[0], d[1], d[2]);
  Serial.println(line);
#endif

//for first distance only, calcs based on anchor location
  if (first) {  //intermediate fixed vectors
    first = false;
  // These arrays are used for temporary calculations within this block.
    float x[N_ANCHORS], y[N_ANCHORS]; //intermediate vectors
    float A[2][2];  //the A matrix for system of equations to solve


    //iterates through each anchor and extracts the x and y coordinates
    for (i = 0; i < N_ANCHORS; i++) {
      x[i] = anchor_matrix[i][0];
      y[i] = anchor_matrix[i][1];

      //formula for constant value ,based on the anchor squared coordinates 
      k[i] = x[i] * x[i] + y[i] * y[i];
    }

    // set up least squares equation
    //sets up the matrix declared as a to be used in the least  squares eqn
    // matrix on relative position of anchors

    for (i = 1; i < N_ANCHORS; i++) {
      //calculates the difference in x and y coordinates between current 
      // anchor in loop and  reference anchor
      A[i - 1][0] = x[i] - x[0];
      A[i - 1][1] = y[i] - y[0];
#ifdef DEBUG_TRILAT
      snprintf(line, sizeof line, "A  %5.2f %5.2f \n", A[i - 1][0], A[i - 1][1]);
      Serial.println(line);
#endif
    }
    //invert A
    //to solve in the system of equations
    float det = A[0][0] * A[1][1] - A[1][0] * A[0][1];
    if (fabs(det) < 1.0E-4) {
      Serial.println("***Singular matrix, check anchor coordinates***");
      while (1) delay(1); //hang
    }

#ifdef DEBUG_TRILAT
    snprintf(line, sizeof line, "det A %8.3e\n", det);
    Serial.println(line);
#endif

    det = 1.0 / det;
    //scale adjoint
    Ainv[0][0] =  det * A[1][1];
    Ainv[0][1] = -det * A[0][1];
    Ainv[1][0] = -det * A[1][0];
    Ainv[1][1] =  det * A[0][0];
  } //end if (first);

  for (i = 1; i < N_ANCHORS; i++) {

    //iterates through the distances d and 
    //calculates the b vector elements used in the least squares solution
    b[i - 1] = d[0] * d[0] - d[i] * d[i] + k[i] - k[0];
  }

  //least squares solution for position
  //solve:  2 A rc = b
  //lines use the inverted matrix Ainv and the b vector 
  //to solve for the tag's coordinates in X (0) and Y (1) dimensions.
  //X = 0.5 * Ainv * b
  current_tag_position[0] = 0.5 * (Ainv[0][0] * b[0] + Ainv[0][1] * b[1]);
  current_tag_position[1] = 0.5 * (Ainv[1][0] * b[0] + Ainv[1][1] * b[1]);

  // calculate rms error for distances
  float rmse = 0.0, dc0 = 0.0, dc1 = 0.0;
  for (i = 0; i < N_ANCHORS; i++) {
    dc0 = current_tag_position[0] - anchor_matrix[i][0];
    dc1 = current_tag_position[1] - anchor_matrix[i][1];
    dc0 = d[i] - sqrt(dc0 * dc0 + dc1 * dc1);
    rmse += dc0 * dc0;
  }
  current_distance_rmse = sqrt(rmse / ((float)N_ANCHORS));

  return 1;
}  //end trilat2D_3A

These are results where everything was working properly


Hi.
I have currently followed all the steps and the work is fine. The problem is that when I try to use 2D_4A code with 4 anchors, it does not display anything. It detects the devices. Whenever I use 3 anchors, it compiles and displays data perfectly. What could be the possible reason? I am attaching the picture of output as well. Thanks in advance.
image