00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 PKG_NAME = 'ackermann_qt'
00011
00012 import sys
00013 import os
00014 import math
00015 import threading
00016
00017 from PyQt4 import QtGui
00018 from PyQt4 import QtCore
00019
00020 import roslib;
00021 roslib.load_manifest(PKG_NAME)
00022 import rospy
00023
00024 from ackermann_msgs.msg import AckermannDrive
00025 from ackermann_msgs.msg import AckermannDriveStamped
00026
00027
00028 from dynamic_reconfigure.server import Server as ReconfigureServer
00029 import ackermann_qt.cfg.QTeleopConfig as Config
00030
00031 g_topic = rospy.Publisher('ackermann_cmd', AckermannDriveStamped)
00032 rospy.init_node('qteleop')
00033
00034
00035 icons_path = os.path.join(roslib.packages.get_pkg_dir(PKG_NAME),
00036 os.path.join("icons", "teleop"))
00037
00038 def find_icon(dir, basename, extlist=['.svg', '.png']):
00039 """Find icon file with basename in dir.
00040
00041 extlist: a list of possible extensions
00042 returns: full path name if file found, None otherwise
00043 """
00044 for ext in extlist:
00045 pathname = os.path.join(dir, basename + ext)
00046 if os.path.exists(pathname):
00047 return pathname
00048 return None
00049
00050 def pkg_icon(name):
00051 """Resolve package-relative icon name to path name.
00052 """
00053 pname = find_icon(icons_path, name)
00054 if pname == None:
00055 raise NameError, 'icon file not found'
00056 return pname
00057
00058
00059 class MainWindow(QtGui.QMainWindow):
00060 "Create vehicle tele-operation control window."
00061
00062 def __init__(self, topic):
00063 QtGui.QMainWindow.__init__(self)
00064
00065 self.topic = topic
00066 self.reconf_server = ReconfigureServer(Config, self.reconfigure)
00067
00068 self.resize(350, 250)
00069 self.setIconSize(QtCore.QSize(32,32))
00070 self.setWindowTitle('Vehicle Tele-Operation')
00071
00072 qexit = QtGui.QAction(QtGui.QIcon(pkg_icon('exit')), 'Exit', self)
00073 qexit.setShortcut('Ctrl+Q')
00074 self.connect(qexit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
00075
00076 center_wheel = QtGui.QAction(QtGui.QIcon(pkg_icon('go-first')),
00077 'center wheel', self)
00078 center_wheel.setShortcut(QtCore.Qt.Key_Home)
00079 self.connect(center_wheel, QtCore.SIGNAL('triggered()'),
00080 self.center_wheel)
00081
00082 go_left = QtGui.QAction(QtGui.QIcon(pkg_icon('go-previous')),
00083 'steer left', self)
00084 go_left.setShortcut(QtCore.Qt.Key_Left)
00085 self.connect(go_left, QtCore.SIGNAL('triggered()'), self.go_left)
00086
00087 go_left_more = QtGui.QAction('steer left more', self)
00088 go_left_more.setShortcut(QtGui.QKeySequence.SelectPreviousWord)
00089 self.connect(go_left_more, QtCore.SIGNAL('triggered()'),
00090 self.go_left_more)
00091
00092 go_left_less = QtGui.QAction('steer left less', self)
00093 go_left_less.setShortcut(QtGui.QKeySequence.MoveToPreviousWord)
00094 self.connect(go_left_less, QtCore.SIGNAL('triggered()'),
00095 self.go_left_less)
00096
00097 slow_down = QtGui.QAction(QtGui.QIcon(pkg_icon('go-down')),
00098 'slow down', self)
00099 slow_down.setShortcut(QtCore.Qt.Key_Down)
00100 self.connect(slow_down, QtCore.SIGNAL('triggered()'), self.slow_down)
00101
00102 speed_up = QtGui.QAction(QtGui.QIcon(pkg_icon('go-up')),
00103 'speed up', self)
00104 speed_up.setShortcut(QtCore.Qt.Key_Up)
00105 self.connect(speed_up, QtCore.SIGNAL('triggered()'), self.speed_up)
00106
00107 go_right = QtGui.QAction(QtGui.QIcon(pkg_icon('go-next')),
00108 'steer right', self)
00109 go_right.setShortcut(QtCore.Qt.Key_Right)
00110 self.connect(go_right, QtCore.SIGNAL('triggered()'), self.go_right)
00111
00112 go_right_less = QtGui.QAction('steer right less', self)
00113 go_right_less.setShortcut(QtGui.QKeySequence.MoveToNextWord)
00114 self.connect(go_right_less, QtCore.SIGNAL('triggered()'),
00115 self.go_right_less)
00116
00117 go_right_more = QtGui.QAction('steer right more', self)
00118 go_right_more.setShortcut(QtGui.QKeySequence.SelectNextWord)
00119 self.connect(go_right_more, QtCore.SIGNAL('triggered()'),
00120 self.go_right_more)
00121
00122 stop_car = QtGui.QAction(QtGui.QIcon(pkg_icon('go-bottom')),
00123 'stop car', self)
00124 stop_car.setShortcut(QtCore.Qt.Key_End)
00125 self.connect(stop_car, QtCore.SIGNAL('triggered()'), self.stop_car)
00126
00127
00128 menubar = self.menuBar()
00129 mfile = menubar.addMenu('&File')
00130 mfile.addAction(qexit)
00131
00132 speed = menubar.addMenu('&Speed')
00133 speed.addAction(slow_down)
00134 speed.addAction(stop_car)
00135 speed.addAction(speed_up)
00136
00137 wheel = menubar.addMenu('&Wheel')
00138 wheel.addAction(center_wheel)
00139 wheel.addAction(go_left)
00140 wheel.addAction(go_left_less)
00141 wheel.addAction(go_left_more)
00142 wheel.addAction(go_right)
00143 wheel.addAction(go_right_less)
00144 wheel.addAction(go_right_more)
00145
00146 toolbar = self.addToolBar('Controls')
00147 toolbar.addAction(qexit)
00148 toolbar.addAction(center_wheel)
00149 toolbar.addAction(go_left)
00150 toolbar.addAction(slow_down)
00151 toolbar.addAction(stop_car)
00152 toolbar.addAction(speed_up)
00153 toolbar.addAction(go_right)
00154
00155 self.ack_cmd = AckermannDriveStamped()
00156 self.ack_cmd.header.stamp = rospy.Time.now()
00157 self.drive = AckermannDrive()
00158 self.ack_cmd.drive = self.drive
00159 self.topic.publish(self.ack_cmd)
00160
00161 self.updateStatusBar()
00162
00163 def updateStatusBar(self):
00164 self.statusBar().showMessage('speed: '
00165 + str(self.drive.speed)
00166 + ' m/s, angle: '
00167 + str(math.degrees(self.drive.steering_angle))
00168 + ' deg')
00169
00170 def adjustCarCmd(self, v, a):
00171 """adjust AckermannDrive command
00172
00173 @param v change in velocity (m/s)
00174 @param a change in steering angle (radians)
00175 """
00176
00177 self.drive.speed += v
00178 self.drive.steering_angle += a
00179
00180
00181 if self.drive.steering_angle > self.config.max_steering:
00182 self.drive.steering_angle = self.config.max_steering
00183 if self.drive.steering_angle < self.config.min_steering:
00184 self.drive.steering_angle = self.config.min_steering
00185
00186
00187 if math.fabs(self.drive.steering_angle) < self.config.epsilon_steering:
00188 self.drive.steering_angle = 0.0
00189
00190 self.ack_cmd.header.stamp = rospy.Time.now()
00191 self.ack_cmd.drive = self.drive
00192 self.topic.publish(self.ack_cmd)
00193
00194 self.updateStatusBar()
00195
00196 def center_wheel(self):
00197 "center steering wheel"
00198 self.adjustCarCmd(0.0, -self.drive.steering_angle)
00199
00200 def go_left(self):
00201 "steer left"
00202 self.adjustCarCmd(0.0, math.radians(1.0))
00203
00204 def go_left_more(self):
00205 "steer more to left"
00206 self.adjustCarCmd(0.0, math.radians(4.0))
00207
00208 def go_left_less(self):
00209 "steer a little to left"
00210 self.adjustCarCmd(0.0, math.radians(0.25))
00211
00212 def go_right(self):
00213 "steer right"
00214 self.adjustCarCmd(0.0, math.radians(-1.0))
00215
00216 def go_right_more(self):
00217 "steer more to right"
00218 self.adjustCarCmd(0.0, math.radians(-4.0))
00219
00220 def go_right_less(self):
00221 "steer far to right"
00222 self.adjustCarCmd(0.0, math.radians(-0.25))
00223
00224 def slow_down(self):
00225 "go one m/s slower"
00226 self.adjustCarCmd(-1.0, 0.0)
00227
00228 def speed_up(self):
00229 "go one m/s faster"
00230 self.adjustCarCmd(1.0, 0.0)
00231
00232 def stop_car(self):
00233 "stop car immediately"
00234 self.adjustCarCmd(-self.drive.speed, 0.0)
00235
00236 def reconfigure(self, config, level):
00237 "Dynamic reconfigure server callback."
00238 rospy.logdebug('Reconfigure callback, level ' + str(level))
00239 rospy.logdebug('New config ' + str(config))
00240 self.config = config
00241 return config
00242
00243
00244 class QtThread(threading.Thread):
00245
00246 def __init__(self, topic):
00247 self.topic = topic
00248 threading.Thread.__init__(self)
00249
00250 def run(self):
00251
00252 app = QtGui.QApplication(sys.argv)
00253
00254 teleop = MainWindow(self.topic)
00255 teleop.show()
00256
00257
00258 exit_status = app.exec_()
00259
00260 rospy.signal_shutdown('Qt exit')
00261
00262 sys.exit(exit_status)
00263
00264
00265 if __name__ == '__main__':
00266
00267
00268
00269 try:
00270 QtThread(g_topic).start()
00271 rospy.spin()
00272 except rospy.ROSInterruptException: pass
00273
00274