pheeno_robot.py
Go to the documentation of this file.
1 """
2 Pheeno Robot Python Class files for coding
3 
4 Notes
5 -----
6 Contains files directly related to Pheeno Robot movement, sensors, etc. This
7 object class can be used in other python scripts to give one direct control
8 of the Pheeno without needing to write extra attributes, callbacks, etc.
9 
10 Written by: Zahi Kakish (zmk5)
11 License: BSD 3-Clause
12 
13 """
14 import random
15 import rospy
16 from geometry_msgs.msg import Twist
17 from geometry_msgs.msg import Vector3
18 from std_msgs.msg import Float32
19 from std_msgs.msg import Int16
20 
21 
22 class PheenoRobot(object):
23  """
24  PheenoRobot Class
25 
26  Provides a easier way of generating all the necessary attributes, ROS
27  publishers and subscribers, and useful methods for running a Pheeno robot.
28  Just instantiate the class to a variable within a script and all the
29  necessary ROS calls are generated automatically. Many of the attributes
30  of the Pheeno are hidden from users (except velocity ones) and require
31  getters to get them.
32 
33  Parameters
34  ----------
35  robot_id : str
36  ID number of the pheeno.
37  linear : int/float
38  Initial linear velocity of the robot.
39  angular : int/float
40  Initial angular velocity of the robot.
41  obstacle_velocity : int/float
42  Velocity for obstacle avoidance. In terms of angular velocity, may be faster or
43  slower than the current value of angular velocity.
44 
45  Attributes
46  ----------
47  pheeno_id : str
48  The ID of the pheeno that is used as the namespace for pheeno ROS publishers and
49  subscribers.
50  linear_velocity : float (default 0)
51  The linear velocity of the robot.
52  angular_velocity : float (default 0)
53  The angular velocity of the robot.
54  obstacle_velocity : float (default 0.5)
55  If a robot approaces an obstacle, this is the angular velocity used for obstacle
56  avoidance.
57 
58  Methods
59  -------
60  get_ir_data
61  Returns the IR data for a specific sensor. If the "all" option is
62  used, then all the data is returned as a list.
63  Options: "center", "back", "right", "left", "cr", "cl", "all"
64  get_encoder_data
65  Returns encoder data for a specific encoder. If the "all" option is
66  used, then all the data is returned as a list.
67  Options: "LL", "LR", "RL", "RR", "all"
68  get_magnetometer_data
69  Returns magnetometer data for a specific coordinate. If the "all" option
70  is used, then all the data is returned as a list.
71  Options: "x", "y", "z", "all"
72  get_gyroscope_data
73  Returns gyroscope data for a specific coordinate. If "all" option is
74  used, then all the data is returned as a list.
75  Options: "x", "y", "z", "all"
76  get_accelerometer_data
77  Returns accelerometer data for a specific coordinate. If "all" option
78  is used, then all the data is returned as a list.
79  Options: "x", "y", "z", "all"
80  avoid_obstacle
81  Obstacle avoidance algorithm based on IR sensors. Call when wanting to
82  plan motion while still trying to avoid obstacles.
83  is_obstacle_detected
84  Bool return if any IR sensor has been tripped.
85  is_ir_sensor_triggered
86  Bool return if a SPECIFIC IR sensor has been tripped.
87 
88  Examples
89  --------
90  >>> # If you have more than 10 robots add a `0` to the first
91  >>> # 9 numbers.
92  >>> pheeno = PheenoRobot("01", linear_vel=0.8, angular_vel=0)
93 
94  """
95  def __init__(self, robot_id, linear_velocity=0, angular_velocity=0, obstacle_velocity=0.5):
96  if robot_id == "":
97  self.pheeno_id = robot_id
98 
99  else:
100  self.pheeno_id = "/pheeno_" + str(robot_id)
101 
102  self._velocity = {"linear": float(linear_velocity),
103  "angular": float(angular_velocity),
104  "obstacle": float(obstacle_velocity)}
105 
106  # Sensor Values
107  self._sensor_data = {"ir": {"center": 0.0,
108  "back": 0.0,
109  "right": 0.0,
110  "left": 0.0,
111  "cr": 0.0,
112  "cl": 0.0},
113  "odom": {"position": 0.0,
114  "orient": 0.0,
115  "linear": 0.0,
116  "angular": 0.0},
117  "encoder": {"LL": 0,
118  "LR": 0,
119  "RL": 0,
120  "RR": 0},
121  "magnetometer": {"x": 0.0,
122  "y": 0.0,
123  "z": 0.0},
124  "gyroscope": {"x": 0.0,
125  "y": 0.0,
126  "z": 0.0},
127  "accelerometer": {"x": 0.0,
128  "y": 0.0,
129  "z": 0.0}}
130 
131  # cmd_vel Publisher
132  self._pub = rospy.Publisher(self.pheeno_id + "/cmd_vel",
133  Twist,
134  queue_size=100)
135 
136  # IR Sensor Subscribers
137  rospy.Subscriber(self.pheeno_id + "/scan_center", Float32,
138  self.__callback_ir_sensor, callback_args="center")
139  rospy.Subscriber(self.pheeno_id + "/scan_back", Float32,
140  self.__callback_ir_sensor, callback_args="back")
141  rospy.Subscriber(self.pheeno_id + "/scan_right", Float32,
142  self.__callback_ir_sensor, callback_args="right")
143  rospy.Subscriber(self.pheeno_id + "/scan_left", Float32,
144  self.__callback_ir_sensor, callback_args="left")
145  rospy.Subscriber(self.pheeno_id + "/scan_cr", Float32,
146  self.__callback_ir_sensor, callback_args="cr")
147  rospy.Subscriber(self.pheeno_id + "/scan_cl", Float32,
148  self.__callback_ir_sensor, callback_args="cl")
149 
150  # Encoder Subscribers
151  rospy.Subscriber(self.pheeno_id + "/encoder_LL", Int16,
152  self.__callback_encoder, callback_args="LL")
153  rospy.Subscriber(self.pheeno_id + "/encoder_LR", Int16,
154  self.__callback_encoder, callback_args="LR")
155  rospy.Subscriber(self.pheeno_id + "/encoder_RL", Int16,
156  self.__callback_encoder, callback_args="RL")
157  rospy.Subscriber(self.pheeno_id + "/encoder_RR", Int16,
158  self.__callback_encoder, callback_args="RR")
159 
160  # Magnetometer, Gyroscope, Accelerometer Subscriber
161  rospy.Subscriber(self.pheeno_id + "/magnetometer", Vector3,
163  rospy.Subscriber(self.pheeno_id + "/gyroscope", Vector3,
165  rospy.Subscriber(self.pheeno_id + "/accelerometer", Vector3,
167 
168  def __callback_ir_sensor(self, msg, location):
169  """ Callback for IR sensors subscriber """
170  self._sensor_data["ir"][location] = msg.data
171 
172  def __callback_encoder(self, msg, location):
173  """ Callback for Encoder sensors subscriber """
174  self._sensor_data["encoder"][location] = msg.data
175 
176  def __callback_magnetometer(self, msg):
177  """ Callback for Magnetometer sensor subscriber """
178  self._sensor_data["magnetometer"]["x"] = msg.x
179  self._sensor_data["magnetometer"]["y"] = msg.y
180  self._sensor_data["magnetometer"]["z"] = msg.z
181 
182  def __callback_gyroscope(self, msg):
183  """ Callback for Gyroscope sensor subscriber """
184  self._sensor_data["gyroscope"]["x"] = msg.x
185  self._sensor_data["gyroscope"]["y"] = msg.y
186  self._sensor_data["gyroscope"]["z"] = msg.z
187 
188  def __callback_accelerometer(self, msg):
189  """ Callback for Accelerometer sensor subscriber """
190  self._sensor_data["accelerometer"]["x"] = msg.x
191  self._sensor_data["accelerometer"]["y"] = msg.y
192  self._sensor_data["accelerometer"]["z"] = msg.z
193 
194  @property
195  def linear_velocity(self):
196  """ Linear velocity property """
197  return self._velocity["linear"]
198 
199  @linear_velocity.setter
200  def linear_velocity(self, value):
201  """ Linear velocity setter property """
202  if -1.0 < value < 1.0:
203  self._velocity["linear"] = value
204 
205  else:
206  raise ValueError("Motor values must be between -1 and 1.")
207 
208  @property
209  def angular_velocity(self):
210  """ Angular velocity property """
211  return self._velocity["angular"]
212 
213  @angular_velocity.setter
214  def angular_velocity(self, value):
215  """ Angular velocity setter property """
216  if -1.0 < value < 1.0:
217  self._velocity["angular"] = value
218 
219  else:
220  raise ValueError("Motor values must be between -1 and 1.")
221 
222  @property
223  def obstacle_velocity(self):
224  """ Obstacle velocity property """
225  return self._velocity["obstacle"]
226 
227  @obstacle_velocity.setter
228  def obstacle_velocity(self, value):
229  """ Obstacle velocity setter property """
230  if 0 < value < 1.0:
231  self._velocity["obstacle"] = value
232 
233  else:
234  raise ValueError("Obstacle velocity should be an positive and between (0 < v < 1)")
235 
236  def get_ir_data(self, key):
237  """ Getter for IR Sensor data """
238  if key == "all":
239  values = []
240  for sensor in ["center", "back", "right", "left", "cr", "cl"]:
241  values.append(self._sensor_data["ir"][sensor])
242 
243  return values
244 
245  return self._sensor_data["ir"][key]
246 
247  def get_encoder_data(self, key):
248  """ Getter for Encoder data """
249  if key == "all":
250  values = []
251  for sensor in ["LL", "LR", "RL", "RR"]:
252  values.append(self._sensor_data["encoder"][sensor])
253 
254  return values
255 
256  return self._sensor_data["encoder"][key]
257 
258  def get_magnetometer_data(self, key):
259  """ Getter for Magnetometer data """
260  if key == "all":
261  values = []
262  for sensor in ["x", "y", "z"]:
263  values.append(self._sensor_data["magnetometer"][sensor])
264 
265  return values
266 
267  return self._sensor_data["magnetometer"][key]
268 
269  def get_gyroscope_data(self, key):
270  """ Getter for Gyroscope data """
271  if key == "all":
272  values = []
273  for sensor in ["x", "y", "z"]:
274  values.append(self._sensor_data["gyroscope"][sensor])
275 
276  return values
277 
278  return self._sensor_data["gyroscope"][key]
279 
280  def get_accelerometer_data(self, key):
281  """ Getter for Accelerometer data """
282  if key == "all":
283  values = []
284  for sensor in ["x", "y", "z"]:
285  values.append(self._sensor_data["accelerometer"][sensor])
286 
287  return values
288 
289  return self._sensor_data["accelerometer"][key]
290 
291  def publish_cmd_vel(self, twist_msg):
292  """ Publish an movement velocity on the `cmd_vel` topic """
293  self._pub.publish(twist_msg)
294 
295  def avoid_obstacle(self, limit, linear_vel, angular_vel):
296  """
297  Obstacle avoidance algorithm
298 
299  Given a desired linear and angular velocity, the algorithm will set those
300  values as the robots linear and angular velocity if no obstacle is in
301  the way. If there is, the linear velocity of the robot is set to zero,
302  and the angular velocity is set of a positive or negative version of the
303  obstacle velocity robot attribute depending on certain conditions.
304 
305  Arguments
306  ---------
307  limit : int/float
308  The ir sensor limit.
309  linear_vel : float
310  The desired linear velocity a user would like to set if there are
311  no obstacles in the way.
312  angular_vel : float
313  The desired angular velocity a user would like to set if there are
314  no obstacles in the way.
315 
316  Examples
317  -------
318  For a robot moving along a linear path, the method may be called as such:
319  >>> ir_limit = 15 # in cm
320  >>> desired_linear = 0.7
321  >>> desired_angular = 0 # b/c we don't want it to turn
322  >>> pheeno.avoid_obstacle(ir_limit, desired_linear, desired_angular)
323 
324  If no obstacle is in the path of the robot, the attributes `linear_velocity`
325  and `angular_velocity` are set to 0.7 and 0, respectively. If not, the
326  values are set based on the internal logic of the algorithm. Assign these
327  values to the proper places in a `geometry_msg.Twist()` msg and call the
328  `publish_cmd_vel(twist_msg)` method to enact the changes.
329 
330 
331  """
332  if self.is_obstacle_detected(limit):
333  if self.is_ir_sensor_triggered("center", limit):
334  if (self._sensor_data["ir"]["right"] < 10 and
335  self._sensor_data["ir"]["left"] < 10):
336  angular = self.random_turn() * self._velocity["obstacle"]
337 
338  if self._sensor_data["ir"]["right"] < self._sensor_data["ir"]["left"]:
339  angular = -1 * self._velocity["obstacle"] # Turn Left
340 
341  else:
342  angular = self._velocity["obstacle"] # Turn Right
343 
344  elif (self.is_ir_sensor_triggered("cl", limit) and
345  self.is_ir_sensor_triggered("cr", limit)):
346  angular = self.random_turn() * self._velocity["obstacle"]
347 
348  # If individual IR sensors are triggered.
349  else:
350  angular = self._velocity["obstacle"]
351 
352  # Set linear velocity to 0, so only angular velocity will occur.
353  linear = 0
354 
355  # If no obstacle detected, use the default terms.
356  else:
357  angular = angular_vel
358  linear = linear_vel
359 
360  # Set new velocities
361  self._velocity["linear"] = linear
362  self._velocity["angular"] = angular
363 
364  def is_obstacle_detected(self, limit):
365  """ Returns bool if an obstacle is in the way of any IR sensor """
366  for key in self._sensor_data["ir"]:
367  if self._sensor_data["ir"][key] <= limit:
368  return True
369 
370  return False
371 
372  def is_ir_sensor_triggered(self, key, limit):
373  """ Returns bool if the specific IR sensor is triggered """
374  if self._sensor_data["ir"][key] <= limit:
375  return True
376 
377  return False
378 
379  @staticmethod
380  def random_turn():
381  """ Given a velocity, generate a random turn direction """
382  if random.random() < 0.5:
383  rospy.loginfo("Turning Left")
384  return -1.0
385 
386  rospy.loginfo("Turning Right")
387  return 1.0
def is_obstacle_detected(self, limit)
def get_encoder_data(self, key)
def publish_cmd_vel(self, twist_msg)
def get_accelerometer_data(self, key)
def __callback_encoder(self, msg, location)
def get_ir_data(self, key)
def get_magnetometer_data(self, key)
def get_gyroscope_data(self, key)
def is_ir_sensor_triggered(self, key, limit)
def avoid_obstacle(self, limit, linear_vel, angular_vel)
def __callback_gyroscope(self, msg)
def __callback_accelerometer(self, msg)
def __init__(self, robot_id, linear_velocity=0, angular_velocity=0, obstacle_velocity=0.5)
Definition: pheeno_robot.py:95
def __callback_ir_sensor(self, msg, location)
def __callback_magnetometer(self, msg)


pheeno_ros_sim
Author(s): Zahi Kakish
autogenerated on Mon Jun 10 2019 14:25:55