00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 PKG = 'test_pr2_2dnav_gazebo'
00038 NAME = 'test_set_goal'
00039
00040 import math
00041 import roslib
00042 roslib.load_manifest(PKG)
00043 roslib.load_manifest('rostest')
00044
00045
00046 import sys, unittest
00047 import os, os.path, threading, time
00048 import rospy, rostest
00049 import actionlib
00050 from std_msgs.msg import String
00051 from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal
00052 from geometry_msgs.msg import Pose,Quaternion,Point, PoseStamped, PoseWithCovariance, PoseWithCovarianceStamped
00053 from nav_msgs.msg import Odometry
00054 from actionlib_msgs.msg import GoalStatusArray
00055 from gazebo_msgs.msg import ContactState,ContactsState
00056 import tf.transformations as tft
00057 from numpy import float64
00058
00059 FLOAT_TOL = 0.0001
00060 AMCL_TOL = 0.5
00061 COV = [float64(0.5*0.5),float64(0) ,float64(0),float64(0),float64(0),float64(0), \
00062 float64(0) ,float64(0.5*0.5),float64(0),float64(0),float64(0),float64(0), \
00063 float64(0) ,float64(0) ,float64(0),float64(0),float64(0),float64(0), \
00064 float64(0) ,float64(0) ,float64(0),float64(0),float64(0),float64(0), \
00065 float64(0) ,float64(0) ,float64(0),float64(0),float64(0),float64(0), \
00066 float64(0) ,float64(0) ,float64(0),float64(0),float64(0),float64(math.pi/12.0*math.pi/12.0) ]
00067
00068 def normalize_angle_positive(angle):
00069 return math.fmod(math.fmod(angle, 2*math.pi) + 2*math.pi, 2*math.pi)
00070
00071 def normalize_angle(angle):
00072 anorm = normalize_angle_positive(angle)
00073 if anorm > math.pi:
00074 anorm -= 2*math.pi
00075 return anorm
00076
00077 def shortest_angular_distance(angle_from, angle_to):
00078 angle_diff = normalize_angle_positive(angle_to) - normalize_angle_positive(angle_from)
00079 if angle_diff > math.pi:
00080 angle_diff = -(2*math.pi - angle_diff)
00081 return normalize_angle(angle_diff)
00082
00083 class NavStackTest(unittest.TestCase):
00084 def __init__(self, *args):
00085 super(NavStackTest, self).__init__(*args)
00086 self.bumped = False
00087 self.success = False
00088
00089 self.initialize_amcl = False
00090 self.initialpose_published = False
00091 self.initialpose = [25.7, 25.7, 0]
00092 self.goal_sent = False
00093
00094 self.odom_x_initial = 0
00095 self.odom_y_initial = 0
00096 self.odom_q_initial = [0,0,0,1]
00097 self.odom_initialized = False;
00098
00099 self.odom_x = 0
00100 self.odom_y = 0
00101 self.odom_q = [0,0,0,1]
00102
00103 self.p3d_x_initial = 0
00104 self.p3d_y_initial = 0
00105 self.p3d_q_initial = [0,0,0,1]
00106 self.p3d_initialized = False;
00107
00108 self.p3d_x = 0
00109 self.p3d_y = 0
00110 self.p3d_q = [1,0,0,0]
00111
00112
00113 self.nav_t_tol = 0.1
00114 self.nav_xy_tol = 0.7
00115 self.odom_t_tol = 0.1
00116 self.odom_xy_tol = 0.1
00117 self.test_timeout = 50.0
00118
00119 self.target_x = 25.70
00120 self.target_y = 25.70
00121 self.target_t = 0.0
00122
00123 self.target_q = [0,0,0,1]
00124
00125 self.args = sys.argv
00126
00127
00128 def printBaseOdom(self, odom):
00129 print "odom received"
00130 print "odom pose " + "x: " + str(odom.pose.pose.position.x)
00131 print "odom pose " + "y: " + str(odom.pose.pose.position.y)
00132 print "odom pose " + "t: " + str(odom.pose.pose.position.z)
00133 print "odom twist " + "x: " + str(odom.twist.twist.linear.x)
00134 print "odom twist " + "y: " + str(odom.twist.twist.linear.y)
00135 print "odom twist " + "t: " + str(odom.twist.twist.angular.z)
00136
00137 def printBaseP3D(self, p3d):
00138 print "base pose ground truth received"
00139 print "P3D pose translan: " + "x: " + str(p3d.pose.pose.position.x)
00140 print " " + "y: " + str(p3d.pose.pose.position.y)
00141 print " " + "z: " + str(p3d.pose.pose.position.z)
00142 print "P3D pose rotation: " + "x: " + str(p3d.pose.pose.orientation.x)
00143 print " " + "y: " + str(p3d.pose.pose.orientation.y)
00144 print " " + "z: " + str(p3d.pose.pose.orientation.z)
00145 print " " + "w: " + str(p3d.pose.pose.orientation.w)
00146 print "P3D rate translan: " + "x: " + str(p3d.twist.twist.linear.x)
00147 print " " + "y: " + str(p3d.twist.twist.linear.y)
00148 print " " + "z: " + str(p3d.twist.twist.linear.z)
00149 print "P3D rate rotation: " + "x: " + str(p3d.twist.twist.angular.x)
00150 print " " + "y: " + str(p3d.twist.twist.angular.y)
00151 print " " + "z: " + str(p3d.twist.twist.angular.z)
00152
00153
00154 def quaternionMsgToList(self, q):
00155 return [q.x, q.y, q.z, q.w]
00156
00157 def odomInput(self, odom):
00158
00159
00160 if self.odom_initialized == False or self.p3d_initialized == False:
00161 self.odom_initialized = True
00162 self.odom_x_initial = odom.pose.pose.position.x
00163 self.odom_y_initial = odom.pose.pose.position.y
00164 self.odom_q_initial = self.quaternionMsgToList(odom.pose.pose.orientation)
00165 else:
00166
00167 self.odom_x = odom.pose.pose.position.x
00168 self.odom_y = odom.pose.pose.position.y
00169 self.odom_q = self.quaternionMsgToList(odom.pose.pose.orientation)
00170
00171 def p3dInput(self, p3d):
00172
00173
00174
00175 if self.odom_initialized == False or self.p3d_initialized == False:
00176 self.p3d_initialized = True
00177 self.p3d_x_initial = p3d.pose.pose.position.x
00178 self.p3d_y_initial = p3d.pose.pose.position.y
00179 self.p3d_q_initial = self.quaternionMsgToList(p3d.pose.pose.orientation)
00180 else:
00181
00182 self.p3d_x = p3d.pose.pose.position.x
00183 self.p3d_y = p3d.pose.pose.position.y
00184 self.p3d_q = self.quaternionMsgToList(p3d.pose.pose.orientation)
00185
00186 def bumpedInput(self, contactsState):
00187 if len(contactsState.states) > 0:
00188 for i in range(0,len(contactsState.states)):
00189 print "robot touched something! ", contactsState.states[i].info
00190 self.bumped = True
00191
00192 def amclInput(self, amcl_pose):
00193 if self.initialize_amcl:
00194 print "/amcl_pose received, ",amcl_pose
00195 amcl_eul = tft.euler_from_quaternion([amcl_pose.pose.pose.orientation.x,\
00196 amcl_pose.pose.pose.orientation.y,\
00197 amcl_pose.pose.pose.orientation.z,\
00198 amcl_pose.pose.pose.orientation.w])
00199 if abs(amcl_pose.pose.pose.position.x - self.initialpose[0]) < AMCL_TOL and \
00200 abs(amcl_pose.pose.pose.position.y - self.initialpose[1]) < AMCL_TOL and \
00201 abs(amcl_eul[2] - self.initialpose[2]) < AMCL_TOL:
00202 print "initial_pose matches, stop setPose begin publishing goal."
00203 self.initialize_amcl = False
00204 else:
00205 print "initial_pose mismatch, continue setPose."
00206
00207 def cmd_velInput(self, cmd_vel):
00208 print "cmd_vel: ", cmd_vel.vel.vx, ",", cmd_vel.vel.vy, ",", cmd_vel.vel.vz \
00209 , cmd_vel.ang_vel.vx, ",", cmd_vel.ang_vel.vy, ",", cmd_vel.ang_vel.vz
00210
00211 def publish_initialpose(self, tmph):
00212 print "pub init pose"
00213 p = Point(self.initialpose[0], self.initialpose[1], 0)
00214 tmpq = tft.quaternion_from_euler(0,0,self.initialpose[2],'rxyz')
00215 q = Quaternion(tmpq[0],tmpq[1],tmpq[2],tmpq[3])
00216 pose = Pose(p,q)
00217
00218 pub_pose = rospy.Publisher("/initialpose" , PoseWithCovarianceStamped, None, False, True)
00219 pub_pose.publish(PoseWithCovarianceStamped(tmph, PoseWithCovariance(pose,COV)))
00220 self.initialpose_published = True
00221
00222 def test_set_goal(self):
00223 print "LNK\n"
00224
00225
00226 rospy.Subscriber("/base_pose_ground_truth" , Odometry , self.p3dInput)
00227 rospy.Subscriber("/base_odometry/odom" , Odometry , self.odomInput)
00228 rospy.Subscriber("/base_bumper/state" , ContactsState , self.bumpedInput)
00229 rospy.Subscriber("/torso_lift_bumper/state", ContactsState , self.bumpedInput)
00230 rospy.Subscriber("/amcl_pose" , PoseWithCovarianceStamped , self.amclInput)
00231
00232
00233
00234
00235 rospy.init_node(NAME, anonymous=True)
00236 print "debug1:",rospy.is_shutdown()," time(",time.time(),")"
00237 client = actionlib.SimpleActionClient('/move_base', MoveBaseAction)
00238 print "debug2:",rospy.is_shutdown()," time(",time.time(),")"
00239 client.wait_for_server()
00240 print "debug3:",rospy.is_shutdown()," time(",time.time(),")"
00241
00242
00243 print "------------------------"
00244 for i in range(0,len(self.args)):
00245 print " sys argv:", self.args[i]
00246 if self.args[i] == '-x':
00247 if len(self.args) > i+1:
00248 self.target_x = float(self.args[i+1])
00249 print "target x set to:",self.target_x
00250 if self.args[i] == '-y':
00251 if len(self.args) > i+1:
00252 self.target_y = float(self.args[i+1])
00253 print "target y set to:",self.target_y
00254 if self.args[i] == '-t':
00255 if len(self.args) > i+1:
00256 self.target_t = float(self.args[i+1])
00257 self.target_q = tft.quaternion_from_euler(0,0,self.target_t,'rxyz')
00258 print "target t set to:",self.target_t
00259 if self.args[i] == '-nav_t_tol':
00260 if len(self.args) > i+1:
00261 self.nav_t_tol = float(self.args[i+1])
00262 if self.nav_t_tol == 0:
00263 print "nav_t_tol check disabled"
00264 else:
00265 print "nav_t_tol set to:",self.nav_t_tol
00266 if self.args[i] == '-nav_xy_tol':
00267 if len(self.args) > i+1:
00268 self.nav_xy_tol = float(self.args[i+1])
00269 if self.nav_xy_tol == 0:
00270 print "nav_xy_tol check disabled"
00271 else:
00272 print "nav_xy_tol set to:",self.nav_xy_tol
00273 if self.args[i] == '-odom_t_tol':
00274 if len(self.args) > i+1:
00275 self.odom_t_tol = float(self.args[i+1])
00276 if self.odom_t_tol == 0:
00277 print "odom_t_tol check disabled"
00278 else:
00279 print "odom_t_tol set to:",self.odom_t_tol
00280 if self.args[i] == '-odom_xy_tol':
00281 if len(self.args) > i+1:
00282 self.odom_xy_tol = float(self.args[i+1])
00283 if self.odom_xy_tol == 0:
00284 print "odom_xy_tol check disabled"
00285 else:
00286 print "odom_xy_tol set to:",self.odom_xy_tol
00287 if self.args[i] == '-timeout':
00288 if len(self.args) > i+1:
00289 self.test_timeout = float(self.args[i+1])
00290 print "test_timeout set to:",self.test_timeout
00291 if self.args[i] == '-amcl':
00292 if len(self.args) > i+3:
00293 self.initialize_amcl = True
00294 self.initialpose = [float(self.args[i+1]),float(self.args[i+2]),float(self.args[i+3])]
00295 print "using amcl, will try to initialize pose to: ",self.initialpose
00296 else:
00297 self.initialize_amcl = False
00298 print "using fake localization, need 3 arguments for amcl (x,y,th)"
00299
00300 print " target:", self.target_x, self.target_y, self.target_t
00301 print "------------------------"
00302
00303 timeout_t = time.time() + self.test_timeout
00304
00305 print "rospy debug",rospy.is_shutdown(),self.success,time.time(),timeout_t
00306
00307 while not rospy.is_shutdown() and not self.success and time.time() < timeout_t:
00308
00309
00310 tmph = rospy.Header();
00311 tmph.stamp = rospy.get_rostime();
00312 tmph.frame_id = "/map"
00313
00314 if not self.initialpose_published:
00315 self.publish_initialpose(tmph)
00316 else:
00317 if self.initialize_amcl:
00318 print "waiting for amcl to initialize (/amcl_pose received]"
00319 else:
00320 print "check move base"
00321 if not self.goal_sent:
00322 print "pub goal"
00323
00324 tmpp = Point(self.target_x, self.target_y, 0)
00325 tmpq0 = tft.quaternion_from_euler(0,0,self.target_t,'rxyz')
00326 tmpq = Quaternion(tmpq0[0],tmpq0[1],tmpq0[2],tmpq0[3])
00327 tmppose = Pose(tmpp,tmpq)
00328
00329 move_base_goal = MoveBaseGoal()
00330 move_base_goal.target_pose.header = tmph
00331 move_base_goal.target_pose.pose = tmppose
00332 client.send_goal(move_base_goal)
00333
00334
00335 self.goal_sent = True
00336
00337 time.sleep(1.0)
00338
00339
00340
00341 print "========================"
00342 print "time: remaining: ", timeout_t - time.time()
00343
00344 tmpoqi = tft.quaternion_inverse(self.odom_q_initial)
00345 odom_q_delta = tft.quaternion_multiply(tmpoqi,self.odom_q)
00346
00347
00348
00349
00350
00351
00352 tmppqi = tft.quaternion_inverse(self.p3d_q_initial)
00353 p3d_q_delta = tft.quaternion_multiply(tmppqi,self.p3d_q)
00354
00355
00356
00357
00358
00359
00360
00361 tmpdqi = tft.quaternion_inverse(p3d_q_delta)
00362 delta = tft.quaternion_multiply(tmpdqi,odom_q_delta)
00363 delta_euler = tft.euler_from_quaternion(delta)
00364 odom_drift_dyaw = delta_euler[2]
00365
00366
00367
00368
00369 tmptqi = tft.quaternion_inverse(self.target_q)
00370 navdq = tft.quaternion_multiply(tmptqi,self.p3d_q)
00371 navde = tft.euler_from_quaternion(navdq)
00372 nav_dyaw = navde[2]
00373
00374
00375
00376
00377
00378 odom_xy_err = max(abs(self.odom_x - self.p3d_x - self.odom_x_initial + self.p3d_x_initial ),abs(self.odom_y - self.p3d_y - self.odom_y_initial + self.p3d_y_initial ))
00379 odom_t_err = abs(odom_drift_dyaw)
00380
00381
00382 nav_xy_err = max(abs(self.p3d_x - self.target_x),abs(self.p3d_y - self.target_y))
00383 nav_t_err = abs(nav_dyaw)
00384
00385 print "odom drift error:"
00386 print " translation: ",self.odom_x - self.p3d_x - self.odom_x_initial + self.p3d_x_initial,",",self.odom_y - self.p3d_y - self.odom_y_initial + self.p3d_y_initial
00387 print " heading: " + str(odom_t_err) + " odom_t_tol: " + str(self.odom_t_tol)
00388
00389 print "ground truth - target error:"
00390 print " translation: ",self.p3d_x - self.target_x,",",self.p3d_y - self.target_y
00391 print " heading: " + str(nav_t_err) + " nav_t_tol:" + str(self.nav_t_tol)
00392
00393 print "summary:"
00394 self.success = True
00395
00396 if self.odom_initialized == False:
00397 print "waiting for first /base_odom/odom message to initialize test"
00398 if self.p3d_initialized == False:
00399 print "waiting for first /base_pose_ground_truth message to initialize test"
00400
00401 if self.bumped == True:
00402 self.success = False
00403 print " Hit the wall."
00404
00405 if self.nav_t_tol > 0 and nav_t_err > self.nav_t_tol:
00406 self.success = False
00407 print " Target heading out of tol."
00408 if self.nav_xy_tol > 0 and nav_xy_err > self.nav_xy_tol:
00409 self.success = False
00410 print " Target xy out of tol."
00411
00412 if self.odom_t_tol > 0 and odom_t_err > self.odom_t_tol:
00413 self.success = False
00414 print " Odom drift heading out of tol."
00415 if self.odom_xy_tol > 0 and odom_xy_err > self.odom_xy_tol:
00416 print " Odom drift xy out of tol."
00417 self.success = False
00418
00419 self.assert_(self.success)
00420
00421 def print_usage(exit_code = 0):
00422 print '''Commands:
00423 -x <x target position> - target x location
00424 -y <y target position> - target y location
00425 -t <target yaw> - target robot yaw
00426 -nav_xy_tol <tol> - target xy location error tolerance, set to 0 to disable, default = 0.7 m
00427 -nav_t_tol <tol> - target yaw error tolerance, set to 0 to disable, default = 0.1 rad
00428 -odom_xy_tol <tol> - odom and ground truth xy drift error tolerance, set to 0 to disable, default = 0.1 m
00429 -odom_t_tol <tol> - odom and ground truth yaw drift error tolerance, set to 0 to disable, default = 0.1 rad
00430 -timeout <seconds> - test timeout in seconds. default to 50 seconds
00431 -amcl <x initial position> <y initial position> <initial yaw> - initial pose for amcl. If unspecified, assume fake localization is used.
00432 '''
00433
00434 if __name__ == '__main__':
00435
00436 if len(sys.argv) == 1:
00437 print_usage()
00438 else:
00439 rostest.run(PKG, sys.argv[0], NavStackTest, sys.argv)
00440
00441
00442