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 from __future__ import with_statement
00027
00028 """
00029 wheeled_robin serial control Interface (WSCI) is based on Roomba SCI and Create OI and implements a
00030 subset of the opcodes specified there.
00031
00032 wheeled_robin_driver.py uses the SerialCommandInterface of create_driver from ROS.
00033 The Wheeled_Robin class is adopted from the Roomba class in create_driver.
00034 """
00035 __author__ = "joh.mayr@jku.at (Johannes Mayr)"
00036
00037 import struct
00038 import time
00039 from create_driver import SerialCommandInterface, DriverError
00040
00041 WHEELED_ROBIN_OPCODES = dict(
00042 start = 128,
00043 safe = 131,
00044 full = 132,
00045 drive = 137,
00046 leds = 139,
00047 sensors = 142,
00048 direct_drive = 145,
00049 digital_outputs = 147,
00050 stream = 148,
00051 query_list = 149,
00052 pause_resume_stream = 150,
00053 control_params = 255,
00054 )
00055
00056 WHEELED_ROBIN_MODES = (
00057 'off',
00058 'passive',
00059 'safe',
00060 'full')
00061
00062 SENSOR_GROUP_PACKET_LENGTHS = {
00063 0: 29,
00064 1: 17,
00065 7: 2,
00066 8: 2,
00067 9: 2,
00068 10: 2,
00069 11: 2,
00070 12: 2,
00071 13: 2,
00072 14: 2,
00073 15: 1,
00074 16: 2,
00075 17: 2,
00076 18: 2,
00077 19: 2,
00078 20: 2,
00079 21: 2,
00080 22: 1,
00081 }
00082
00083
00084 VELOCITY_X_MAX = 500
00085 VELOCITY_X_SLOW = int(VELOCITY_X_MAX * 0.33)
00086 VELOCITY_X_FAST = int(VELOCITY_X_MAX * 0.66)
00087
00088 VELOCITY_G_MAX = 500
00089 VELOCITY_G_SLOW = int(VELOCITY_G_MAX * 0.33)
00090 VELOCITY_G_FAST = int(VELOCITY_G_MAX * 0.66)
00091
00092 MAX_WHEEL_SPEED = 500
00093 WHEEL_SEPARATION = 260
00094
00095 SERIAL_TIMEOUT = 2
00096 START_DELAY = 5
00097
00098 assert struct.calcsize('H') == 2, 'Expecting 2-byte shorts.'
00099
00100 class WheeledRobin(object):
00101
00102 """Represents a wheeled_robin robot."""
00103
00104 def __init__(self):
00105 self.tty = None
00106 self.sci = None
00107 self.safe = True
00108
00109 def start(self, tty='/dev/ttyUSB0'):
00110 self.tty = tty
00111 self.sci = SerialCommandInterface(tty, 38400)
00112 self.sci.add_opcodes(WHEELED_ROBIN_OPCODES)
00113
00114 def passive(self):
00115 """Put the robot in passive mode."""
00116 self.sci.start()
00117 time.sleep(0.5)
00118
00119 def control(self):
00120 """Start the robot's SCI interface and place it in safe mode."""
00121 self.passive()
00122 if not self.safe:
00123 self.sci.full()
00124 else:
00125 self.sci.safe()
00126
00127 time.sleep(0.5)
00128
00129 def direct_drive(self, velocity_left, velocity_right):
00130
00131 vl = int(velocity_left) & 0xffff
00132 vr = int(velocity_right) & 0xffff
00133 self.sci.direct_drive(*struct.unpack('4B', struct.pack('>2H', vr, vl)))
00134
00135 def drive(self, velocity_x, velocity_g):
00136 """controls wheeled_robins's drive wheels.
00137
00138 NOTE(Johannes Mayr): This commands differs slightly from the SCI and the OI
00139 as the second argument is not the radius. Instead the angular velocity around
00140 z is used as second argument.
00141
00142 The wheeled_robin takes four data bytes, interpreted as two 16-bit signed values
00143 using two's complement. The first two bytes specify the average linear velocity
00144 of the drive wheels in millimeters per second (mm/s), with the high byte
00145 being sent first. The next two bytes specify the average angular velocity in millirad per second (mrad/s) at
00146 which wheeled_robin will turn.
00147
00148 Also see drive_straight and turn_in_place convenience methods.
00149 """
00150
00151 velocity_x = int(velocity_x) & 0xffff
00152 velocity_g = int(velocity_g) & 0xffff
00153
00154
00155
00156
00157 bytes = struct.unpack('4B', struct.pack('>2H', velocity_x, velocity_g))
00158 self.sci.drive(*bytes)
00159
00160 def stop(self):
00161 """Set velocities to 0 to stop movement."""
00162 self.drive(0, 0)
00163
00164 def slow_stop(self, velocity):
00165 """Slowly reduce the velocities to 0 to stop movement."""
00166 velocities = xrange(velocity, VELOCITY_X_SLOW, -25)
00167 if velocity < 0:
00168 velocities = xrange(velocity, -VELOCITY_X_SLOW, 25)
00169 for v in velocities:
00170 self.drive(v, 0)
00171 time.sleep(0.05)
00172 self.stop()
00173
00174 def drive_straight(self, velocity):
00175 """drive in a straight line."""
00176 self.drive(velocity, 0)
00177
00178 def turn_in_place(self, velocity):
00179 """Turn in place either clockwise or counter-clockwise."""
00180 self.drive(0, velocity)
00181
00182 def set_digital_outputs(self, value):
00183 """Enable or disable digital outputs."""
00184 self.sci.digital_outputs(value)