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 (ros_ips/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=4): 30 Minimum number of beacons to be used for UWB ranging. Should be 3 (two possible points) or 4 31 - ~max_z (double, default=None): 32 Maximum z-coordinate the receiver should have after ranging. Used as bounds for trilateration. 39 from std_msgs.msg
import String
40 from geometry_msgs.msg
import PointStamped
41 from ros_ips.msg
import StringStamped
46 """Configure ROS node for metraTec IPS+ indoor positioning system with UWB ranging functionality.""" 53 Initialize instance variables with values from ROS parameter server (or default values) and zone/beacon 54 configuration from YAML file. 57 config_dir = rospy.get_param(
'~config_file')
if rospy.has_param(
'~config_file')
else 'config/zones.yml' 58 abs_dir = os.path.join(rospkg.RosPack().get_path(
'ros_ips'), config_dir)
60 min_beacons = rospy.get_param(
'~min_beacons')
if rospy.has_param(
'~min_beacons')
else 4
62 max_z = rospy.get_param(
'~max_z')
if rospy.has_param(
'~max_z')
else None 66 n_beacons = self.positioning.n_beacons
69 self.
position_pub = rospy.Publisher(
'ips/receiver/position', PointStamped, queue_size=1)
76 self.
bcn_buffer_length = rospy.get_param(
'~bcn_len')
if rospy.has_param(
'~bcn_len')
else 2*n_beacons
83 self.
srg_buffer_length = rospy.get_param(
'~srg_len')
if rospy.has_param(
'srg_len')
else n_beacons
92 self.
tf = tf.TransformListener()
94 self.
frame_id = rospy.get_param(
'~frame_id')
if rospy.has_param(
'~frame_id')
else 'map' 97 self.
rate = rospy.Rate(rospy.get_param(
'~rate'))
if rospy.has_param(
'~rate')
else rospy.Rate(0.1)
101 Append incoming messages to list of previous message. Differentiate between BCN messages (regular beacon pings) 102 and SRG messages (responses of UWB ranging responses) 103 :param msg: String, message of subscribed topic 106 if msg.data.split(
' ')[0] ==
'BCN':
110 elif msg.data.split(
' ')[0] ==
'SRG':
118 buff.append(msg.data)
120 if len(buff) > buff_len:
124 """Publish the estimated position of the receiver""" 125 while not rospy.is_shutdown():
129 if position
is not None and len(position) == 3:
130 point = PointStamped()
131 point.header.stamp = rospy.Time.now()
132 point.header.frame_id = self.
frame_id 133 point.point.x, point.point.y, point.point.z = position[0], position[1], position[2]
134 self.position_pub.publish(point)
139 Estimate the position of the receiver using UWB ranging responses. 140 :return: [Float, Float, Float]: estimated position of the UWB receiver [x, y, z] 142 while not rospy.is_shutdown():
144 beacons = self.positioning.in_range(self.
bcn_buffer)
145 print(
'Using the following beacons for ranging: {}'.format(beacons))
149 request =
'SRG ' + b +
'\r' 151 self.receiver_send_pub.publish(request)
156 print(
'Abort ranging with {} due to timeout.'.format(b))
165 ranges = self.positioning.parse_srg(self.
srg_buffer)
167 transformed_ranges = []
168 for i, r
in enumerate(ranges):
174 (trans, rot) = self.tf.lookupTransform(
'/' + self.
frame_id,
'/' + r[0].frame_id, rospy.Time(0))
176 tp = r[0].position[:]
178 tp = tf.transformations.quaternion_multiply(
179 tf.transformations.quaternion_multiply(rot, tp),
180 tf.transformations.quaternion_conjugate(rot)
182 tp = [tp[0]+trans[0], tp[1]+trans[1], tp[2]+trans[2]]
184 transformed_ranges.append((tp, r[1]))
185 except (tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException):
186 print(
'Cannot transform {} into {}. Check your tf tree!'.format(r[0].frame_id, self.
frame_id))
189 transformed_ranges.append((r[0].position, r[1]))
191 position = self.positioning.trilaterate(transformed_ranges)
198 if __name__ ==
'__main__':
200 rospy.init_node(
'positioning_plus', anonymous=
False)
206 except rospy.ROSInterruptException:
def estimate_position(self)