Pozyx

Accurate positioning

Home > Documentation > Tutorials > Tutorial 2: Ready to localize

Tutorial 2: Ready to localize

Go to Arduino versionGo to Python version

Ready to localize

This is the second Pozyx tutorial, in which we’ll go through the process of performing (remote) positioning with the Pozyx system. If you missed

the first one, check that one out first, as each tutorial assumes knowledge of the concepts explained in the ones before.

For this example, you need to own at least the contents of the Ready to Localize kit and a supported Arduino device. Remember that we recommend the Arduino Uno Rev. 3. If all tools are installed, open up the ready to localize example in the Arduino IDE under File > Examples > Pozyx > ready_to_localize.

For this example, you need to own at least the contents of the Ready to Localize kit. If all tools are installed, open up ready_to_localize.py in the Pozyx library’s tutorial folder. Probably, the path to this file will be "Downloads/Pozyx-Python-library/tutorials/ready_to_localize.py". You can run the script from either command line or a text editor that allows running the scripts as well. If you're on Windows and have installed Python, you might be able to simply double-click it. If you you get an error, you likely forgot to install pythonosc, you can do this easily using pip install python-osc

In this example, we will first set up and measure the anchor locations to perform positioning, after which we’ll be able to get accurate position data of the Pozyx, relative to the anchor setup. We will go in detail through other Pozyx core concepts as well, such as how to check the Pozyx’s device list, and how to read its error status. Mastering these debugging tools now will make using Pozyx in your own projects easier. After this, we will go through how to visualize this data in 2D in Processing.

Contents:

  1. Anchor setup and measurement
  2. The code explained
  3. Remote positioning
  4. Visualization
  5. Extras

Anchor setup and measurement

Anchor setup

The Pozyx positioning system requires that the four anchors are placed inside the area where you wish to do positioning. In the guide 'Where to place the anchors?', it is explained how to place the anchors for the best possible positioning accuracy. The rules of thumb for the anchor placement were:

  1. Place the anchors high and in line-of-sight of the user.
  2. Spread the anchors around the user, never all on a straight line!
  3. Place the anchors vertically with the antenna at the top, power plug at the bottom.
  4. For 3D positioning: place the anchors at different heights.

It's also important to keep metallic objects out of immediate range of the antennas.

Before you install the anchors (with the provided Velcros or screws) on the walls or ceiling, it is usually a good idea to make a small sketch of the room, writing down the anchors’ IDs and sketching where they will be placed. You can find the network ID as the hexadecimal number on the label adorning your anchor.

Remember that, for optimal antenna performance, it is recommended to place the anchors vertically with their antenna at the top, and to orient your tags that will be positioning vertically as well. Also make sure that no heavy metal objects are placed near the antenna, as this might degrade performance. We stress this again because it's just that important.

Measurement

For this first use of Pozyx, we recommend using manual calibration. While you might be tempted to try automatic calibration, because, well, it’s automatic, there are some reasons we’re going to use manual calibration regardless of its existence:

  • Manual calibration allows the most accurate positioning!
  • The same but different: automatic calibration will always be less precise than an accurate manual measurement.
  • Insights in how positioning is done by the anchors is useful in consistent use of Pozyx, and this is a tutorial.
  • Automatic calibration is currently possible, but needs the right environment and settings.
  • Automatic calibration is being worked on to be more robust, and might change. Manual calibration won’t change.
  • Automatic calibration is therefore currently considered an advanced and experimental feature.

While there are different ways to measure your distance, if you are serious about using Pozyx we recommend to have a laser measurer. This will be much more convenient, and much more accurate, than trying to measure out several meters between your anchors with a conventional foldable or extendable We recommend placing them in an approximate rectangle as in the image above. This allows you to simplify both measurement and setup, and to follow along better with the example.

The Pozyx coordinate system

Pozyx determines its position relative to the anchor coordinates you supplied it with. This gives you the freedom to position and rotate your axis in any way you want. This is shown in the images below:

You can see that the coordinate system on the left, which is conventional to the room’s shape and orientation, is made by using anchor 0x256B as an element of the x-axis, giving it a zero y-coordinate. Then the y-axis is perpendicular to this, and anchor 0x3325 therefore has a non-zero x-component to fit into this orthogonal system. Anchor 0x1156 is selected as the origin, again for convenience. This origin doesn’t need to be one of the anchors, however, and you could use the center of the room as the origin point just as well, modifying the anchor’s coordinates accordingly: anchors 0x1156 and 0x3325 would have a negative x-component, while 0x2568 and 0x4244 have a positive coordinate. In turn, 0x1156 and 0x256B have a negative y-component while 0x3325 and 0x4244 will have a positive one.
In the right example, the origin is chosen to be anchor 0x3325’s location, and the x-axis is matched with the path between anchors 0x3325 and 0x1156.

Let’s go over the plan of attack in measuring out your setup, following the approach shown on the left:

  • Use the antenna’s center as Pozyx’s position in space as best as you can.
  • Pick one of your anchors as the origin, like we did 0x1156.
  • Pick the anchor on the same wall to be the one defining your x-axis. We picked 0x2568.
  • Measure out the horizontal distance between both anchors. Not a direct distance as this will include the vertical distance as well (Pythagoras). This will be that anchor’s x-coordinate. In our case, this was 4.5 m.
  • Now measure the distance to the opposite wall. Mark this point on the wall, as this is the x-axis’s zero value on that wall. If both walls are parallel, and there are anchors attached directly to this wall, you can set their y-coordinate to this distance. In our case, we could set 0x4244’s y-coordinate directly to 3.5 m.
  • Now measure the x-coordinates of every anchor on that wall directly using the point you marked earlier, again assuming both walls are parallel. Measurements can be complicated if they are not, so use more reference points then.
  • If there are anchors a bit apart from the wall, like 0x3325, be sure to account for this in in its y-coordinate.

In this example, we’ve used the approach described above. We’ll also assume the anchors are on heights between 1.1 and 2 meter, leading to these coordinates for each anchor:

  • 0x1156: (0, 0, 1500)
  • 0x256B: (4500, 0, 1800)
  • 0x3325: (500, 3300, 1100)
  • 0x4244: (4450, 3500, 2000)

These are the coordinates used in the example, but you can’t copy these! You will have to change both anchor IDs and coordinates to match your own setup. All these coordinates are expressed in millimeters.

Plug and play

To get the example working and see what exactly is happening before we delve into the code, we’ll need to change the parameters to match your device IDs and the coordinates you measured.

Don’t forget to set the Arduino IDE’s serial monitor’s baudrate to 115200. At the top of the Arduino sketch, we find the sketch’s parameters. You can see that there’s an array for the anchor IDs and each of the anchor coordinates. You’ll have to translate your measurement to these parameters, where each anchor’s properties has the same index in its respective array. We translated the example setup from above, as a reference.

uint16_t remote_id = 0x6000;
bool remote = false;                                        // set this to true to use the remote ID

boolean use_processing = false;                             // set this to true to output data for the processing sketch

uint8_t num_anchors = 4;                                    // the number of anchors
uint16_t anchors[4] = {0x1156, 0x256B, 0x3325, 0x4244};     // the network id of the anchors: change these to the network ids of your anchors.
int32_t anchors_x[4] = {0, 4500, 500, 4450};               // anchor x-coorindates in mm
int32_t anchors_y[4] = {0, 0, 3300, 3500};                  // anchor y-coordinates in mm
int32_t heights[4] = {1500, 1800, 1100, 2000};              // anchor z-coordinates in mm

uint8_t algorithm = POZYX_POS_ALG_UWB_ONLY;
uint8_t dimension = POZYX_3D;
int32_t height = 1000;

At the bottom of the Python script, in the ifmain structure, we find the script’s parameters. You can see that the first one is the serial port address of your device. We use serial_port = get_serial_ports()[0].device to automate the port selection, and will do so from now on.
The anchors' data is created as a list of DeviceCoordinates objects. DeviceCoordinates' second parameter, POZYX_ANCHOR, is a flag indicating you’re adding an anchor device. You’ll have to match the IDs and coordinates to the ones of your setup. We’ve done that for the example setup from above, as a reference.

if __name__ == "__main__":
    # shortcut to not have to find out the port yourself
    serial_port = get_serial_ports()[0].device

    remote_id = 0x1000                 # remote device network ID
    remote = False                     # whether to use a remote device
    if not remote:
        remote_id = None

    use_processing = False             # enable to send position data through OSC
    ip = "127.0.0.1"                   # IP for the OSC UDP
    network_port = 8888                # network port for the OSC UDP
    osc_udp_client = None
    if use_processing:
        osc_udp_client = SimpleUDPClient(ip, network_port)
    # necessary data for calibration, change the IDs and coordinates yourself
    anchors = [DeviceCoordinates(0x0001, 1, Coordinates(0, 0, 2000)),
               DeviceCoordinates(0x0002, 1, Coordinates(3000, 0, 2000)),
               DeviceCoordinates(0x0003, 1, Coordinates(0, 3000, 2000)),
               DeviceCoordinates(0x0004, 1, Coordinates(3000, 3000, 2000))]

    algorithm = POZYX_POS_ALG_UWB_ONLY # positioning algorithm to use
    dimension = POZYX_3D               # positioning dimension
    height = 1000                      # height of device, required in 2.5D positioning

You will also see the remote_id, remote, algorithm, dimension, and use_processing parameters. remote_id and remote should be familiar from the first tutorial. use_processing will be used for the visualization. algorithm, dimension, and height will allow for customization regarding the positioning, and we'll get back to these when we're looking at the code in detail. Leave these parameters unchanged for now, which will result in 3D positioning being done with the UWB-only algorithm.

Now that you’ve done all the plugging, it’s time for play. Run the example, and if all goes well, you’re now looking at coordinates filling up the window, after the manual anchor configuration is checked and printed. As you are using your local device, the ID will be set to 0.

POS ID 0x0000, x(mm): 1231 y(mm): 2354 z(mm): 1167
POS ID 0x0000, x(mm): 1236 y(mm): 2241 z(mm): 1150
etc...

That’s that! You’re now getting accurate position coordinates from the Pozyx! You might also be seeing one of the following:

POS ID 0x0000, x(mm): 0 y(mm): 0 z(mm): 0
or
ERROR configuration on ID 0x0000, error code 0xXX
or
ERROR positioning on ID 0x0000, error code 0xXX
or
Coordinates that are nothing even remotely close to what they should be

If so, you probably miswrote one of the anchor IDs or entered either wrong or mixed up coordinates, causing the positioning algorithm to fail. If the error persists despite having put in everything correctly, check out the troubleshooting guide. Now that you've seen the positioning in action, let's look at the code that made this possible.

The code explained

We will now cover the essential code to get positioning working, but there's a lot more code in the file. The extras segment will cover how to access the Pozyx's device list, how to retrieve error codes returned by the Pozyx, and how to retrieve additional sensor data each time you position.

Setup and manual calibration

Both the serial communication and Pozyx are initialized in the setup function.

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

  if(Pozyx.begin() == POZYX_FAILURE){
    Serial.println(F("ERROR: Unable to connect to POZYX shield"));
    Serial.println(F("Reset required"));
    delay(100);
    abort();
  }

  if(!remote){
    remote_id = NULL;
  }

  Serial.println(F("----------POZYX POSITIONING V1.0----------"));
  Serial.println(F("NOTES:"));
  Serial.println(F("- No parameters required."));
  Serial.println();
  Serial.println(F("- System will auto start anchor configuration"));
  Serial.println();
  Serial.println(F("- System will auto start positioning"));
  Serial.println(F("----------POZYX POSITIONING V1.0----------"));
  Serial.println();
  Serial.println(F("Performing manual anchor configuration:"));

  // clear all previous devices in the device list
  Pozyx.clearDevices(remote_id);
  // sets the anchor manually
  setAnchorsManual();

  printCalibrationResult();
  delay(2000);

  Serial.println(F("Starting positioning: "));
}

The Pozyx serial connection is initialized, together with the ReadyToLocalize object, and its setup is called.

def setup(self):
    """Sets up the Pozyx for positioning by calibrating its anchor list."""
    print("------------POZYX POSITIONING V1.0 -------------")
    print("NOTES: ")
    print("- No parameters required.")
    print()
    print("- System will auto start calibration")
    print()
    print("- System will auto start positioning")
    print("------------POZYX POSITIONING V1.0 --------------")
    print()
    print("START Ranging: ")
    self.pozyx.clearDevices(self.remote_id)
    self.setAnchorsManual()
    self.printPublishConfigurationResult()

The setup function is straightforward. After its initialization of the Pozyx, it performs the anchor configuration. It first clears the Pozyx’s device list using clearDevices(), and then manually adds the anchors in setAnchorsManual()we set up and measured out. Let’s look at how this is done:

void setAnchorsManual(){
  for(int i = 0; i < num_anchors; i++){
    device_coordinates_t anchor;
    anchor.network_id = anchors[i];
    anchor.flag = 0x1;
    anchor.pos.x = anchors_x[i];
    anchor.pos.y = anchors_y[i];
    anchor.pos.z = heights[i];
    Pozyx.addDevice(anchor, remote_id);
 }
 if (num_anchors > 4){
  Pozyx.setSelectionOfAnchors(POZYX_ANCHOR_SEL_AUTO, num_anchors);
 }
}

We define each anchor as a device_coordinates_t object, which takes an ID, a flag to indicate what kind of device it is – POZYX_ANCHOR or POZYX_TAG - and the device’s coordinates. We then add this anchor to the device’s stored device list, which it will use when positioning. If you’d use more than four anchors, the device’s anchor selection is set to automatically use all available anchors from this set.

def setAnchorsManual(self):
    """Adds the manually measured anchors to the Pozyx's device list one for one."""
    status = self.pozyx.clearDevices(self.remote_id)
    for anchor in self.anchors:
        status &= self.pozyx.addDevice(anchor, self.remote_id)
    if len(anchors) > 4:
        status &= self.pozyx.setSelectionOfAnchors(POZYX_ANCHOR_SEL_AUTO, len(anchors))
    return status

As each anchor is already defined as a DeviceCoordinates object, we can just iterate over the list of anchors and add each anchor to the device’s stored device list, which it will use when positioning. If you’d use more than four anchors, the device’s anchor selection is set to automatically use all available anchors from this set.

Loop

Now that we’ve properly configured the device’s device list with our set of anchors, let’s look at just how easy the actual positioning is in the short loop() function:

void loop(){
  coordinates_t position;
  int status;
  if(remote){
    status = Pozyx.doRemotePositioning(remote_id, &position, dimension, height, algorithm);
  }else{
    status = Pozyx.doPositioning(&position, dimension, height, algorithm);
  }

  if (status == POZYX_SUCCESS){
    // prints out the result
    printCoordinates(position);
  }else{
    // prints out the error code
    printErrorCode("positioning");
  }
}
def loop(self):
    """Performs positioning and displays/exports the results."""
    position = Coordinates()
    status = self.pozyx.doPositioning(
        position, self.dimension, self.height, self.algorithm, remote_id=self.remote_id)
    if status == POZYX_SUCCESS:
        self.printPublishPosition(position)
    else:
        self.printPublishErrorCode("positioning")

We first create a new object that will contain the Pozyx’s measured coordinates, after which we call the Pozyx’s doPositioning function, which will perform the positioning algorithm and store the coordinates, measured respectively to the anchors, in the position object. That’s essentially the entire positioning loop! If doPositioning returns POZYX_SUCCESS, the position will be printed in a human readable way. We see that we can pass the positioning’s algorithm, dimension, and Pozyx height (used in 2.5D) as parameters in the positioning. There are three dimensions supported by Pozyx: POZYX_2D, POZYX_2_5D, and POZYX_3D.

In 2D the anchors and tags must all be located in the same horizontal plane. This is not the case for semi-3D or 3D positioning. In semi-3D positioning the height of the tag must be supplied in the height parameter (for example, when the tag is mounted on a driving robot with fixed height). The reason why semi-3D exists is because in many cases it is not possible to obtain an accurate estimate for the z-coordinate in 3D-positioning. This is a result of how the anchors are placed and is explained in the guide 'Where to place the anchors?'. As a final parameter you can supply which algorithm to use. Possible values are POZYX_POS_ALG_UWB_ONLY and POZYX_POS_ALG_TRACKING. By default POZYX_POS_ALG_UWB_ONLY is used. For more information about the algorithms we refer to POZYX_POS_ALG. We've defined the algorithm as a parameter, so you can change the algorithm in the paramaters section instead of directly doing so in the function.

Some example usages:

  • status = Pozyx.doPositioning(&position, POZYX_2_5D, 1000 ) semi-3D positioning with the height of the tag fixed at 1000mm (about 3.3feet).
  • status = Pozyx.doPositioning(&position, POZYX_3D); 3D positioning, this requires at least 4 anchors. Note that the anchors must be placed at different heights to obtain a good accuracy of the z-coordinate.

Remote positioning

Positioning the Pozyx attached to your PC means that, except for when you have a long cable, you'll also need to be able to move your PC around if you want to position over a larger area than your desk. This would also mean that if you'd want to track objects, you'd need to attach a processing unit to these objects as well, instead of only the Pozyx. Luckily, the Pozyx attached to your computer can act as a master device, commanding one or more remote 'slaves'.

To position a remote device, you need to do the same as on a local device: put anchors in its device list that it will use for the positioning. A common misconception about the use of Pozyx is that configuring the anchors on your local device will make remote devices capable of positioning straight off the bat, but this isn't the case. You will notice that the addDevice function in setAnchorsManual adds the device to a remote device if remote positioning is enabled, thusly configuring the anchors on the remote device and not on the local one.

Visualization

Printing out coordinates to a terminal isn't all that sexy, and representing a position through text instead of visually just doesn't give the same insight. Here's where Processing comes in. Using Processing, we set up a sketch that reads out the coordinates from the serial port and draws them on the screen like this (0x0000 denoting a local device).

Please download the Processing sketches on github and edit the serialPort so that it matches with your Arduino's. Make sure serial is set to true. In your Arduino sketch, set use_processing = true instead of the default false and reupload. Press play in Processing and you should see all the anchors and the tag. Note that the Processing sketch reads out all the data from the serial port which is required to be in a fixed format. More specifically, the Processing sketch expects the anchor data prefixed with the string "ANCHOR" as follows:

"ANCHOR,anchor_id,anchor_x,anchor_y,anchor_z"

and with the Pozyx's position data, prefixed with "POS" as:

"POS,network_id,pos_x,pos_y,pos_z"

Please download the Processing sketches on github if you hadn't already, and edit the oscPort so that it matches with your script's. The default value is 8888, so if you didn't change the port, Make sure serial is set to true. In your Python script, set use_processing = True instead of False. Make sure that your processing program is running before you start the positioning script, as the anchor coordinates are sent at the start of the script, and otherwise you'll only see your device moving without the anchors as references. Press play and you should see an empty field. Once you run your Python script, all the anchors and the tag should be showing.

As you can see in the code, the Python script and the Processing sketch communicate through a UDP socket, through which Open Sound Control-formatted data is sent. The use of OSC as a protocol for the data instead of using raw UDP data, which would be perfectly possible as well, is portability and readability. If you want to communicate with other software than Processing, there's bound to be an OSC library or plugin for that software as well. For more information, a separate tutorial will be available soon.

That was it for the localization! If you're interested in some extra insights about Pozyx or its usage, definitely check out the extras below. If you have more devices that you want to track, a logical next step is the multitag tutorial. Otherwise, finishing the next tutorial focussing on the Pozyx's IMU is highly recommended.

Extras

While not necessary for positioning, the added functionality in the code can go a long way for extending the use of Pozyx or when things go wrong, so going over these extras is recommended if you want to take things further without needing to figure things out yourself.

Printing the configuration result

To find out whether the calibration was in fact successful, we will retrieve the Pozyx’s device list. This is the reverse of the configuration step, as we now retrieve the IDs and coordinates in turn from the Pozyx. Pozyx requires this to be done in several steps:

  • Firstly we retrieve the size of the device list through getDeviceListSize.
  • We use this size to create an appropriately sized device list container
  • We retrieve the IDs in the device’s device list using this container with getDeviceIds. You can also use getTagIds and getAnchorIds to have more control over which device IDs you’re getting.
  • Now we can get the device coordinates belonging to each of these IDs using getDeviceCoordinates

This is what is done in the code below, and we print the anchor’s ID with its retrieved coordinates. If these don’t match the anchors you passed to the device, something went wrong and it is recommended to try again. If this keeps failing, try going through the troubleshooting.

void printCalibrationResult(){
  uint8_t list_size;
  int status;

  status = Pozyx.getDeviceListSize(&list_size, remote_id);
  Serial.print("list size: ");
  Serial.println(status*list_size);

  if(list_size == 0){
    printErrorCode("configuration");
    return;
  }

  uint16_t device_ids[list_size];
  status &= Pozyx.getDeviceIds(device_ids, list_size, remote_id);

  Serial.println(F("Calibration result:"));
  Serial.print(F("Anchors found: "));
  Serial.println(list_size);

  coordinates_t anchor_coor;
  for(int i = 0; i < list_size; i++)
  {
    Serial.print("ANCHOR,");
    Serial.print("0x");
    Serial.print(device_ids[i], HEX);
    Serial.print(",");
    Pozyx.getDeviceCoordinates(device_ids[i], &anchor_coor, remote_id);
    Serial.print(anchor_coor.x);
    Serial.print(",");
    Serial.print(anchor_coor.y);
    Serial.print(",");
    Serial.println(anchor_coor.z);
  }
}
def printPublishConfigurationResult(self):
    """Prints and potentially publishes the anchor configuration result in a human-readable way."""
    list_size = SingleRegister()

    status = self.pozyx.getDeviceListSize(list_size, self.remote_id)
    print("List size: {0}".format(list_size[0]))
    if list_size[0] != len(self.anchors):
        self.printPublishErrorCode("configuration")
        return
    device_list = DeviceList(list_size=list_size[0])
    status = self.pozyx.getDeviceIds(device_list, self.remote_id)
    print("Calibration result:")
    print("Anchors found: {0}".format(list_size[0]))
    print("Anchor IDs: ", device_list)

    for i in range(list_size[0]):
        anchor_coordinates = Coordinates()
        status = self.pozyx.getDeviceCoordinates(
            device_list[i], anchor_coordinates, self.remote_id)
        print("ANCHOR,0x%0.4x, %s" % (device_list[i], str(anchor_coordinates)))
        if self.osc_udp_client is not None:
            self.osc_udp_client.send_message(
                "/anchor", [device_list[i], int(anchor_coordinates.x), int(anchor_coordinates.y), int(anchor_coordinates.z)])
            sleep(0.025)

Printing the error code

Pozyx’s operation can misbehave due to various reasons. Other devices not being in range, being on different settings, or another firmware version… As you’re getting started with Pozyx, it’s hard to keep track of where exactly things go wrong, and it’s for this reason that Pozyx keeps track of what went wrong in the error status register, POZYX_ERRORCODE. The error code can be read in two ways, one uses the getErrorCode function to directly read out the value from the error register, while the other, through getSystemError, returns a more verbose error message, representing the relative error textually. For example, as the error code would be 0x05, getSystemError would return "Error 0x05: Error reading from a register from the I2C bus". While the latter is ideal when working in a printed environment, if you work with visualizations it’s less than ideal to handle this entire string and you can perform custom functionality with the error code.

void printErrorCode(String operation){
  uint8_t error_code;
  if (remote_id == NULL){
    Pozyx.getErrorCode(&error_code);
    Serial.print("ERROR ");
    Serial.print(operation);
    Serial.print(", local error code: 0x");
    Serial.println(error_code, HEX);
    return;
  }
  int status = Pozyx.getErrorCode(&error_code, remote_id);
  if(status == POZYX_SUCCESS){
    Serial.print("ERROR ");
    Serial.print(operation);
    Serial.print(" on ID 0x");
    Serial.print(remote_id, HEX);
    Serial.print(", error code: 0x");
    Serial.println(error_code, HEX);
  }else{
    Pozyx.getErrorCode(&error_code);
    Serial.print("ERROR ");
    Serial.print(operation);
    Serial.print(", couldn't retrieve remote error code, local error: 0x");
    Serial.println(error_code, HEX);
  }
}
def printPublishErrorCode(self, operation):
    """Prints the Pozyx's error and possibly sends it as a OSC packet"""
    error_code = SingleRegister()
    network_id = self.remote_id
    if network_id is None:
        self.pozyx.getErrorCode(error_code)
        print("ERROR %s, local error code %s" % (operation, str(error_code)))
        if self.osc_udp_client is not None:
            self.osc_udp_client.send_message("/error", [operation, 0, error_code[0]])
        return
    status = self.pozyx.getErrorCode(error_code, self.remote_id)
    if status == POZYX_SUCCESS:
        print("ERROR %s on ID %s, error code %s" %
              (operation, "0x%0.4x" % network_id, str(error_code)))
        if self.osc_udp_client is not None:
            self.osc_udp_client.send_message(
                "/error", [operation, network_id, error_code[0]])
    else:
        self.pozyx.getErrorCode(error_code)
        print("ERROR %s, couldn't retrieve remote error code, local error code %s" %
              (operation, str(error_code)))
        if self.osc_udp_client is not None:
            self.osc_udp_client.send_message("/error", [operation, 0, -1])

In this example, simple but comprehensive error checking is executed. If the positioning is purely local, the local error is read. When remote positioning fails, indicated by the positioning function returning POZYX_FAILURE, the error register is read. If the error couldn’t be read remotely, the error is read locally. We are using getErrorCode instead of getSystemError because this allows us to efficiently send the error data to the visualization, and customize our error output. You can find the resulting error codes’ meaning at the register documentation of POZYX_ERRORCODE.

Adding sensor information

Sensor information, such as orientation or acceleration, can easily be added to the code, as to return this sensor data every positioning loop. Adding orientation and/or acceleration allows you to get a better insight in the object you're tracking, but you'll have to account for the reduced positioning update rate caused by this additional operation. Especially remotely, this will delay your update rate. In this example code, not present in the actual script, we'll retrieve both orientation and acceleration.

void printOrientationAcceleration(){
  orientation = euler_angles_t;
  acceleration = acceleration_t;
  Pozyx.getEulerAngles_deg(&orientation, remote_id);
  Pozyx.getAcceleration_mg(&acceleration, remote_id);
  Serial.print("Orientation: Heading:");
  Serial.print(orientation.heading);
  Serial.print(", Roll:");
  Serial.print(orientation.roll);
  Serial.print(", Pitch:");
  Serial.print(orientation.pitch);
  Serial.print(", acceleration: X:");
  Serial.print(acceleration.x);
  Serial.print(", Y:");
  Serial.print(acceleration.y);
  Serial.print(", Z:");
  Serial.print(acceleration.z);
}
def printOrientationAcceleration(self):
    orientation = EulerAngles()
    acceleration = Acceleration()
    self.pozyx.getEulerAngles_deg(orientation, self.remote_id)
    self.pozyx.getAcceleration_mg(acceleration, self.remote_id)
    print("Orientation: %s, acceleration: %s" % (str(orientation), str(acceleration))

Home > Documentation > Tutorials > Tutorial 2: Ready to localize