Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
laelaps_sane.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 
3 ###############################################################################
4 #
5 # Package: RoadNarrows Robotics Laelaps Robotic Mobile Platform Package
6 #
7 # File: laelaps_sane
8 #
9 ## \file
10 ##
11 ## $LastChangedDate: 2015-09-09 14:00:46 -0600 (Wed, 09 Sep 2015) $
12 ## $Rev: 4080 $
13 ##
14 ## \brief Place a Laelaps subsystem in a sane state.
15 ##
16 ## \author Robin Knight (robin.knight@roadnarrows.com)
17 ##
18 ## \par Copyright
19 ## \h_copy 2015-2017. RoadNarrows LLC.\n
20 ## http://www.roadnarrows.com\n
21 ## All Rights Reserved
22 ##
23 # @EulaBegin@
24 #
25 # Unless otherwise stated explicitly, all materials contained are copyrighted
26 # and may not be used without RoadNarrows LLC's written consent,
27 # except as provided in these terms and conditions or in the copyright
28 # notice (documents and software) or other proprietary notice provided with
29 # the relevant materials.
30 #
31 # IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
32 # MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
33 # BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
34 # CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
35 # DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
36 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #
38 # THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
39 # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
40 # FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
41 # "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
42 # PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
43 #
44 # @EulaEnd@
45 #
46 ###############################################################################
47 
48 import sys
49 import os
50 import getopt
51 import time
52 
53 from serial.serialutil import SerialException, SerialTimeoutException
54 
55 import Laelaps.SysConf as SysConf
56 import Laelaps.WatchDog as WatchDog
57 import Laelaps.RoboClaw as RoboClaw
58 
59 SaneSubSys = ['motors']
60 BigSep = "\
61 -------------------------------------------------------------------------------"
62 
63 # ------------------------------------------------------------------------------
64 # Sanity Class
65 # ------------------------------------------------------------------------------
66 
67 class Sanity():
68  def __init__(self, kwargs):
69  self.kwargs = kwargs
70 
71  def run(self):
72  tf = True
73  for subsys in self.kwargs['subsys']:
74  if subsys == 'motors':
75  tf &= self.runMotorControllersSanity()
76  return tf
77 
78  def runMotorControllersSanity(self):
79  # Controller info
80  ctlrInfo = {
81  'front': {'addr': SysConf.MotorCtlrAddrFront},
82  'rear': {'addr': SysConf.MotorCtlrAddrRear}
83  }
84 
85  # TODO enable here
86  #powered = Gpio.areMotorCtlrsPowered()
87  #if powered == True:
88  # print "Warning: Motor controllers may be in use by another application."
89  #else:
90  # Gpio.enableMotorCtlrsPower(True)
91  # time.sleep(0.5)
92 
93  # Create motor controller object
94  motorctlr = RoboClaw.RoboClaw()
95 
96  # Open communiction on serial bus to Laelaps multi-dropped motor controllers
97  device = SysConf.MotorCtlrDevName
98  baud = SysConf.MotorCtlrBaudRate
99  #cs = SysConf.MotorCtlrChipSelectGpio # deprecated
100  print "Opening motor controller serial interface %s@%d" % (device, baud)
101  try:
102  motorctlr.open(device, baud)
103  except RoboClaw.RoboClawException as inst:
104  print 'Error:', inst.message
105  print
106 
107  tf = True
108 
109  if motorctlr.isOpen():
110  for k, d in ctlrInfo.iteritems():
111  tf &= self.saneMotorCtlr(motorctlr, k, d['addr'])
112  motorctlr.close()
113 
114  if not powered:
115  Gpio.enableMotorCtlrsPower(False)
116 
117  return tf
118 
119  def saneMotorCtlr(self, motorctlr, key, addr):
120  print
121  print BigSep
122  print "*** Restore Laelaps %s motor controller sanity ***" % (key)
123  print
124 
125  # defaults (very weak PID)
126  #Kp = RoboClaw.ParamVelPidPDft
127  #Ki = RoboClaw.ParamVelPidIDft
128  #Kd = RoboClaw.ParamVelPidDDft
129  #Qpps = RoboClaw.ParamVelPidQppsDft
130 
131  # typical laelaps PID
132  Kp = 500 * RoboClaw.ParamVelPidCvt
133  Ki = 100 * RoboClaw.ParamVelPidCvt
134  Kd = 5 * RoboClaw.ParamVelPidCvt
135  Qpps = 10000
136 
137  # prevent motor burn out and/or robot shut off from system max limits
138  maxAmps= 4.5 / RoboClaw.ParamAmpScale
139 
140  try:
141  motorctlr.setMainBatterySettings(addr, 60, 340) # 6.0V - 34.0V
142  except RoboClaw.RoboClawException as inst:
143  print "Failed to set main battery cutoffs.", inst.message
144  return False
145  try:
146  motorctlr.setLogicBatterySettings(addr, 55, 340) # 5.5V - 34.0V
147  except RoboClaw.RoboClawException as inst:
148  print "Failed to set logic battery cutoffs.", inst.message
149  return False
150  try:
151  motorctlr.setM1EncoderMode(addr, 0)
152  except RoboClaw.RoboClawException as inst:
153  print "Failed to set motor 1 encoder mode to quadrature.", inst.message
154  return False
155  try:
156  motorctlr.setM2EncoderMode(addr, 0)
157  except RoboClaw.RoboClawException as inst:
158  print "Failed to set motor 2 encoder mode to quadrature.", inst.message
159  return False
160  try:
161  motorctlr.setM1Pidq(addr, Kp, Ki, Kd, Qpps)
162  except RoboClaw.RoboClawException as inst:
163  print "Failed to set motor 1 velocity PID.", inst.message
164  return False
165  try:
166  motorctlr.setM2Pidq(addr, Kp, Ki, Kd, Qpps)
167  except RoboClaw.RoboClawException as inst:
168  print "Failed to set motor 2 velocity PID:", inst.message
169  return False
170  try:
171  motorctlr.setM1MaxCurrentLimit(addr, maxAmps)
172  except RoboClaw.RoboClawException as inst:
173  print "Failed to set motor 1 maximum current.", inst.message
174  return False
175  try:
176  motorctlr.setM2MaxCurrentLimit(addr, maxAmps)
177  except RoboClaw.RoboClawException as inst:
178  print "Failed to set motor 2 maximum current.", inst.message
179  return False
180  # Note: write does not seem to work on controller
181  #try:
182  # motorctlr.writeSettings(addr)
183  #except RoboClaw.RoboClawException as inst:
184  # print "Failed to save to EEPROM:", inst.message
185  # return False
186  return True
187 
188 
189 # ------------------------------------------------------------------------------
190 # Exception Class usage
191 # ------------------------------------------------------------------------------
192 
193 ##
194 ## \brief Unit test command-line exception class.
195 ##
196 ## Raise usage excpetion.
197 ##
198 class usage(Exception):
199 
200  ##
201  ## \brief Constructor.
202  ##
203  ## \param msg Error message string.
204  ##
205  def __init__(self, msg):
206  ## error message attribute
207  self.msg = msg
208 
209 
210 # ------------------------------------------------------------------------------
211 # Class application
212 # ------------------------------------------------------------------------------
213 
214 ##
215 ## \brief Laelaps diagnositcs application class.
216 ##
217 class application():
218 
219  #
220  ## \brief Constructor.
221  #
222  def __init__(self):
223  self._Argv0 = os.path.basename(__file__)
224  self.m_win = None
225 
226  #
227  ## \brief Print usage error.
228  ##
229  ## \param emsg Error message string.
230  #
231  def printUsageErr(self, emsg):
232  if emsg:
233  print "%s: Error: %s" % (self._Argv0, emsg)
234  else:
235  print "%s: Error" % (self._Argv0)
236  print "Try '%s --help' for more information." % (self._Argv0)
237 
238  ## \brief Print Command-Line Usage Message.
239  def printUsage(self):
240  print \
241 """
242 usage: %s [OPTIONS] SUBSYS [SUBSYS ...]
243  %s --help
244 
245 Laelaps sanity.
246 
247 Options and arguments:
248 -h, --help : Display this help and exit.
249 
250 SUBSYS : Laelaps subsystem. One of: motors
251  motors - Motors and motor controllers
252 
253 Exit Status:
254  On success, 0. An exit status of 128 indicates usage
255  error.
256 """ % (self._Argv0, self._Argv0)
257 
258  #
259  ## \brief Get command-line options
260  ##
261  ## \param argv Argument list. If not None, then overrides
262  ## command-line arguments.
263  ## \param [out] kwargs Keyword argument list.
264  ##
265  ## \return Parsed keyword arguments.
266  #
267  def getOptions(self, argv=None, **kwargs):
268  if argv is None:
269  argv = sys.argv
270 
271  self._Argv0 = os.path.basename(kwargs.get('argv0', __file__))
272 
273  # defaults
274  kwargs['debug'] = False
275  kwargs['subsys'] = []
276 
277  # parse command-line options
278  try:
279  opts, args = getopt.getopt(argv[1:], "?h",
280  ['help', ''])
281  except getopt.error, msg:
282  raise usage(msg)
283  for opt, optarg in opts:
284  if opt in ('-h', '--help', '-?'):
285  self.printUsage()
286  sys.exit(0)
287 
288  if len(args) < 1:
289  self.printUsageErr("No subsystems specified.")
290  sys.exit(128)
291 
292  for subsys in args:
293  if subsys in SaneSubSys:
294  kwargs['subsys'] += [subsys]
295  else:
296  self.printUsageErr("%s: Unknown/unsupported subsystem." % (subsys))
297  sys.exit(128)
298 
299  return kwargs
300 
301  #
302  ## \brief Run application.
303  ##
304  ## \param argv Optional argument list to override command-line arguments.
305  ## \param kwargs Optional keyword argument list.
306  ##
307  ## \return Exit code.
308  #
309  def run(self, argv=None, **kwargs):
310 
311  # parse command-line options and arguments
312  try:
313  self.kwargs = self.getOptions(argv, **kwargs)
314  except usage, e:
315  print e.msg
316  return 128
317 
318  sane = Sanity(self.kwargs)
319 
320  tf = sane.run()
321 
322  if tf:
323  return 0
324  else:
325  return 256
326 
327 
328 # ------------------------------------------------------------------------------
329 # main
330 # ------------------------------------------------------------------------------
331 if __name__ == '__main__':
332  app = application();
333  sys.exit( app.run() );
def run(self, argv=None, kwargs)
Run application.
def __init__(self)
Constructor.
Laelaps diagnositcs application class.
def printUsage(self)
Print Command-Line Usage Message.
Unit test command-line exception class.
def runMotorControllersSanity(self)
Definition: laelaps_sane.py:78
msg
error message attribute
def __init__(self, msg)
Constructor.
def saneMotorCtlr(self, motorctlr, key, addr)
def getOptions(self, argv=None, kwargs)
Get command-line options.
def printUsageErr(self, emsg)
Print usage error.