aruco.py
Go to the documentation of this file.
1 #!/usr/bin/python2
2 from __future__ import print_function
3 
4 import sys
5 import math
6 from copy import deepcopy
7 import rospy
8 import tf2_ros
9 import numpy as np
10 import cv2
11 
12 from vision_msgs.msg import Detection2D, Detection2DArray, ObjectHypothesisWithPose
13 from geometry_msgs.msg import TransformStamped, Vector3Stamped, Vector3, Quaternion
14 from fiducial_msgs.msg import FiducialTransformArray, FiducialTransform
15 from sensor_msgs.msg import CameraInfo
16 from gazebo_msgs.msg import ModelStates
17 from tf2_geometry_msgs import do_transform_vector3
18 from tf.transformations import quaternion_from_euler, euler_from_quaternion, quaternion_multiply, quaternion_conjugate
19 
20 
21 def transformVector(x, y, z, quat):
22  v = Vector3Stamped()
23  v.vector.x = x
24  v.vector.y = y
25  v.vector.z = z
26  t = TransformStamped()
27  t.transform.rotation = quat
28 
29  return do_transform_vector3(v, t).vector
30 
31 
33  invq = quaternion_conjugate([quat.x, quat.y, quat.z, quat.w])
34  return Quaternion(invq[0], invq[1], invq[2], invq[3])
35 
36 
37 def quaternionMultiply(quat1, quat2):
38  q1 = [quat1.x, quat1.y, quat1.z, quat1.w]
39  q2 = [quat2.x, quat2.y, quat2.z, quat2.w]
40  out = quaternion_multiply(q1, q2)
41  return Quaternion(out[0], out[1], out[2], out[3])
42 
43 
45  """Represents the Fiducial Markers properties."""
46 
47  def __init__(self, fid_id, pos, rot):
48  self.id = fid_id
49  self.translation = Vector3(pos.x, pos.y, pos.z)
50  self.rotation = Quaternion(rot.x, rot.y, rot.z, rot.w)
51 
52  def __repr__(self):
53  return "fiducial_%d (%f, %f, %f)" % (self.id, self.translation.x, self.translation.y, self.translation.z)
54 
55 
57  """Publishes detected Fiducial Markers."""
58 
59  def __init__(self):
60  rospy.init_node('aruco_gazebo', anonymous=False)
61  self.buffer = tf2_ros.Buffer(rospy.Time(30))
62  self.listener = tf2_ros.TransformListener(self.buffer)
63  self.broadcaster = tf2_ros.TransformBroadcaster()
64 
65  self.camera_frame = rospy.get_param("~camera_frame", "raspicam")
66  self.framerate = rospy.get_param("~framerate", 10)
67 
68  self.camera_info_sub = rospy.Subscriber(
69  "/camera_info", CameraInfo, self.camera_info)
70  if VIS_MSGS:
71  self.fid_pub = rospy.Publisher(
72  "/fiducial_transforms", Detection2DArray, queue_size=5)
73  else:
74  self.fid_pub = rospy.Publisher(
75  "/fiducial_transforms", FiducialTransformArray, queue_size=5)
76 
77  # a counter to emulate aruco_detect's image numbering
79 
80  self.candidates = []
81  self.magnipose = None
82  try:
83  self.static_cam_pose = self.buffer.lookup_transform(
84  "base_footprint", self.camera_frame, rospy.Time.now(), rospy.Duration(5.0))
85  except (tf2_ros.LookupException, tf2_ros.ConnectivityException, tf2_ros.ExtrapolationException):
86  rospy.logfatal("Could not get camera transform.")
87  rospy.signal_shutdown("Could not get camera transform.")
88  return
89 
90  def camera_info(self, msg):
91  self.cam_data = msg
92  self.camera_info_sub.unregister()
93 
94  fov_h = math.atan(msg.K[2] / msg.K[0]) * 2
95  fov_v = math.atan(msg.K[5] / msg.K[4]) * 2
96  self.conefov = math.sqrt(fov_h * fov_h + fov_v * fov_v)
97 
98  self.gazebo_sub = rospy.Subscriber(
99  "/gazebo/model_states", ModelStates, self.gazebo_poses)
100 
101  def gazebo_poses(self, msg):
102  del self.candidates[:] # Reset current models state
103  for i in range(len(msg.name)):
104  if "aruco" in msg.name[i]:
105  fid_id = int(msg.name[i].split("-")[1].split("_")[0])
106  self.candidates.append(
107  FiducialState(fid_id, msg.pose[i].position, msg.pose[i].orientation))
108  elif "magni" in msg.name[i]:
109  self.magnipose = msg.pose[i]
110 
111  def publish_markers(self, fid_data_array):
112  fidarray = FiducialTransformArray()
113  fidarray.header.stamp = rospy.Time.now()
114  vis = Detection2DArray()
115  vis.header.stamp = rospy.Time.now()
116 
117  for fid in fid_data_array:
118  if VIS_MSGS:
119  obj = Detection2D()
120  oh = ObjectHypothesisWithPose()
121  oh.id = fid.id
122  oh.pose.pose.position.x = fid.translation.x
123  oh.pose.pose.position.y = fid.translation.y
124  oh.pose.pose.position.z = fid.translation.z
125  oh.pose.pose.orientation.w = fid.rotation.w
126  oh.pose.pose.orientation.x = fid.rotation.x
127  oh.pose.pose.orientation.y = fid.rotation.y
128  oh.pose.pose.orientation.z = fid.rotation.z
129  oh.score = math.exp(-2 * OBJECT_ERROR)
130 
131  obj.results.append(oh)
132  vis.detections.append(obj)
133  else:
134  data = FiducialTransform()
135  data.fiducial_id = fid.id
136  data.transform.translation = fid.translation
137  data.transform.rotation = fid.rotation
138  data.image_error = IMAGE_ERROR
139  data.object_error = OBJECT_ERROR
140  data.fiducial_area = FIDUCIAL_AREA
141 
142  fidarray.transforms.append(data)
143 
144  if VIS_MSGS:
145  self.fid_pub.publish(vis)
146  else:
147  self.fid_pub.publish(fidarray)
148 
149  def transmit_TF(self, id, x, y, z, rotation):
150  t = TransformStamped()
151  t.child_frame_id = "fiducial_" + str(id)
152  t.header.frame_id = "raspicam"
153  t.header.stamp = rospy.Time.now()
154 
155  t.transform.translation.x = x
156  t.transform.translation.y = y
157  t.transform.translation.z = z
158  t.transform.rotation = rotation
159 
160  self.broadcaster.sendTransform(t)
161 
162  def update(self):
163  self.pointless_counter += 1
164  rospy.loginfo("Got data " + str(self.pointless_counter))
165  if self.candidates is None:
166  rospy.loginfo("Detected 0 markers")
167  return
168 
169  if self.magnipose is None:
170  rospy.logwarn("Robot pose not received!")
171  return
172 
173  # obtain camera and magni pose in odom space
174  stat_cpos = self.static_cam_pose.transform.translation
175  stat_crot = self.static_cam_pose.transform.rotation
176  mag_pos = self.magnipose.position
177  mag_rot = self.magnipose.orientation
178  cpos = transformVector(stat_cpos.x, stat_cpos.y, stat_cpos.z, mag_rot)
179  cpos.x += mag_pos.x
180  cpos.y += mag_pos.y
181  cpos.z += mag_pos.z
182  crot = quaternionMultiply(mag_rot, stat_crot)
183  crot_inv = quaternionInverse(crot)
184 
185  # we clone the array because of threading and the transforms we're about to do
186  fiducials = deepcopy(self.candidates)
187  finalists = []
188  # transform the marker translations into raspicam frame
189  for fid in fiducials:
190  x = fid.translation.x - cpos.x
191  y = fid.translation.y - cpos.y
192  z = fid.translation.z - cpos.z
193 
194  v = transformVector(x, y, z, crot_inv)
195  vlen = math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
196 
197  # max distance under 7m for rough elimination
198  if vlen > 7.0 or vlen < 0.0:
199  continue
200 
201  # dot product of z forward with normalized z direction should be in the max fov angle
202  if v.z / vlen < math.cos(self.conefov * 0.5):
203  continue
204 
205  # fiducial has 7 pixels at "len" and we need 9 for detection, so that's times 1.3, then halved for each delta
206  dist = FIDUCIAL_LEN * 0.65
207  # define 4 points as edges of fiducial
208  p = []
209  p.append(transformVector(x + dist, y + dist, z, crot_inv))
210  p.append(transformVector(x + dist, y - dist, z, crot_inv))
211  p.append(transformVector(x - dist, y + dist, z, crot_inv))
212  p.append(transformVector(x - dist, y - dist, z, crot_inv))
213 
214  src = np.array([[p[0].x, p[0].y, p[0].z], [p[1].x, p[1].y, p[1].z], [
215  p[2].x, p[2].y, p[2].z], [p[3].x, p[3].y, p[3].z]])
216  rvec = np.array([0, 0, 0], np.float)
217  tvec = np.array([0, 0, 0], np.float)
218  projMatrix = np.array([[self.cam_data.K[0], 0, self.cam_data.K[2]], [
219  0, self.cam_data.K[4], self.cam_data.K[5]], [0, 0, 1]])
220  image_points = cv2.projectPoints(
221  src, rvec, tvec, projMatrix, None)[0]
222 
223  # check if the points project into bounds of the image sensor
224  outside = False
225  for d in image_points:
226  dx = d[0][0]
227  dy = d[0][1]
228  if dx < 0 or dy < 0 or self.cam_data.height < dy or self.cam_data.width < dx:
229  outside = True
230  break
231 
232  if outside:
233  continue
234 
235  (roll, pitch, yaw) = euler_from_quaternion(
236  [fid.rotation.x, fid.rotation.y, fid.rotation.z, fid.rotation.w])
237  rfinal = quaternion_from_euler(roll, pitch, yaw - math.radians(90))
238  r = Quaternion(rfinal[0], rfinal[1], rfinal[2], rfinal[3])
239 
240  finalists.append(fid)
241  fid.rotation = quaternionMultiply(crot_inv, r)
242  self.transmit_TF(fid.id, v.x, v.y, v.z, fid.rotation)
243 
244  rospy.loginfo("Detected %d markers" % len(finalists))
245  self.publish_markers(finalists)
246 
247 
248 if __name__ == '__main__':
249  IMAGE_ERROR = rospy.get_param("~image_error", 0.001)
250  OBJECT_ERROR = rospy.get_param("~object_error", 0.001)
251  FIDUCIAL_AREA = rospy.get_param("~fiducial_area", 0.0196)
252  FIDUCIAL_LEN = rospy.get_param("~fiducial_len", 0.14)
253  VIS_MSGS = rospy.get_param("~vis_msgs", False)
254 
255  try:
257  rate = rospy.Rate(ar.framerate)
258  while not rospy.is_shutdown():
259  ar.update()
260  rate.sleep()
261 
262  except rospy.ROSInterruptException:
263  print("Script interrupted", file=sys.stderr)
def camera_info(self, msg)
Definition: aruco.py:90
def quaternionInverse(quat)
Definition: aruco.py:32
def update(self)
Definition: aruco.py:162
def publish_markers(self, fid_data_array)
Definition: aruco.py:111
def gazebo_poses(self, msg)
Definition: aruco.py:101
def quaternionMultiply(quat1, quat2)
Definition: aruco.py:37
def __init__(self, fid_id, pos, rot)
Definition: aruco.py:47
def __repr__(self)
Definition: aruco.py:52
def transformVector(x, y, z, quat)
Definition: aruco.py:21
def transmit_TF(self, id, x, y, z, rotation)
Definition: aruco.py:149
def __init__(self)
Definition: aruco.py:59


aruco_gazebo
Author(s):
autogenerated on Tue Jun 1 2021 03:03:25