Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
diagMotors.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelaps
4 //
5 // Program: laelaps_diag
6 //
7 // File: diagMotors.cxx
8 //
9 /*! \file
10  *
11  * \brief Perform Laelaps motors diagnostics.
12  *
13  * \author Robin Knight (robin.knight@roadnarrows.com)
14  *
15  * \par Copyright
16  * \h_copy 2015-2017. RoadNarrows LLC.\n
17  * http://www.roadnarrows.com\n
18  * All Rights Reserved
19  */
20 /*
21  * @EulaBegin@
22  *
23  * Unless otherwise stated explicitly, all materials contained are copyrighted
24  * and may not be used without RoadNarrows LLC's written consent,
25  * except as provided in these terms and conditions or in the copyright
26  * notice (documents and software) or other proprietary notice provided with
27  * the relevant materials.
28  *
29  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
30  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
31  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
32  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
33  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
34  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  *
36  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
37  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
38  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
39  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
40  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41  *
42  * @EulaEnd@
43  */
44 ////////////////////////////////////////////////////////////////////////////////
45 
46 #include <unistd.h>
47 #include <termios.h>
48 #include <string.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <stdarg.h>
52 #include <errno.h>
53 
54 #include <iostream>
55 #include <fstream>
56 #include <string>
57 #include <vector>
58 
59 #include "rnr/rnrconfig.h"
60 #include "rnr/log.h"
61 #include "rnr/opts.h"
62 #include "rnr/pkg.h"
63 
64 // common
65 #include "Laelaps/laelaps.h"
66 #include "Laelaps/laeUtils.h"
67 
68 // hardware
69 #include "Laelaps/laeSysDev.h"
70 #include "Laelaps/RoboClaw.h"
71 #include "Laelaps/laeGpio.h"
72 #include "Laelaps/laeWd.h"
73 #include "Laelaps/laeMotor.h"
74 
75 #include "laelaps_diag.h"
76 
77 using namespace std;
78 using namespace laelaps;
79 using namespace motor::roboclaw;
80 
81 static const char *SubSysName = "Motors";
82 static const char *ProdName = "RoboClaw";
83 
84 /*!
85  * \brief Laelaps motors subsystem information structure.
86  */
87 struct LaelapsMotorsInfo
88 {
89  const char *m_sCtlrKey; ///< motor controller key
90  uint_t m_addr; ///< motor controller address.
91  int m_idx; ///< motor controller index
92  bool m_bIsStd; ///< motor controller is [not] standard option
93  bool m_bBlackListed; ///< motor controller is black listed (bad)
94  struct ///< motors info sub-structure
95  {
96  const char *m_sMotorKey; ///< motor key
97  int m_idx; ///< motor index in controller
98  } m_motors[LaeNumMotorsPerCtlr]; ///< motors info
99 };
100 
101 /*!
102  * \brief Laelaps motors subsystem information.
103  */
104 static LaelapsMotorsInfo MotorsInfo[LaeNumMotorCtlrs] =
105 {
106  {
107  LaeKeyFront, LaeMotorCtlrAddrFront, LaeMotorCtlrIdFront, true, false,
108  { {LaeKeyLeftFront, LaeMotorLeft}, {LaeKeyRightFront, LaeMotorRight} }
109  },
110 
111  {
112  LaeKeyRear, LaeMotorCtlrAddrRear, LaeMotorCtlrIdRear, true, false,
113  { {LaeKeyLeftRear, LaeMotorLeft}, {LaeKeyRightRear, LaeMotorRight} }
114  },
115 };
116 
117 //
118 // The interface.
119 //
120 #if 0 // Deprecated
121 static LaeMotorCtlrEnable MotorCtlrEnable; ///< motor controllers power enable
122 static LaeMotorCtlrChipSelect MotorCtlrCs; ///< motor controllers chip select
123 #endif // Deprecated
124 
125 static RoboClawComm MotorCtlrComm; ///< motor ctrl serial communication bus
126 static RoboClaw *MotorCtlr[LaeNumMotorCtlrs];
127  ///< RoboClaw motor controllers
128 
129 static DiagStats initMotorInterfaces()
130 {
131  string strDevSymName(LaeDevMotorCtlrs); // symbolic name
132  string strDevName; // real device names
133  int nBaudRate = LaeBaudRateMotorCtlrs; // serial baudrate
134  int nCtlr; // motor controller id
135  DiagStats stats; // test stats
136 
137  printSubHdr("Initialize Motor Controller Interfaces");
138 
139  //
140  // Enable power to the motor controllers via watchdog subprocessor gpio.
141  //
142  ++stats.testCnt;
143 
144  if( WatchDog.cmdEnableMotorCtlrs(true) < 0 )
145  {
146  printTestResult(FatalTag, "Motor controllers power enable.");
147  stats.fatal = true;
148  return stats;
149  }
150  else
151  {
152  printTestResult(PassTag, "Enabled power to motor controllers.");
153  ++stats.passCnt;
154  }
155 
156 #if 0 // Deprecated
157  //
158  // Enable power to the motor controllers via odroid gpio.
159  //
160  MotorCtlrEnable.sync();
161  if( MotorCtlrEnable.isConfigured() )
162  {
163  ++stats.testCnt;
164  if( MotorCtlrEnable.isEnabled() )
165  {
166  printTestResult(WarnTag,
167  "Motor controllers are already enabled - may be in usse by another "
168  "application.");
169  ++stats.passCnt;
170  }
171  else if( MotorCtlrEnable.enable() )
172  {
173  printTestResult(PassTag, "Motor controllers enabled.");
174  ++stats.passCnt;
175  }
176  else
177  {
178  printTestResult(FatalTag, "Motor controllers failed to be enabled.");
179  stats.fatal = true;
180  return stats;
181  }
182  }
183 #endif // Deprecated
184 
185 #if 0 // Deprecated
186  //
187  // Open the motor controller chip select via gpio
188  //
189  ++stats.testCnt;
190 
191  if( MotorCtlrCs.open(LaeGpioMotorCtlrCs) < 0 )
192  {
193  printTestResult(FatalTag,
194  "Failed to open motor controllers chip select on GPIO pin %d: %s(%d).",
195  LaeGpioMotorCtlrCs, strerror(errno), errno);
196  stats.fatal = true;
197  }
198  else
199  {
200  printTestResult(PassTag,
201  "Open motor controllers chip select on GPIO pin %d.",
202  LaeGpioMotorCtlrCs);
203  ++stats.passCnt;
204  }
205 #endif // Deprecated
206 
207  //
208  // Open the motor controller.
209  //
210  ++stats.testCnt;
211 
212  // get real device name, not any symbolic links
213  strDevName = getRealDeviceName(strDevSymName);
214 
215  if( MotorCtlrComm.open(strDevName, nBaudRate) < 0 )
216  {
217  printTestResult(FatalTag,
218  "%s: Failed to open motor controllers comm at %d baud: %s(%d)",
219  strDevName.c_str(), nBaudRate, strerror(errno), errno);
220  stats.fatal = true;
221  }
222  else
223  {
224  printTestResult(PassTag,
225  "Open motor controllers serial communication on %s@%d.",
226  strDevName.c_str(), nBaudRate);
227  ++stats.passCnt;
228  }
229 
230  if( stats.fatal )
231  {
232  return stats;
233  }
234 
235  //
236  // Create front motor controller interface.
237  //
238  ++stats.testCnt;
239 
240  nCtlr = LaeMotorCtlrIdFront;
241  MotorCtlr[nCtlr] =
242  new RoboClaw(MotorCtlrComm, LaeMotorCtlrAddrFront, LaeKeyFront);
243  MotorCtlr[nCtlr]->setMotorDir(LaeMotorLeft, LaeMotorDirNormal);
244  MotorCtlr[nCtlr]->setMotorDir(LaeMotorRight, LaeMotorDirNormal);
245  printTestResult(PassTag,
246  "Created %s motor controller interface, address=0x%02x.",
247  LaeKeyFront, LaeMotorCtlrAddrFront);
248  ++stats.passCnt;
249 
250  //
251  // Create rear motor controller interface.
252  //
253  ++stats.testCnt;
254 
255  nCtlr = LaeMotorCtlrIdRear;
256  MotorCtlr[nCtlr] =
257  new RoboClaw(MotorCtlrComm, LaeMotorCtlrAddrRear, LaeKeyRear);
258  MotorCtlr[nCtlr]->setMotorDir(LaeMotorLeft, LaeMotorDirNormal);
259  MotorCtlr[nCtlr]->setMotorDir(LaeMotorRight, LaeMotorDirNormal);
260  printTestResult(PassTag,
261  "Created %s motor controller interface, address=0x%02x.",
262  LaeKeyRear, LaeMotorCtlrAddrRear);
263  ++stats.passCnt;
264 
265  return stats;
266 }
267 
268 static DiagStats readCtlrState(LaelapsMotorsInfo &info)
269 {
270  RoboClaw *pCtlr; // motor controller
271  string strFwVer; // firmware version
272  double fMainBattMinV, fMainBattMaxV; // main battery voltage cutoffs
273  double fLogicMinV, fLogicMaxV; // logic voltage cutoffs
274  uint_t uErrors; // error bits
275  double fTemp; // board temperature
276  double fMainBattV; // main battery voltage
277  double fLogicV; // logic voltage
278 
279  const char *sTag; // pass/fail/fatal tag
280  DiagStats stats; // test stats
281 
282  printSubHdr("Read Motor Controller Information and State");
283 
284  pCtlr = MotorCtlr[info.m_idx];
285 
286  ++stats.testCnt;
287  sTag = FailTag;
288  if( pCtlr->cmdReadFwVersion(strFwVer) == OK )
289  {
290  sTag = PassTag;
291  ++stats.passCnt;
292  }
293  printTestResult(sTag, "Motor Controller %s(0x%02x): Read firmware version.",
294  info.m_sCtlrKey, info.m_addr);
295 
296  ++stats.testCnt;
297  fMainBattMinV = 0.0;
298  fMainBattMaxV = 0.0;
299  sTag = FailTag;
300  if( pCtlr->cmdReadMainBattCutoffs(fMainBattMinV, fMainBattMaxV) == OK )
301  {
302  sTag = PassTag;
303  ++stats.passCnt;
304  }
305  printTestResult(sTag,
306  "Motor Controller %s(0x%02x): Read main battery voltage cutoffs.",
307  info.m_sCtlrKey, info.m_addr);
308 
309  ++stats.testCnt;
310  fLogicMinV = 0.0;
311  fLogicMaxV = 0.0;
312  sTag = FailTag;
313  if( pCtlr->cmdReadLogicCutoffs(fLogicMinV, fLogicMaxV) == OK )
314  {
315  sTag = PassTag;
316  ++stats.passCnt;
317  }
318  printTestResult(sTag,
319  "Motor Controller %s(0x%02x): Read logic voltage cutoffs.",
320  info.m_sCtlrKey, info.m_addr);
321 
322  ++stats.testCnt;
323  uErrors = 0;
324  sTag = FailTag;
325  if( pCtlr->cmdReadStatus(uErrors) == OK )
326  {
327  sTag = PassTag;
328  ++stats.passCnt;
329  }
330  printTestResult(sTag, "Motor Controller %s(0x%02x): Read error status.",
331  info.m_sCtlrKey, info.m_addr);
332 
333  ++stats.testCnt;
334  fTemp = 0.0;
335  sTag = FailTag;
336  if( pCtlr->cmdReadBoardTemperature(fTemp) == OK )
337  {
338  sTag = PassTag;
339  ++stats.passCnt;
340  }
341  printTestResult(sTag, "Motor Controller %s(0x%02x): Read board temperature.",
342  info.m_sCtlrKey, info.m_addr);
343 
344 
345  ++stats.testCnt;
346  fMainBattV = 0.0;
347  sTag = FailTag;
348  if( pCtlr->cmdReadMainBattVoltage(fMainBattV) == OK )
349  {
350  sTag = PassTag;
351  ++stats.passCnt;
352  }
353  printTestResult(sTag,
354  "Motor Controller %s(0x%02x): Read main battery voltage.",
355  info.m_sCtlrKey, info.m_addr);
356 
357  ++stats.testCnt;
358  fLogicV = 0.0;
359  sTag = FailTag;
360  if( pCtlr->cmdReadLogicVoltage(fLogicV) == OK )
361  {
362  sTag = PassTag;
363  ++stats.passCnt;
364  }
365  printTestResult(sTag,
366  "Motor Controller %s(0x%02x): Read logic voltage.",
367  info.m_sCtlrKey, info.m_addr);
368 
369  printf("\n");
370  printf("Motor controller %s Info and State Summary:\n", info.m_sCtlrKey);
371  printf(" Position: %s\n", info.m_sCtlrKey);
372  printf(" Address: 0x%02x\n", pCtlr->getAddress());
373  printf(" Motor Controller: %s\n", ProdName);
374  printf(" FW Version: %s\n", strFwVer.c_str());
375  printf(" Main Battery Cutoffs(V): [%.1lf, %.1lf]\n",
376  fMainBattMinV, fMainBattMaxV);
377  printf(" Logic Cutoffs(V): [%.1lf, %.1lf]\n", fLogicMinV, fLogicMaxV);
378  printf(" Status: 0x%04x\n", uErrors);
379  printf(" Temperature(C): %.1lf\n", fTemp);
380  printf(" Battery(V): %.1lf\n", fMainBattV);
381  printf(" Logic(v): %.1lf\n", fLogicV);
382  printf("\n");
383 
384  return stats;
385 }
386 
387 static DiagStats testMotorCtlrsState()
388 {
389  int nCtlr;
390 
391  DiagStats stats;
392  DiagStats statsSubTotal;
393 
394  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
395  {
396  stats = readCtlrState(MotorsInfo[nCtlr]);
397 
398  printSubTotals(stats);
399 
400  statsSubTotal += stats;
401  }
402 
403  return statsSubTotal;
404 }
405 
406 static DiagStats readMotorState(LaelapsMotorsInfo &info, int nMotor)
407 {
408  RoboClaw *pCtlr; // motor controller
409  const char *sCtlrKey; // motor controller key
410  const char *sMotorKey; // motor key
411  byte_t mode, dummy; // motor encoder modes
412  string strEncMode; // encoder mode, translated
413  u32_t uKp, uKi, uKd; // PID constants
414  u32_t qpps; // quadrature pulses/second
415  double Kp, Ki, Kd; // PID constants (translated)
416  s64_t encoder; // motor encoder position
417  s32_t speed; // motor speed (qpps)
418  double fMaxMaxMotorAmps; // motor maximum maximum current limit
419  double fMaxAmps; // motor maximum current limit
420  double amps, dummy2; // motor current draw (amperes)
421  int rc; // return code
422 
423  const char *sTag; // pass/fail/fatal tag
424  DiagStats stats; // test stats
425 
426  printSubHdr("Read Motor Information and State");
427 
428  pCtlr = MotorCtlr[info.m_idx];
429  sCtlrKey = info.m_sCtlrKey;
430  sMotorKey = info.m_motors[nMotor].m_sMotorKey;
431 
432  ++stats.testCnt;
433  mode = 0xff;
434  sTag = FailTag;
435  if( nMotor == Motor1 )
436  {
437  rc = pCtlr->cmdReadEncoderMode(mode, dummy);
438  }
439  else
440  {
441  rc = pCtlr->cmdReadEncoderMode(dummy, mode);
442  }
443  if( rc == OK )
444  {
445  sTag = PassTag;
446  ++stats.passCnt;
447  }
448  printTestResult(sTag, "Motor %s: Read encoder mode.", sMotorKey);
449 
450  ++stats.testCnt;
451  sTag = FailTag;
452  if( mode == ParamEncModeQuad )
453  {
454  strEncMode = "Quadrature";
455  sTag = PassTag;
456  ++stats.passCnt;
457  }
458  else if( mode & ParamEncModeAbs )
459  {
460  strEncMode = "Absolute";
461  }
462  else if( mode & ParamEncModeRCAnalogEn )
463  {
464  strEncMode = "RC Analog";
465  }
466  else
467  {
468  strEncMode = "Unknown";
469  }
470  printTestResult(sTag, "Motor %s: Encoder mode 0x%02x = %s.",
471  sMotorKey, mode, strEncMode.c_str());
472 
473  ++stats.testCnt;
474  Kp = Ki = Kd = 0.0;
475  qpps = 0;
476  sTag = FailTag;
477  if( pCtlr->cmdReadVelocityPidConst(nMotor, uKp, uKi, uKd, qpps) == OK )
478  {
479  sTag = PassTag;
480  ++stats.passCnt;
481 
482  Kp = (double)uKp / (double)ParamVelPidCvt;
483  Ki = (double)uKi / (double)ParamVelPidCvt;
484  Kd = (double)uKd / (double)ParamVelPidCvt;
485  }
486  printTestResult(sTag, "Motor %s: Read velocity PID constants.", sMotorKey);
487 
488  ++stats.testCnt;
489  fMaxAmps = 0.0;
490  sTag = FailTag;
491  rc = pCtlr->cmdReadMotorMaxCurrentLimit(mode, fMaxAmps);
492  if( rc == OK )
493  {
494  sTag = PassTag;
495  ++stats.passCnt;
496  }
497  printTestResult(sTag, "Motor %s: Read maximum current limit.", sMotorKey);
498 
499  if( RobotDesc.getProdHwVer() > LAE_VERSION(2, 0, 0) )
500  {
501  fMaxMaxMotorAmps = LaeMotorMaxAmps_2_1;
502  }
503  else
504  {
505  fMaxMaxMotorAmps = LaeMotorMaxAmps_2_0;
506  }
507 
508  ++stats.testCnt;
509  sTag = FailTag;
510  if( fMaxAmps <= fMaxMaxMotorAmps )
511  {
512  sTag = PassTag;
513  ++stats.passCnt;
514  }
515  printTestResult(sTag,
516  "Motor %s: Maximum current limit %.2lf <= system max of %.2lf amps.",
517  sMotorKey, fMaxAmps, fMaxMaxMotorAmps);
518 
519  ++stats.testCnt;
520  encoder = 0;
521  sTag = FailTag;
522  if( pCtlr->cmdReadQEncoder(nMotor, encoder) == OK )
523  {
524  sTag = PassTag;
525  ++stats.passCnt;
526  }
527  printTestResult(sTag, "Motor %s: Read motor encoder position.", sMotorKey);
528 
529  ++stats.testCnt;
530  speed = 0;
531  sTag = FailTag;
532  if( pCtlr->cmdReadQSpeed(nMotor, speed) == OK )
533  {
534  sTag = PassTag;
535  ++stats.passCnt;
536  }
537  printTestResult(sTag, "Motor %s: Read motor speed.", sMotorKey);
538 
539  ++stats.testCnt;
540  amps = 0.0;
541  sTag = FailTag;
542  if( nMotor == Motor1 )
543  {
544  rc = pCtlr->cmdReadMotorCurrentDraw(amps, dummy2);
545  }
546  else
547  {
548  rc = pCtlr->cmdReadMotorCurrentDraw(dummy2, amps);
549  }
550  if( rc == OK )
551  {
552  sTag = PassTag;
553  ++stats.passCnt;
554  }
555  printTestResult(sTag, "Motor %s: Read motor current draw.", sMotorKey);
556 
557  printf("\n");
558  printf("Motor %s Info and State Summary:\n", sMotorKey);
559  printf(" Encoder Mode: 0x%02x (%s)\n", mode, strEncMode.c_str());
560  printf(" Velocity PID:\n");
561  printf(" Kp: %3lf (raw = 0x%08x)\n", Kp, uKp);
562  printf(" Ki: %3lf (raw = 0x%08x)\n", Ki, uKi);
563  printf(" Kd: %3lf (raw = 0x%08x)\n", Kd, uKd);
564  printf(" Max QPPS: %u\n", qpps);
565  printf(" Max. Current: %.2lf (amperes)\n", fMaxAmps);
566 #ifdef ARCH_x86_64
567  printf(" Encoder: %ld (pulses)\n", encoder);
568 #else
569  printf(" Encoder: %lld (pulses)\n", encoder);
570 #endif
571  printf(" Speed: %d (qpps)\n", speed);
572  printf(" Draw: %.2lf (amperes)\n", amps);
573 
574  printf("\n");
575 
576  return stats;
577 }
578 
579 static DiagStats testMotorsState()
580 {
581  int i, j; // working indices
582 
583  DiagStats stats; // single motor test stats
584  DiagStats statsSubTotal; // subtotal stats
585 
586  for(i = 0; i < LaeNumMotorCtlrs; ++i)
587  {
588  for(j = 0; j < LaeNumMotorCtlrs; ++j)
589  {
590  stats = readMotorState(MotorsInfo[i], MotorsInfo[i].m_motors[j].m_idx);
591 
592  printSubTotals(stats);
593 
594  statsSubTotal += stats;
595  }
596  }
597 
598  return statsSubTotal;
599 }
600 
601 static DiagStats testMotors()
602 {
603  // test speed profile
604  static s32_t speedProf[] =
605  {
606  1000, 5000, 8000, 5000, 1000, -1000, -5000, -8000, -5000, -1000
607  };
608 
609  uint_t red, green, blue;
610  float wt;
611 
612  int i, j; // working indices
613  size_t k; // working speed profile index
614  double t; // working time
615  RoboClaw *pCtlr; // motor controller
616  const char *sCtlrKey; // motor controller key
617  int nMotor; // motor index
618  const char *sMotorKey; // motor key
619  s64_t encoder; // motor encoder position
620  s32_t speed; // motor speed (qpps)
621  s32_t safeSpeed; // motor target safe speed for mathy things
622  bool bAtSpeed; // did [not] reach target speed
623 
624  const char *sTag; // wait/pass/fail/fatal tag
625  DiagStats stats; // test stats
626 
627  //
628  // Initialize
629  //
630  for(i = 0; i < LaeNumMotorCtlrs; ++i)
631  {
632  pCtlr = MotorCtlr[MotorsInfo[i].m_idx];
633  sCtlrKey = MotorsInfo[i].m_sCtlrKey;
634 
635  ++stats.testCnt;
636  sTag = FailTag;
637  if( pCtlr->cmdQStop() == OK )
638  {
639  sTag = PassTag;
640  ++stats.passCnt;
641  WatchDog.cmdResetRgbLed();
642  }
643  printTestResult(sTag, "%s motors stopped.", sCtlrKey);
644 
645  ++stats.testCnt;
646  sTag = FailTag;
647  if( pCtlr->cmdResetQEncoders() == OK )
648  {
649  sTag = PassTag;
650  ++stats.passCnt;
651  }
652  printTestResult(sTag, "%s motor encoders reset.", sCtlrKey);
653  }
654 
655  //
656  // Drive motors
657  //
658  for(i = 0; i < LaeNumMotorCtlrs; ++i)
659  {
660  pCtlr = MotorCtlr[MotorsInfo[i].m_idx];
661  sCtlrKey = MotorsInfo[i].m_sCtlrKey;
662 
663  for(j = 0; j < LaeNumMotorCtlrs; ++j)
664  {
665  nMotor = MotorsInfo[i].m_motors[j].m_idx;
666  sMotorKey = MotorsInfo[i].m_motors[j].m_sMotorKey;
667 
668  for(k = 0; k < arraysize(speedProf); ++k)
669  {
670  ++stats.testCnt;
671  if( pCtlr->cmdQDrive(nMotor, speedProf[k]) == OK )
672  {
673  ++stats.passCnt;
674  printTestResult(PassTag, "Motor %s: Set target speed=%d.",
675  sMotorKey, speedProf[k]);
676 
677  wt = fabs((float)speedProf[k] / 8000.0);
678  if( speedProf[k] < 0 )
679  {
680  red = (uint_t)(255.0 * wt);
681  green = 0;
682  blue = 0;
683  }
684  else
685  {
686  red = 0;
687  green = 0;
688  blue = (uint_t)(255.0 * wt);
689  }
690  WatchDog.cmdSetRgbLed(red, green, blue);
691 
692  usleep(2000);
693  }
694  else
695  {
696  printTestResult(FailTag, "Motor %s: Set target speed=%d.",
697  sMotorKey, speedProf[k]);
698  continue;
699  }
700 
701  ++stats.testCnt;
702  sTag = FailTag;
703  safeSpeed = speedProf[k] != 0? speedProf[k]: 1;
704  bAtSpeed = false;
705 
706  for(t = 0.0; !bAtSpeed && t <= 4.0; t += 0.1)
707  {
708  speed = 0;
709  encoder = 0;
710  pCtlr->cmdReadQSpeed(nMotor, speed);
711  pCtlr->cmdReadQEncoder(nMotor, encoder);
712  if( fabs(speed - speedProf[k])/ fabs(safeSpeed) <= 0.05 )
713  {
714  bAtSpeed = true;
715  }
716  else
717  {
718  usleep(100000); // 1/10th of a second
719  }
720  if( isatty(fileno(stdout)) )
721  {
722 #ifdef ARCH_x86_64
723  printf("%s Motor %s: Accelerating: speed=%5d: encoder=%8ld\r",
724  WaitTag, sMotorKey, speed, encoder);
725 #else
726  printf("%s Motor %s: Accelerating: speed=%5d: encoder=%8lld\r",
727  WaitTag, sMotorKey, speed, encoder);
728 #endif // ARCH_x86_64
729  fflush(stdout);
730  }
731  }
732 
733  if( bAtSpeed )
734  {
735  sTag = PassTag;
736  ++stats.passCnt;
737  }
738 #ifdef ARCH_x86_64
739  printTestResult(sTag,
740  "Motor %s: Accelerating: speed=%5d: encoder %8ld.",
741  sMotorKey, speed, encoder);
742 #else
743  printTestResult(sTag,
744  "Motor %s: Accelerating: speed=%5d: encoder %8lld.",
745  sMotorKey, speed, encoder);
746 #endif // ARCH_x86_64
747  }
748 
749  ++stats.testCnt;
750  sTag = FailTag;
751  if( pCtlr->cmdStop(nMotor) == OK )
752  {
753  sTag = PassTag;
754  ++stats.passCnt;
755  WatchDog.cmdResetRgbLed();
756  }
757  printTestResult(sTag, "Motor %s stopped.", sMotorKey);
758  }
759  }
760 
761  return stats;
762 }
763 
764 static DiagStats deinitMotorInterfaces()
765 {
766  DiagStats stats; // test stats
767 
768  printSubHdr("De-Initialize Motor Controller Interfaces");
769 
770  //
771  // Enable power to the motor controllers via watchdog subprocessor gpio.
772  //
773  ++stats.testCnt;
774 
775  if( WatchDog.cmdEnableMotorCtlrs(false) < 0 )
776  {
777  printTestResult(FailTag, "Motor controllers power disable.");
778  }
779  else
780  {
781  printTestResult(PassTag, "Disabled power to motor controllers.");
782  ++stats.passCnt;
783  }
784 
785  //
786  // Close serial connection with motor controllers.
787  //
788  ++stats.testCnt;
789 
790  if( MotorCtlrComm.close() < 0 )
791  {
792  printTestResult(FailTag, "Close motor controllers serial connection.");
793  }
794  else
795  {
796  printTestResult(PassTag, "Close serial connection with motor controllers.");
797  ++stats.passCnt;
798  }
799 
800  return stats;
801 }
802 
803 DiagStats runMotorsDiagnostics(bool bTestMotion)
804 {
805  DiagStats statsTest;
806  DiagStats statsTotal;
807 
808  printHdr("Motors Diagnostics");
809 
810  if( bTestMotion )
811  {
812  printf(" (Warning: motors and wheels will rotate)");
813  }
814 
815  printf("\n\n");
816 
817  //
818  // Init Tests
819  //
820  statsTest = initMotorInterfaces();
821 
822  printSubTotals(statsTest);
823 
824  statsTotal += statsTest;
825 
826  //
827  // Read and Verify Motor Controller State Tests
828  //
829  if( !statsTotal.fatal )
830  {
831  statsTest = testMotorCtlrsState();
832 
833  printSubTotals(statsTest);
834 
835  statsTotal += statsTest;
836  }
837 
838  //
839  // Read and Verify Motor State Tests
840  //
841  if( !statsTotal.fatal )
842  {
843  statsTest = testMotorsState();
844 
845  printSubTotals(statsTest);
846 
847  statsTotal += statsTest;
848  }
849 
850  //
851  // Motor Operation Tests
852  //
853  if( bTestMotion )
854  {
855  if( !statsTotal.fatal )
856  {
857  statsTest = testMotors();
858 
859  printSubTotals(statsTest);
860 
861  statsTotal += statsTest;
862  }
863  }
864 
865  //
866  // Deinit Tests
867  //
868  statsTest = deinitMotorInterfaces();
869 
870  printSubTotals(statsTest);
871 
872  statsTotal += statsTest;
873 
874  printSubTotals(statsTest);
875 
876  statsTotal += statsTest;
877 
878  //
879  // Summary
880  //
881  printTotals(statsTotal);
882 
883  return statsTotal;
884 }
Diagnotics header file.
uint_t getProdHwVer() const
Get this robot&#39;s packed hardware version number.
Definition: laeDesc.h:608
virtual int cmdEnableMotorCtlrs(bool bEnable)
Enable/disable power in to motor controllers.
Definition: laeWd.cxx:668
RoboClaw 2 motor controller class.
Definition: RoboClaw.h:808
static LaelapsMotorsInfo MotorsInfo[LaeNumMotorCtlrs]
Laelaps motors subsystem information.
Definition: diagMotors.cxx:104
virtual int cmdReadEncoderMode(byte_t &mode1, byte_t &mode2)
Read encoder mode.
Definition: RoboClaw.cxx:1100
virtual void setMotorDir(int motor, int motorDir)
set the direction of motor rotation.
Definition: RoboClaw.h:943
virtual int cmdQDrive(int motor, s32_t speed)
Drive a motor at the given speed.
Definition: RoboClaw.cxx:1356
virtual int open(std::string &strDevName, int nBaudRate, RoboClawChipSelect *pChipSelect=NULL)
Open connection to motor controller(s).
Definition: RoboClaw.cxx:234
virtual int cmdResetQEncoders()
Reset both motors&#39; encoders.
Definition: RoboClaw.cxx:1152
uint_t m_addr
motor controller address.
Definition: diagBatt.cxx:89
bool isEnabled()
Test if power to motor controllers is enabled.
Definition: laeGpio.cxx:240
Laelaps motor controller power enable class.
Definition: laeGpio.h:200
struct LaelapsMotorsInfo::@0 m_motors[LaeNumMotorsPerCtlr]
motors info
virtual int cmdReadMotorMaxCurrentLimit(int motor, double &maxAmps)
Read a motor&#39;s maximum current limit.
Definition: RoboClaw.cxx:793
virtual int cmdReadMotorCurrentDraw(double &amps1, double &amps2)
Read the motors ampere draw.
Definition: RoboClaw.cxx:761
Laelaps motors subsystem information structure.
Definition: diagBatt.cxx:86
RoboClaw motor controller chip select class.
Definition: laeMotor.h:190
RoboClaw motor controller class interface.
Laelaps WatchDog software class interface.
bool enable()
Enable power to motor controllers.
Definition: laeGpio.cxx:179
virtual int cmdResetRgbLed()
Reset the LED RGB color to state defaults.
Definition: laeWd.cxx:376
virtual int cmdSetRgbLed(uint_t red, uint_t green, uint_t blue)
Set the LED RGB color command.
Definition: laeWd.cxx:353
Laelaps Odroid General Purpose I/O class interfaces.
virtual int cmdReadBoardTemperature(double &temp)
Read RoboClaw board&#39;s temperature.
Definition: RoboClaw.cxx:1034
virtual int cmdReadMainBattCutoffs(double &min, double &max)
Read the RoboClaw&#39;s main battery minimum and maximum voltage cutoff settings.
Definition: RoboClaw.cxx:694
virtual int cmdQStop()
Stop both motors.
Definition: RoboClaw.h:1543
The <b><i>Laelaps</i></b> namespace encapsulates all <b><i>Laelaps</i></b> related constructs...
Definition: laeAlarms.h:64
Simple diagnostics statistics class.
Definition: laelaps_diag.h:106
virtual int cmdReadFwVersion(std::string &strFwVer)
Read the RoboClaw&#39;s firmware version.
Definition: RoboClaw.cxx:608
Laelaps common utilities.
const char * m_sMotorKey
motor key
Definition: diagBatt.cxx:95
bool isConfigured() const
Is the exported GPIO number configured to match this ojbect?
Definition: laeGpio.h:171
virtual int cmdReadQEncoder(int motor, s64_t &encoder)
Read motor&#39;s encoder.
Definition: RoboClaw.cxx:1174
Laelaps system devices.
virtual int cmdReadQSpeed(int motor, s32_t &speed)
Read motor&#39;s speed.
Definition: RoboClaw.cxx:1323
Laelaps motors, encoder, and controllers hardware abstraction interfaces.
virtual int cmdReadStatus(uint_t &status)
Read RoboClaw board&#39;s status bits.
Definition: RoboClaw.cxx:1055
virtual int cmdStop(int motor)
Stop a motor.
Definition: RoboClaw.cxx:1705
int m_idx
motor controller index
Definition: diagBatt.cxx:90
virtual int cmdReadMainBattVoltage(double &volts)
Read the RoboClaw&#39;s main battery input voltage.
Definition: RoboClaw.cxx:667
virtual int close()
Close connection to motor controller.
Definition: RoboClaw.cxx:258
byte_t getAddress() const
Get the controller address associated with this class instance.
Definition: RoboClaw.h:872
#define LAE_VERSION(major, minor, revision)
Convert version triplet to integer equivalent.
Definition: laelaps.h:158
virtual int cmdReadLogicVoltage(double &volts)
Read the RoboClaw&#39;s LB-/LB+ terminals input voltage powered by and optional logic dedicated battery...
Definition: RoboClaw.cxx:873
const char * m_sCtlrKey
motor controller key
Definition: diagBatt.cxx:88
static RoboClawComm MotorCtlrComm
motor ctrl serial communication bus
Definition: diagMotors.cxx:125
RoboClaw communication class.
Definition: RoboClaw.h:487
virtual int open(int pinGpio)
Open GPIO pin interface.
Definition: laeMotor.cxx:83
virtual int cmdReadLogicCutoffs(double &min, double &max)
Read the RoboClaw&#39;s optional logic dedicated battery minimum and maximum voltage cutoff settings...
Definition: RoboClaw.cxx:900
virtual int cmdReadVelocityPidConst(int motor, u32_t &Kp, u32_t &Ki, u32_t &Kd, u32_t &qpps)
Read motor&#39;s velocity PID constants.
Definition: RoboClaw.cxx:958
virtual void sync()
Synchronized this with GPIO hardware state.
Definition: laeGpio.cxx:168
std::string getRealDeviceName(const std::string &strDevName)
Get real device name.
static RoboClaw * MotorCtlr[LaeNumMotorCtlrs]
RoboClaw motor controllers.
Definition: diagMotors.cxx:126
Top-level package include file.