gyro_perf.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 #AUTHOR: Younghun Ju <yhju@yujinrobot.com>, <yhju83@gmail.com>
3 
4 import roslib; roslib.load_manifest('kobuki_testsuite')
5 import rospy
6 
7 import time
8 from math import pi
9 from kobuki_msgs.msg import ButtonEvent
10 from kobuki_msgs.msg import Sound
11 from geometry_msgs.msg import Twist
12 
13 from sensor_msgs.msg import Imu
14 from geometry_msgs.msg import Quaternion
15 from geometry_msgs.msg import Pose2D
16 
17 from tf.transformations import euler_from_quaternion
18 from math import degrees, radians
19 from numpy import mod
20 
21 from kobuki_msgs.msg import ScanAngle
22 
23 def wrap_to_pi(x):
24  a = mod(mod(x,2*pi)+2*pi, 2*pi)
25  if (a > pi):
26  a -= 2*pi
27  return a
28 
29 class Tester(object):
30  def __init__(self):
31  rospy.init_node("gyro_perf")
32  self.state='INIT'
33  self.done = False
34  self.reset_angle = True
35  self.triggered = False
36  self.scan_angle = None # Place holder until the first scan angle is received
37 
38  self.debug = rospy.get_param('~debug', False)
39 
40  self.command_vx = rospy.get_param('~command_vx', 0.1) # In [m/s], default is 0.1 m/s
41  self.command_wz = radians(rospy.get_param('~command_wz', 5.0)) # In [deg/s], default is 5 deg/s
42  # convert into [rad/s]
43  if self.debug: print self.command_vx, self.command_wz
44 
45  self.init_imu = True
46  self.init_angle = 0.0
47  self.angle = 0.0
48  self.angle_rate = 0.0
49  self.diff_angle = 0.0
50  self.accumulated_angle = 0.0
51 
52  self.init_time = rospy.Time.now().to_time()
53  if self.debug: print 'init time: {0:2.4f}'.format(self.init_time)
54 
55  self.test_angle = rospy.get_param('~test_angle', 360.0) # in [deg], default is 360 deg
56  self.max_sample = rospy.get_param('~max_sample', 500) # in count, default is 500 sample
57 
58  self.use_button = rospy.get_param('~use_button', False)
59 
60  self.sub_angle = rospy.Subscriber("angle_abs", ScanAngle, self.angleCallback) # Absolute angle from laser scanner
61  self.sub_gyro = rospy.Subscriber("imu_data", Imu, self.imuCallback) # /mobile_base/sensors/imu_data
62  self.sub_button = rospy.Subscriber("button", ButtonEvent, self.buttonCallback)
63  self.pub_velocity = rospy.Publisher("cmd_vel", Twist) # /mobile_base/commands/velocity
64  self.pub_sound = rospy.Publisher("sound", Sound)
65 
66  if self.debug: print 'state:', self.state
67  self.state='WAIT_CONNECTION'
68  if self.debug: print 'state:', self.state
69  rospy.Timer(rospy.Duration.from_sec(0.02), self.timerCallback) # 50 Hz
70 
71  def ending_condition(self):
72  cond = abs(self.accumulated_angle) >= abs(radians(self.test_angle))
73  return cond
74 
75  def timerCallback(self, event):
76  if rospy.is_shutdown(): return
77  if self.done: return
78 
79  self.elapsed_time = event.current_real.to_time()-self.init_time
80  if event.last_real == None:
81  event.last_real = rospy.Time.from_sec(0.0)
82  event.last_expected = rospy.Time.from_sec(0.0)
83  event.last_duration = 0.0
84 
85  if self.state == 'WAIT_CONNECTION':
86  if self.sub_angle.get_num_connections() > 0 and\
87  self.sub_gyro.get_num_connections() > 0 and\
88  self.pub_velocity.get_num_connections() > 0:
89  if self.debug: print 'connection ready.'
90 
91  self.state = 'ALIGNING'
92  if self.debug: print 'state:', self.state
93  return
94  else:
95  if self.debug: print 'not ready yet.'
96  return
97 
98  if self.state == "ALIGNING":
99  if self.scan_angle is None:
100  return
101  scan_angle = self.scan_angle
102  if abs(scan_angle) > radians(1.0):
103  cmd_vel = Twist()
104  cmd_vel.angular.z = -0.33*scan_angle/abs(scan_angle)
105  self.pub_velocity.publish(cmd_vel)
106  self.init_time = rospy.Time.now().to_time()
107  if self.debug: print 'state:', self.state, ':', scan_angle, '-->', cmd_vel.angular.z
108  return
109  else:
110  cmd_vel = Twist()
111  cmd_vel.linear.x = 0.0 # in [m/s]
112  cmd_vel.angular.z = 0.0 # in [rad/s]
113  self.pub_velocity.publish(cmd_vel)
114 
115  if self.elapsed_time > 5.0:
116  if self.debug: print 'alining ready.'
117 
118  self.pub_sound.publish(Sound(value=Sound.RECHARGE))
119  self.state = 'WAIT_TRIGGER'
120  if self.debug: print 'state:', self.state
121  return
122  else:
123  if self.debug: print 'state:', self.state
124  return
125 
126  if self.state == 'WAIT_TRIGGER':
127  if self.triggered or not self.use_button:
128  self.pub_sound.publish(Sound(value=Sound.CLEANINGEND))
129  self.state = 'CALC_ANGLE_PRERUN'
130  self.reset_angle = True
131  self.angle_count = 0
132  if self.debug: print 'state:', self.state
133  return
134  else:
135  return
136 
137  if self.state == 'CALC_ANGLE_PRERUN':
138  if self.angle_count >= self.max_sample:
140  if self.debug: print 'angle prerun: ', self.angle_prerun
141 
142  self.pub_sound.publish(Sound(value=Sound.RECHARGE))
143 
144  self.state = 'RUNNING'
145  self.init_time = rospy.Time.now().to_time()
146  if self.debug: print 'state:', self.state
147 
148  self.init_imu = True
149  self.init_angle = 0.0
150  self.angle = 0.0
151  self.angle_rate = 0.0
152  self.diff_angle = 0.0
153  self.accumulated_angle = 0.0
154 
155  return
156  else:
157  if self.debug: print 'state:', self.state, ':', self.angle_count, self.scan_angle, self.angle_sum, self.angle_avg
158  return
159 
160  if self.state == 'RUNNING':
161  if self.ending_condition():
162  self.state = 'STOP'
163  cmd_vel = Twist()
164  cmd_vel.linear.x = 0.0 # in [m/s]
165  cmd_vel.angular.z = 0.0 # in [rad/s]
166  self.pub_velocity.publish(cmd_vel)
168  self.init_time = rospy.Time.now().to_time()
169  if self.debug: print 'state:', self.state
170  return
171  else:
172  cmd_vel = Twist()
173  cmd_vel.linear.x = self.command_vx # In [m/s]
174  cmd_vel.angular.z = self.command_wz # In [rad/s]
175  self.pub_velocity.publish(cmd_vel)
176  if self.debug: print 'state:', self.state, ':', degrees(self.accumulated_angle), self.test_angle
177  return
178 
179  if self.state == 'STOP':
180  if self.elapsed_time > 5.0:
181  self.reset_angle = True
182  self.angle_count = 0
183  self.state = 'CALC_ANGLE_POSTRUN'
184  if self.debug: print 'state:', self.state
185  return
186  else:
187  if self.debug: print 'state:', self.state
188  return
189 
190  if self.state == 'CALC_ANGLE_POSTRUN':
191  if self.angle_count >= self.max_sample:
193 
194  angle_gyro = self.accumulated_angle
195  sign = angle_gyro/abs(angle_gyro)
196  angle_laser = sign*radians(self.test_angle)+wrap_to_pi(self.angle_postrun - self.angle_prerun)
197 
198  rospy.loginfo('test_angle: {0} deg'.format(self.test_angle))
199  rospy.loginfo('test_command: {0} m/s {1} deg/s'.format(self.command_vx, degrees(self.command_wz)))
200  rospy.loginfo('running_time: {0} s'.format(self.running_time))
201  rospy.loginfo('')
202  rospy.loginfo('angle_prerun: {0} deg'.format(degrees(self.angle_prerun)))
203  rospy.loginfo('angle_postrun: {0} deg'.format(degrees(self.angle_postrun)))
204  rospy.loginfo('angle_gyro: {0} deg'.format(degrees(angle_gyro)))
205  rospy.loginfo('angle_laser: {0} deg'.format(degrees(angle_laser)))
206  rospy.loginfo('')
207  rospy.loginfo('error: {0} deg/rev'.format(angle_gyro/angle_laser*360.0-360.0))
208  rospy.loginfo('Done')
209  self.pub_sound.publish(Sound(value=Sound.CLEANINGSTART))
210 
211  self.state = 'DONE'
212  self.done = True
213  if self.debug: print 'state:', self.state
214  rospy.signal_shutdown('jobs_done')
215  time.sleep(5.0)
216  return
217  else:
218  if self.debug: print 'state:', self.state, ':', self.angle_count, self.scan_angle, self.angle_sum, self.angle_avg
219  return
220 
221  def imuCallback(self,data):
222  if rospy.is_shutdown(): return
223  quat = data.orientation
224  q = [quat.x, quat.y, quat.z, quat.w]
225  roll, pitch, yaw = euler_from_quaternion(q)
226 
227  if self.init_imu:
228  self.init_angle = yaw
229  self.init_imu = False
230 
231  self.diff_angle = wrap_to_pi(yaw - self.init_angle - self.angle)
232  self.accumulated_angle += self.diff_angle
233  self.angle = wrap_to_pi(yaw - self.init_angle)
234  self.angle_rate = data.angular_velocity.z
235 
236  def angleCallback(self,data):
237  if rospy.is_shutdown(): return
238 
239  self.scan_angle = data.scan_angle
240 
241  if self.reset_angle:
242  if self.debug: print 'Resetted'
243  self.angle_count = 0
244  self.angle_sum = 0
245  self.scan_angle = 0
246  self.reset_angle = False
247 
248  if self.angle_count >= self.max_sample:
249  return
250 
251  self.angle_count += 1
252  self.angle_sum += data.scan_angle
253  self.angle_avg = self.angle_sum / self.angle_count
254 
255  def buttonCallback(self,data):
256  if rospy.is_shutdown(): return
257  if data.button == ButtonEvent.Button0 and data.state == ButtonEvent.RELEASED:
258  #if self.debug: print 'button0 pressed'
259  self.triggered=True
260  return
261  #if data.button == ButtonEvent.Button1 and data.state == ButtonEvent.RELEASED:
262  # if self.debug: print 'button1 pressed'; return
263  #if data.button == ButtonEvent.Button2 and data.state == ButtonEvent.RELEASED:
264  # if self.debug: print 'button2 pressed'; return
265 
266 if __name__ == '__main__':
267  print
268  print "It test gyro on kobuki and print gyro drift errors."
269  print
270  try:
271  instance = Tester()
272  rospy.spin()
273  except rospy.ROSInterruptException: pass
def buttonCallback(self, data)
Definition: gyro_perf.py:255
def angleCallback(self, data)
Definition: gyro_perf.py:236
def ending_condition(self)
Definition: gyro_perf.py:71
def timerCallback(self, event)
Definition: gyro_perf.py:75
def wrap_to_pi(x)
Definition: gyro_perf.py:23
def imuCallback(self, data)
Definition: gyro_perf.py:221
def __init__(self)
Definition: gyro_perf.py:30


kobuki_testsuite
Author(s): Jorge Santos Simon
autogenerated on Mon Jun 10 2019 13:45:22