Dynamixel  2.9.5
RoadNarrows Robotics Dynamixel Package
DynaBgThread.cxx
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: Dynamixel
4 //
5 // Library: libDynamixel
6 //
7 // File: DynaBgThread.cxx
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2015-07-08 12:19:29 -0600 (Wed, 08 Jul 2015) $
12  * $Rev: 4024 $
13  *
14  * \brief Dynamixel background thread class.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \copyright
19  * \h_copy 2011-2018. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 /*
24  * @EulaBegin@
25  *
26  * Unless otherwise stated explicitly, all materials contained are copyrighted
27  * and may not be used without RoadNarrows LLC's written consent,
28  * except as provided in these terms and conditions or in the copyright
29  * notice (documents and software) or other proprietary notice provided with
30  * the relevant materials.
31  *
32  * IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY
33  * MEMBERS/EMPLOYEES/CONTRACTORS OF ROADNARROWS OR DISTRIBUTORS OF THIS SOFTWARE
34  * BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
35  * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS
36  * DOCUMENTATION, EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN
37  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38  *
39  * THE AUTHORS AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
40  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
41  * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
42  * "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
43  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
44  *
45  * @EulaEnd@
46  */
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <sys/time.h>
50 #include <time.h>
51 #include <limits.h>
52 #include <stdio.h>
53 #include <pthread.h>
54 #include <unistd.h>
55 #include <iostream>
56 #include <map>
57 
58 #include "rnr/rnrconfig.h"
59 #include "rnr/log.h"
60 #include "rnr/new.h"
61 #include "rnr/units.h"
62 
63 #include "Dynamixel/Dynamixel.h"
64 #include "Dynamixel/MX.h"
65 
66 #include "Dynamixel/DynaError.h"
67 #include "Dynamixel/DynaComm.h"
68 #include "Dynamixel/DynaServo.h"
69 #include "Dynamixel/DynaChain.h"
70 #include "Dynamixel/DynaBgThread.h"
71 #include "Dynamixel/DynaOlio.h"
72 
73 #include "DynaLibInternal.h"
74 
75 using namespace std;
76 
77 
78 // ---------------------------------------------------------------------------
79 // Private Interface
80 // ---------------------------------------------------------------------------
81 
82 // big switch
83 #undef BG_TRACE_ENABLE
84 
85 #ifdef BG_TRACE_ENABLE
86 
87 #define BG_TRACE_FILE "/tmp/bgtrace.log" ///< trace file
88 #define BG_TRACE_SERVO_ALL (-1) ///< all servos filter
89 #define BG_TRACE_FILTER_POSCTL 0x01 ///< position control filter
90 #define BG_TRACE_FILTER_TORQCTL 0x02 ///< torque control filter
91 #define BG_TRACE_FILTER_DYNA 0x04 ///< dynamics topic filter
92 #define BG_TRACE_FILTER_HEALTH 0x08 ///< health topic filter
93 #define BG_TRACE_FILTER_ALL 0xff ///< all topic filters
94 
95 // trace filtering switches
96 #define BG_TRACE_SERVO BG_TRACE_SERVO_ALL ///< all or specify servo id
97 #define BG_TRACE_FILTER (BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL)
98  ///< or'ed filter bits
99 
100 static FILE *FpBgTrace = NULL; ///< trace file pointer
101 
102 /*!
103  * \brief Trace macro.
104  *
105  * \param mask Trace filter mask.
106  * \param servoid Servo id.
107  * \param prefix Prefix string.
108  * \param fmt Format string.
109  * \param ... Variable format arguments.
110  */
111 #define BG_TRACE(mask, servoid, prefix, fmt, ...) \
112  do \
113  { \
114  if( (FpBgTrace != NULL) && (mask & BG_TRACE_FILTER) && \
115  ((BG_TRACE_SERVO==BG_TRACE_SERVO_ALL) || (servoid==BG_TRACE_SERVO)) )\
116  { \
117  fprintf(FpBgTrace, prefix ": Servo: %d: " fmt, servoid, ##__VA_ARGS__); \
118  fflush(FpBgTrace); \
119  } \
120  } while(0)
121 
122 /*!
123  * \brief Open trace file.
124  */
125 #define BG_TRACE_OPEN() \
126  do \
127  { \
128  if( (FpBgTrace = fopen(BG_TRACE_FILE, "w")) != NULL ) \
129  { \
130  fprintf(stderr, "BgThread tracing enabled. File: %s\n", BG_TRACE_FILE); \
131  fprintf(FpBgTrace, "### Start Background Thread Tracing.\n\n"); \
132  fflush(FpBgTrace); \
133  } \
134  } while(0)
135 
136 /*!
137  * \brief Close trace file.
138  */
139 #define BG_TRACE_CLOSE() \
140  do \
141  { \
142  if( FpBgTrace != NULL ) \
143  { \
144  fclose(FpBgTrace); \
145  } \
146  } while(0)
147 
148 #else
149 
150 /*! \brief No tracing output. */
151 #define BG_TRACE(mask, servoid, prefix, fmt, ...)
152 
153 /*! \brief No tracing file open. */
154 #define BG_TRACE_OPEN()
155 
156 /*! \brief No tracing file close. */
157 #define BG_TRACE_CLOSE()
158 
159 #endif // BG_TRACE_ENABLE
160 
161 /*!
162  * \brief Calculate the difference between two time-of-day times.
163  *
164  * \param t1 Later time instance.
165  * \param t0 Earlier time instance.
166  *
167  * \return Microsecond difference.
168  */
169 static long dt_usec(struct timeval t1, struct timeval t0)
170 {
171  long dt;
172 
173  if( t1.tv_usec >= t0.tv_usec)
174  {
175  dt = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - t0.tv_usec);
176  }
177  else
178  {
179  dt = (t1.tv_sec - t0.tv_sec - 1) * 1000000 +
180  (1000000 + t1.tv_usec - t0.tv_usec);
181  }
182 
183  return dt;
184 }
185 
186 
187 // ---------------------------------------------------------------------------
188 // Dynamixel Virtual Servo Class
189 // ---------------------------------------------------------------------------
190 
192  m_histTorqueIn(TORQUE_WIN_SIZE, 0.0)
193 {
195  m_nTolerance = 10;
196  m_nOdGoalPos = 0;
198  m_bOverTorqueCond = false;
199  m_bOverTorqueCtl = false;
200  m_fTorqueOut = 0.0;
201 
202  pthread_mutex_init(&m_mutexSync, NULL);
203 }
204 
206 {
207  m_eState = src.m_eState;
208  m_pidPos = src.m_pidPos;
216 
217  pthread_mutex_init(&m_mutexSync, NULL);
218 }
219 
221 {
222  m_eState = rhs.m_eState;
223  m_pidPos = rhs.m_pidPos;
231 
232  return *this;
233 }
234 
235 void DynaVServo::setToleranceInTicks(DynaServo *pServo, double fTolerance)
236 {
237  double fTicks;
238  double fAngleMin;
239  double fAngleMax;
240  double fDegrees;
241 
242  //
243  // Servo rotation range.
244  //
245  if( pServo->GetServoMode() == DYNA_MODE_CONTINUOUS )
246  {
247  fDegrees = 360.0;
248  }
249  else
250  {
251  fAngleMin = pServo->GetSpecification().m_fAngleMin;
252  fAngleMax = pServo->GetSpecification().m_fAngleMax;
253  fDegrees = fAngleMax - fAngleMin;
254  }
255 
256  fTicks = (double)pServo->GetSpecification().m_uRawPosModulo;
257 
258  m_nTolerance = (int)(fTicks * fTolerance / fDegrees);
259 }
260 
261 double DynaVServo::filterTorques(int nServoLoad)
262 {
263  double fTorque0;
264  double fTorqueK;
265 
266  fTorque0 = (double)nServoLoad / (double)TORQUE_WIN_SIZE;
267  fTorqueK = m_histTorqueIn.back();
268  m_histTorqueIn.pop_back();
269  m_histTorqueIn.push_front(fTorque0);
270 
271  m_fTorqueOut = m_fTorqueOut - fTorqueK + fTorque0;
272 
273  return m_fTorqueOut;
274 }
275 
276 
277 // ---------------------------------------------------------------------------
278 // Dynamixel Backgroud Thread Class
279 // ---------------------------------------------------------------------------
280 
281 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
282 // Public Interface
283 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
284 
285 const double DynaBgThread::HZ_EXEC_MIN = 1.0;
286 const double DynaBgThread::HZ_EXEC_DFT = 50.0;
287 const long DynaBgThread::T_EXEC_MIN = 100;
288 const double DynaBgThread::TOLERANCE_DFT = 1.0;
289 
290 DynaBgThread::DynaBgThread(double fHz, double fTolerance)
291 {
292  BG_TRACE_OPEN();
293 
294  pthread_mutex_init(&m_mutexSync, NULL);
295  pthread_cond_init(&m_condSync, NULL);
296 
297  m_pServo = NULL;
298  m_pChain = NULL;
299  m_uNumServos = 0;
300 
301  setTolerance(fTolerance);
302 
303  m_agent.m_fnWriteGoalPos = WriteGoalPos;
304  m_agent.m_fnWriteGoalSpeed = WriteGoalSpeed;
305  m_agent.m_fnWriteGoalSpeedPos = WriteGoalSpeedPos;
306 
307  m_fnUserCb = NULL;
308  m_pUserArg = NULL;
309 
310  m_iterDynamics = m_mapVServo.end();
311  m_iterHealth = m_mapVServo.end();
312  m_nHealthServoId = DYNA_ID_NONE;
313 
314  setHz(fHz);
315 
316  // create background thread
317  createThread();
318 }
319 
321 {
322  UnregisterAgent();
323  UnregisterUserCallback();
324 
325  if( m_eState != BgThreadStateZombie )
326  {
327  terminateThread();
328  }
329 
330  pthread_cond_destroy(&m_condSync);
331  pthread_mutex_destroy(&m_mutexSync);
332 
333  BG_TRACE_CLOSE();
334 }
335 
337 {
338  int nServoId;
339  DynaVServo vServo;
340 
341  UnregisterAgent();
342 
343  if( pServo != NULL )
344  {
345  m_pServo = pServo;
346  m_pChain = NULL;
347  m_uNumServos = 1;
348 
349  nServoId = pServo->GetServoId();
350 
351  m_mapVServo[nServoId] = vServo;
352 
353  m_mapVServo[nServoId].setToleranceInTicks(pServo, m_fTolerance);
354 
355  pServo->RegisterAgent(&m_agent, this);
356  }
357 
358  setHz(m_fHz);
359 
360  m_iterDynamics = m_mapVServo.begin();
361  m_iterHealth = m_mapVServo.begin();
362  m_nHealthServoId = m_iterHealth->first;
363 
364  LOGDIAG3("Registered servo: t_sched=%lu", m_TSched);
365 }
366 
368 {
369  int nServoId;
370  DynaServo *pServo;
371  int iter;
372  DynaVServo vServo;
373 
374  UnregisterAgent();
375 
376  if( pChain != NULL )
377  {
378  m_pServo = NULL;
379  m_pChain = pChain;
380  m_uNumServos = pChain->GetNumberInChain();
381 
382  for(nServoId = m_pChain->IterStart(&iter);
383  nServoId != DYNA_ID_NONE;
384  nServoId = m_pChain->IterNext(&iter))
385  {
386  if( (pServo = m_pChain->GetServo(nServoId)) != NULL )
387  {
388  m_mapVServo[nServoId] = vServo;
389  m_mapVServo[nServoId].setToleranceInTicks(pServo, m_fTolerance);
390 
391  pServo->RegisterAgent(&m_agent, this);
392  }
393  }
394  }
395 
396  setHz(m_fHz);
397 
398  m_iterDynamics = m_mapVServo.begin();
399  m_iterHealth = m_mapVServo.begin();
400  m_nHealthServoId = m_iterHealth->first;
401 
402  LOGDIAG3("Registered chain: t_sched=%lu", m_TSched);
403 }
404 
406 {
407  int nServoId;
408  DynaServo *pServo;
409  int iter;
410 
411  if( m_pServo != NULL )
412  {
413  m_pServo->UnregisterAgent();
414  }
415  else if( m_pChain != NULL )
416  {
417  for(nServoId = m_pChain->IterStart(&iter);
418  nServoId != DYNA_ID_NONE;
419  nServoId = m_pChain->IterNext(&iter))
420  {
421  if( (pServo = m_pChain->GetServo(nServoId)) != NULL )
422  {
423  pServo->UnregisterAgent();
424  }
425  }
426  }
427 
428  m_pServo = NULL;
429  m_pChain = NULL;
430  m_uNumServos = 0;
431 
432  m_mapVServo.clear();
433 }
434 
436 {
437  int rc; // return code
438 
439  switch( m_eState )
440  {
441  case BgThreadStateReady:
442  gettimeofday(&m_tvSched, NULL);
443  changeState(BgThreadStateRunning);
444  LOGDIAG3("Background thread running.");
445  rc = DYNA_OK;
446  break;
447  case BgThreadStateZombie:
448  rc = -DYNA_ECODE_GEN;
449  DYNA_LOG_ERROR(rc, "No background thread.");
450  break;
451  case BgThreadStateRunning:
452  case BgThreadStatePaused:
453  case BgThreadStateExit:
454  default:
455  rc = -DYNA_ECODE_GEN;
456  DYNA_LOG_ERROR(rc, "Background thread in invalid state %d.", m_eState);
457  break;
458  }
459 
460  return rc;
461 }
462 
464 {
465  int rc;
466 
467  switch( m_eState )
468  {
469  case BgThreadStateReady:
470  case BgThreadStateRunning:
471  case BgThreadStatePaused:
472  changeState(BgThreadStateReady);
473  LOGDIAG3("Background thread stopped.");
474  rc = DYNA_OK;
475  break;
476  case BgThreadStateZombie:
477  rc = -DYNA_ECODE_GEN;
478  DYNA_LOG_ERROR(rc, "No background thread.");
479  break;
480  case BgThreadStateExit:
481  default:
482  rc = -DYNA_ECODE_GEN;
483  DYNA_LOG_ERROR(rc, "Background thread in invalid state %d.", m_eState);
484  break;
485  }
486 
487  return rc;
488 }
489 
491 {
492  int rc;
493 
494  switch( m_eState )
495  {
496  case BgThreadStateRunning:
497  case BgThreadStatePaused:
498  changeState(BgThreadStatePaused);
499  LOGDIAG3("Background thread paused.");
500  rc = DYNA_OK;
501  break;
502  case BgThreadStateZombie:
503  rc = -DYNA_ECODE_GEN;
504  DYNA_LOG_ERROR(rc, "No background thread.");
505  break;
506  case BgThreadStateReady:
507  case BgThreadStateExit:
508  default:
509  rc = -DYNA_ECODE_GEN;
510  DYNA_LOG_ERROR(rc, "Background thread in invalid state %d.", m_eState);
511  break;
512  }
513 
514  return rc;
515 }
516 
518 {
519  int rc; // return code
520 
521  switch( m_eState )
522  {
523  case BgThreadStatePaused:
524  gettimeofday(&m_tvSched, NULL);
525  changeState(BgThreadStateRunning);
526  LOGDIAG3("Background thread resumed.");
527  rc = DYNA_OK;
528  break;
529  case BgThreadStateZombie:
530  rc = -DYNA_ECODE_GEN;
531  DYNA_LOG_ERROR(rc, "No background thread.");
532  break;
533  case BgThreadStateReady:
534  case BgThreadStateRunning:
535  case BgThreadStateExit:
536  default:
537  rc = -DYNA_ECODE_GEN;
538  DYNA_LOG_ERROR(rc, "Background thread in invalid state %d.", m_eState);
539  break;
540  }
541 
542  return rc;
543 }
544 
545 int DynaBgThread::WriteGoalPos(int nServoId, int nOdGoalPos, void *pUserArg)
546 {
547  DynaBgThread *pThis = (DynaBgThread *)pUserArg;
548  MapVServo::iterator iter;
549  DynaServo *pServo;
550  DynaVServo *pVServo;
551  int nOdCurPos;
552  int nGoalSpeed;
553  int rc;
554 
555  pServo = pThis->getRegisteredServo(nServoId);
556 
557  if( pServo == NULL )
558  {
559  rc = -DYNA_ECODE_NO_SERVO;
560  }
561 
562  iter = pThis->m_mapVServo.find(nServoId);
563 
564  if( iter == pThis->m_mapVServo.end() )
565  {
566  rc = -DYNA_ECODE_NO_SERVO;
567  }
568 
569  else
570  {
571  pVServo = &(iter->second);
572  pVServo->lock();
573  nOdCurPos = pServo->GetOdometer();
575  pVServo->m_nOdGoalPos = nOdGoalPos;
576  nGoalSpeed = pVServo->m_nGoalSpeed;
577 
578  pVServo->m_pidPos.SpecifySetPoint(nOdCurPos,
579  nOdGoalPos,
580  nGoalSpeed,
581  pServo->GetCurSpeed(),
582  pServo->IsOdometerReversed(),
583  true);
584 
585  pVServo->unlock();
586 
587  BG_TRACE(BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL, nServoId,
588  "WriteGoalPos",
589  "speedlimit=%d, goalpos=%d, curpos=%d, tol=%d.\n",
590  nGoalSpeed,
591  nOdGoalPos,
592  nOdCurPos,
593  pVServo->m_nTolerance);
594 
595  rc = DYNA_OK;
596  }
597 
598  return rc;
599 }
600 
601 int DynaBgThread::WriteGoalSpeed(int nServoId, int nGoalSpeed, void *pUserArg)
602 {
603  DynaBgThread *pThis = (DynaBgThread *)pUserArg;
604  MapVServo::iterator iter;
605  DynaServo *pServo;
606  DynaVServo *pVServo;
607  int nOdCurPos;
608  int nOdGoalPos;
609  int rc;
610 
611  pServo = pThis->getRegisteredServo(nServoId);
612 
613  if( pServo == NULL )
614  {
615  rc = -DYNA_ECODE_NO_SERVO;
616  }
617 
618  iter = pThis->m_mapVServo.find(nServoId);
619 
620  if( iter == pThis->m_mapVServo.end() )
621  {
622  rc = -DYNA_ECODE_NO_SERVO;
623  }
624 
625  else if( nGoalSpeed == DYNA_SPEED_CONT_STOP )
626  {
627  pVServo = &(iter->second);
628 
629  pThis->stopPosCtl(pServo, pVServo);
630 
631  rc = DYNA_OK;
632  }
633 
634  else
635  {
636  pVServo = &(iter->second);
637  pVServo->lock();
638  nOdCurPos = pServo->GetOdometer();
640  pVServo->m_nGoalSpeed = nGoalSpeed;
641  nOdGoalPos = pVServo->m_nOdGoalPos;
642 
643  pVServo->m_pidPos.SpecifySetPoint(nOdCurPos,
644  nOdGoalPos,
645  nGoalSpeed,
646  pServo->GetCurSpeed(),
647  pServo->IsOdometerReversed(),
648  true);
649 
650  pVServo->unlock();
651 
652  BG_TRACE(BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL, nServoId,
653  "WriteGoalSpeed",
654  "speedlimit=%d, goalpos=%d, curpos=%d, tol=%d.\n",
655  nGoalSpeed,
656  nOdGoalPos,
657  nOdCurPos,
658  pVServo->m_nTolerance);
659 
660  rc = DYNA_OK;
661  }
662 
663  return rc;
664 }
665 
666 int DynaBgThread::WriteGoalSpeedPos(int nServoId,
667  int nGoalSpeed,
668  int nOdGoalPos,
669  void *pUserArg)
670 {
671  DynaBgThread *pThis = (DynaBgThread *)pUserArg;
672  MapVServo::iterator iter;
673  DynaServo *pServo;
674  DynaVServo *pVServo;
675  int nOdCurPos;
676  int rc;
677 
678  pServo = pThis->getRegisteredServo(nServoId);
679 
680  if( pServo == NULL )
681  {
682  return -DYNA_ECODE_NO_SERVO;
683  }
684 
685  iter = pThis->m_mapVServo.find(nServoId);
686 
687  if( iter == pThis->m_mapVServo.end() )
688  {
689  return -DYNA_ECODE_NO_SERVO;
690  }
691 
692  pVServo = &(iter->second);
693 
694  pVServo->lock();
695 
696  switch( pServo->GetServoMode() )
697  {
699  nOdCurPos = pServo->GetOdometer();
701  pVServo->m_nOdGoalPos = nOdGoalPos;
702  pVServo->m_nGoalSpeed = nGoalSpeed;
703 
704  pVServo->m_pidPos.SpecifySetPoint(nOdCurPos,
705  nOdGoalPos,
706  nGoalSpeed,
707  pServo->GetCurSpeed(),
708  pServo->IsOdometerReversed(),
709  true);
710 
711  break;
712 
713  case DYNA_MODE_SERVO:
714  default:
716  pVServo->m_nOdGoalPos = nOdGoalPos;
717  pVServo->m_nGoalSpeed = nGoalSpeed;
718  if( pServo->WriteGoalSpeed(nGoalSpeed) == DYNA_OK )
719  {
720  pServo->WriteGoalPos(nOdGoalPos);
721  }
722  break;
723  }
724 
725  pVServo->unlock();
726 
727  BG_TRACE(BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL, nServoId,
728  "WriteGoalPosSpeed",
729  "speedlimit=%d, goalpos=%d, curpos=%d, tol=%d.\n",
730  nGoalSpeed,
731  nOdGoalPos,
732  nOdCurPos,
733  pVServo->m_nTolerance);
734 
735  return DYNA_OK;
736 }
737 
738 void DynaBgThread::setHz(double fHz)
739 {
740  if( fHz < HZ_EXEC_MIN )
741  {
742  fHz = HZ_EXEC_MIN;
743  }
744 
745  m_fHz = fHz;
746  m_TExec = (long)(1.0/m_fHz * 1000000.0);
747 
748  if( m_mapVServo.size() > 0 )
749  {
750  m_TSched = m_TExec / m_mapVServo.size();
751  }
752  else
753  {
754  m_TSched = 5000000;
755  }
756  if( m_TSched < T_EXEC_MIN )
757  {
758  m_TSched = T_EXEC_MIN;
759  }
760 
761  LOGDIAG3("BGThread: Hz: %.3lf, Scheduled period: %ldusecs.", m_fHz, m_TSched);
762 }
763 
764 
765 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
766 // Protected Interface
767 // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
768 
770 {
771  m_eState = eNewState;
772  pthread_cond_signal(&m_condSync);
773 }
774 
776 {
777  lock();
778  while( m_eState == BgThreadStateReady )
779  {
780  pthread_cond_wait(&m_condSync, &m_mutexSync);
781  }
782  unlock();
783 }
784 
785 void DynaBgThread::timeWait(long lMicroSecs)
786 {
787  struct timespec tsTimeout;
788  long int lSecs;
789 
790  if( lMicroSecs <= 0 )
791  {
792  return;
793  }
794 
795  clock_gettime(CLOCK_REALTIME, &tsTimeout);
796 
797  tsTimeout.tv_nsec = lMicroSecs * 1000;
798  if( tsTimeout.tv_nsec > 1000000000 )
799  {
800  tsTimeout.tv_sec += 1000000000;
801  tsTimeout.tv_nsec -= 1000000000;
802  }
803 
804  // wait with timeout
805  lock();
806  pthread_cond_timedwait(&m_condSync, &m_mutexSync, &tsTimeout);
807  unlock();
808 }
809 
811 {
812  if( m_pChain != NULL )
813  {
814  return m_pChain->GetServo(nServoId);
815  }
816  else if( m_pServo != NULL )
817  {
818  return m_pServo->GetServoId() == nServoId? m_pServo: NULL;
819  }
820  else
821  {
822  return NULL;
823  }
824 }
825 
826 void DynaBgThread::sched(long lPeriod)
827 {
828  static bool bLogWarnings = false; // true to help tune
829 
830  struct timeval tvNow; // now
831  long dt; // delta time (microseconds)
832  long dtRemaining; // remaining delta time to next scheduled event
833 
834  // now
835  gettimeofday(&tvNow, NULL);
836 
837  // delta time between calls to this scheduler
838  dt = dt_usec(tvNow, m_tvSched);
839 
840  // time remaining till next background thread event
841  dtRemaining = lPeriod - dt;
842 
843  // wait
844  if( dtRemaining > 0 )
845  {
846  timeWait(dtRemaining);
847  gettimeofday(&tvNow, NULL);
848  m_dTSched = dt_usec(tvNow, m_tvSched);
849  }
850 
851  // slipped task period - don't wait
852  else
853  {
854  // only log if really bad
855  dtRemaining = fabs(dtRemaining);
856  if( bLogWarnings && (dtRemaining >= lPeriod) )
857  {
858  LOGWARN("BGThread: Scheduled task slipped by %.2fmsec for servo %d",
859  dtRemaining/1000.0, m_iterDynamics->first);
860  }
861  m_dTSched = dt;
862  }
863 
864  // save now
865  m_tvSched = tvNow;
866 }
867 
869 {
870  int nServoId;
871  DynaVServo *pVServo;
872  DynaServo *pServo;
873 
874  //
875  // Get the next virtual servo.
876  //
877  if( (pVServo = getNextVServo(m_iterDynamics, nServoId)) == NULL )
878  {
879  return;
880  }
881 
882  //
883  // Get the associated registered servo.
884  //
885  if( (pServo = getRegisteredServo(nServoId)) == NULL )
886  {
887  // can't find servo - erase entry from vServo map
888  m_mapVServo.erase(nServoId);
889  m_uNumServos = (uint_t)m_mapVServo.size();
890  return;
891  }
892 
893  //
894  // Execute servo dynamics monitoring and control.
895  //
896  execDynamics(pServo, pVServo);
897 
898  //
899  // Monitor servo health.
900  //
901  if( nServoId == m_nHealthServoId )
902  {
903  monitorHealth(pServo, pVServo);
904  getPrevVServo(m_iterHealth, m_nHealthServoId);
905  }
906 
907  //
908  // User callback
909  //
910  if( m_fnUserCb != NULL )
911  {
912  m_fnUserCb(m_pUserArg);
913  }
914 }
915 
917 {
918  pVServo->lock();
919 
920  //
921  // Read relavent servo state dynamics.
922  //
923  monitorDynamics(pServo, pVServo);
924 
925  //
926  // Servo in torque overload condition, ease motor drive if possible.
927  //
928  if( pServo->GetState().m_bTorqueEnabled &&
929  (pVServo->m_bOverTorqueCond || pVServo->m_bOverTorqueCtl) )
930  {
931  execTorqueCtl(pServo, pVServo);
932  }
933 
934  //
935  // Servo not driven or state is idle.
936  //
937  if( !pServo->GetState().m_bTorqueEnabled ||
938  (pVServo->m_eState == DynaVServo::StateIdle) )
939  {
940  // do nothing
941  }
942 
943  //
944  // Stop servo.
945  //
946  else if( pVServo->m_nGoalSpeed == DYNA_SPEED_CONT_STOP )
947  {
948  stopPosCtl(pServo, pVServo);
949  }
950 
951  //
952  // Servo in torque overload condition and goal direction is in the same
953  // direction as the load, which would exasperate the condition.
954  //
955  else if( pVServo->m_bOverTorqueCond &&
956  (pVServo->getGoalDir(pServo) != DYNA_GET_DIR(pVServo->m_fTorqueOut)) )
957  {
958  //stopPosCtl(pServo, pVServo);
959  }
960 
961  //
962  // PID control servo to goal position.
963  //
964  else
965  {
966  execPosCtl(pServo, pVServo);
967  }
968 
969  pVServo->unlock();
970 }
971 
973 {
974  double fTorqueAvg; // filtered load
975  uint_t uOverTorqueTh; // set over torque condition threshold
976  uint_t uClearTorqueTh; // clear over torque condition threshold
977  int nCurPos; // current position
978  int sign; // torque release direction sign
979  int nGoalSpeed; // goal speed
980  int nGoalPos; // goal position
981 
982  LOGWARN("BGThread: Executing torque control for servo %d, torque=%.2lf.",
983  pServo->GetServoId(), pVServo->m_fTorqueOut);
984 
985  pServo->GetSoftTorqueThresholds(uOverTorqueTh, uClearTorqueTh);
986 
987  fTorqueAvg = pVServo->m_fTorqueOut;
988 
989  if( (uint_t)fabs(fTorqueAvg) > uOverTorqueTh )
990  {
991  sign = DYNA_GET_DIR(fTorqueAvg);
992 
993  nGoalSpeed = DynaVServo::GOAL_SPEED_DFT;
994 
995  nCurPos = pServo->GetOdometer();
996  nGoalPos = nCurPos + sign * 5;
997 
998  switch( pServo->GetServoMode() )
999  {
1000  case DYNA_MODE_CONTINUOUS:
1001  nGoalSpeed = nGoalSpeed * sign;
1002  pServo->WriteGoalSpeed(nGoalSpeed);
1003  usleep(1000); // RDK hack
1004  stopMotion(pServo);
1005  break;
1006 
1007  case DYNA_MODE_SERVO:
1008  default:
1009  pServo->WriteGoalSpeed(nGoalSpeed);
1010  pServo->WriteGoalPos(nGoalPos);
1011  break;
1012  }
1013 
1014  pVServo->m_bOverTorqueCtl = true;
1015 
1016  BG_TRACE(BG_TRACE_FILTER_TORQCTL, pServo->GetServoId(), "TORQUECTL",
1017  "cur_load=%d, filtered_load=%.2lf, cur_od_pos=%d, "
1018  "goal_speed=%d, goal_od_pos=%d\n",
1019  nCurLoad, fTorqueAvg, nCurPos, nGoalSpeed, nGoalPos);
1020  }
1021 
1022  // stop servo, but don't touch current goals
1023  else
1024  {
1025  pVServo->m_bOverTorqueCtl = false;
1026  pServo->WriteGoalSpeed(DYNA_SPEED_CONT_STOP);
1027  }
1028 }
1029 
1031 {
1032  int nOdGoalPos; // goal position
1033  int nGoalSpeed; // goal speed
1034  int nCurPos; // current position
1035  int nCurSpeed; // current speed
1036  int nErr; // position difference error
1037  double dt; // delta time
1038 
1039  //
1040  // Once goal position is set, servo mode servos move on there own.
1041  //
1042  if( pServo->GetServoMode() == DYNA_MODE_SERVO )
1043  {
1044  nGoalSpeed = pVServo->m_nGoalSpeed;
1045  nOdGoalPos = pVServo->m_nOdGoalPos;
1046 
1047  // stop
1048  if( iabs(nGoalSpeed) == 0 )
1049  {
1050  stopPosCtl(pServo, pVServo);
1051  }
1052 
1053  pVServo->m_eState = DynaVServo::StateIdle;
1054 
1055  return;
1056  }
1057 
1058  else
1059  {
1060  nCurPos = pServo->GetOdometer();
1061 
1062  dt = (double)m_dTSched/1000000.0;
1063 
1064  nGoalSpeed = (int)pVServo->m_pidPos.Control((double)nCurPos, dt);
1065  nErr = (int)pVServo->m_pidPos.GetError();
1066 
1067  if( (iabs(nErr) < pVServo->m_nTolerance) ||
1068  (nGoalSpeed == DYNA_SPEED_CONT_STOP) )
1069  {
1070  stopPosCtl(pServo, pVServo);
1071  }
1072  else if( pServo->WriteGoalSpeed(nGoalSpeed) == DYNA_OK )
1073  {
1074  BG_TRACE(BG_TRACE_FILTER_POSCTL, pServo->GetServoId(), "MOVE",
1075  "goalspeed=%d, cur_od_pos=%d, goal_od_pos=%d, diff=%d\n",
1076  nGoalSpeed,
1077  nCurPos,
1078  pVServo->m_nOdGoalPos,
1079  nErr);
1080  }
1081  }
1082 }
1083 
1085 {
1086  int nGoalOdPos;
1087 
1088  // stop servo movement
1089  stopMotion(pServo);
1090 
1091  nGoalOdPos = pVServo->m_nOdGoalPos;
1092  pVServo->m_nOdGoalPos = pServo->GetOdometer();
1093  pVServo->m_eState = DynaVServo::StateIdle;
1094 
1095  BG_TRACE(BG_TRACE_FILTER_POSCTL,
1096  pServo->GetServoId(), "STOP",
1097  "cur_od_pos=%d, goal_od_pos=%d\n",
1098  pVServo->m_nOdGoalPos, nOdGoalPos);
1099 }
1100 
1102 {
1103  int nCurOdPos;
1104 
1105  // stop servo
1106  switch( pServo->GetServoMode() )
1107  {
1108  //
1109  // In continuous mode: Set the goal speed to zero.
1110  //
1111  case DYNA_MODE_CONTINUOUS:
1112  pServo->WriteGoalSpeed(DYNA_SPEED_CONT_STOP);
1113  break;
1114 
1115  //
1116  // In servo mode: Set the goal position to the current position (a 0 speed
1117  // value sets the speed to the default maximum).
1118  //
1119  case DYNA_MODE_SERVO:
1120  default:
1121  if( pServo->ReadCurPos(&nCurOdPos) == DYNA_OK )
1122  {
1123  pServo->WriteGoalPos(nCurOdPos);
1124  }
1125  break;
1126  }
1127 }
1128 
1130 {
1131  int nCurPos;
1132  int nCurSpeed;
1133  int nCurLoad;
1134  uint_t uTorqueAvg;
1135  uint_t uOverTorqueTh;
1136  uint_t uClearTorqueTh;
1137  bool bCurOverCond;
1138 
1139  // read the servo dynamics.
1140  if( pServo->ReadDynamics(&nCurPos, &nCurSpeed, &nCurLoad) >= 0 )
1141  {
1142  uTorqueAvg = (uint_t)fabs(pVServo->filterTorques(nCurLoad));
1143 
1144  pServo->GetSoftTorqueThresholds(uOverTorqueTh, uClearTorqueTh);
1145 
1146  bCurOverCond = pServo->HasSoftTorqueOverCond();
1147 
1148  // set over torque limit condition
1149  if( uTorqueAvg > uOverTorqueTh )
1150  {
1151  pVServo->m_bOverTorqueCond = true;
1152  pServo->SetSoftTorqueOverCond(true);
1153  }
1154 
1155  // clear over torque limit condition
1156  else if( (uTorqueAvg < uClearTorqueTh) && bCurOverCond )
1157  {
1158  pVServo->m_bOverTorqueCond = false;
1159  pServo->SetSoftTorqueOverCond(false);
1160  }
1161 
1162  BG_TRACE(BG_TRACE_FILTER_DYNA, pServo->GetServoId(), "DYNAMICS",
1163  "pos=%d, speed=%d, cur_load=%d, filtered_load=%.2lf, overtorque=%s\n",
1164  nCurPos, nCurSpeed, nCurLoad, pVServo->m_fTorqueOut,
1165  (pServo->HasSoftTorqueOverCond()? "true": "false"));
1166  }
1167 }
1168 
1170 {
1171  uint_t uAlarms;
1172  int nCurLoad;
1173  uint_t uCurVolt;
1174  uint_t uCurTemp;
1175 
1176  LOGDIAG4("BGThread: Monitoring health for servo %d.", pServo->GetServoId());
1177 
1178  pServo->ReadHealth(&uAlarms, &nCurLoad, &uCurVolt, &uCurTemp);
1179 
1180  //pVServo->filterTorques(nCurLoad);
1181 
1182  BG_TRACE(BG_TRACE_FILTER_HEALTH, pServo->GetServoId(), "HEALTH",
1183  "load=%d, voltage=%u, temperature=%u, alarms=0x%02x\n",
1184  nCurLoad, uCurVolt, uCurTemp, uAlarms);
1185 }
1186 
1187 void *DynaBgThread::bgThread(void *pArg)
1188 {
1189  DynaBgThread *pThis = (DynaBgThread *)pArg;
1190  int rc;
1191 
1192  LOGDIAG3("Dynamixel background thread created.");
1193 
1194  //
1195  // Loop forever until exit
1196  //
1197  while( (pThis->m_eState != BgThreadStateExit) )
1198  {
1199  switch( pThis->m_eState )
1200  {
1201  case BgThreadStateReady:
1202  case BgThreadStatePaused:
1203  pThis->readyWait();
1204  LOGDIAG3("New state: %d: t_sched=%lu",
1205  pThis->m_eState, pThis->m_TSched);
1206  break;
1207  case BgThreadStateRunning:
1208  if( pThis->m_uNumServos > 0 )
1209  {
1210  pThis->sched(pThis->m_TSched);
1211  if( pThis->m_eState == BgThreadStateRunning )
1212  {
1213  pThis->exec();
1214  }
1215  }
1216  else // nothing to do
1217  {
1218  // slowly
1219  pThis->sched(500000);
1220  }
1221  break;
1222  case BgThreadStateExit:
1223  break;
1224  default:
1225  LOGERROR("%d: Unexpected dynamixel background thread state.",
1226  pThis->m_eState);
1227  pThis->m_eState = BgThreadStateExit;
1228  break;
1229  }
1230  }
1231 
1232  pThis->m_eState = BgThreadStateZombie;
1233 
1234  LOGDIAG3("Dynamixel background thread exited.");
1235 
1236  return NULL;
1237 }
1238 
1240 {
1241  int rc;
1242 
1243  m_eState = BgThreadStateReady;
1244 
1245  rc = pthread_create(&m_thread, NULL, DynaBgThread::bgThread, (void *)this);
1246 
1247  if( rc != 0 )
1248  {
1249  LOGSYSERROR("pthread_create()");
1250  m_eState = BgThreadStateZombie;
1251  }
1252 }
1253 
1255 {
1256  changeState(BgThreadStateExit);
1257  pthread_join(m_thread, NULL);
1258 }
RoadNarrows Dynamixel Bus Communications Abstract Base Class Interface.
MapVServo m_mapVServo
virtual servo associative map
Definition: DynaBgThread.h:498
virtual uint_t GetServoMode() const
Get the servo operational mode.
Definition: DynaServo.h:165
double GetError() const
Get current error.
Definition: DynaPid.h:199
virtual const DynaServoState_T & GetState() const
Get servo state.
Definition: DynaServo.h:205
virtual double Control(double fPV, double dt)
Apply PID control.
Definition: DynaPid.cxx:83
virtual void SetSoftTorqueOverCond(bool bNewCond)
Set or clear servo in soft over torque condition.
Definition: DynaServo.h:558
virtual void SpecifySetPoint(int nOdPosStart, int nOdPosGoal, int nSpeedLim, int nCurSpeed, bool bIsReversed, bool bUnwind=false)
Specify position setpoint.
Definition: DynaPidPos.cxx:71
virtual void RegisterChainAgent(DynaChain *pChain)
Register the Dynamixel chain for control and monitoring.
#define DYNA_OK
not an error, success
Definition: Dynamixel.h:78
BgThreadState
Background thread states.
Definition: DynaBgThread.h:251
RoadNarrows MX Series Dynamixel Declarations.
int m_nGoalSpeed
current goal speed
Definition: DynaBgThread.h:115
virtual int GetCurSpeed() const
Get the current servo speed.
Definition: DynaServo.h:455
std::deque< double > m_histTorqueIn
recent history of joint torques
Definition: DynaBgThread.h:119
#define DYNA_MODE_SERVO
servo mode with limited rotation
Definition: Dynamixel.h:170
double m_fTorqueOut
low-pass filtered torque
Definition: DynaBgThread.h:118
#define BG_TRACE(mask, servoid, prefix, fmt,...)
No tracing output.
void createThread()
Create the background thread.
static const double TOLERANCE_DFT
default pos. tolerance (deg)
Definition: DynaBgThread.h:246
virtual void UnregisterAgent()
Unregister servo proxy agent.
Definition: DynaServo.h:594
bool m_bOverTorqueCtl
[not] in torque overload control
Definition: DynaBgThread.h:117
void setToleranceInTicks(DynaServo *pServo, double fTolerance)
Set position tolerance in encoder ticks.
RoadNarrows Dynamixel Servo Chain Container Base Class Interface.
INLINE_IN_H int iabs(int a)
Return absolute value of a.
Definition: DynaOlio.h:91
#define DYNA_ECODE_NO_SERVO
no servo found
Definition: Dynamixel.h:86
virtual void execPosCtl(DynaServo *pServo, DynaVServo *pVServo)
Execute servo position control.
static const double HZ_EXEC_DFT
default exec hertz
Definition: DynaBgThread.h:244
Miscellaneous collection of useful utilities.
double m_fAngleMin
min rotation angle in servo mode (deg)
Definition: DynaTypes.h:92
virtual uint_t GetNumberInChain() const
Get the number of servos currently in chain.
Definition: DynaChain.h:140
uint_t m_uNumServos
number of servos to monitor
Definition: DynaBgThread.h:499
virtual int Run()
Run the background thread control and monitoring tasks.
Dynamixel Servo Abstract Base Class.
Definition: DynaServo.h:78
long m_TSched
scheduler &mu;s period
Definition: DynaBgThread.h:513
Dynamixel background thread class declarations.
bool m_bOverTorqueCond
is [not] in torque overload condition
Definition: DynaBgThread.h:116
void changeState(DynaBgThread::BgThreadState eNewState)
Change the background thread state.
#define DYNA_LOG_ERROR(ecode, efmt,...)
Log Error.
virtual int Stop()
Stop control and monitoring tasks.
void unlock()
Unlock the virtual servo.
Definition: DynaBgThread.h:220
uint_t m_uRawPosModulo
raw position modulo
Definition: DynaTypes.h:103
virtual void UnregisterAgent()
Unregister any chain or servo from control and monitoring.
#define DYNA_ECODE_GEN
general, unspecified error
Definition: Dynamixel.h:80
bool_t m_bTorqueEnabled
torque [not] enabled
Definition: DynaTypes.h:197
virtual void RegisterServoAgent(DynaServo *pServo)
Register the Dynamixel servo for control and monitoring.
BgThreadState m_eState
thread state
Definition: DynaBgThread.h:490
double filterTorques(int nServoLoad)
Apply a low-pass band filter on the sensed torques (loads).
DynaVServo()
Default constructor.
#define BG_TRACE_OPEN()
No tracing file open.
static void * bgThread(void *pArg)
Dynamixel background thread.
in poistion control
Definition: DynaBgThread.h:105
The libDynamixel internal declarations.
pthread_mutex_t m_mutexSync
synchonization mutex
Definition: DynaBgThread.h:226
int getGoalDir(DynaServo *pServo)
Get the current goal rotation direction.
Definition: DynaBgThread.h:186
virtual DynaServo * getRegisteredServo(int nServoId)
Get registered servo.
void timeWait(long lMicroSecs)
Wait until state change or time out.
VServoState m_eState
virtual servo state
Definition: DynaBgThread.h:111
static const int GOAL_SPEED_DFT
default virutal goal speed
Definition: DynaBgThread.h:96
Background Thread Virtual Servo Class.
Definition: DynaBgThread.h:87
#define DYNA_MODE_CONTINUOUS
continuous mode with/without position
Definition: Dynamixel.h:171
Dynamixel Chain Container Base Class.
Definition: DynaChain.h:75
virtual void sched(long lPeriod)
Schedule the Dynamixel background thread for the next task(s) to perform.
static const double HZ_EXEC_MIN
minimum exec hertz
Definition: DynaBgThread.h:243
bool IsOdometerReversed()
Test if the virtual odometer is reversed.
Definition: DynaServo.h:334
virtual void monitorDynamics(DynaServo *pServo, DynaVServo *pVServo)
Execute servo dynamics monitoring background task.
virtual const DynaServoSpec_T & GetSpecification() const
Get servo specification.
Definition: DynaServo.h:185
DynaBgThread(double fHz=HZ_EXEC_DFT, double fTolerance=TOLERANCE_DFT)
Default initializer constructor.
RoadNarrows Dynamixel Archetype Servo Abstract Base Class.
no active position control
Definition: DynaBgThread.h:103
virtual int Pause()
Pause control and monitoring tasks.
virtual bool HasSoftTorqueOverCond()
Test if servo is in a soft over torque condition.
Definition: DynaServo.h:568
DynaVServo operator=(const DynaVServo &rhs)
Assignment operator.
virtual void execTorqueCtl(DynaServo *pServo, DynaVServo *pVServo)
Execute servo torque control.
void terminateThread()
Terminate the background thread.
#define DYNA_GET_DIR(scalar)
Get the direction component, given the rotational scalar.
Definition: Dynamixel.h:200
#define BG_TRACE_CLOSE()
No tracing file close.
RoadNarrows Dynamixel Top-Level Package Header File.
int m_nOdGoalPos
current goal position
Definition: DynaBgThread.h:114
virtual uint_t GetServoId() const
Get servo id.
Definition: DynaServo.h:155
static long dt_usec(struct timeval t1, struct timeval t0)
Calculate the difference between two time-of-day times.
static int WriteGoalPos(int nServoId, int nOdGoalPos, void *pUserArg)
Start controlling servo to rotate it to the given goal position.
static const long T_EXEC_MIN
task exec period min (100usec)
Definition: DynaBgThread.h:245
void lock()
Lock the virtual servo.
Definition: DynaBgThread.h:206
#define DYNA_ID_NONE
no servo id
Definition: Dynamixel.h:145
void setHz(double fHz)
Set new control and monitoring execution hertz rate.
virtual void stopMotion(DynaServo *pServo)
Stop servo motion.
virtual void RegisterAgent(DynaAgent_T *pAgent, void *pAgentArg)
Register servo proxy agent.
Definition: DynaServo.h:584
virtual void execDynamics(DynaServo *pServo, DynaVServo *pVServo)
Execute servo dynamics monitoring and control.
static const size_t TORQUE_WIN_SIZE
Torque sliding window size for low-pass band filtering of input torques.
Definition: DynaBgThread.h:94
DynaPidPos m_pidPos
servo position PID
Definition: DynaBgThread.h:112
virtual void monitorHealth(DynaServo *pServo, DynaVServo *pVServo)
Execute servo health monitoring background task.
double m_fAngleMax
max rotation angle in servo mode (deg)
Definition: DynaTypes.h:93
virtual void exec()
Execute background tasks.
virtual ~DynaBgThread()
Desctructor.
RoadNarrows Dynamixel Library Error and Logging Routines.
int GetOdometer()
Get the current virtual odometer value.
Definition: DynaServo.h:304
virtual void stopPosCtl(DynaServo *pServo, DynaVServo *pVServo)
Stop servo position control.
#define DYNA_SPEED_CONT_STOP
continuous mode: stop
Definition: Dynamixel.h:261
virtual void GetSoftTorqueThresholds(uint_t &uOverTorqueTh, uint_t &uClearTorqueTh)
Get soft torque thresholds.
Definition: DynaServo.h:546
int m_nTolerance
position &plusmn; tolerance (ticks)
Definition: DynaBgThread.h:113
virtual int Resume()
Resume control and monitoring tasks.
void readyWait()
Wait indefinitely in ready state.