58 #include "rnr/rnrconfig.h" 61 #include "rnr/units.h" 83 #undef BG_TRACE_ENABLE 85 #ifdef BG_TRACE_ENABLE 87 #define BG_TRACE_FILE "/tmp/bgtrace.log" 88 #define BG_TRACE_SERVO_ALL (-1)
89 #define BG_TRACE_FILTER_POSCTL 0x01
90 #define BG_TRACE_FILTER_TORQCTL 0x02
91 #define BG_TRACE_FILTER_DYNA 0x04
92 #define BG_TRACE_FILTER_HEALTH 0x08
93 #define BG_TRACE_FILTER_ALL 0xff
96 #define BG_TRACE_SERVO BG_TRACE_SERVO_ALL 97 #define BG_TRACE_FILTER (BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL)
100 static FILE *FpBgTrace = NULL;
111 #define BG_TRACE(mask, servoid, prefix, fmt, ...) \ 114 if( (FpBgTrace != NULL) && (mask & BG_TRACE_FILTER) && \ 115 ((BG_TRACE_SERVO==BG_TRACE_SERVO_ALL) || (servoid==BG_TRACE_SERVO)) )\ 117 fprintf(FpBgTrace, prefix ": Servo: %d: " fmt, servoid, ##__VA_ARGS__); \ 125 #define BG_TRACE_OPEN() \ 128 if( (FpBgTrace = fopen(BG_TRACE_FILE, "w")) != NULL ) \ 130 fprintf(stderr, "BgThread tracing enabled. File: %s\n", BG_TRACE_FILE); \ 131 fprintf(FpBgTrace, "### Start Background Thread Tracing.\n\n"); \ 139 #define BG_TRACE_CLOSE() \ 142 if( FpBgTrace != NULL ) \ 151 #define BG_TRACE(mask, servoid, prefix, fmt, ...) 154 #define BG_TRACE_OPEN() 157 #define BG_TRACE_CLOSE() 159 #endif // BG_TRACE_ENABLE 169 static long dt_usec(
struct timeval t1,
struct timeval t0)
173 if( t1.tv_usec >= t0.tv_usec)
175 dt = (t1.tv_sec - t0.tv_sec) * 1000000 + (t1.tv_usec - t0.tv_usec);
179 dt = (t1.tv_sec - t0.tv_sec - 1) * 1000000 +
180 (1000000 + t1.tv_usec - t0.tv_usec);
192 m_histTorqueIn(TORQUE_WIN_SIZE, 0.0)
253 fDegrees = fAngleMax - fAngleMin;
295 pthread_cond_init(&m_condSync, NULL);
301 setTolerance(fTolerance);
303 m_agent.m_fnWriteGoalPos = WriteGoalPos;
304 m_agent.m_fnWriteGoalSpeed = WriteGoalSpeed;
305 m_agent.m_fnWriteGoalSpeedPos = WriteGoalSpeedPos;
310 m_iterDynamics = m_mapVServo.end();
311 m_iterHealth = m_mapVServo.end();
323 UnregisterUserCallback();
325 if(
m_eState != BgThreadStateZombie )
330 pthread_cond_destroy(&m_condSync);
351 m_mapVServo[nServoId] = vServo;
360 m_iterDynamics = m_mapVServo.begin();
361 m_iterHealth = m_mapVServo.begin();
362 m_nHealthServoId = m_iterHealth->first;
364 LOGDIAG3(
"Registered servo: t_sched=%lu", m_TSched);
382 for(nServoId = m_pChain->IterStart(&iter);
384 nServoId = m_pChain->IterNext(&iter))
386 if( (pServo = m_pChain->GetServo(nServoId)) != NULL )
388 m_mapVServo[nServoId] = vServo;
398 m_iterDynamics = m_mapVServo.begin();
399 m_iterHealth = m_mapVServo.begin();
400 m_nHealthServoId = m_iterHealth->first;
402 LOGDIAG3(
"Registered chain: t_sched=%lu", m_TSched);
411 if( m_pServo != NULL )
415 else if( m_pChain != NULL )
417 for(nServoId = m_pChain->IterStart(&iter);
419 nServoId = m_pChain->IterNext(&iter))
421 if( (pServo = m_pChain->GetServo(nServoId)) != NULL )
441 case BgThreadStateReady:
442 gettimeofday(&m_tvSched, NULL);
443 changeState(BgThreadStateRunning);
444 LOGDIAG3(
"Background thread running.");
447 case BgThreadStateZombie:
451 case BgThreadStateRunning:
452 case BgThreadStatePaused:
453 case BgThreadStateExit:
469 case BgThreadStateReady:
470 case BgThreadStateRunning:
471 case BgThreadStatePaused:
472 changeState(BgThreadStateReady);
473 LOGDIAG3(
"Background thread stopped.");
476 case BgThreadStateZombie:
480 case BgThreadStateExit:
496 case BgThreadStateRunning:
497 case BgThreadStatePaused:
498 changeState(BgThreadStatePaused);
499 LOGDIAG3(
"Background thread paused.");
502 case BgThreadStateZombie:
506 case BgThreadStateReady:
507 case BgThreadStateExit:
523 case BgThreadStatePaused:
524 gettimeofday(&m_tvSched, NULL);
525 changeState(BgThreadStateRunning);
526 LOGDIAG3(
"Background thread resumed.");
529 case BgThreadStateZombie:
533 case BgThreadStateReady:
534 case BgThreadStateRunning:
535 case BgThreadStateExit:
548 MapVServo::iterator iter;
571 pVServo = &(iter->second);
587 BG_TRACE(BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL, nServoId,
589 "speedlimit=%d, goalpos=%d, curpos=%d, tol=%d.\n",
601 int DynaBgThread::WriteGoalSpeed(
int nServoId,
int nGoalSpeed,
void *pUserArg)
604 MapVServo::iterator iter;
627 pVServo = &(iter->second);
636 pVServo = &(iter->second);
652 BG_TRACE(BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL, nServoId,
654 "speedlimit=%d, goalpos=%d, curpos=%d, tol=%d.\n",
666 int DynaBgThread::WriteGoalSpeedPos(
int nServoId,
672 MapVServo::iterator iter;
692 pVServo = &(iter->second);
718 if( pServo->WriteGoalSpeed(nGoalSpeed) ==
DYNA_OK )
720 pServo->WriteGoalPos(nOdGoalPos);
727 BG_TRACE(BG_TRACE_FILTER_POSCTL|BG_TRACE_FILTER_TORQCTL, nServoId,
729 "speedlimit=%d, goalpos=%d, curpos=%d, tol=%d.\n",
740 if( fHz < HZ_EXEC_MIN )
746 m_TExec = (long)(1.0/m_fHz * 1000000.0);
748 if( m_mapVServo.size() > 0 )
750 m_TSched = m_TExec / m_mapVServo.size();
756 if( m_TSched < T_EXEC_MIN )
758 m_TSched = T_EXEC_MIN;
761 LOGDIAG3(
"BGThread: Hz: %.3lf, Scheduled period: %ldusecs.", m_fHz, m_TSched);
772 pthread_cond_signal(&m_condSync);
778 while(
m_eState == BgThreadStateReady )
787 struct timespec tsTimeout;
790 if( lMicroSecs <= 0 )
795 clock_gettime(CLOCK_REALTIME, &tsTimeout);
797 tsTimeout.tv_nsec = lMicroSecs * 1000;
798 if( tsTimeout.tv_nsec > 1000000000 )
800 tsTimeout.tv_sec += 1000000000;
801 tsTimeout.tv_nsec -= 1000000000;
806 pthread_cond_timedwait(&m_condSync, &
m_mutexSync, &tsTimeout);
812 if( m_pChain != NULL )
814 return m_pChain->GetServo(nServoId);
816 else if( m_pServo != NULL )
818 return m_pServo->
GetServoId() == nServoId? m_pServo: NULL;
828 static bool bLogWarnings =
false;
830 struct timeval tvNow;
835 gettimeofday(&tvNow, NULL);
838 dt =
dt_usec(tvNow, m_tvSched);
841 dtRemaining = lPeriod - dt;
844 if( dtRemaining > 0 )
846 timeWait(dtRemaining);
847 gettimeofday(&tvNow, NULL);
848 m_dTSched =
dt_usec(tvNow, m_tvSched);
855 dtRemaining = fabs(dtRemaining);
856 if( bLogWarnings && (dtRemaining >= lPeriod) )
858 LOGWARN(
"BGThread: Scheduled task slipped by %.2fmsec for servo %d",
859 dtRemaining/1000.0, m_iterDynamics->first);
877 if( (pVServo = getNextVServo(m_iterDynamics, nServoId)) == NULL )
885 if( (pServo = getRegisteredServo(nServoId)) == NULL )
888 m_mapVServo.erase(nServoId);
889 m_uNumServos = (uint_t)m_mapVServo.size();
896 execDynamics(pServo, pVServo);
901 if( nServoId == m_nHealthServoId )
903 monitorHealth(pServo, pVServo);
904 getPrevVServo(m_iterHealth, m_nHealthServoId);
910 if( m_fnUserCb != NULL )
912 m_fnUserCb(m_pUserArg);
923 monitorDynamics(pServo, pVServo);
931 execTorqueCtl(pServo, pVServo);
948 stopPosCtl(pServo, pVServo);
966 execPosCtl(pServo, pVServo);
975 uint_t uOverTorqueTh;
976 uint_t uClearTorqueTh;
982 LOGWARN(
"BGThread: Executing torque control for servo %d, torque=%.2lf.",
989 if( (uint_t)fabs(fTorqueAvg) > uOverTorqueTh )
996 nGoalPos = nCurPos + sign * 5;
1001 nGoalSpeed = nGoalSpeed * sign;
1002 pServo->WriteGoalSpeed(nGoalSpeed);
1009 pServo->WriteGoalSpeed(nGoalSpeed);
1010 pServo->WriteGoalPos(nGoalPos);
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);
1048 if(
iabs(nGoalSpeed) == 0 )
1050 stopPosCtl(pServo, pVServo);
1062 dt = (double)m_dTSched/1000000.0;
1070 stopPosCtl(pServo, pVServo);
1072 else if( pServo->WriteGoalSpeed(nGoalSpeed) ==
DYNA_OK )
1075 "goalspeed=%d, cur_od_pos=%d, goal_od_pos=%d, diff=%d\n",
1097 "cur_od_pos=%d, goal_od_pos=%d\n",
1121 if( pServo->ReadCurPos(&nCurOdPos) ==
DYNA_OK )
1123 pServo->WriteGoalPos(nCurOdPos);
1135 uint_t uOverTorqueTh;
1136 uint_t uClearTorqueTh;
1140 if( pServo->ReadDynamics(&nCurPos, &nCurSpeed, &nCurLoad) >= 0 )
1142 uTorqueAvg = (uint_t)fabs(pVServo->
filterTorques(nCurLoad));
1149 if( uTorqueAvg > uOverTorqueTh )
1156 else if( (uTorqueAvg < uClearTorqueTh) && bCurOverCond )
1163 "pos=%d, speed=%d, cur_load=%d, filtered_load=%.2lf, overtorque=%s\n",
1176 LOGDIAG4(
"BGThread: Monitoring health for servo %d.", pServo->
GetServoId());
1178 pServo->ReadHealth(&uAlarms, &nCurLoad, &uCurVolt, &uCurTemp);
1183 "load=%d, voltage=%u, temperature=%u, alarms=0x%02x\n",
1184 nCurLoad, uCurVolt, uCurTemp, uAlarms);
1192 LOGDIAG3(
"Dynamixel background thread created.");
1197 while( (pThis->
m_eState != BgThreadStateExit) )
1201 case BgThreadStateReady:
1202 case BgThreadStatePaused:
1204 LOGDIAG3(
"New state: %d: t_sched=%lu",
1207 case BgThreadStateRunning:
1211 if( pThis->
m_eState == BgThreadStateRunning )
1219 pThis->
sched(500000);
1222 case BgThreadStateExit:
1225 LOGERROR(
"%d: Unexpected dynamixel background thread state.",
1227 pThis->
m_eState = BgThreadStateExit;
1232 pThis->
m_eState = BgThreadStateZombie;
1234 LOGDIAG3(
"Dynamixel background thread exited.");
1249 LOGSYSERROR(
"pthread_create()");
1256 changeState(BgThreadStateExit);
1257 pthread_join(m_thread, NULL);
RoadNarrows Dynamixel Bus Communications Abstract Base Class Interface.
MapVServo m_mapVServo
virtual servo associative map
virtual uint_t GetServoMode() const
Get the servo operational mode.
double GetError() const
Get current error.
virtual const DynaServoState_T & GetState() const
Get servo state.
virtual double Control(double fPV, double dt)
Apply PID control.
virtual void SetSoftTorqueOverCond(bool bNewCond)
Set or clear servo in soft over torque condition.
virtual void SpecifySetPoint(int nOdPosStart, int nOdPosGoal, int nSpeedLim, int nCurSpeed, bool bIsReversed, bool bUnwind=false)
Specify position setpoint.
virtual void RegisterChainAgent(DynaChain *pChain)
Register the Dynamixel chain for control and monitoring.
#define DYNA_OK
not an error, success
BgThreadState
Background thread states.
RoadNarrows MX Series Dynamixel Declarations.
int m_nGoalSpeed
current goal speed
virtual int GetCurSpeed() const
Get the current servo speed.
std::deque< double > m_histTorqueIn
recent history of joint torques
#define DYNA_MODE_SERVO
servo mode with limited rotation
double m_fTorqueOut
low-pass filtered torque
#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)
virtual void UnregisterAgent()
Unregister servo proxy agent.
bool m_bOverTorqueCtl
[not] in torque overload control
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.
#define DYNA_ECODE_NO_SERVO
no servo found
virtual void execPosCtl(DynaServo *pServo, DynaVServo *pVServo)
Execute servo position control.
static const double HZ_EXEC_DFT
default exec hertz
Miscellaneous collection of useful utilities.
double m_fAngleMin
min rotation angle in servo mode (deg)
virtual uint_t GetNumberInChain() const
Get the number of servos currently in chain.
uint_t m_uNumServos
number of servos to monitor
virtual int Run()
Run the background thread control and monitoring tasks.
Dynamixel Servo Abstract Base Class.
long m_TSched
scheduler μs period
Dynamixel background thread class declarations.
bool m_bOverTorqueCond
is [not] in torque overload condition
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.
uint_t m_uRawPosModulo
raw position modulo
virtual void UnregisterAgent()
Unregister any chain or servo from control and monitoring.
#define DYNA_ECODE_GEN
general, unspecified error
bool_t m_bTorqueEnabled
torque [not] enabled
virtual void RegisterServoAgent(DynaServo *pServo)
Register the Dynamixel servo for control and monitoring.
BgThreadState m_eState
thread state
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.
The libDynamixel internal declarations.
pthread_mutex_t m_mutexSync
synchonization mutex
int getGoalDir(DynaServo *pServo)
Get the current goal rotation direction.
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
static const int GOAL_SPEED_DFT
default virutal goal speed
Background Thread Virtual Servo Class.
#define DYNA_MODE_CONTINUOUS
continuous mode with/without position
Dynamixel Chain Container Base Class.
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
bool IsOdometerReversed()
Test if the virtual odometer is reversed.
virtual void monitorDynamics(DynaServo *pServo, DynaVServo *pVServo)
Execute servo dynamics monitoring background task.
virtual const DynaServoSpec_T & GetSpecification() const
Get servo specification.
DynaBgThread(double fHz=HZ_EXEC_DFT, double fTolerance=TOLERANCE_DFT)
Default initializer constructor.
RoadNarrows Dynamixel Archetype Servo Abstract Base Class.
no active position control
virtual int Pause()
Pause control and monitoring tasks.
virtual bool HasSoftTorqueOverCond()
Test if servo is in a soft over torque condition.
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.
#define BG_TRACE_CLOSE()
No tracing file close.
RoadNarrows Dynamixel Top-Level Package Header File.
int m_nOdGoalPos
current goal position
virtual uint_t GetServoId() const
Get servo id.
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)
void lock()
Lock the virtual servo.
#define DYNA_ID_NONE
no servo id
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.
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.
DynaPidPos m_pidPos
servo position PID
virtual void monitorHealth(DynaServo *pServo, DynaVServo *pVServo)
Execute servo health monitoring background task.
double m_fAngleMax
max rotation angle in servo mode (deg)
virtual void exec()
Execute background tasks.
virtual ~DynaBgThread()
Desctructor.
RoadNarrows Dynamixel Library Error and Logging Routines.
int GetOdometer()
Get the current virtual odometer value.
virtual void stopPosCtl(DynaServo *pServo, DynaVServo *pVServo)
Stop servo position control.
#define DYNA_SPEED_CONT_STOP
continuous mode: stop
virtual void GetSoftTorqueThresholds(uint_t &uOverTorqueTh, uint_t &uClearTorqueTh)
Get soft torque thresholds.
int m_nTolerance
position ± tolerance (ticks)
virtual int Resume()
Resume control and monitoring tasks.
void readyWait()
Wait indefinitely in ready state.