Laelaps  2.3.5
RoadNarrows Robotics Small Outdoor Mobile Robot Project
laeKin.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Laelaps
4 //
5 // Library: liblaelaps
6 //
7 // File: laeKin.cxx
8 //
9 /*! \file
10  *
11  * \brief The Laelaps kinematics and dynamics class implemenation.
12  *
13  * \par Copyright
14  * \h_copy 2015-2017. RoadNarrows LLC.\n
15  * http://www.roadnarrows.com\n
16  * All Rights Reserved
17  */
18 /*
19  * @EulaBegin@
20  *
21  * Unless otherwise stated explicitly, all materials contained are copyrighted
22  * and may not be used without RoadNarrows LLC's written consent,
23  * except as provided in these terms and conditions or in the copyright
24  * notice (documents and software) or other proprietary notice provided with
25  * the relevant materials.
26  *
27  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
28  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
29  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
30  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
31  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
35  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
36  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
37  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
38  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
39  *
40  * @EulaEnd@
41  */
42 ////////////////////////////////////////////////////////////////////////////////
43 
44 #include <sys/time.h>
45 #include <time.h>
46 #include <limits.h>
47 #include <unistd.h>
48 #include <stdio.h>
49 #include <pthread.h>
50 #include <sched.h>
51 #include <math.h>
52 
53 #include <vector>
54 #include <map>
55 
56 #include "rnr/rnrconfig.h"
57 #include "rnr/gpio.h"
58 #include "rnr/log.h"
59 
60 #include "Laelaps/RoboClaw.h"
61 
62 #include "Laelaps/laelaps.h"
63 #include "Laelaps/laeTune.h"
64 #include "Laelaps/laeUtils.h"
65 #include "Laelaps/laeSysDev.h"
66 #include "Laelaps/laeMotor.h"
67 #include "Laelaps/laeDb.h"
68 #include "Laelaps/laeTraj.h"
69 #include "Laelaps/laePowertrain.h"
70 #include "Laelaps/laePlatform.h"
71 #include "Laelaps/laeKin.h"
72 
73 using namespace std;
74 using namespace laelaps;
75 using namespace motor::roboclaw;
76 
77 //
78 // RDK TODO Put these parameters in tunes
79 //
80 static u32_t MoveAccel = 100000;
81 static u32_t StopDecel = 100000;
82 
83 static int SpeedReallyZero = 5; // motors speed is really zero
84 
85 // ---------------------------------------------------------------------------
86 // LaeKinematics Class
87 // ---------------------------------------------------------------------------
88 
89 LaeKinematics::LaeKinematics()
90 {
91  int nCtlr; // controller id
92 
93  // motor controllers i/f
94  for(nCtlr=0; nCtlr<LaeNumMotorCtlrs; ++nCtlr)
95  {
96  m_pMotorCtlr[nCtlr] = NULL;
97  }
98 
99  // actions
100  m_pAction = new LaeKinAction(*this);
101 
102  pthread_mutex_init(&m_mutex, NULL);
103 
104  m_fnEnableMotorCtlrs = NULL;
105  m_pEnableArg = NULL;
106  m_bIsEnabled = false;
107  m_bAreMotorsPowered = false;
108  m_bIsStopped = true;
109 }
110 
111 LaeKinematics::~LaeKinematics()
112 {
113  int nCtlr;
114 
115  if( m_pAction != NULL )
116  {
117  delete m_pAction;
118  m_pAction = NULL;
119  }
120 
121  // motor controllers i/f
122  for(nCtlr=0; nCtlr<LaeNumMotorCtlrs; ++nCtlr)
123  {
124  if( m_pMotorCtlr[nCtlr] != NULL )
125  {
126  delete m_pMotorCtlr[nCtlr];
127  m_pMotorCtlr[nCtlr] = NULL;
128  }
129  }
130 
131  close();
132 
133  pthread_mutex_destroy(&m_mutex);
134 }
135 
136 
137 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
138 // Basic I/O
139 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
140 
141 int LaeKinematics::open(const std::string &strDevMotorCtlrs,
142  const int nBaudRate,
143  int (*fnEnable)(void *, bool),
144  void *pEnableArg)
145 {
146  string strDevName; // real device names
147  int nCtlr; // motor controller id
148 
149  m_fnEnableMotorCtlrs = fnEnable;
150  m_pEnableArg = pEnableArg;
151  m_bIsEnabled = false;
152 
153  // Enable power to motor controllers
154  enableMotorCtlrs();
155 
156  if( !m_bIsEnabled )
157  {
158  LOGERROR("Failed to enable power to motor controllers.");
159  return -LAE_ECODE_IO;
160  }
161 
162 #ifdef LAE_DEPRECATED
163  // Open the motor controller chip select via gpio
164  if( m_csMotorCtlrs.open(LaeGpioMotorCtlrCs) < 0 )
165  {
166  LOGSYSERROR("Failed to open motor controller chip select on GPIO pin %d.",
167  LaeGpioMotorCtlrCs);
168  return -LAE_ECODE_MOT_CTLR;
169  }
170 
171  LOGDIAG2("Opened motor controllers chip select signal on GPIO %d.",
172  LaeGpioMotorCtlrCs);
173 #endif // LAE_DEPRECATED
174 
175  // Get the real device name, not any symbolic links.
176  strDevName = getRealDeviceName(strDevMotorCtlrs);
177 
178  // Open the motor controller.
179  if( m_commMotorCtlrs.open(strDevName, nBaudRate) < 0 )
180  {
181  LOGERROR("%s: Failed to open motor controller port at %d baud.",
182  strDevName.c_str(), nBaudRate);
183  return -LAE_ECODE_MOT_CTLR;
184  }
185 
186  LOGDIAG3("Open motor controllers serial communication on %s@%d.",
187  strDevName.c_str(), nBaudRate);
188 
189  //
190  // Create front motor controller interface.
191  //
192  nCtlr = LaeMotorCtlrIdFront;
193  m_pMotorCtlr[nCtlr] =
194  new RoboClaw(m_commMotorCtlrs, LaeMotorCtlrAddrFront, LaeKeyFront);
195  m_pMotorCtlr[nCtlr]->setMotorDir(LaeMotorLeft, LaeMotorDirNormal);
196  m_pMotorCtlr[nCtlr]->setMotorDir(LaeMotorRight, LaeMotorDirNormal);
197 
198  //
199  // Create rear motor controller interface.
200  //
201  nCtlr = LaeMotorCtlrIdRear;
202  m_pMotorCtlr[nCtlr] =
203  new RoboClaw(m_commMotorCtlrs, LaeMotorCtlrAddrRear, LaeKeyRear);
204  m_pMotorCtlr[nCtlr]->setMotorDir(LaeMotorLeft, LaeMotorDirNormal);
205  m_pMotorCtlr[nCtlr]->setMotorDir(LaeMotorRight, LaeMotorDirNormal);
206 
207  m_bAreMotorsPowered = false;
208  m_bIsStopped = true;
209 
210  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
211  RtDb.m_robotstatus.m_bInMotion = m_bIsStopped? false: true;
212 
213  LOGDIAG2("Created front and rear motor controllers on %s@%d.",
214  strDevName.c_str(), nBaudRate);
215  //LOGDIAG2("Front and rear motor controllers created on %s@%d, cs=%d.",
216  // strDevName.c_str(), nBaudRate, LaeGpioMotorCtlrCs);
217 
218  return LAE_OK;
219 }
220 
221 int LaeKinematics::close()
222 {
223  if( m_pAction != NULL )
224  {
225  m_pAction->terminate();
226  }
227 
228 #ifdef LAE_DEPRECATED
229  m_csMotorCtlrs.close(); // gpio
230 #endif // LAE_DEPRECATED
231 
232  m_commMotorCtlrs.close(); // on serial device
233 
234  disableMotorCtlrs();
235 
236  if( m_bIsEnabled )
237  {
238  LOGWARN("Failed to disable power to motor controllers.");
239  }
240 
241  LOGDIAG2("Closed motor controllers serial communication.");
242 
243  return LAE_OK;
244 }
245 
246 void LaeKinematics::enableMotorCtlrs()
247 {
248  int rc;
249 
250 #ifdef LAE_DEPRECATED
251  m_bIsEnabled = m_power.enable();
252  return;
253 #endif // LAE_DEPRECATED
254 
255  //
256  // Enable power to motor controllers (and motors).
257  //
258  if( m_fnEnableMotorCtlrs != NULL )
259  {
260  if( (rc = m_fnEnableMotorCtlrs(m_pEnableArg, true)) == LAE_OK )
261  {
262  m_bIsEnabled = true;
263  LOGDIAG2("Enabled power to motor controllers.");
264  }
265  }
266 
267  //
268  // In old versions of Laelaps, motors are always enabled.
269  //
270  else
271  {
272  m_bIsEnabled = true;
273  }
274 }
275 
276 void LaeKinematics::disableMotorCtlrs()
277 {
278  int rc;
279 
280 #ifdef LAE_DEPRECATED
281  m_bIsEnabled = m_power.disable();
282  return;
283 #endif // LAE_DEPRECATED
284 
285  //
286  // Disable power to motor controllers (and motors).
287  //
288  if( m_fnEnableMotorCtlrs != NULL )
289  {
290  if( (rc = m_fnEnableMotorCtlrs(m_pEnableArg, false)) == LAE_OK )
291  {
292  m_bIsEnabled = false;
293  m_bAreMotorsPowered = false;
294  m_bIsStopped = true;
295 
296  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
297  RtDb.m_robotstatus.m_bInMotion = m_bIsStopped? false: true;
298 
299  LOGDIAG2("Disabled power to motor controllers.");
300  }
301  }
302 
303  //
304  // In old versions of Laelaps, motors are always enabled. So only "disable"
305  // in software.
306  //
307  else
308  {
309  m_bIsEnabled = false;
310  }
311 }
312 
313 void LaeKinematics::resyncComm()
314 {
315  LOGDIAG3("Resynchronizing motor controller serial communication.");
316 
317  m_bIsEnabled = false;
318 
319  enableMotorCtlrs();
320 
321  stop();
322 
323  resetOdometers();
324 }
325 
326 
327 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
328 // Configuration
329 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
330 
331 int LaeKinematics::configure(const LaeDesc &desc)
332 {
333  LaeDesc::MapDescPowertrain::const_iterator iter;
334  LaePowertrain train;
335  int rc = LAE_OK;
336 
337  //
338  // Powertrains
339  //
340  for(iter = desc.m_mapDescPowertrain.begin();
341  iter != desc.m_mapDescPowertrain.end();
342  ++iter)
343  {
344  // configure powertrain kinematics
345  train.configure(*iter->second);
346 
347  //
348  // Sanity checks.
349  //
350 
351  //
352  // Add to kinematic chain.
353  //
354  m_kinPowertrains[train.m_strName] = train;
355  }
356 
357  //
358  // Robot platform
359  //
360  m_kinPlatform.configure(*desc.m_pDescBase);
361 
362  LOGDIAG2("Configured motor controllers from robot description.");
363 
364  return rc;
365 }
366 
367 int LaeKinematics::configure(const LaeTunes &tunes)
368 {
369  int nCtlr; // motor controller id
370  int rc; // return code
371 
372  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
373  {
374  if( (rc = configureMotorController(tunes, nCtlr)) != LAE_OK )
375  {
376  break;
377  }
378  }
379 
380  m_kinPlatform.configure(tunes);
381 
382  if( rc == LAE_OK )
383  {
384  LOGDIAG2("Tuned motor controllers.");
385  }
386 
387  return rc;
388 }
389 
390 int LaeKinematics::configureMotorController(const LaeTunes &tunes, int nCtlr)
391 {
392  bool bNvmSave; // do [not] save changes to ctlr's eeprom
393  int rc; // return code
394 
395  bNvmSave = false;
396 
397  //
398  // Make sure motor controllers are configured correctly.
399  //
400  // All operations are read, conditional writes.
401  // Both controllers have identical config.
402  //
403 
404  // battery cutoffs
405  if( (rc = configMotorCtlrBatteryCutoffs(tunes, nCtlr, bNvmSave)) != LAE_OK )
406  {
407  return rc;
408  }
409 
410  // logic cutoffs
411  if( (rc = configMotorCtlrLogicCutoffs(tunes, nCtlr, bNvmSave)) != LAE_OK )
412  {
413  return rc;
414  }
415 
416  // encoder modes
417  if( (rc = configMotorCtlrEncoderModes(tunes, nCtlr, bNvmSave)) != LAE_OK )
418  {
419  return rc;
420  }
421 
422  //
423  // Configure the motor controller's powertrain pair.
424  //
425  if( (rc = configurePtp(tunes, nCtlr, bNvmSave)) != LAE_OK )
426  {
427  return rc;
428  }
429 
430  //
431  // If any settings, that can be saved, have changed, then save to
432  // non-volatile EEPROM.
433  //
434  if( bNvmSave )
435  {
436  saveConfigToNvm(nCtlr);
437  }
438 
439  //
440  // Set controller state values.
441  //
442  resetMotorCtlrEncoders(nCtlr);
443 
444  return LAE_OK;
445 }
446 
447 int LaeKinematics::configMotorCtlrBatteryCutoffs(const LaeTunes &tunes,
448  int nCtlr,
449  bool &bNvmSave)
450 {
451  string strTag = LaeDesc::prettyMotorCtlrName(nCtlr,
452  m_pMotorCtlr[nCtlr]->getAddress());
453 
454  double fMin, fMax; // working min,max values
455  double fMinMin, fMaxMax; // working min min,max max values
456  int rc; // return code
457 
458  //
459  // Read the main battery voltage cutoff settings (min, max) and set if
460  // necessary.
461  //
462 
463  LOGDIAG3("%s: Verifying main battery cutoff voltages...", strTag.c_str());
464 
465  //
466  // Read current settings.
467  //
468  rc = m_pMotorCtlr[nCtlr]->cmdReadMainBattCutoffs(fMin, fMax);
469 
470  if( rc != OK )
471  {
472  LOGWARN("%s: Failed to read main battery cutoff voltages.", strTag.c_str());
473  return LAE_OK;
474  }
475 
476  LOGDIAG3(" Current settings: [%.1lf, %.1lf]", fMin, fMax);
477 
478  // Get supported full main battery range.
479  m_pMotorCtlr[nCtlr]->getMainBattCutoffRange(fMinMin, fMaxMax);
480 
481  //
482  // Change settings. The settings are always reset to defaults on power-up,
483  // so cannot save to non-volatile memory.
484  //
485  if( (fMin != fMinMin) || (fMax != fMaxMax) )
486  {
487  rc = m_pMotorCtlr[nCtlr]->cmdSetMainBattCutoffs(fMinMin, fMaxMax);
488 
489  if( rc != OK )
490  {
491  LOGWARN("%s: Failed to set main battery cutoff voltages to "
492  "[%.1lf, %.1lf].",
493  strTag.c_str(), fMinMin, fMaxMax);
494  }
495  else
496  {
497  LOGDIAG3(" Changed settings: [%.1lf, %.1lf]", fMinMin, fMaxMax);
498  }
499  }
500 
501  //
502  // The settings are good as configured.
503  //
504  else
505  {
506  LOGDIAG3(" Good.");
507  }
508 
509  return LAE_OK;
510 }
511 
512 int LaeKinematics::configMotorCtlrLogicCutoffs(const LaeTunes &tunes,
513  int nCtlr,
514  bool &bNvmSave)
515 {
516  string strTag = LaeDesc::prettyMotorCtlrName(nCtlr,
517  m_pMotorCtlr[nCtlr]->getAddress());
518 
519  double fMin, fMax; // working min,max values
520  double fMinMin, fMaxMax; // working min min,max max values
521  int rc; // return code
522 
523  //
524  // Read the logic 'battery' voltage cutoff settings (min, max) and set if
525  // necessary.
526  //
527  // Currently, the LB-/LB+ terminals are not connected, and the logic uses
528  // the main battery.
529  //
530 
531  LOGDIAG3("%s: Verifying logic cutoff voltages...", strTag.c_str());
532 
533  //
534  // Read current settings.
535  //
536  rc = m_pMotorCtlr[nCtlr]->cmdReadLogicCutoffs(fMin, fMax);
537 
538  if( rc != OK )
539  {
540  LOGWARN("%s: Failed to read logic cutoff voltages.", strTag.c_str());
541  return LAE_OK;
542  }
543 
544  LOGDIAG3(" Current settings: [%.1lf, %.1lf]", fMin, fMax);
545 
546  // Get supported full logic range.
547  m_pMotorCtlr[nCtlr]->getLogicCutoffRange(fMinMin, fMaxMax);
548 
549  //
550  // Change settings. The settings are always reset to defaults on power-up,
551  // so cannot save to non-volatile memory.
552  //
553  if( (fMin != fMinMin) || (fMax != fMaxMax) )
554  {
555  rc = m_pMotorCtlr[nCtlr]->cmdSetLogicCutoffs(fMinMin, fMaxMax);
556 
557  if( rc != OK )
558  {
559  LOGWARN("%s: Failed to set logic cutoff voltages to [%.1lf, %.1lf].",
560  strTag.c_str(), fMinMin, fMaxMax);
561  }
562  else
563  {
564  LOGDIAG3(" Changed settings: [%.1lf, %.1lf]", fMinMin, fMaxMax);
565  }
566  }
567 
568  //
569  // The settings are good as configured.
570  //
571  else
572  {
573  LOGDIAG3(" Good.");
574  }
575 
576  return LAE_OK;
577 }
578 
579 int LaeKinematics::configMotorCtlrEncoderModes(const LaeTunes &tunes,
580  int nCtlr,
581  bool &bNvmSave)
582 {
583  string strTag = LaeDesc::prettyMotorCtlrName(nCtlr,
584  m_pMotorCtlr[nCtlr]->getAddress());
585 
586  byte_t byMode1, byMode2; // motor encoder modes
587  int rc; // return code
588 
589  //
590  // Read both motors' encoder modes and set if necessary. Laelaps uses
591  // quadrature encoding. This is a required configuration for operation.
592  //
593 
594  LOGDIAG3("%s: Verifying motor quadrature encoder modes...", strTag.c_str());
595 
596  //
597  // Read current settings.
598  //
599  rc = m_pMotorCtlr[nCtlr]->cmdReadEncoderMode(byMode1, byMode2);
600 
601  if( rc != OK )
602  {
603  LOGERROR("%s: Failed to read motor encoder modes.", strTag.c_str());
604  return -LAE_ECODE_MOT_CTLR;
605  }
606 
607  LOGDIAG3(" Current settings: [0x%02x, 0x%02x]", byMode1, byMode2);
608 
609  //
610  // Change settings. The new settings should also be saved to non-volatile
611  // memory.
612  //
613  if( (byMode1 != ParamEncModeQuad) || (byMode2 != ParamEncModeQuad) )
614  {
615  rc = m_pMotorCtlr[nCtlr]->cmdSetEncoderMode2(ParamEncModeQuad,
616  ParamEncModeQuad);
617 
618  if( rc != OK )
619  {
620  LOGERROR("%s: Failed to set motor encoders to quadrature encoding "
621  "[0x%02x, 0x%02x].",
622  strTag.c_str(), ParamEncModeQuad, ParamEncModeQuad);
623  return -LAE_ECODE_MOT_CTLR;
624  }
625 
626  LOGDIAG3(" Changed settings: [0x%02x, 0x%02x]",
627  ParamEncModeQuad, ParamEncModeQuad);
628 
629  bNvmSave = true;
630  }
631 
632  //
633  // The settings are good as configured.
634  //
635  else
636  {
637  LOGDIAG3(" Good.");
638  }
639 
640  return LAE_OK;
641 }
642 
643 int LaeKinematics::resetMotorCtlrEncoders(int nCtlr)
644 {
645  string strTag = LaeDesc::prettyMotorCtlrName(nCtlr,
646  m_pMotorCtlr[nCtlr]->getAddress());
647 
648  int rc; // return code
649 
650  rc = m_pMotorCtlr[nCtlr]->cmdResetQEncoders();
651 
652  if( rc == OK )
653  {
654  LOGDIAG3("%s: Encoders reset.", strTag.c_str());
655  return LAE_OK;
656  }
657  else
658  {
659  LOGWARN("%s: Failed to reset encoders.", strTag.c_str());
660  return -LAE_ECODE_MOT_CTLR;
661  }
662 }
663 
664 int LaeKinematics::configurePtp(const LaeTunes &tunes,
665  int nCtlr,
666  bool &bNvmSave)
667 {
668  LaeMapPowertrain::iterator pos;
669 
670  int nMotor; // motor index
671  int rc; // return code
672 
673  string strKey = m_pMotorCtlr[nCtlr]->getNameId();
674 
675  for(nMotor = LaeMotorLeft; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
676  {
677  strKey = LaePowertrain::toKey(nCtlr, nMotor);
678 
679  // - - - -
680  // Update powertrain attributes with tuning parameters values.
681  // - - - -
682  if( (pos = m_kinPowertrains.find(strKey)) == m_kinPowertrains.end() )
683  {
684  LOGWARN("Cannot find powertrain %s - ignoring.", strKey.c_str());
685  continue;
686  }
687 
688  // configure parameters for powertrain kinodynamics
689  pos->second.configure(tunes);
690 
691  // velocity PID
692  if( (rc = configMotorVelocityPid(tunes, pos->second, bNvmSave)) != LAE_OK )
693  {
694  return rc;
695  }
696 
697  // maximum ampere limit
698  if( (rc = configMotorMaxAmpLimit(tunes, pos->second, bNvmSave)) != LAE_OK )
699  {
700  return rc;
701  }
702 
703  //
704  // RDK TODO 63,64 Read motor position PID
705  // RDK TODO 61,62 Set motor position PID
706  }
707 
708  return LAE_OK;
709 }
710 
711 int LaeKinematics::configMotorVelocityPid(const LaeTunes &tunes,
712  LaePowertrain &powertrain,
713  bool &bNvmSave)
714 {
715  string strKey = powertrain.m_strName;
716  int nCtlrId = powertrain.m_attr.m_nMotorCtlrId;
717  int nMotorId = powertrain.m_attr.m_nMotorId;
718  int nMotor = powertrain.m_attr.m_nMotorIndex;
719  byte_t addr = m_pMotorCtlr[nCtlrId]->getAddress();
720  string strTag = LaeDesc::prettyMotorName(nCtlrId, addr, nMotorId);
721 
722  u32_t uKp, uKi, uKd, uQpps; // existing PID settings.
723  double fTuneKp, fTuneKi, fTuneKd; // tune PID parameters
724  u32_t uTuneKp, uTuneKi, uTuneKd, uTuneQpps; // converted tune target PID
725  int rc; // return code
726 
727  //
728  // Read motor's velocity PID parameters, and set if necessary.
729  //
730 
731  LOGDIAG3("%s: Verifying velocity PID parameters...", strTag.c_str());
732 
733  //
734  // Read current settings.
735  //
736  rc = m_pMotorCtlr[nCtlrId]->cmdReadVelocityPidConst(nMotor,
737  uKp, uKi, uKd, uQpps);
738 
739  if( rc != OK )
740  {
741  LOGWARN("%s: Failed to read velocity PID constants.", strTag.c_str());
742  return LAE_OK;
743  }
744 
745  fTuneKp = (double)uKp / (double)ParamVelPidCvt;
746  fTuneKi = (double)uKi / (double)ParamVelPidCvt;
747  fTuneKd = (double)uKd / (double)ParamVelPidCvt;
748 
749  LOGDIAG3(" Current settings: "
750  "Kp=%lf (raw=0x%08x), Ki=%lf (raw=0x%08x), Kd=%lf (raw=0x%08x), Qpps=%u.",
751  fTuneKp, uKp, fTuneKi, uKi, fTuneKd, uKd, uQpps);
752 
753  // Get the tune parameters in "natural" floating-point representation
754  tunes.getVelPidKParams(strKey, fTuneKp, fTuneKi, fTuneKd);
755 
756  // Convert to motor controller speak.
757  uTuneKp = (u32_t)(fTuneKp * (double)ParamVelPidCvt);
758  uTuneKi = (u32_t)(fTuneKi * (double)ParamVelPidCvt);
759  uTuneKd = (u32_t)(fTuneKd * (double)ParamVelPidCvt);
760  uTuneQpps = (u32_t)(powertrain.m_attr.m_uMaxQpps);
761 
762  //
763  // Change settings. The new settings should also be saved to non-volatile
764  // memory.
765  //
766  if( (uKp != uTuneKp) || (uKi != uTuneKi) || (uKd != uTuneKd) ||
767  (uQpps != uTuneQpps) )
768  {
769  rc = m_pMotorCtlr[nCtlrId]->cmdSetVelocityPidConst(nMotor,
770  uTuneKp, uTuneKi, uTuneKd, uTuneQpps);
771 
772  if( rc != OK)
773  {
774  LOGWARN("%s: Failed to set velocity PID constants.", strTag.c_str());
775  }
776  else
777  {
778  LOGDIAG3(" Changed settings: "
779  "Kp=%lf (raw=0x%08x), Ki=%lf (raw=0x%08x), Kd=%lf (raw=0x%08x), "
780  "Qpps=%u.",
781  fTuneKp, uTuneKp, fTuneKi, uTuneKi, fTuneKd, uTuneKd, uTuneQpps);
782 
783  bNvmSave = true;
784  }
785  }
786 
787  //
788  // The settings are good as configured.
789  //
790  else
791  {
792  LOGDIAG3(" Good.");
793  }
794 
795  return LAE_OK;
796 }
797 
798 int LaeKinematics::configMotorMaxAmpLimit(const LaeTunes &tunes,
799  LaePowertrain &powertrain,
800  bool &bNvmSave)
801 {
802  string strKey = powertrain.m_strName;
803  int nCtlrId = powertrain.m_attr.m_nMotorCtlrId;
804  int nMotorId = powertrain.m_attr.m_nMotorId;
805  int nMotor = powertrain.m_attr.m_nMotorIndex;
806  byte_t addr = m_pMotorCtlr[nCtlrId]->getAddress();
807  string strTag = LaeDesc::prettyMotorName(nCtlrId, addr, nMotorId);
808 
809  double fCurMaxAmps, fMaxAmps; // max. amp limits
810  int rc; // return code
811 
812  //
813  // Read motor's maximum current limit, and set if necessary.
814  //
815  // Note: Not limiting the amps can cause motor burn out.
816  //
817 
818  LOGDIAG3("%s: Verifying maximum amp limit parameter...", strTag.c_str());
819 
820  //
821  // Read current settings.
822  //
823  rc = m_pMotorCtlr[nCtlrId]->cmdReadMotorMaxCurrentLimit(nMotor, fCurMaxAmps);
824 
825  if( rc != OK )
826  {
827  LOGERROR("%s: Failed to read maximum current limit.", strTag.c_str());
828  return -LAE_ECODE_MOT_CTLR;
829  }
830 
831  LOGDIAG3(" Current settings: %.2lf amps.", fCurMaxAmps);
832 
833  if( RtDb.m_product.m_uProdHwVer == LAE_VERSION(2, 0, 0) )
834  {
835  fMaxAmps = LaeMotorMaxAmps_2_0 - 0.05;
836  }
837  else
838  {
839  fMaxAmps = LaeMotorMaxAmps_2_1 - 0.05;
840  }
841 
842  //
843  // Change settings. The new settings should also be saved to non-volatile
844  // memory.
845  //
846  if( fCurMaxAmps != fMaxAmps )
847  {
848  rc = m_pMotorCtlr[nCtlrId]->cmdSetMotorMaxCurrentLimit(nMotor, fMaxAmps);
849 
850  if( rc != OK)
851  {
852  LOGERROR("%s: Failed to set maximum current limit to %.2lf.",
853  strTag.c_str(), fMaxAmps);
854  return -LAE_ECODE_MOT_CTLR;
855  }
856  else
857  {
858  LOGDIAG3(" Changed settings: %.2lf amps.", fMaxAmps);
859  bNvmSave = true;
860  }
861  }
862 
863  //
864  // The settings are good as configured.
865  //
866  else
867  {
868  LOGDIAG3(" Good.");
869  }
870 
871  return LAE_OK;
872 }
873 
874 int LaeKinematics::saveConfigToNvm(int nCtlrId)
875 {
876  string strTag = LaeDesc::prettyMotorCtlrName(nCtlrId,
877  m_pMotorCtlr[nCtlrId]->getAddress());
878 
879  int rc; // return code
880 
881  LOGDIAG3("%s: Saving new values to EEPROM.", strTag.c_str());
882 
883  rc = m_pMotorCtlr[nCtlrId]->cmdWriteSettingsToEEPROM();
884 
885  if( rc == OK )
886  {
887  LOGDIAG3(" Saved.");
888  }
889  else
890  {
891  LOGWARN("%s: Failed to write settings to EEPROM.", strTag.c_str());
892  }
893 
894  return LAE_OK;
895 }
896 
897 int LaeKinematics::reload(const LaeTunes &tunes)
898 {
899  LaeMapPowertrain::iterator iter; // kinematics chain iterator
900 
901  LOGDIAG2("Reloading motor controller tuning parameters...");
902 
903  for(iter = m_kinPowertrains.begin(); iter != m_kinPowertrains.end(); ++iter)
904  {
905  iter->second.reload(tunes);
906  }
907 
908  m_kinPlatform.reload(tunes);
909 
910  return LAE_OK;
911 }
912 
913 
914 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
915 // Attribute Methods
916 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
917 
918 RoboClaw *LaeKinematics::getMotorCtlr(const int nMotorCtlrId)
919 {
920  if( (nMotorCtlrId >= 0) && (nMotorCtlrId < LaeNumMotorCtlrs) )
921  {
922  return m_pMotorCtlr[nMotorCtlrId];
923  }
924  else
925  {
926  return NULL;
927  }
928 }
929 
930 LaePowertrain *LaeKinematics::getPowertrain(const string &strName)
931 {
932  if( m_kinPowertrains.find(strName) != m_kinPowertrains.end() )
933  {
934  return &m_kinPowertrains[strName];
935  }
936  else
937  {
938  return NULL;
939  }
940 }
941 
942 LaePowertrain *LaeKinematics::getPowertrain(int nCtlr, int nMotor)
943 {
944  std::string strName = LaePowertrain::toKey(nCtlr, nMotor);
945 
946  if( m_kinPowertrains.find(strName) != m_kinPowertrains.end() )
947  {
948  return &m_kinPowertrains[strName];
949  }
950  else
951  {
952  return NULL;
953  }
954 }
955 
956 LaePowertrain *LaeKinematics::getPowertrain(int nMotorId)
957 {
958  std::string strName = LaePowertrain::toKey(nMotorId);
959 
960  if( m_kinPowertrains.find(strName) != m_kinPowertrains.end() )
961  {
962  return &m_kinPowertrains[strName];
963  }
964  else
965  {
966  return NULL;
967  }
968 }
969 
970 int LaeKinematics::getPowertrainState(const std::string &strName,
971  LaePowertrainState &jointState)
972 {
973  LaeMapPowertrain::iterator pos;
974 
975  if( (pos = m_kinPowertrains.find(strName)) != m_kinPowertrains.end() )
976  {
977  jointState = pos->second.m_state;
978  return LAE_OK;
979  }
980  else
981  {
982  return -LAE_ECODE_BAD_VAL;
983  }
984 }
985 
986 bool LaeKinematics::isStopped()
987 {
988  bool bIsStopped; // all motors are [not] stopped
989  LaeMapPowertrain::iterator iter; // kinematics chain iterator
990 
991  for(iter = m_kinPowertrains.begin(), bIsStopped = true;
992  (iter != m_kinPowertrains.end()) && bIsStopped;
993  ++iter)
994  {
995  if( iabs(iter->second.m_state.m_nSpeed) > SpeedReallyZero )
996  {
997  bIsStopped = false;
998  }
999  }
1000 
1001  m_bIsStopped = bIsStopped;
1002  RtDb.m_robotstatus.m_bInMotion = m_bIsStopped? false: true;
1003 
1004  return m_bIsStopped;
1005 }
1006 
1007 bool LaeKinematics::isStopped(const std::string &strName)
1008 {
1009  LaeMapPowertrain::iterator pos;
1010 
1011  if( (pos = m_kinPowertrains.find(strName)) != m_kinPowertrains.end() )
1012  {
1013  return iabs(pos->second.m_state.m_nSpeed) <= SpeedReallyZero;
1014  }
1015  else // non-existent joints don't move
1016  {
1017  return true;
1018  }
1019 }
1020 
1021 
1022 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1023 // Kinodynamic Methods
1024 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1025 
1026 int LaeKinematics::resetOdometers()
1027 {
1028  int nCtlr; // motor controller id
1029  int nMotor; // motor index
1030  LaePowertrain *pTrain; // working powertrain
1031  int n; // number of powertrain odometers actually resetted
1032 
1033  lock();
1034 
1035  n = 0;
1036 
1037  if( m_bIsEnabled )
1038  {
1039  m_pAction->terminate();
1040 
1041  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1042  {
1043  if( m_pMotorCtlr[nCtlr]->cmdResetQEncoders() == OK )
1044  {
1045  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
1046  {
1047  if( (pTrain = getPowertrain(nCtlr, nMotor)) != NULL )
1048  {
1049  pTrain->resetOdometer();
1050  ++n;
1051  }
1052  }
1053  }
1054  }
1055 
1056  m_kinPlatform.resetOdometer();
1057  }
1058 
1059  unlock();
1060 
1061  return n;
1062 }
1063 
1064 void LaeKinematics::estop()
1065 {
1066  int nCtlr; // motor controller id
1067 
1068  lock();
1069 
1070  if( m_bIsEnabled )
1071  {
1072  m_pAction->terminate();
1073 
1074  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1075  {
1076  m_pMotorCtlr[nCtlr]->cmdEStop();
1077  }
1078 
1079  disableMotorCtlrs();
1080  }
1081 
1082  unlock();
1083 }
1084 
1085 void LaeKinematics::resetEStop()
1086 {
1087  lock();
1088 
1089  enableMotorCtlrs();
1090 
1091  unlock();
1092 }
1093 
1094 int LaeKinematics::release()
1095 {
1096  int nCtlr; // motor controller id
1097  int rc; // return code
1098 
1099  lock();
1100 
1101  if( m_bIsEnabled )
1102  {
1103  m_pAction->terminate();
1104 
1105  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1106  {
1107  m_pMotorCtlr[nCtlr]->cmdDutyDrive2(0.0, 0.0);
1108  }
1109 
1110  m_bAreMotorsPowered = false;
1111  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1112 
1113  rc = LAE_OK;
1114  }
1115  else
1116  {
1117  rc = -LAE_ECODE_BAD_OP;
1118  }
1119 
1120  unlock();
1121 
1122  return rc;
1123 }
1124 
1125 int LaeKinematics::stop()
1126 {
1127  int nCtlr; // motor controller id
1128  int rc; // return code
1129 
1130  lock();
1131 
1132  if( m_bIsEnabled )
1133  {
1134  m_pAction->terminate();
1135 
1136  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1137  {
1138  //m_pMotorCtlr[nCtlr]->cmdQStop();
1139  m_pMotorCtlr[nCtlr]->cmdStopWithDecel(StopDecel);
1140  }
1141 
1142  m_bAreMotorsPowered = true;
1143  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1144 
1145  rc = LAE_OK;
1146  }
1147  else
1148  {
1149  rc = -LAE_ECODE_BAD_OP;
1150  }
1151 
1152  unlock();
1153 
1154  return rc;
1155 }
1156 
1157 int LaeKinematics::stop(const vector<string> &vecNames)
1158 {
1159  vector<string>::const_iterator iter; // joint name iterator
1160 
1161  LaeMapPowertrain::iterator pos; // kinematics chain position
1162  int n; // number of actual joints stopped
1163  int nCtlr; // motor controller id
1164  int nMotor; // motor index
1165  int rc; // return code
1166 
1167  lock();
1168 
1169  n = 0;
1170 
1171  if( m_bIsEnabled )
1172  {
1173  m_pAction->terminate();
1174 
1175  for(iter = vecNames.begin(); iter != vecNames.end(); ++iter)
1176  {
1177  if( (pos = m_kinPowertrains.find(*iter)) != m_kinPowertrains.end() )
1178  {
1179  nCtlr = pos->second.getMotorCtlrId();
1180  nMotor = pos->second.getMotorIndex();
1181 
1182  //if( m_pMotorCtlr[nCtlr]->cmdStop(nMotor) == OK )
1183  if( m_pMotorCtlr[nCtlr]->cmdStopWithDecel(nMotor, StopDecel) == OK )
1184  {
1185  ++n;
1186  }
1187  }
1188  }
1189 
1190  m_bAreMotorsPowered = true;
1191  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1192 
1193  rc = LAE_OK;
1194  }
1195  else
1196  {
1197  rc = -LAE_ECODE_BAD_OP;
1198  }
1199 
1200  unlock();
1201 
1202  return n;
1203 }
1204 
1205 int LaeKinematics::stop(const string &strName)
1206 {
1207  LaeMapPowertrain::iterator pos; // kinematics chain position
1208  int nCtlr; // motor controller id
1209  int nMotor; // motor index
1210  int rc; // return code
1211 
1212  lock();
1213 
1214  if( m_bIsEnabled )
1215  {
1216  m_pAction->terminate();
1217 
1218  if( (pos = m_kinPowertrains.find(strName)) != m_kinPowertrains.end() )
1219  {
1220  nCtlr = pos->second.getMotorCtlrId();
1221  nMotor = pos->second.getMotorIndex();
1222 
1223  //rc = m_pMotorCtlr[nCtlr]->cmdStop(nMotor);
1224  rc = m_pMotorCtlr[nCtlr]->cmdStopWithDecel(nMotor, StopDecel);
1225 
1226  if( rc == OK )
1227  {
1228  m_bAreMotorsPowered = true;
1229  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1230  rc = LAE_OK;
1231  }
1232  else
1233  {
1234  rc = -LAE_ECODE_MOT_CTLR;
1235  }
1236  }
1237  else
1238  {
1239  LOGWARN("Powertrain %s: Not found.", strName.c_str());
1240  rc = -LAE_ECODE_BAD_VAL;
1241  }
1242  }
1243  else
1244  {
1245  rc = -LAE_ECODE_BAD_OP;
1246  }
1247 
1248  unlock();
1249 
1250  return rc;
1251 }
1252 
1253 int LaeKinematics::waitForAllStop(double fSeconds)
1254 {
1255  static const useconds_t WaitDt = 100; // usec wait between tests
1256 
1257  int nMaxTries = (int)ceil(fSeconds * MILLION / (double)WaitDt);
1258 
1259  for(int i = 0; i < nMaxTries; ++i)
1260  {
1261  if( isStopped() )
1262  {
1263  return LAE_OK;
1264  }
1265  usleep(WaitDt);
1266  }
1267 
1268  LOGERROR("Timed out waiting for %.2lf seconds for all joints to stop.",
1269  fSeconds);
1270 
1271  return -LAE_ECODE_TIMEDOUT;
1272 }
1273 
1274 int LaeKinematics::setGoalVelocities(const LaeMapVelocity &velocity)
1275 {
1276  LaeKinActionVelocity *pAction; // velocity action pointer
1277  int rc; // return code
1278 
1279  lock();
1280 
1281  if( m_bIsEnabled )
1282  {
1283  //
1284  // Current action is not a velocity action. Terminate the action and
1285  // create an this new action.
1286  //
1287  if( m_pAction->getActionType() != LaeKinAction::ActionTypeVelocity )
1288  {
1289  m_pAction = LaeKinAction::replaceAction(*this, m_pAction,
1290  LaeKinAction::ActionTypeVelocity);
1291  }
1292 
1293  pAction = (LaeKinActionVelocity *)m_pAction;
1294 
1295  // update with new goals
1296  rc = pAction->update(velocity);
1297 
1298  if( pAction->isPlanningRequired() )
1299  {
1300  rc = pAction->plan();
1301  }
1302 
1303  if( pAction->isExecutionRequired() )
1304  {
1305  if( (rc = pAction->execute()) == LAE_OK )
1306  {
1307  m_bAreMotorsPowered = true;
1308  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1309  }
1310  }
1311  }
1312  else
1313  {
1314  rc = -LAE_ECODE_BAD_OP;
1315  }
1316 
1317  unlock();
1318 
1319  return rc;
1320 }
1321 
1322 int LaeKinematics::setGoalDutyCycles(const LaeMapDutyCycle &duty)
1323 {
1324  LaeKinActionDutyCycle *pAction; // duty cycle action pointer
1325  int rc; // return code
1326 
1327  lock();
1328 
1329  if( m_bIsEnabled )
1330  {
1331  //
1332  // Current action is not a velocity action. Terminate the action and
1333  // create an this new action.
1334  //
1335  if( m_pAction->getActionType() != LaeKinAction::ActionTypeDutyCycle )
1336  {
1337  m_pAction = LaeKinAction::replaceAction(*this, m_pAction,
1338  LaeKinAction::ActionTypeDutyCycle);
1339  }
1340 
1341  pAction = (LaeKinActionDutyCycle *)m_pAction;
1342 
1343  // update with new goals
1344  rc = pAction->update(duty);
1345 
1346  if( pAction->isPlanningRequired() )
1347  {
1348  rc = pAction->plan();
1349  }
1350 
1351  if( pAction->isExecutionRequired() )
1352  {
1353  if( (rc = pAction->execute()) == LAE_OK )
1354  {
1355  m_bAreMotorsPowered = true;
1356  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1357  }
1358  }
1359  }
1360  else
1361  {
1362  rc = -LAE_ECODE_BAD_OP;
1363  }
1364 
1365  unlock();
1366 
1367  return rc;
1368 }
1369 
1370 int LaeKinematics::setGoalTwist(double fVelLinear, double fVelAngular)
1371 {
1372  LaeKinActionTwist *pAction; // velocity action pointer
1373  int rc; // return code
1374 
1375  lock();
1376 
1377  if( m_bIsEnabled )
1378  {
1379  //
1380  // Current action is not a velocity action. Terminate the action and
1381  // create an this new action.
1382  //
1383  if( m_pAction->getActionType() != LaeKinAction::ActionTypeTwist )
1384  {
1385  m_pAction = LaeKinAction::replaceAction(*this, m_pAction,
1386  LaeKinAction::ActionTypeTwist);
1387  }
1388 
1389  pAction = (LaeKinActionTwist *)m_pAction;
1390 
1391  // update with new goals
1392  rc = pAction->update(fVelLinear, fVelAngular);
1393 
1394  if( pAction->isPlanningRequired() )
1395  {
1396  rc = pAction->plan();
1397  }
1398 
1399  if( pAction->isExecutionRequired() )
1400  {
1401  if( (rc = pAction->execute()) == LAE_OK )
1402  {
1403  m_bAreMotorsPowered = true;
1404  RtDb.m_robotstatus.m_bAreMotorsPowered = m_bAreMotorsPowered;
1405  }
1406  }
1407  }
1408  else
1409  {
1410  rc = -LAE_ECODE_BAD_OP;
1411  }
1412 
1413  unlock();
1414 
1415  return rc;
1416 }
1417 
1418 
1419 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1420 // Kinodynamic Thread Operations
1421 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1422 
1423 int LaeKinematics::sense()
1424 {
1425  int rc;
1426 
1427  // sense real-time kinodynamics
1428  rc = senseDynamics();
1429 
1430  // add other sensor reads here (e.g. imu, gps, etc).
1431 
1432  return rc;
1433 }
1434 
1435 int LaeKinematics::senseDynamics()
1436 {
1437  int nCtlr; // motor controller id
1438  int nMotor; // motor index
1439  double amp[LaeNumMotorsPerCtlr]; // motor ampere draws
1440  uint_t buflen[LaeNumMotorsPerCtlr]; // command buffer lengths
1441  s64_t encoder; // motor encoder (quad pulses)
1442  s32_t speed; // motor speed (qpps)
1443  bool bIsStopped; // all motors are [not] stopped
1444  LaePowertrain *pTrain; // working powertrain
1445 
1446  if( !m_bIsEnabled )
1447  {
1448  return LAE_OK;
1449  }
1450 
1451  bIsStopped = true;
1452 
1453  //
1454  // Sense real-time dynamics.
1455  //
1456  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1457  {
1458  //
1459  // Board level sensing.
1460  //
1461  m_pMotorCtlr[nCtlr]->cmdReadMotorCurrentDraw(amp[0], amp[1]);
1462  m_pMotorCtlr[nCtlr]->cmdReadCmdBufLengths(buflen[0], buflen[1]);
1463 
1464  //
1465  // Motor level sensing.
1466  //
1467  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
1468  {
1469  m_pMotorCtlr[nCtlr]->cmdReadQEncoder(nMotor, encoder);
1470  m_pMotorCtlr[nCtlr]->cmdReadQSpeed(nMotor, speed);
1471 
1472  if( (pTrain = getPowertrain(nCtlr, nMotor)) == NULL )
1473  {
1474  continue;
1475  }
1476 
1477  pTrain->updateStateDynamics(encoder, speed, amp[nMotor], buflen[nMotor]);
1478 
1479  if( iabs(speed) > SpeedReallyZero )
1480  {
1481  bIsStopped = false;
1482  }
1483 
1484  RtDb.m_motorctlr[nCtlr].m_fMotorCurrent[nMotor] = amp[nMotor];
1485  }
1486  }
1487 
1488  m_kinPlatform.updateStateDynamics(m_kinPowertrains);
1489 
1490  m_bIsStopped = bIsStopped;
1491  RtDb.m_robotstatus.m_bInMotion = m_bIsStopped? false: true;
1492 
1493  return LAE_OK;
1494 }
1495 
1496 int LaeKinematics::react()
1497 {
1498  return LAE_OK; // RDK TODO
1499 }
1500 
1501 int LaeKinematics::plan()
1502 {
1503  int rc;
1504 
1505  // motors are not enabled
1506  if( !m_bIsEnabled )
1507  {
1508  return LAE_OK;
1509  }
1510 
1511  // planning requried, so plan
1512  else if( m_pAction->isPlanningRequired() )
1513  {
1514  rc = m_pAction->plan();
1515  }
1516 
1517  // nothing to plan
1518  else
1519  {
1520  rc = LAE_OK;
1521  }
1522 
1523  return rc;
1524 }
1525 
1526 int LaeKinematics::act()
1527 {
1528  int rc;
1529 
1530  // motors are not enabled
1531  if( !m_bIsEnabled )
1532  {
1533  rc = LAE_OK;
1534  }
1535 
1536  // action requried, so execute
1537  else if( m_pAction->isExecutionRequired() )
1538  {
1539  rc = m_pAction->execute();
1540  }
1541 
1542  // nothing to do
1543  else
1544  {
1545  rc = LAE_OK;
1546  }
1547 
1548  return rc;
1549 }
1550 
1551 int LaeKinematics::monitorHealth()
1552 {
1553  int nCtlr; // motor controller id
1554  int nMotor; // motor index
1555  double volts; // main battery voltage
1556  double temp; // board temperature
1557  uint_t status; // board status
1558  LaePowertrain *pTrain; // pointer to joint
1559 
1560  if( !m_bIsEnabled )
1561  {
1562  return LAE_OK;
1563  }
1564 
1565  lock();
1566 
1567  //
1568  // Read health data.
1569  //
1570  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1571  {
1572  m_pMotorCtlr[nCtlr]->cmdReadMainBattVoltage(volts);
1573  m_pMotorCtlr[nCtlr]->cmdReadBoardTemperature(temp);
1574  m_pMotorCtlr[nCtlr]->cmdReadStatus(status);
1575 
1576  RtDb.m_motorctlr[nCtlr].m_uStatus = status;
1577  RtDb.m_motorctlr[nCtlr].m_fTemperature = temp;
1578  RtDb.m_motorctlr[nCtlr].m_fBatteryVoltage = volts;
1579 
1580  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
1581  {
1582  if( (pTrain = getPowertrain(nCtlr, nMotor)) != NULL )
1583  {
1584  pTrain->updateHealth(volts, temp, status);
1585  }
1586  }
1587 
1588  m_kinPlatform.updateCtlrHealth(nCtlr, volts, temp, status);
1589  }
1590 
1591  m_kinPlatform.updateHealth(m_kinPowertrains);
1592 
1593  LOGDIAG4("Kinematics Thread: Monitored health of motor controllers.");
1594 
1595  unlock();
1596 
1597  return LAE_OK;
1598 }
1599 
1600 void LaeKinematics::exec()
1601 {
1602  lock();
1603 
1604  //
1605  // Auto-Repair
1606  //
1607  // The motor controllers should be enabled, but the watchdog thread has
1608  // detected that they are not. This should not happen, so re-enable.
1609  //
1610  if( m_bIsEnabled && !RtDb.m_enable.m_bMotorCtlr )
1611  {
1612  LOGERROR("Motor controllers have been unexpectedly disaabled. Re-enable.");
1613  unlock();
1614  resyncComm();
1615  lock();
1616  }
1617 
1618  // sense environment and dynamic state
1619  sense();
1620 
1621  // react to immediate stimulii
1622  react();
1623 
1624  // plan motions
1625  plan();
1626 
1627  // act on planned motions
1628  act();
1629 
1630  unlock();
1631 }
1632 
1633 
1634 // ---------------------------------------------------------------------------
1635 // LaeKinAction
1636 // ---------------------------------------------------------------------------
1637 
1638 LaeKinAction *LaeKinAction::newAction(LaeKinematics &kin,
1639  const ActionType eActionType)
1640 {
1641  switch( eActionType )
1642  {
1643  case ActionTypeVelocity:
1644  return new LaeKinActionVelocity(kin);
1645  case ActionTypeDutyCycle:
1646  return new LaeKinActionDutyCycle(kin);
1647  case ActionTypeTwist:
1648  return new LaeKinActionTwist(kin);
1649  case ActionTypeNavForDist: // future
1650  case ActionTypeNavToPos: // future
1651  return new LaeKinAction(kin);
1652  case ActionTypeIdle:
1653  return new LaeKinAction(kin);
1654  default:
1655  return new LaeKinAction(kin);
1656  }
1657 }
1658 
1659 LaeKinAction *LaeKinAction::replaceAction(LaeKinematics &kin,
1660  LaeKinAction *pAction,
1661  const ActionType eNewActionType)
1662 {
1663  if( pAction != NULL )
1664  {
1665  pAction->terminate();
1666  delete pAction;
1667  }
1668 
1669  return newAction(kin, eNewActionType);
1670 }
1671 
1672 
1673 // ---------------------------------------------------------------------------
1674 // LaeKinActionVelocity
1675 // ---------------------------------------------------------------------------
1676 
1677 LaeKinActionVelocity::LaeKinActionVelocity(LaeKinematics &kin)
1678  : LaeKinAction(kin, ActionTypeVelocity)
1679 {
1680  clear();
1681 }
1682 
1684 {
1685  LaeMapVelocity::const_iterator iter;
1686  LaeMapVelocity::iterator pos;
1687 
1688  // set state to update
1690 
1691  //
1692  // Iterate through key,velocity map.
1693  //
1694  for(iter = velocity.begin(); iter != velocity.end(); ++iter)
1695  {
1696  pos = m_mapGoalVel.find(iter->first);
1697 
1698  // velocity on a new action powertrain
1699  if( pos == m_mapGoalVel.end() )
1700  {
1701  m_mapGoalVel[iter->first] = iter->second;
1703  }
1704 
1705  // different velocity on an existing action powertrain
1706  else if( pos->second != iter->second )
1707  {
1708  pos->second = iter->second;
1710  }
1711 
1712  // else same velocity
1713 
1714  LOGDIAG3("Wheel %12s velocity = %lf rads/s.",
1715  iter->first.c_str(), iter->second);
1716  }
1717 
1718  // updated, but no planning and execution required
1720  {
1722  }
1723 
1724  return LAE_OK;
1725 }
1726 
1728 {
1729  int nCtlr; // motor controller id
1730  int nMotor; // motor index
1731  LaePowertrain *pTrain; // working powertrain
1732  LaeMapVelocity::iterator goal; // working goal
1733  int nSpeed; // motor speed (qpps)
1734 
1735  if( !isPlanningRequired() )
1736  {
1737  return LAE_OK;
1738  }
1739 
1741 
1742  //
1743  // Iterate through motors and convert velocities to speeds.
1744  //
1745  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1746  {
1747  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
1748  {
1749  if( (pTrain = m_kin.getPowertrain(nCtlr, nMotor)) == NULL )
1750  {
1751  continue;
1752  }
1753 
1754  goal = m_mapGoalVel.find(pTrain->getName());
1755 
1756  // converted goal velocity speed is target speed
1757  if( goal != m_mapGoalVel.end() )
1758  {
1759  nSpeed = goal->second / pTrain->m_attr.m_fWheelRadsPerPulse;
1760  }
1761  // current speed is target speed
1762  else
1763  {
1764  nSpeed = pTrain->m_state.m_nSpeed;
1765  }
1766 
1767  //
1768  // New speed
1769  //
1770  if( m_speed[nCtlr][nMotor] != nSpeed )
1771  {
1772  m_speed[nCtlr][nMotor] = nSpeed;
1774  }
1775  }
1776  }
1777 
1778  // planned, but no execution required
1780  {
1782  }
1783 }
1784 
1786 {
1787  int nCtlr;
1788  RoboClaw *pMotorCtlr;
1789  int rc = LAE_OK;
1790 
1791  // no execution required
1792  if( !isExecutionRequired() )
1793  {
1794  return LAE_OK;
1795  }
1796 
1798 
1799  // set motor speeds
1800  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1801  {
1802  pMotorCtlr = m_kin.getMotorCtlr(nCtlr);
1803 
1804  //rc = pMotorCtlr->cmdQDrive2(m_speed[nCtlr][LaeMotorLeft],
1805  // m_speed[nCtlr][LaeMotorRight]);
1806 
1807  rc = pMotorCtlr->cmdQDriveWithAccel(
1808  m_speed[nCtlr][LaeMotorLeft], MoveAccel,
1809  m_speed[nCtlr][LaeMotorRight], MoveAccel);
1810 
1811  if( rc != OK )
1812  {
1813  rc = -LAE_ECODE_MOT_CTLR;
1814  break;
1815  }
1816  }
1817 
1819 
1820  return rc;
1821 }
1822 
1824 {
1825  LaeMapVelocity::iterator iter;
1826  int nCtlr;
1827  RoboClaw *pMotorCtlr;
1828 
1829  // already terminated
1830  if( hasTerminated() )
1831  {
1832  return LAE_OK;
1833  }
1834 
1835  //
1836  // Stop all motors.
1837  //
1838  // Note: Do not call m_kin.stop() here, since it calls this function.
1839  //
1840  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1841  {
1842  pMotorCtlr = m_kin.getMotorCtlr(nCtlr);
1843  //pMotorCtlr->cmdQStop();
1844  pMotorCtlr->cmdStopWithDecel(StopDecel);
1845  }
1846 
1847  clear();
1848 
1850 
1851  return LAE_OK;
1852 }
1853 
1855 {
1856  m_mapGoalVel.clear();
1857 
1858  for(int nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1859  {
1860  for(int nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
1861  {
1862  m_speed[nCtlr][nMotor] = 0;
1863  }
1864  }
1865 }
1866 
1867 
1868 // ---------------------------------------------------------------------------
1869 // LaeKinActionDutyCycle
1870 // ---------------------------------------------------------------------------
1871 
1874 {
1875 }
1876 
1878 {
1879  LaeMapDutyCycle::const_iterator iter;
1880  LaeMapDutyCycle::iterator pos;
1881 
1882  // set state to update
1884 
1885  //
1886  // Iterate through key,duty map.
1887  //
1888  for(iter = duty.begin(); iter != duty.end(); ++iter)
1889  {
1890  pos = m_mapGoalDutyCycle.find(iter->first);
1891 
1892  // duty on a new action powertrain
1893  if( pos == m_mapGoalDutyCycle.end() )
1894  {
1895  m_mapGoalDutyCycle[iter->first] = iter->second;
1897  }
1898 
1899  // different duty on an existing action powertrain
1900  else if( pos->second != iter->second )
1901  {
1902  pos->second = iter->second;
1904  }
1905 
1906  // else same duty
1907 
1908  LOGDIAG3("Motor %12s duty = %5.3lf.", iter->first.c_str(), iter->second);
1909  }
1910 
1911  // no planning and execution required
1913  {
1915  }
1916 
1917  return LAE_OK;
1918 }
1919 
1921 {
1922  int nCtlr; // motor controller id
1923  int nMotor; // motor index
1924  LaePowertrain *pTrain; // working powertrain
1925  LaeMapDutyCycle::iterator goal; // working goal
1926  double fDuty[LaeNumMotorsPerCtlr]; // working duty cycles
1927  bool bDoExec; // do [not] exec
1928  RoboClaw *pMotorCtlr; // motor controller
1929  int rc; // return code
1930 
1931  // no execution required
1932  if( !isExecutionRequired() )
1933  {
1934  return LAE_OK;
1935  }
1936 
1938 
1939  //
1940  // Iterate through motors and executre new duty cycle commands.
1941  //
1942  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
1943  {
1944  bDoExec = false;
1945 
1946  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
1947  {
1948  if( (pTrain = m_kin.getPowertrain(nCtlr, nMotor)) == NULL )
1949  {
1950  continue;
1951  }
1952 
1953  goal = m_mapGoalDutyCycle.find(pTrain->getName());
1954 
1955  if( goal != m_mapGoalDutyCycle.end() )
1956  {
1957  fDuty[nMotor] = goal->second;
1958  bDoExec = true;
1959  }
1960  else
1961  {
1962  fDuty[nMotor] = 0.0;
1963  }
1964  }
1965 
1966  if( bDoExec )
1967  {
1968  pMotorCtlr = m_kin.getMotorCtlr(nCtlr);
1969 
1970  rc = pMotorCtlr->cmdDutyDrive2(fDuty[0], fDuty[1]);
1971 
1972  if( rc != OK )
1973  {
1974  rc = -LAE_ECODE_MOT_CTLR;
1975  break;
1976  }
1977  }
1978  }
1979 
1981 
1982  return rc;
1983 }
1984 
1986 {
1987  LaeMapDutyCycle::iterator iter;
1988  int nCtlr;
1989  RoboClaw *pMotorCtlr;
1990 
1991  // already terminated
1992  if( hasTerminated() )
1993  {
1994  return LAE_OK;
1995  }
1996 
1997  //
1998  // Coast all motors.
1999  //
2000  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2001  {
2002  pMotorCtlr = m_kin.getMotorCtlr(nCtlr);
2003  pMotorCtlr->cmdDutyDrive2(0.0, 0.0);
2004  }
2005 
2006  clear();
2007 
2009 
2010  return LAE_OK;
2011 }
2012 
2014 {
2015  m_mapGoalDutyCycle.clear();
2016 }
2017 
2018 
2019 // ---------------------------------------------------------------------------
2020 // LaeKinActionVelocity
2021 // ---------------------------------------------------------------------------
2022 
2025 {
2026  clear();
2027 }
2028 
2029 int LaeKinActionTwist::update(double fVelLinear, double fVelAngular)
2030 {
2031  // set state to update
2033 
2034  if( (fVelLinear != m_fGoalVelLinear) || (fVelAngular != m_fGoalVelAngular) )
2035  {
2036  m_fGoalVelLinear = fVelLinear;
2037  m_fGoalVelAngular = fVelAngular;
2039 
2040  LOGDIAG3("Robot linear velocity = %lf m/s, angular velocity = %lf deg/s.",
2042  }
2043 
2044  // updated, but no planning and execution required
2045  else
2046  {
2048  }
2049 
2050  return LAE_OK;
2051 }
2052 
2054 {
2055  int nCtlr; // motor controller id
2056  int nMotor; // motor index
2057  LaePowertrain *pTrain; // working powertrain
2058  int nSpeed; // motor speed (qpps)
2059 
2060  if( !isPlanningRequired() )
2061  {
2062  return LAE_OK;
2063  }
2064 
2066 
2067  double halftrack = m_kin.getPlatform().m_fWheeltrack / 2.0; // radius
2068  double velAngLin = m_fGoalVelAngular * halftrack; // anglur to linear
2069  double ppm; // quad pulses/meter
2070  double ppsLin; // quad pulses/second for linear component
2071  double ppsAng; // quad pulses/second for angular component
2072  double ppsAbsMax = 0.0; // absolute max qqps
2073  double ppsRatedMinMax = 10e10; // lowest max rated powertrain qpps
2074  double pps[LaeNumMotorCtlrs][LaeNumMotorsPerCtlr]; // goal qpps
2075 
2076  //
2077  // Iterate through motors and convert twist to speeds.
2078  //
2079  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2080  {
2081  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
2082  {
2083  if( (pTrain = m_kin.getPowertrain(nCtlr, nMotor)) == NULL )
2084  {
2085  continue;
2086  }
2087 
2088  ppm = 1.0 / pTrain->m_attr.m_fMetersPerPulse;
2089 
2090  ppsLin = m_fGoalVelLinear * ppm;
2091  ppsAng = velAngLin * ppm;
2092 
2093  if( nMotor == LaeMotorLeft )
2094  {
2095  pps[nCtlr][nMotor] = ppsLin - ppsAng;
2096  }
2097  else
2098  {
2099  pps[nCtlr][nMotor] = ppsLin + ppsAng;
2100  }
2101 
2102  // powetrain with the highest absolute speed
2103  if( fabs(pps[nCtlr][nMotor]) > ppsAbsMax )
2104  {
2105  ppsAbsMax = fabs(pps[nCtlr][nMotor]);
2106  }
2107 
2108  // powertrain with the lowest maximum speed
2109  if( (double)pTrain->m_attr.m_uMaxQpps < ppsRatedMinMax )
2110  {
2111  ppsRatedMinMax = (double)pTrain->m_attr.m_uMaxQpps;
2112  }
2113  }
2114  }
2115 
2116  //
2117  // Derate speed to work within the specs of the lowest motor capabilites.
2118  //
2119  if( ppsAbsMax > ppsRatedMinMax )
2120  {
2121  double derate = ppsRatedMinMax / ppsAbsMax;
2122 
2123  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2124  {
2125  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
2126  {
2127  pps[nCtlr][nMotor] *= derate;
2128  }
2129  }
2130  }
2131 
2132  //
2133  // Now set the target motor speeds.
2134  //
2135  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2136  {
2137  for(nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
2138  {
2139  nSpeed = (int)pps[nCtlr][nMotor];
2140 
2141  //
2142  // New speed
2143  //
2144  if( m_speed[nCtlr][nMotor] != nSpeed )
2145  {
2146  m_speed[nCtlr][nMotor] = nSpeed;
2148  }
2149  }
2150  }
2151 
2152  // planned, but no execution required
2154  {
2156  }
2157 }
2158 
2160 {
2161  int nCtlr;
2162  RoboClaw *pMotorCtlr;
2163  int rc = LAE_OK;
2164 
2165  // no execution required
2166  if( !isExecutionRequired() )
2167  {
2168  return LAE_OK;
2169  }
2170 
2172 
2173  // set motor speeds
2174  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2175  {
2176  pMotorCtlr = m_kin.getMotorCtlr(nCtlr);
2177 
2178  //rc = pMotorCtlr->cmdQDrive2(m_speed[nCtlr][LaeMotorLeft],
2179  // m_speed[nCtlr][LaeMotorRight]);
2180 
2181  rc = pMotorCtlr->cmdQDriveWithAccel(
2182  m_speed[nCtlr][LaeMotorLeft], MoveAccel,
2183  m_speed[nCtlr][LaeMotorRight], MoveAccel);
2184 
2185  if( rc != OK )
2186  {
2187  rc = -LAE_ECODE_MOT_CTLR;
2188  break;
2189  }
2190  }
2191 
2193 
2194  return rc;
2195 }
2196 
2198 {
2199  LaeMapVelocity::iterator iter;
2200  int nCtlr;
2201  RoboClaw *pMotorCtlr;
2202 
2203  // already terminated
2204  if( hasTerminated() )
2205  {
2206  return LAE_OK;
2207  }
2208 
2209  //
2210  // Stop all motors.
2211  //
2212  // Note: Do not call m_kin.stop() here, since it calls this function.
2213  //
2214  for(nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2215  {
2216  pMotorCtlr = m_kin.getMotorCtlr(nCtlr);
2217  pMotorCtlr->cmdStopWithDecel(StopDecel);
2218  }
2219 
2220  clear();
2221 
2223 
2224  return LAE_OK;
2225 }
2226 
2228 {
2229  m_fGoalVelLinear = 0.0;
2230  m_fGoalVelAngular = 0.0;
2231 
2232  for(int nCtlr = 0; nCtlr < LaeNumMotorCtlrs; ++nCtlr)
2233  {
2234  for(int nMotor = 0; nMotor < LaeNumMotorsPerCtlr; ++nMotor)
2235  {
2236  m_speed[nCtlr][nMotor] = 0;
2237  }
2238  }
2239 }
double m_fWheeltrack
wheeltrack (m)
Definition: laePlatform.h:146
Laelaps kinematics base action class.
Definition: laeKin.h:774
std::map< std::string, double > LaeMapDutyCycle
Duty cycle trajectory type.
Definition: laeTraj.h:269
LaeKinActionTwist(LaeKinematics &kin)
Default initialization constructor.
Definition: laeKin.cxx:2023
RoboClaw 2 motor controller class.
Definition: RoboClaw.h:808
int m_nMotorCtlrId
motor controller id
Definition: laePowertrain.h:81
Laelaps tuning data class.
Definition: laeTune.h:566
Trajectory classes interfaces.
LaeDescBase * m_pDescBase
base description
Definition: laeDesc.h:523
Laelaps robotic mobile platform full description class.
Definition: laeDesc.h:451
int update(const LaeMapDutyCycle &dutycycle)
Update with new velocities.
Definition: laeKin.cxx:1877
LaePowertrain * getPowertrain(const std::string &strName)
Get pointer to powertrain by name (key).
virtual int terminate()
Terminate action.
Definition: laeKin.cxx:1985
virtual int updateStateDynamics(s64_t nEncoder, s32_t nSpeed, double fAmps, uint_t uBufLen)
Update state dynamics.
ActionType
Supported kinematic extended actions.
Definition: laeKin.h:780
virtual int execute()
Execution new velocities.
Definition: laeKin.cxx:1785
void getVelPidKParams(const std::string &strName, double &fKp, double &fKi, double &fKd) const
Get motor velocity PID K tune parameters.
Definition: laeTune.cxx:356
Laelaps kinematics class.
Definition: laeKin.h:119
std::string m_strName
powertrain unique name (key)
LaeKinematics & m_kin
bound kinematics driver
Definition: laeKin.h:948
s32_t m_speed[LaeNumMotorCtlrs][LaeNumMotorsPerCtlr]
target motor speeds (qpps)
Definition: laeKin.h:1021
int m_nMotorIndex
motor controller unique motor index
Definition: laePowertrain.h:82
move robot by twist values
Definition: laeKin.h:785
RoboClaw motor controller class interface.
virtual bool hasTerminated() const
Test if action has been terminated.
Definition: laeKin.h:922
virtual int updateHealth(double fVolts, double fTemp, uint_t uCtlrStatus)
Update motor health state.
virtual bool isExecutionRequired() const
Test if more action requires (more) execution.
Definition: laeKin.h:909
virtual int configure(const LaeDescPowertrain &desc)
Configure powertrain from product description.
double m_fGoalVelLinear
goal platform linear velocity
Definition: laeKin.h:1163
Laelaps kinematics duty cycle action class.
Definition: laeKin.h:1042
void clear()
Clear goal.
Definition: laeKin.cxx:2013
virtual bool isPlanningRequired()
Test if action requires (re)planning.
Definition: laeKin.h:899
virtual int cmdDutyDrive2(double duty1, double duty2)
Drive both motors at the given duty cycle.
Definition: RoboClaw.cxx:1297
motor::roboclaw::RoboClaw * getMotorCtlr(const int nMotorCtlrId)
Get pointer to motor controller by name (key).
Definition: laeKin.cxx:918
static const int LaeMotorLeft
left motors
Definition: laeMotor.h:128
void clear()
Clear goal.
Definition: laeKin.cxx:2227
double radToDeg(double r)
Convert radians to degrees.
Definition: laeUtils.h:136
ActionState m_eActionState
action state.
Definition: laeKin.h:950
virtual int cmdQDriveWithAccel(int motor, s32_t speed, u32_t accel)
Drive a motor at the given speed and with the given acceleration.
Definition: RoboClaw.cxx:1399
static const int LAE_ECODE_MOT_CTLR
motor controller error
Definition: laelaps.h:97
LaePowertrainAttr m_attr
semi-fixed attribute data
void clear()
Clear goal.
Definition: laeKin.cxx:1854
MapDescPowertrain m_mapDescPowertrain
powertrain descriptions
Definition: laeDesc.h:525
int update(const LaeMapVelocity &velocity)
Update with new velocities.
Definition: laeKin.cxx:1683
The <b><i>Laelaps</i></b> namespace encapsulates all <b><i>Laelaps</i></b> related constructs...
Definition: laeAlarms.h:64
s64_t iabs(s64_t a)
Integer absolute value.
Definition: RoboClaw.cxx:176
uint_t m_uMaxQpps
maximum quadrature pulses/second rps
Definition: laePowertrain.h:90
std::string getName() const
Get this powertrain unique name (key).
Laelaps common utilities.
Laelaps kinematics velocity action class.
Definition: laeKin.h:1108
The Laelaps kinematics and dynamics class interface.
Laelaps powertrain class interfaces.
virtual int execute()
Execution [sub]action.
Definition: laeKin.h:866
Laelaps tuning.
virtual int resetOdometer()
Reset odometry to zero.
Laelaps system devices.
virtual int execute()
Execution new velocities.
Definition: laeKin.cxx:1920
Laelaps robotic platform control and dynamics state interface.
int update(double fVelLinear, double fVelAngular)
Update with new velocities.
Definition: laeKin.cxx:2029
move by motor duty cycles
Definition: laeKin.h:784
double m_fMetersPerPulse
tire meters per encoder pulse
Definition: laePowertrain.h:99
double m_fGoalVelAngular
goal platform angular velocity
Definition: laeKin.h:1164
LaePlatform & getPlatform()
Get robot platform kinodynamics.
Definition: laeKin.h:226
Laelaps motors, encoder, and controllers hardware abstraction interfaces.
virtual int terminate()
Terminate action.
Definition: laeKin.cxx:2197
virtual int cmdStopWithDecel(int motor, u32_t decel)
Stop a motor with the given maximum decelerations.
Definition: RoboClaw.cxx:1711
static const int LaeNumMotorCtlrs
number of motor controllers
Definition: laeMotor.h:115
double m_fWheelRadsPerPulse
output shaft radians per encoder pulse
Definition: laePowertrain.h:92
virtual int terminate()
Terminate action.
Definition: laeKin.h:878
virtual int execute()
Execution new velocities.
Definition: laeKin.cxx:2159
#define LAE_VERSION(major, minor, revision)
Convert version triplet to integer equivalent.
Definition: laelaps.h:158
LaeMapVelocity m_mapGoalVel
map of goal velocities
Definition: laeKin.h:1020
LaeKinActionDutyCycle(LaeKinematics &kin)
Default initialization constructor.
Definition: laeKin.cxx:1872
LaeMapDutyCycle m_mapGoalDutyCycle
map of goal duty cycles
Definition: laeKin.h:1087
Powertrain state data class.
LaePowertrainState m_state
dynamic state data
static const int LaeMotorRight
right motors
Definition: laeMotor.h:129
virtual int plan()
Plan next execution.
Definition: laeKin.cxx:1727
std::map< std::string, double > LaeMapVelocity
Velocity trajectory type.
Definition: laeTraj.h:258
Powertrain data class.
Laelaps real-time "database".
Laelaps kinematics velocity action class.
Definition: laeKin.h:966
virtual int terminate()
Terminate action.
Definition: laeKin.cxx:1823
s32_t m_speed[LaeNumMotorCtlrs][LaeNumMotorsPerCtlr]
target motor speeds (qpps)
Definition: laeKin.h:1165
virtual int plan()
Plan next execution.
Definition: laeKin.h:854
update action specific data
Definition: laeKin.h:796
static const int LaeNumMotorsPerCtlr
number of motors/controller
Definition: laeMotor.h:130
virtual int plan()
Plan next execution.
Definition: laeKin.cxx:2053
int m_nSpeed
raw speed (qpps)
std::string getRealDeviceName(const std::string &strDevName)
Get real device name.
Top-level package include file.
static const int LAE_OK
not an error, success
Definition: laelaps.h:71