59 from serial.serialutil
import SerialException, SerialTimeoutException
64 Diagnostics = [
'motors',
'tofs',
'imu',
'watchdog']
66 -------------------------------------------------------------------------------" 76 def open(self, dev, baud, fnChipSelect=None):
87 def readVersion(self, addr):
88 return "1.5.6 firmwareXYZ" 90 def readMainBatterySettings(self, addr):
93 def readLogicBatterySettings(self, addr):
94 raise SerialTimeoutException(
'bogus')
96 def readEncoderMode(self, addr):
99 def readM1Pidq(self, addr):
100 return (0x00010000, 0x00008000, 0x00004000, 10000)
102 def readM2Pidq(self, addr):
103 return (0x00010000, 0x00008000, 0x00004000, 10000)
105 def readErrorState(self, addr):
108 def readTemperature(self, addr):
111 def readMainBattery(self, addr):
114 def readLogicBattery(self, addr):
117 def readCurrents(self, addr):
120 def readM1Speed(self, addr):
121 if not self.m1_speed.has_key(addr):
122 self.m1_speed[addr] = 0
123 return (self.m1_speed[addr], 0x00)
125 def readM2Speed(self, addr):
126 if not self.m2_speed.has_key(addr):
127 self.m2_speed[addr] = 0
128 return (self.m2_speed[addr], 0x00)
130 def setM1Speed(self, addr, speed):
131 self.m1_speed[addr] = speed
133 def setM2Speed(self, addr, speed):
134 self.m2_speed[addr] = speed
136 def readM1Encoder(self, addr):
137 if not self.m1_encoder.has_key(addr):
138 self.m1_encoder[addr] = random.randint(0, 1000000)
139 return (self.m1_encoder[addr], 0)
141 def readM2Encoder(self, addr):
142 if not self.m2_encoder.has_key(addr):
143 self.m2_encoder[addr] = random.randint(0, 1000000)
144 return (self.m2_encoder[addr], 0x02)
146 def resetEncoderCnts(self, addr):
147 self.m1_encoder[addr] = 0
148 self.m2_encoder[addr] = 0
156 def __init__(self, kwargs):
161 'front': {
'addr': SysConf.MotorCtlrAddrFront,
'name':
'front'},
162 'rear': {
'addr': SysConf.MotorCtlrAddrRear,
'name':
'rear'}
165 self.
motorKeys = [
'left_front',
'right_front',
'left_rear',
'right_rear']
168 {
'ctlr_key':
'front',
'motor_index': 0,
'name':
'left_front'},
170 {
'ctlr_key':
'front',
'motor_index': 1,
'name':
'right_front'},
172 {
'ctlr_key':
'rear',
'motor_index': 0,
'name':
'left_rear'},
174 {
'ctlr_key':
'rear',
'motor_index': 1,
'name':
'right_rear'},
180 print "*** Laelaps Motors Diagnostics ***" 190 motorctlr = RoboClaw.RoboClaw()
193 device = SysConf.MotorCtlrDevName
194 baud = SysConf.MotorCtlrBaudRate
195 cs = SysConf.MotorCtlrChipSelectGpio
197 print "Test: Open motor controller serial interface %s@%d" % (device, baud)
199 motorctlr.open(device, baud, cs)
201 except RoboClaw.RoboClawException
as inst:
202 print 'Error:', inst.message
205 print " ... %d/%d tests passed." % (passCnt, testCnt)
230 print " ... %d/%d motors diagnostic tests passed." % (passCnt, testCnt)
232 if passCnt == testCnt:
237 def testMotorCtlrState(self, motorctlr, addr, name):
238 print "Test: Read %s motor controller state (address=0x%02x)." % \
249 [
" Main Battery Range: ",
250 self.
serReadHelper, (motorctlr.readMainBatterySettings, addr),
252 lambda v:
"%.1fV, %.1fV" % (v[0]/10.0, v[1]/10.0) ],
253 [
" Logic Battery Range: ",
254 self.
serReadHelper, (motorctlr.readLogicBatterySettings, addr),
256 lambda v:
"%.1fV, %.1fV" % (v[0]/10.0, v[1]/10.0) ],
260 lambda v:
"0x%02x" % (v) ],
264 lambda v:
"%.1fC" % (v/10.0) ],
268 lambda v:
"%.1fV" % (v/10.0) ],
272 lambda v:
"%.1fV" % (v/10.0) ],
278 if self.
runTest(t[0], t[1], t[2], t[3], t[4]):
281 print " ... %d/%d tests passed." % (passCnt, testCnt)
284 return (passCnt, testCnt)
286 def testMotorState(self, motorctlr, addr, motorIndex, name):
287 print "Test: Read %s motor state." % (name)
293 readPidq = motorctlr.readM1Pidq
294 readEncoder = motorctlr.readM1Encoder
295 readSpeed = motorctlr.readM1Speed
297 readPidq = motorctlr.readM2Pidq
298 readEncoder = motorctlr.readM2Encoder
299 readSpeed = motorctlr.readM2Speed
309 lambda v: self.
fmtPidq(motorIndex, v) ],
313 lambda v:
"%u, 0x%02x" % (v[0], v[1]) ],
317 lambda v:
"%u, 0x%02x" % (v[0], v[1]) ],
321 lambda v:
"%.3fA" % (v[motorIndex]/100.0) ],
327 if self.
runTest(t[0], t[1], t[2], t[3], t[4]):
330 print " ... %d/%d tests passed." % (passCnt, testCnt)
333 return (passCnt, testCnt)
335 def testMotors(self, motorctlr):
336 print "Test: Drive motors." 341 addrFront = self.
ctlrInfo[
'front'][
'addr']
342 addrRear = self.
ctlrInfo[
'rear'][
'addr']
344 speedProf = [500, 1000, 2500, 5000, 2500, 1000, 500,
345 -500, -1000, -2500, -5000, -2500, -1000, -500,
356 if self.
runTest(
" Reset front encoders: ",
363 if self.
runTest(
" Reset rear encoders: ",
369 print " Loop through speed profile:" 371 for speed
in speedProf:
374 if self.
runTest(
" Set %*s motor speed = %-6d: " % (11, k, speed),
377 lambda v:
"[PASS] (speed = %d)" % (v)):
382 print " ... %d/%d tests passed." % (passCnt, testCnt)
385 return (passCnt, testCnt)
387 def runTest(self, what, fnExec, argsExec, fnChk, fnFmt):
388 v, pf, e = fnExec(*argsExec)
394 s =
"[FAIL] (%s)" % (e)
397 print "%s%s" % (what, s)
400 def serReadHelper(self, fnRead, *argsRead):
402 v = fnRead(*argsRead)
405 except RoboClaw.RoboClawException
as inst:
411 def serWriteHelper(self, fnWrite, *argsWrite):
416 except RoboClaw.RoboClawException
as inst:
421 def stopHelper(self, motorctlr):
423 motorctlr.setM1Speed(self.
ctlrInfo[
'front'][
'addr'], 0)
424 motorctlr.setM2Speed(self.
ctlrInfo[
'front'][
'addr'], 0)
425 motorctlr.setM1Speed(self.
ctlrInfo[
'rear'][
'addr'], 0)
426 motorctlr.setM2Speed(self.
ctlrInfo[
'rear'][
'addr'], 0)
430 except RoboClaw.RoboClawException
as inst:
436 def setSpeedHelper(self, motorctlr, motorkey, speed):
438 mi = self.
motorInfo[motorkey][
'motor_index']
440 setSpeed = motorctlr.setM1Speed
441 readSpeed = motorctlr.readM1Speed
443 setSpeed = motorctlr.setM2Speed
444 readSpeed = motorctlr.readM2Speed
446 setSpeed(addr, speed)
447 except RoboClaw.RoboClawException
as inst:
448 return (
None,
False, inst.message)
450 for i
in range(0, 10):
452 curspeed, status = readSpeed(addr)
454 if abs(curspeed) < 10:
455 return (curspeed,
True,
None)
457 if math.fabs(speed-curspeed)/math.fabs(speed) <= 0.05:
458 return (curspeed,
True,
None)
459 except RoboClaw.RoboClawException
as inst:
460 return (
None,
False, inst.message)
462 return (curspeed,
False,
"timeout reaching speed, curspeed=%d" % (curspeed))
464 def good(self, args):
467 def chkEncoderMode(self, mi, v):
471 e =
"0x%02x != required quadrature encoding" % (v[mi])
474 def fmtEncoderMode(self, mi, v):
475 s =
"0x%02x " % (v[mi])
477 s +=
"(quadrature encoding)" 480 def fmtPidq(self, motorIndex, v):
482 s =
"Kp=%f, Ki=%f, Kd=%f, max_qpps=%u" % (v[0]/f, v[1]/f, v[2]/f, v[3])
491 def __init__(self, kwargs):
497 print "*** Laelaps Time-of-Flight Diagnostics ***" 500 print "Diagnostics not supported yet: [FAIL]" 510 def __init__(self, kwargs):
516 print "*** Laelaps Inertia Measurement Unit Diagnostics ***" 519 print "Diagnostics not supported yet: [FAIL]" 529 def __init__(self, kwargs):
535 print "*** Laelaps Arduino Watchdog Diagnostics ***" 538 print "Diagnostics not supported yet: [FAIL]" 577 self.
_Argv0 = os.path.basename(__file__)
587 print "%s: Error: %s" % (self.
_Argv0, emsg)
589 print "%s: Error" % (self.
_Argv0)
590 print "Try '%s --help' for more information." % (self.
_Argv0)
596 usage: %s [OPTIONS] DIAG [DIAG ...] 599 Run Laelaps diagnostic(s). 601 Options and arguments: 602 --fake : Run diagnostics on fake hardware." 603 -h, --help : Display this help and exit. 605 DIAG : Diagnostic to run. One of: 606 all - Run all dignostics. 607 motors - Run motors diagnotics. 608 tofs - Run time-of-flight sensors diagnostics. 609 imu - Run Inertia Measurement Unit diagnostics. 610 watchdog - Run Arduino watchdog sub-processor diagnostics. 613 The number of diagnostics passed. An exit status of 128 indicates usage 630 self.
_Argv0 = os.path.basename(kwargs.get(
'argv0', __file__))
633 kwargs[
'debug'] =
False 634 kwargs[
'fake'] =
False 638 opts, args = getopt.getopt(argv[1:],
"f?h",
639 [
'help',
'fake',
''])
640 except getopt.error, msg:
642 for opt, optarg
in opts:
643 if opt
in (
'-f',
'--fake'):
644 kwargs[
'fake'] =
True 645 elif opt
in (
'-h',
'--help',
'-?'):
654 kwargs[
'diags'] = Diagnostics
656 kwargs[
'diags'] = args
668 def run(self, argv=None, **kwargs):
679 for diag
in self.
kwargs[
'diags']:
681 Diagnostics.index(diag)
683 print "%s: Unknown diagnostic - ignoring" % (diag)
692 elif diag ==
'watchdog':
694 passCnt += diag.run()
698 print "%d/%d diagnostics passed." % (passCnt, diagCnt)
707 if __name__ ==
'__main__':
709 sys.exit( app.run() );
def testMotorState(self, motorctlr, addr, motorIndex, name)
def getOptions(self, argv=None, kwargs)
Get command-line options.
def serReadHelper(self, fnRead, argsRead)
def __init__(self)
Constructor.
def fmtPidq(self, motorIndex, v)
def printUsageErr(self, emsg)
Print usage error.
def run(self, argv=None, kwargs)
Run application.
def stopHelper(self, motorctlr)
def __init__(self, msg)
Constructor.
def fmtEncoderMode(self, mi, v)
def serWriteHelper(self, fnWrite, argsWrite)
def setSpeedHelper(self, motorctlr, motorkey, speed)
def chkEncoderMode(self, mi, v)
def runTest(self, what, fnExec, argsExec, fnChk, fnFmt)
def testMotors(self, motorctlr)
def testMotorCtlrState(self, motorctlr, addr, name)
msg
error message attribute
Laelaps diagnositcs application class.
def printUsage(self)
Print Command-Line Usage Message.