3 Use this node to perform indoor positioning using the metraTec IPS+ tracking system. Prerequisites for using this node 4 is a running receiver-node that handles communication with the receiver and thus with the beacons in the vicinity. 5 Also, make sure that tf is broadcasting transformations between all the different coordinate frames used in the config 6 file and the coordinate frame of the receiver (which is specified via rosparam, see below). 9 - ips/receiver/raw (indoor_positioning/StringStamped): 10 Raw messages received by the UWB receiver 13 - ips/receiver/send (std_msgs/String): 14 Message to be sent to the receiver, e.g. 'SRG <EID> \r' ranging request 15 - ips/receiver/position (geometry_msgs/PointStamped): 16 Estimated position of the receiver after UWB ranging and trilateration 19 - ~config_file (string, default='PKG_DIR/config/zones.yml'): 20 Path to the configuration file of zones and beacons relative to the package directory 21 - ~frame_id (string, default='map'): 22 Coordinate frame the receiver position should be estimated in 23 - ~rate (double, default=0.1): 24 The publishing rate in messages per second 25 - ~bcn_len (int, default=2*number_of_beacons): 26 Buffer length for BCN messages 27 - ~srg_len (int, default=number_of_beacons): 28 Buffer length for SRG messages 29 - ~min_beacons (int, default=3): 30 Minimum number of beacons to be used for UWB ranging. Should be 3 (two possible points) or 4 31 - ~max_beacons (int, default=6): 32 Maximum number of beacons to be used for UWB ranging. Higher values lead to higher computation time. 33 - ~rssi_thresh (int, default=-127): 34 Minimum RSSI value a beacon should have to be used for ranging. Default uses all beacons. 35 - ~max_z (double, default=None): 36 Maximum z-coordinate the receiver should have after ranging. Used as bounds for trilateration. 37 - ~dilation (double, default=0.0): 38 Only position estimates within a dilated convex hull around the beacons used for ranging are published to 39 minimize errors and improve accuracy. This is the dilation distance in m. 46 from std_msgs.msg
import String
47 from geometry_msgs.msg
import PointStamped
48 from indoor_positioning.msg
import StringStamped
53 """Configure ROS node for metraTec IPS+ indoor positioning system with UWB ranging functionality.""" 60 Initialize instance variables with values from ROS parameter server (or default values) and zone/beacon 61 configuration from YAML file. 64 config_dir = rospy.get_param(
'~config_file')
if rospy.has_param(
'~config_file')
else 'config/zones.yml' 65 abs_dir = os.path.join(rospkg.RosPack().get_path(
'indoor_positioning'), config_dir)
67 min_beacons = rospy.get_param(
'~min_beacons')
if rospy.has_param(
'~min_beacons')
else 4
69 self.
max_beacons = rospy.get_param(
'~max_beacons')
if rospy.has_param(
'~max_beacons')
else 6
71 self.
rssi_thresh = rospy.get_param(
'~rssi_thresh')
if rospy.has_param(
'~rssi_thresh')
else -127
73 max_z = rospy.get_param(
'~max_z')
if rospy.has_param(
'~max_z')
else None 75 dilation = rospy.get_param(
'~dilation')
if rospy.has_param(
'~dilation')
else 0.0
79 n_beacons = self.positioning.n_beacons
82 self.
position_pub = rospy.Publisher(
'ips/receiver/position', PointStamped, queue_size=1)
89 self.
bcn_buffer_length = rospy.get_param(
'~bcn_len')
if rospy.has_param(
'~bcn_len')
else 2*n_beacons
97 self.
srg_buffer_length = rospy.get_param(
'~srg_len')
if rospy.has_param(
'srg_len')
else n_beacons
107 self.
tf = tf.TransformListener()
109 self.
frame_id = rospy.get_param(
'~frame_id')
if rospy.has_param(
'~frame_id')
else 'map' 112 self.
rate = rospy.Rate(rospy.get_param(
'~rate'))
if rospy.has_param(
'~rate')
else rospy.Rate(0.1)
116 Append incoming messages to list of previous message. Differentiate between BCN messages (regular beacon pings) 117 and SRG messages (responses of UWB ranging responses) 118 :param msg: String, message of subscribed topic 121 if msg.data.split(
' ')[0] ==
'BCN':
125 elif msg.data.split(
' ')[0] ==
'SRG':
133 buff.append(msg.data)
135 if len(buff) > buff_len:
139 """Publish the estimated position of the receiver""" 140 while not rospy.is_shutdown():
144 if position
is not None and len(position) == 3:
145 point = PointStamped()
146 point.header.stamp = rospy.Time.now()
147 point.header.frame_id = self.
frame_id 148 point.point.x, point.point.y, point.point.z = position[0], position[1], position[2]
149 self.position_pub.publish(point)
154 Estimate the position of the receiver using UWB ranging responses. 155 :return: [Float, Float, Float]: estimated position of the UWB receiver [x, y, z] 157 while not rospy.is_shutdown():
160 print(
'Beacons found: {}'.format(beacons))
164 request =
'SRG ' + b +
'\r' 166 self.receiver_send_pub.publish(request)
171 print(
'Abort ranging with {} due to timeout.'.format(b))
182 print(
'Using the following responses for ranging: {}'.format(self.
srg_buffer))
184 ranges = self.positioning.parse_srg(self.
srg_buffer)
186 transformed_ranges = []
187 for i, r
in enumerate(ranges):
193 (trans, rot) = self.tf.lookupTransform(
'/' + self.
frame_id,
'/' + r[0].frame_id, rospy.Time(0))
195 tp = r[0].position[:]
197 tp = tf.transformations.quaternion_multiply(
198 tf.transformations.quaternion_multiply(rot, tp),
199 tf.transformations.quaternion_conjugate(rot)
201 tp = [tp[0]+trans[0], tp[1]+trans[1], tp[2]+trans[2]]
203 transformed_ranges.append((tp, r[1]))
204 except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
205 print(
'Cannot transform {} into {}. Check your tf tree!'.format(r[0].frame_id, self.
frame_id))
208 transformed_ranges.append((r[0].position, r[1]))
210 ret, position = self.positioning.trilaterate(transformed_ranges)
214 print(
'Trilateration in Polygon: ' + str(ret))
215 return position
if ret
else None 218 if __name__ ==
'__main__':
220 rospy.init_node(
'positioning_plus', anonymous=
False)
226 except rospy.ROSInterruptException:
def estimate_position(self)