gyro_perf.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #AUTHOR: Younghun Ju <yhju@yujinrobot.com>, <yhju83@gmail.com>
00003 
00004 import roslib; roslib.load_manifest('kobuki_testsuite')
00005 import rospy
00006 
00007 import time
00008 from math import pi
00009 from kobuki_msgs.msg import ButtonEvent
00010 from kobuki_msgs.msg import Sound
00011 from geometry_msgs.msg import Twist
00012 
00013 from sensor_msgs.msg import Imu
00014 from geometry_msgs.msg import Quaternion
00015 from geometry_msgs.msg import Pose2D
00016 
00017 from tf.transformations import euler_from_quaternion
00018 from math import degrees, radians
00019 from numpy import mod
00020 
00021 from kobuki_msgs.msg import ScanAngle
00022 
00023 def wrap_to_pi(x):
00024     a = mod(mod(x,2*pi)+2*pi, 2*pi)
00025     if (a > pi):
00026       a -= 2*pi
00027     return a
00028 
00029 class Tester(object):
00030   def __init__(self):
00031     rospy.init_node("gyro_perf")
00032     self.state='INIT'
00033     self.done = False
00034     self.reset_angle = True
00035     self.triggered = False
00036     self.scan_angle = None # Place holder until the first scan angle is received
00037 
00038     self.debug = rospy.get_param('~debug', False)
00039 
00040     self.command_vx = rospy.get_param('~command_vx', 0.1) # In [m/s], default is 0.1 m/s
00041     self.command_wz = radians(rospy.get_param('~command_wz', 5.0)) # In [deg/s], default is 5 deg/s
00042     # convert into [rad/s]
00043     if self.debug: print self.command_vx, self.command_wz
00044 
00045     self.init_imu = True
00046     self.init_angle = 0.0
00047     self.angle = 0.0
00048     self.angle_rate = 0.0
00049     self.diff_angle = 0.0
00050     self.accumulated_angle = 0.0
00051 
00052     self.init_time = rospy.Time.now().to_time()
00053     if self.debug: print 'init time: {0:2.4f}'.format(self.init_time)
00054 
00055     self.test_angle = rospy.get_param('~test_angle', 360.0) # in [deg], default is 360 deg
00056     self.max_sample = rospy.get_param('~max_sample', 500) # in count, default is 500 sample
00057 
00058     self.use_button = rospy.get_param('~use_button', False)
00059 
00060     self.sub_angle = rospy.Subscriber("angle_abs", ScanAngle, self.angleCallback) # Absolute angle from laser scanner
00061     self.sub_gyro = rospy.Subscriber("imu_data", Imu, self.imuCallback)   # /mobile_base/sensors/imu_data
00062     self.sub_button = rospy.Subscriber("button", ButtonEvent, self.buttonCallback)
00063     self.pub_velocity = rospy.Publisher("cmd_vel", Twist)                 # /mobile_base/commands/velocity
00064     self.pub_sound = rospy.Publisher("sound", Sound)
00065 
00066     if self.debug: print 'state:', self.state
00067     self.state='WAIT_CONNECTION'
00068     if self.debug: print 'state:', self.state
00069     rospy.Timer(rospy.Duration.from_sec(0.02), self.timerCallback) # 50 Hz
00070 
00071   def ending_condition(self):
00072     cond = abs(self.accumulated_angle) >= abs(radians(self.test_angle))
00073     return cond
00074 
00075   def timerCallback(self, event):
00076     if rospy.is_shutdown(): return
00077     if self.done: return
00078 
00079     self.elapsed_time = event.current_real.to_time()-self.init_time
00080     if event.last_real == None:
00081       event.last_real = rospy.Time.from_sec(0.0)
00082       event.last_expected = rospy.Time.from_sec(0.0)
00083       event.last_duration = 0.0
00084 
00085     if self.state == 'WAIT_CONNECTION':
00086       if self.sub_angle.get_num_connections() > 0 and\
00087         self.sub_gyro.get_num_connections() > 0 and\
00088         self.pub_velocity.get_num_connections() > 0:
00089           if self.debug: print 'connection ready.'
00090 
00091           self.state = 'ALIGNING'
00092           if self.debug: print 'state:', self.state
00093           return
00094       else:
00095         if self.debug: print 'not ready yet.'
00096         return
00097 
00098     if self.state == "ALIGNING":
00099       if self.scan_angle is None:
00100         return
00101       scan_angle = self.scan_angle
00102       if abs(scan_angle) > radians(1.0):
00103         cmd_vel = Twist()
00104         cmd_vel.angular.z = -0.33*scan_angle/abs(scan_angle)
00105         self.pub_velocity.publish(cmd_vel)
00106         self.init_time = rospy.Time.now().to_time()
00107         if self.debug: print 'state:', self.state, ':', scan_angle, '-->', cmd_vel.angular.z
00108         return
00109       else:
00110         cmd_vel = Twist()
00111         cmd_vel.linear.x = 0.0  # in [m/s]
00112         cmd_vel.angular.z = 0.0 # in [rad/s]
00113         self.pub_velocity.publish(cmd_vel)
00114 
00115         if self.elapsed_time > 5.0:
00116           if self.debug: print 'alining ready.'
00117 
00118           self.pub_sound.publish(Sound(value=Sound.RECHARGE))
00119           self.state = 'WAIT_TRIGGER'
00120           if self.debug: print 'state:', self.state
00121           return
00122         else:
00123           if self.debug: print 'state:', self.state
00124           return
00125 
00126     if self.state == 'WAIT_TRIGGER':
00127       if self.triggered or not self.use_button:
00128           self.pub_sound.publish(Sound(value=Sound.CLEANINGEND))
00129           self.state = 'CALC_ANGLE_PRERUN'
00130           self.reset_angle = True
00131           self.angle_count = 0
00132           if self.debug: print 'state:', self.state
00133           return
00134       else:
00135           return
00136 
00137     if self.state == 'CALC_ANGLE_PRERUN':
00138       if self.angle_count >= self.max_sample:
00139         self.angle_prerun = self.angle_avg
00140         if self.debug: print 'angle prerun: ', self.angle_prerun
00141 
00142         self.pub_sound.publish(Sound(value=Sound.RECHARGE))
00143 
00144         self.state = 'RUNNING'
00145         self.init_time = rospy.Time.now().to_time()
00146         if self.debug: print 'state:', self.state
00147 
00148         self.init_imu = True
00149         self.init_angle = 0.0
00150         self.angle = 0.0
00151         self.angle_rate = 0.0
00152         self.diff_angle = 0.0
00153         self.accumulated_angle = 0.0
00154 
00155         return
00156       else:
00157         if self.debug: print 'state:', self.state, ':', self.angle_count, self.scan_angle, self.angle_sum, self.angle_avg
00158         return
00159 
00160     if self.state == 'RUNNING':
00161       if self.ending_condition():
00162         self.state = 'STOP'
00163         cmd_vel = Twist()
00164         cmd_vel.linear.x = 0.0  # in [m/s]
00165         cmd_vel.angular.z = 0.0 # in [rad/s]
00166         self.pub_velocity.publish(cmd_vel)
00167         self.running_time = self.elapsed_time
00168         self.init_time = rospy.Time.now().to_time()
00169         if self.debug: print 'state:', self.state
00170         return
00171       else:
00172         cmd_vel = Twist()
00173         cmd_vel.linear.x = self.command_vx  # In [m/s]
00174         cmd_vel.angular.z = self.command_wz # In [rad/s]
00175         self.pub_velocity.publish(cmd_vel)
00176         if self.debug: print 'state:', self.state, ':', degrees(self.accumulated_angle), self.test_angle
00177         return
00178 
00179     if self.state == 'STOP':
00180       if self.elapsed_time > 5.0:
00181         self.reset_angle = True
00182         self.angle_count = 0
00183         self.state = 'CALC_ANGLE_POSTRUN'
00184         if self.debug: print 'state:', self.state
00185         return
00186       else:
00187         if self.debug: print 'state:', self.state
00188         return
00189 
00190     if self.state == 'CALC_ANGLE_POSTRUN':
00191       if self.angle_count >= self.max_sample:
00192         self.angle_postrun = self.angle_avg
00193 
00194         angle_gyro = self.accumulated_angle
00195         sign = angle_gyro/abs(angle_gyro)
00196         angle_laser = sign*radians(self.test_angle)+wrap_to_pi(self.angle_postrun - self.angle_prerun)
00197 
00198         rospy.loginfo('test_angle:    {0} deg'.format(self.test_angle))
00199         rospy.loginfo('test_command:  {0} m/s {1} deg/s'.format(self.command_vx, degrees(self.command_wz)))
00200         rospy.loginfo('running_time:  {0} s'.format(self.running_time))
00201         rospy.loginfo('')
00202         rospy.loginfo('angle_prerun:  {0} deg'.format(degrees(self.angle_prerun)))
00203         rospy.loginfo('angle_postrun: {0} deg'.format(degrees(self.angle_postrun)))
00204         rospy.loginfo('angle_gyro:    {0} deg'.format(degrees(angle_gyro)))
00205         rospy.loginfo('angle_laser:   {0} deg'.format(degrees(angle_laser)))
00206         rospy.loginfo('')
00207         rospy.loginfo('error:         {0} deg/rev'.format(angle_gyro/angle_laser*360.0-360.0))
00208         rospy.loginfo('Done')
00209         self.pub_sound.publish(Sound(value=Sound.CLEANINGSTART))
00210 
00211         self.state = 'DONE'
00212         self.done = True
00213         if self.debug: print 'state:', self.state
00214         rospy.signal_shutdown('jobs_done')
00215         time.sleep(5.0)
00216         return
00217       else:
00218         if self.debug: print 'state:', self.state, ':', self.angle_count, self.scan_angle, self.angle_sum, self.angle_avg
00219         return
00220 
00221   def imuCallback(self,data):
00222     if rospy.is_shutdown(): return
00223     quat = data.orientation
00224     q = [quat.x, quat.y, quat.z, quat.w]
00225     roll, pitch, yaw = euler_from_quaternion(q)
00226 
00227     if self.init_imu:
00228       self.init_angle = yaw
00229       self.init_imu = False
00230 
00231     self.diff_angle = wrap_to_pi(yaw - self.init_angle - self.angle)
00232     self.accumulated_angle += self.diff_angle
00233     self.angle = wrap_to_pi(yaw - self.init_angle)
00234     self.angle_rate = data.angular_velocity.z
00235 
00236   def angleCallback(self,data):
00237     if rospy.is_shutdown(): return
00238 
00239     self.scan_angle = data.scan_angle
00240 
00241     if self.reset_angle:
00242       if self.debug: print 'Resetted'
00243       self.angle_count = 0
00244       self.angle_sum = 0
00245       self.scan_angle = 0
00246       self.reset_angle = False
00247 
00248     if self.angle_count >= self.max_sample:
00249       return
00250 
00251     self.angle_count += 1
00252     self.angle_sum += data.scan_angle
00253     self.angle_avg = self.angle_sum / self.angle_count
00254 
00255   def buttonCallback(self,data):
00256     if rospy.is_shutdown(): return
00257     if data.button == ButtonEvent.Button0 and data.state == ButtonEvent.RELEASED:
00258       #if self.debug: print 'button0 pressed'
00259       self.triggered=True
00260       return
00261     #if data.button == ButtonEvent.Button1 and data.state == ButtonEvent.RELEASED:
00262     #  if self.debug: print 'button1 pressed'; return
00263     #if data.button == ButtonEvent.Button2 and data.state == ButtonEvent.RELEASED:
00264     #  if self.debug: print 'button2 pressed'; return
00265 
00266 if __name__ == '__main__':
00267   print
00268   print "It test gyro on kobuki and print gyro drift errors."
00269   print
00270   try:
00271     instance = Tester()
00272     rospy.spin()
00273   except rospy.ROSInterruptException: pass


kobuki_testsuite
Author(s): Jorge Santos Simon
autogenerated on Thu Jun 6 2019 17:38:11