3 Use this class to perform indoor positioning with the metraTec IPS+ system. The position of a receiver is evaluated by 4 UWB ranging of RF beacons placed in the environment. Requires a YAML file with the configuration of beacon positions. 7 Initialize the class by passing the directory of the config file. Then collect a number of beacon responses to ranging 8 requests (SRG) and use the parse_srg() function to get a list of beacon objects and their respective ranges from a list 10 Use this list as input for the trilaterate() function to estimate the position of the receiver in 3D space. 12 A few examples can be found in the main function below. 16 from scipy.optimize
import minimize
17 from positioning
import Positioning
21 """Class to extend the functionality of the Positioning class with UWB ranging capabilities.""" 24 minimization_params = dict(method=
'TNC',
25 options={
'disp':
True})
27 def __init__(self, config_dir, min_beacons=4, max_z=None):
29 Initialize the class with zone and beacon definitions from YAML configuration file. 30 :param config_dir: String: Directory of the YAML config file 31 :param min_beacons: Int: Minimum number of beacons to use for trilateration 32 :param max_z: Float: Maximum z position the receiver can have after ranging 35 Positioning.__init__(self, config_dir)
41 self.
eids = self.get_defined_beacons()
48 Get the top n beacons in terms of RSSI values from a list of beacon pings. 49 :param pings: [String]: list of beacon pings 50 :param n: Int: Number of top beacons to return 51 :return: [Beacon]: list of beacon objects ordered according to RSSI values 53 means = self.get_mean(pings)
57 for key, value
in sorted(means.items(), key=
lambda (k, v): v, reverse=
True):
61 if value < z.threshold:
76 Parse a list of SRG ranging responses of UWB beacons. For each message (if it is valid) return the positions, 77 frame id and estimated ranges. 78 :param responses: [String]: list of SRG responses from UWB beacons 79 :return: [(Beacon, Float)]: list beacon-object and range pairs 88 print(
'Encountered invalid SRG response: {}'.format(r))
91 beacon = self.get_beacon(split[1])
93 range = float(split[2]) / 1000.
95 ranges.append((beacon, range))
100 Get a list of beacons that are in range and are configured in the configuration YAML file from a list of 101 beacon pings recorded over a short duration. The returned beacons (EIDs can be used for UWB ranging) 102 :param pings: [String]: List of BCN pings 103 :return: [String]: List of EIDs that can be used for ranging 107 mean = self.get_mean(pings)
116 Estimate the position of the receiver by using trilateration with at least three UWB responses 117 :param ranges: [([Float, Float, Float], Float)]: list of position and range pairs (obtained from parse_srg) or 118 [(Beacon, Float)]: list of beacon object and range pairs (output from parse_srg()) 119 :return: [Float, Float, Float]: 3D coordinates of estimated receiver location 123 print(
'Not enough beacons for position estimation ({}/{})'.format(len(ranges), self.
min_beacons))
125 print(
'Using {} beacons for trilateration.'.format(len(ranges)))
127 points, distances, dsum = [], [], 0
130 p = r[0].position
if r[0].__class__.__name__ ==
'Beacon' else r[0]
132 distances.append(r[1])
136 for p, d
in zip(points, distances):
137 weight = 1. - d / dsum
138 initial = [initial[0] + p[0] * weight, initial[1] + p[1] * weight, initial[2] + p[2] * weight]
139 initial = [initial[0] / len(ranges), initial[1] / len(ranges), initial[2] / len(ranges)]
140 if self.
max_z is not None and initial[2] > self.
max_z:
141 initial[2] = self.
max_z 143 print(
'guess: ', initial)
145 bnds = ((
None,
None), (
None,
None), (
None, self.
max_z))
if self.
max_z is not None else None 147 print(
'final: ', res.x)
151 def mse(x, points, distances):
153 Cost-function to use for position estimation. Minimize the squared error of the distance between a variable 154 point x and each beacon and the measured UWB distance. mse = SUM(distance - dist(x, points)^2 155 :param x: [Float, Float, Float]: vector with variable components to be optimized 156 :param points: [[x, y, z], ...]: list of positions of the beacons used for UWB ranging 157 :param distances: [Float]: estimated distances from UWB ranging of above beacons 158 :return: Float: mean squared error for all beacon positions 161 for p, d
in zip(points, distances):
162 d_calc = math.sqrt((x[0]-p[0])**2 + (x[1]-p[1])**2 + (x[2]-p[2])**2)
167 if __name__ ==
'__main__':
168 """Testing the class and its methods.""" 172 dummy_pings = [
'BCN 00124B00090593E6 -060',
'BCN 00124B00090593E6 -070',
'BCN 00124B00090593E6 -070',
173 'BCN 00124B00050CD41E -090',
'BCN 00124B00050CD41E -070',
'BCN 00124B00050CD41E -050',
174 'BCN 00124B00050CDC0A -070',
'BCN 00124B00050CDC0A -060',
'BCN 00124B00050CDC0A -090']
177 beacon = pos.get_beacon(
'00124B00090593E6')
179 zone = pos.get_zone(dummy_pings)
182 resp = [
'SRG 00124B00050CDA71 01650 -076 -081 047\r',
'SRG 00124B00090593E6 04300 -076 -080 105\r',
183 'SRG 00124B00050CD41E 00800 -079 -082 049\r']
185 in_range = pos.in_range(dummy_pings)
187 ranges = pos.parse_srg(in_range)
189 tril = pos.trilaterate(ranges)
def in_range(self, pings)
def get_range(self, responses)
def trilaterate(self, ranges)
def get_top_beacons(self, pings, n)
def parse_srg(self, responses)
def __init__(self, config_dir, min_beacons=4, max_z=None)
def mse(x, points, distances)