Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009 import random
00010 from math import degrees, radians
00011
00012
00013
00014
00015 import rospy
00016 from tf.transformations import euler_from_quaternion
00017 from geometry_msgs.msg import Twist
00018 from nav_msgs.msg import Odometry
00019 from kobuki_msgs.msg import BumperEvent, CliffEvent
00020
00021
00022
00023
00024 import utils
00025
00026 '''
00027 Implements a safe wandering random motion using bump and cliff sensors.
00028
00029 API:
00030 init(speed,distance) : (re)initialise parameters
00031 stop() - stop.
00032 execute() - pass this to a thread to run
00033 shutdown() - cleanup
00034
00035 @param topic names
00036 @type strings
00037
00038 '''
00039 class SafeWandering(object):
00040 '''
00041 Initialise everything, then starts with start()
00042
00043 API:
00044 start() - start to wander.
00045 stop() - stop wandering.
00046 set_vels(lin_xvel,stepback_xvel,ang_zvel)
00047
00048 @param topic names
00049 @type strings
00050 '''
00051 def __init__(self, cmd_vel_topic, odom_topic, bumper_topic, cliff_topic ):
00052
00053 self.bumper_subscriber = rospy.Subscriber(bumper_topic, BumperEvent, self.bumper_event_callback)
00054 self.cliff_subscriber = rospy.Subscriber(cliff_topic, CliffEvent, self.cliff_event_callback)
00055 self.odom_subscriber = rospy.Subscriber(odom_topic, Odometry, self.odometry_callback)
00056 self.cmd_vel_publisher = rospy.Publisher(cmd_vel_topic, Twist, queue_size=10)
00057 self.rate = rospy.Rate(50)
00058
00059 self.ok = True
00060 self.theta = 0.0
00061 self.theta_goal = 0.0
00062 self._stop = False
00063 self._running = False
00064
00065 self._lin_xvel = 0.18
00066 self._stepback_xvel = -0.1
00067 self._ang_zvel = 1.8
00068
00069 def init(self, speed, stepback_speed, angular_speed):
00070 self._lin_xvel = speed
00071 self._stepback_xvel = stepback_speed
00072 self._ang_zvel = angular_speed
00073
00074
00075 def shutdown(self):
00076 self.stop()
00077 while self._running:
00078 self.rate.sleep()
00079
00080 self.odom_subscriber.unregister()
00081 self.bumper_subscriber.unregister()
00082 self.cliff_subscriber.unregister()
00083
00084 def command(self, twist):
00085 '''
00086 Lowest level of command - i.e. this is where the fine grained looping happens
00087 so we check for aborts here. Don't waste time doing anything if this is the case.
00088 '''
00089 if self._stop or rospy.is_shutdown():
00090 return False
00091 self.cmd_vel_publisher.publish(twist)
00092 self.rate.sleep()
00093 return True
00094
00095 def go(self):
00096 twist = Twist()
00097 while self.ok:
00098 twist.linear.x = self._lin_xvel
00099 if not self.command(twist):
00100 return False
00101 return True
00102
00103 def stepback(self):
00104 twist = Twist()
00105 for i in range(0,35):
00106 twist.linear.x = self._stepback_xvel
00107 if not self.command(twist):
00108 return False
00109 return True
00110
00111 def turn(self):
00112 twist = Twist()
00113 while not self.reached():
00114 twist.angular.z = self._ang_zvel * utils.sign(utils.wrap_to_pi(self.theta_goal - self.theta))
00115 if not self.command(twist):
00116 return False
00117 self.ok = True
00118 return True
00119
00120 def reached(self):
00121 if abs(utils.wrap_to_pi(self.theta_goal - self.theta)) < radians(5.0):
00122 return True
00123 else:
00124 return False
00125
00126 def stop(self):
00127 self._stop = True
00128
00129 def execute(self):
00130 if self._running:
00131 rospy.logerr("Kobuki TestSuite: already executing wandering, ignoring the request")
00132 return
00133 self._stop = False
00134 self._running = True
00135 while True:
00136 if not self.go() or not self.stepback() or not self.turn():
00137 break
00138 self._running = False
00139 if not rospy.is_shutdown():
00140 cmd = Twist()
00141 cmd.linear.x = 0.0
00142 self.cmd_vel_publisher.publish(cmd)
00143
00144
00145
00146
00147
00148 def odometry_callback(self, data):
00149 quat = data.pose.pose.orientation
00150 q = [quat.x, quat.y, quat.z, quat.w]
00151 roll, pitch, yaw = euler_from_quaternion(q)
00152 self.theta = yaw
00153
00154
00155 def bumper_event_callback(self, data):
00156 if data.state == BumperEvent.PRESSED:
00157 self.ok = False
00158 if data.bumper == BumperEvent.LEFT:
00159 self.theta_goal = self.theta - 3.141592*random.uniform(0.2, 1.0)
00160 elif data.bumper == BumperEvent.RIGHT:
00161 self.theta_goal = self.theta + 3.141592*random.uniform(0.2, 1.0)
00162 else:
00163 self.theta_goal = utils.wrap_to_pi(self.theta + 3.141592*random.uniform(-1.0, 1.0))
00164
00165 def cliff_event_callback(self, data):
00166 if data.state == CliffEvent.CLIFF:
00167 self.ok = False
00168
00169 if data.sensor == CliffEvent.LEFT:
00170 self.theta_goal = self.theta - 3.141592*random.uniform(0.2, 1.0)
00171 elif data.sensor == CliffEvent.RIGHT:
00172 self.theta_goal = self.theta + 3.141592*random.uniform(0.2, 1.0)
00173 else:
00174 self.theta_goal = utils.wrap_to_pi(self.theta + 3.141592*random.uniform(-1.0, 1.0))
00175