11#include <yarp/os/LogStream.h>
12#include <yarp/os/Value.h>
70 static constexpr std::string_view
kClassName =
"CiA402MotionControl";
174 target.assign(n, VOCAB_CM_IDLE);
225 std::fill(this->jointTorques.
begin(), this->jointTorques.end(), 0.0);
226 std::fill(this->jointVelocities.
begin(), this->jointVelocities.end(), 0.0);
227 std::fill(this->motorCurrents.
begin(), this->motorCurrents.end(), 0.0);
228 std::fill(this->hasTorqueSP.
begin(), this->hasTorqueSP.end(),
false);
229 std::fill(this->hasCurrentSP.
begin(), this->hasCurrentSP.end(),
false);
231 std::fill(this->ppJointTargetsDeg.
begin(), this->ppJointTargetsDeg.end(), 0.0);
232 std::fill(this->ppTargetCounts.
begin(), this->ppTargetCounts.end(), 0);
233 std::fill(this->ppHasPosSP.
begin(), this->ppHasPosSP.end(),
false);
234 std::fill(this->ppIsRelative.
begin(), this->ppIsRelative.end(),
false);
236 std::fill(this->ppPulseCoolDown.
begin(), this->ppPulseCoolDown.end(),
false);
239 this->positionDirectJointTargetsDeg.end(),
242 this->positionDirectTargetCounts.end(),
249 this->jointTorques[axis] = 0.0;
250 this->jointVelocities[axis] = 0.0;
251 this->motorCurrents[axis] = 0.0;
252 this->hasTorqueSP[axis] =
false;
253 this->hasCurrentSP[axis] =
false;
254 this->ppJointTargetsDeg[axis] = 0.0;
255 this->ppTargetCounts[axis] = 0;
256 this->hasVelSP[axis] =
false;
257 this->ppHasPosSP[axis] =
false;
258 this->ppIsRelative[axis] =
false;
259 this->ppPulseHi[axis] =
false;
260 this->ppPulseCoolDown[axis] =
false;
261 this->positionDirectJointTargetsDeg[axis] = 0.0;
262 this->positionDirectTargetCounts[axis] = 0;
299 yarp::dev::InteractionModeEnum::VOCAB_IM_STIFF};
333 double shaftDeg = jointDeg;
338 const double cnt = (res ? (shaftDeg / 360.0) *
static_cast<double>(res) : 0.0);
339 long long q = llround(cnt);
344 return static_cast<int32_t
>(q);
354 const int32_t cAdj = inv ? -counts : counts;
383 const double shaftDeg = res ? (double(cAdj) / double(res)) * 360.0 : 0.0;
395 constexpr auto logPrefix =
"[setPositionCountsLimits]";
403 "%s: setLimits: SDO write 0x607D:01 (min) failed for axis %d",
416 "%s: setLimits: SDO write 0x607D:02 (max) failed for axis %d",
430 constexpr auto logPrefix =
"[readEncoderResolutions]";
437 for (
size_t j = 0; j <
numAxes; ++j)
449 for (
size_t j = 0; j <
numAxes; ++j)
456 yCInfo(CIA402,
"%s successfully read encoder resolutions from SDO", logPrefix);
457 for (
size_t j = 0; j <
numAxes; ++j)
460 "%s j=%zu enc1_resolution=%u (inv=%.9f), enc2_resolution=%u (inv=%.9f)",
476 if (v == 0x00B44700u)
477 return {6.0, 1.0 / 6.0,
"1 RPM"};
478 if (v == 0xFFB44700u)
479 return {0.6, 1.0 / 0.6,
"0.1 RPM"};
480 if (v == 0xFEB44700u)
481 return {0.06, 1.0 / 0.06,
"0.01 RPM"};
482 if (v == 0xFDB44700u)
483 return {0.006, 1.0 / 0.006,
"0.001 RPM"};
485 if (v == 0x00B40300u)
486 return {360.0, 1.0 / 360.0,
"1 RPS"};
487 if (v == 0xFFB40300u)
488 return {36.0, 1.0 / 36.0,
"0.1 RPS"};
489 if (v == 0xFEB40300u)
490 return {3.6, 1.0 / 3.6,
"0.01 RPS"};
491 if (v == 0xFDB40300u)
492 return {0.36, 1.0 / 0.36,
"0.001 RPS"};
495 return {6.0, 1.0 / 6.0,
"UNKNOWN (assume 1 RPM)"};
500 constexpr auto logPrefix =
"[readSiVelocityUnits]";
505 for (
size_t j = 0; j <
numAxes; ++j)
510 uint32_t raw = 0x00B44700u;
513 auto [toDegS, toDev, name] = this->
decode60A9(raw);
519 "%s j=%zu 0x60A9=0x%08X velocity_unit=%s (1_unit=%.6f_deg/s)",
526 yWarning(
"%s j=%zu failed to read 0x60A9, assuming 1_RPM (1_unit=6_deg/s)",
531 yCInfo(CIA402,
"%s successfully read velocity conversion units from SDO", logPrefix);
566 const double shaftDeg = (counts / double(res)) * 360.0;
570 "loopCountsToJointDeg j=%zu counts=%.3f res=%u mount=%d -> shaftDeg=%.9f",
574 static_cast<int>(mount),
584 if (j < 0 || j >=
static_cast<int>(this->
numAxes))
587 "%s: setPositionWindowDeg: invalid joint index",
597 rawWin = 0xFFFFFFFFu;
600 rawWin =
static_cast<uint32_t
>(
605 auto e1 = this->ethercatManager.
writeSDO<uint32_t>(s, 0x6067, 0x00, rawWin);
609 "%s: setPositionWindowDeg: SDO 0x6067 write failed on joint %d",
614 auto e2 = this->ethercatManager.
writeSDO<uint32_t>(s, 0x6068, 0x00, rawTime);
618 "%s: setPositionWindowDeg: SDO 0x6068 write failed on joint %d",
628 constexpr auto logPrefix =
"[getPositionControlStrategy]";
633 =
ethercatManager.readSDO<uint16_t>(slaveIdx, 0x2002, 0x00, strategyValue);
637 "%s failed to read 0x2002:00",
643 "%s read position strategy %u from first axis",
645 static_cast<unsigned>(strategyValue));
651 constexpr auto logPrefix =
"[setPositionControlStrategy]";
653 for (
size_t j = 0; j <
numAxes; ++j)
655 const int slaveIdx =
firstSlave +
static_cast<int>(j);
657 =
ethercatManager.writeSDO<uint16_t>(slaveIdx, 0x2002, 0x00, strategyValue);
661 "%s j=%zu failed to set 0x2002:00 (value=%u)",
664 static_cast<unsigned>(strategyValue));
670 "%s configured %zu axes with position strategy %u",
673 static_cast<unsigned>(strategyValue));
681 constexpr auto logPrefix =
"[readSimplePidGains]";
686 for (
size_t j = 0; j <
numAxes; ++j)
688 const int slaveIdx =
firstSlave +
static_cast<int>(j);
689 float32 kpValue = 0.0f;
690 float32 kdValue = 0.0f;
692 const auto errKp =
ethercatManager.readSDO<float32>(slaveIdx, 0x2012, 0x01, kpValue);
693 const auto errKd =
ethercatManager.readSDO<float32>(slaveIdx, 0x2012, 0x03, kdValue);
699 "%s j=%zu failed to read gains (errs=%d,%d)",
702 static_cast<int>(errKp),
703 static_cast<int>(errKd));
708 const double kpMotor_mNm_per_deg =
static_cast<double>(kpValue) / degPerCount;
709 const double kdMotor_mNmS_per_deg =
static_cast<double>(kdValue) / degPerCount;
711 kpNmPerDeg[j] = (kpMotor_mNm_per_deg *
gearRatio[j]) / 1000.0;
712 kdNmSecPerDeg[j] = (kdMotor_mNmS_per_deg *
gearRatio[j]) / 1000.0;
715 "%s j=%zu read gains: kp_device=%.3f[mNm/inc], kd_device=%.3f[mNm*s/inc] "
716 "=> kp_motor=%.6f[mNm/deg], kd_motor=%.6f[mNm*s/deg] => "
717 "kp_joint=%.6f[Nm/deg], kd_joint=%.6f[Nm*s/deg]",
723 kdMotor_mNmS_per_deg,
727 yCInfo(CIA402,
"%s read simple PID gains for %zu axes", logPrefix,
numAxes);
734 constexpr auto logPrefix =
"[configureSimplePidGains]";
739 "%s mismatched gain vector sizes (kp=%zu kd=%zu expected=%zu)",
742 kdNmSecPerDeg.
size(),
747 for (
size_t j = 0; j <
numAxes; ++j)
751 yCError(CIA402,
"%s j=%zu invalid gear ratio (0)", logPrefix, j);
756 if (degPerCount == 0.0)
759 "%s j=%zu cannot derive counts/deg (loop source missing)",
764 const double kpMotor_mNm_per_deg = (kpNmPerDeg[j] /
gearRatio[j]) * 1000.0;
765 const double kdMotor_mNmS_per_deg = (kdNmSecPerDeg[j] /
gearRatio[j]) * 1000.0;
767 const double kpDevice = kpMotor_mNm_per_deg * degPerCount;
768 const double kdDevice = kdMotor_mNmS_per_deg * degPerCount;
770 const float32 kpValue =
static_cast<float32
>(kpDevice);
771 const float32 kdValue =
static_cast<float32
>(kdDevice);
772 constexpr float32 kiValue = 0.0f;
776 "%s j=%zu computing gains: kp_joint=%.6f[Nm/deg], kd_joint=%.6f[Nm*s/deg], "
777 "gearRatio=%.6f, degPerCount=%.9f => kp_motor=%.6f[mNm/deg], "
778 "kd_motor=%.6f[mNm*s/deg] => kp_device=%.3f[mNm/inc], kd_device=%.3f[mNm*s/inc]",
786 kdMotor_mNmS_per_deg,
790 const int slaveIdx =
firstSlave +
static_cast<int>(j);
791 const auto errKp =
ethercatManager.writeSDO<float32>(slaveIdx, 0x2012, 0x01, kpValue);
792 const auto errKi =
ethercatManager.writeSDO<float32>(slaveIdx, 0x2012, 0x02, kiValue);
793 const auto errKd =
ethercatManager.writeSDO<float32>(slaveIdx, 0x2012, 0x03, kdValue);
800 "%s j=%zu failed to program gains (errs=%d,%d,%d). Gains "
801 "Kp=%.3f[mNm/inc]=%.3f[Nm/deg], Kd=%.3f[mNm*s/inc]=%.3f[Nm*s/deg]",
804 static_cast<int>(errKp),
805 static_cast<int>(errKi),
806 static_cast<int>(errKd),
815 "%s j=%zu Kp=%.3f[mNm/inc] Kd=%.3f[mNm*s/inc]",
822 yCInfo(CIA402,
"%s programmed simple PID gains for %zu axes", logPrefix,
numAxes);
828 constexpr auto logPrefix =
"[readMotorConstants]";
833 for (
size_t j = 0; j <
numAxes; ++j)
836 int32_t tcMicroNmA = 0;
841 yWarning(
"%s j=%zu cannot read torque constant (0x2003:02) or zero, assuming 1.0 "
854 uint32_t ratedCurrentMilliA = 0;
855 double ratedCurrentA = 0.0;
856 if (
ethercatManager.readSDO<uint32_t>(s, 0x6075, 0x00, ratedCurrentMilliA)
858 || ratedCurrentMilliA == 0)
860 yWarning(
"%s j=%zu cannot read motor rated current (0x6075:00) or zero, assuming "
868 ratedCurrentA = double(ratedCurrentMilliA) * 1e-3;
872 uint16_t maxPermille = 0;
876 yWarning(
"%s j=%zu cannot read max current (0x6073:00), assuming 1000 per_mille",
883 "%s j=%zu max_current=%u_permille (%.3fA), torque_constant=%.6f_Nm/A",
887 (
double(maxPermille) / 1000.0) * ratedCurrentA,
888 this->torqueConstants[j]);
890 maxCurrentsA[j] = (double(maxPermille) / 1000.0) * ratedCurrentA;
893 yCInfo(CIA402,
"%s successfully read motor constants from SDO", logPrefix);
908 constexpr auto logPrefix =
"[readGearRatios]";
913 for (
size_t j = 0; j <
numAxes; ++j)
915 const int slaveIdx =
firstSlave +
static_cast<int>(j);
924 yWarning(
"%s j=%zu gear_ratio_numerator not available (0x6091:01), assuming 1",
935 yWarning(
"%s j=%zu gear_ratio_denominator not available/zero (0x6091:02), assuming "
943 "%s j=%zu gear_ratio=%u:%u (ratio=%.6f)",
948 static_cast<double>(num) /
static_cast<double>(den));
951 gearRatio[j] =
static_cast<double>(num) /
static_cast<double>(den);
955 for (
size_t j = 0; j <
numAxes; ++j)
960 yCInfo(CIA402,
"%s successfully read gear ratios from SDO", logPrefix);
966 constexpr auto logPrefix =
"[readTorqueValues]";
971 for (
size_t j = 0; j <
numAxes; ++j)
974 uint32_t rated_mNm = 0;
978 yCError(CIA402,
"%s j=%zu cannot read rated torque (0x6076:00)", logPrefix, j);
983 for (
size_t j = 0; j <
numAxes; ++j)
986 uint16_t maxPerm = 0;
990 yCError(CIA402,
"%s j=%zu cannot read max torque (0x6072:00)", logPrefix, j);
995 "%s j=%zu motor_rated_torque=%.3fNm max_torque=%.3fNm",
1002 yCInfo(CIA402,
"%s successfully read torque values from SDO", logPrefix);
1008 constexpr auto logPrefix =
"[configureMaxTorqueFromJointNm]";
1013 "%s invalid vector size (%zu), expected %zu",
1015 maxJointTorqueNm.
size(),
1020 for (
size_t j = 0; j <
numAxes; ++j)
1022 const double jointNm = maxJointTorqueNm[j];
1026 "%s j=%zu invalid max joint torque %.6f Nm (must be > 0)",
1036 "%s j=%zu invalid gear ratio %.6f",
1046 "%s j=%zu invalid rated motor torque %.6f Nm",
1053 constexpr uint16_t maxTorqueAdmissibleValue = 32767;
1054 const double motorNm = jointNm /
gearRatio[j];
1056 const double clamped = std::clamp(perThousand, 0.0,
static_cast<double>(maxTorqueAdmissibleValue));
1057 const uint16_t maxPerm =
static_cast<uint16_t
>(
std::llround(clamped));
1058 const int s =
firstSlave +
static_cast<int>(j);
1060 const auto err =
ethercatManager.writeSDO<uint16_t>(s, 0x6072, 0x00, maxPerm);
1064 "%s j=%zu failed to write 0x6072:00",
1073 "%s j=%zu configured 0x6072 to %u permille (joint=%.6f Nm, motor=%.6f Nm)",
1076 static_cast<unsigned>(maxPerm),
1087 double shaft_deg_s = spDegS;
1088 switch (this->posLoopSrc[j])
1092 shaft_deg_s = spDegS * this->gearRatio[j];
1094 shaft_deg_s = spDegS;
1098 shaft_deg_s = spDegS * this->gearRatio[j];
1100 shaft_deg_s = spDegS;
1108 shaft_deg_s = spDegS * this->gearRatio[j];
1114 const int32_t vel =
static_cast<int32_t
>(
std::llround(shaft_deg_s * this->degSToVel[j]));
1115 (void)this->ethercatManager.
writeSDO<int32_t>(s, 0x6081, 0x00, vel);
1128 for (
size_t j = 0; j < this->
numAxes; ++j)
1131 auto rx = this->ethercatManager.
getRxPDO(s);
1132 const int opMode = this->controlModeState.
active[j];
1135 rx->TargetPosition = 0;
1136 rx->TargetTorque = 0;
1137 rx->TargetVelocity = 0;
1140 if (opMode == VOCAB_CM_TORQUE)
1142 if (!this->trqLatched[j])
1144 rx->TargetTorque = 0;
1145 this->trqLatched[j] =
true;
1154 const int16_t tq_thousand =
static_cast<int16_t
>(
std::llround(
1157 rx->TargetTorque = this->invertedMotionSenseDirection[j] ? -tq_thousand
1163 if (opMode == VOCAB_CM_VELOCITY)
1167 rx->TargetVelocity = 0;
1178 double shaft_deg_s = joint_deg_s;
1186 shaft_deg_s = joint_deg_s *
gearRatio[j];
1188 shaft_deg_s = joint_deg_s;
1194 shaft_deg_s = joint_deg_s *
gearRatio[j];
1196 shaft_deg_s = joint_deg_s;
1208 const double vel = shaft_deg_s * this->degSToVel[j];
1209 rx->TargetVelocity =
static_cast<int32_t
>(
std::llround(vel))
1210 * (this->invertedMotionSenseDirection[j] ? -1 : 1);
1215 if (opMode == VOCAB_CM_POSITION)
1219 rx->Controlword |= (1u << 8);
1221 rx->Controlword &= ~(1u << 8);
1226 rx->Controlword |= (1u << 5);
1227 rx->Controlword &= ~(1u << 4);
1231 const double currentJointDeg = this->variables.
jointPositions[j];
1232 const int32_t seedStoreCounts
1240 setPoints.ppTargetCounts[j] = seedStoreCounts;
1241 setPoints.ppJointTargetsDeg[j] = currentJointDeg;
1247 const int32_t driveTargetCounts = this->invertedMotionSenseDirection[j]
1250 rx->TargetPosition = driveTargetCounts;
1261 rx->Controlword |= (1u << 5);
1266 rx->Controlword &= ~(1u << 4);
1271 int32_t targetPositionCounts = 0;
1276 rx->Controlword |= (1u << 6);
1278 rx->Controlword &= ~(1u << 6);
1281 targetPositionCounts = this->invertedMotionSenseDirection[j]
1286 rx->Controlword |= (1u << 4);
1294 auto tx = this->ethercatManager.
getTxView(s);
1295 targetPositionCounts = this->invertedMotionSenseDirection[j]
1301 targetPositionCounts);
1304 setPoints.ppTargetCounts[j] = this->invertedMotionSenseDirection[j]
1305 ? -targetPositionCounts
1306 : targetPositionCounts;
1312 rx->TargetPosition = targetPositionCounts;
1317 if (opMode == VOCAB_CM_POSITION_DIRECT)
1321 const double currentJointDeg = this->variables.
jointPositions[j];
1323 setPoints.positionDirectJointTargetsDeg[j] = currentJointDeg;
1324 setPoints.positionDirectTargetCounts[j] = seedCounts;
1328 const int32_t driveCounts = this->invertedMotionSenseDirection[j]
1329 ? -
setPoints.positionDirectTargetCounts[j]
1330 :
setPoints.positionDirectTargetCounts[j];
1331 rx->TargetPosition = driveCounts;
1334 if (opMode == VOCAB_CM_CURRENT)
1336 if (!this->currLatched[j])
1339 rx->TargetTorque = 0;
1340 this->currLatched[j] =
true;
1348 const double torqueNm = currentA * this->torqueConstants[j];
1351 const int16_t tq_thousand =
static_cast<int16_t
>(
std::llround(
1354 rx->TargetTorque = this->invertedMotionSenseDirection[j] ? -tq_thousand
1373 auto shaftFromMount_pos = [&](
double deg,
Mount m,
size_t j,
bool asMotor) ->
double {
1380 return asMotor ? deg
1393 auto shaftFromMount_vel = [&](
double degs,
Mount m,
size_t j,
bool asMotor) ->
double {
1400 return asMotor ? degs *
gearRatio[j] : degs;
1405 for (
size_t j = 0; j < this->
numAxes; ++j)
1408 auto tx = this->ethercatManager.
getTxView(s);
1487 const double k = this->velToDegS[j];
1511 return {double(v606C) * k,
enc1Mount[j]};
1513 return {double(v606C) * k,
enc2Mount[j]};
1519 return {double(v606C) * k,
enc2Mount[j]};
1522 return {double(v606C) * k,
enc1Mount[j]};
1524 return {double(v606C) * k,
enc2Mount[j]};
1536 auto [degJ_src, mountJ] = posDegOnOwnShaft(
posSrcJoint[j]);
1537 auto [degM_src, mountM] = posDegOnOwnShaft(
posSrcMotor[j]);
1540 const double jointDeg = shaftFromMount_pos(degJ_src, mountJ, j,
false);
1541 const double motorDeg = shaftFromMount_pos(degM_src, mountM, j,
true);
1545 = this->invertedMotionSenseDirection[j] ? -jointDeg : jointDeg;
1547 = this->invertedMotionSenseDirection[j] ? -motorDeg : motorDeg;
1556 auto [degsJ_src, mountJ] = velDegSOnOwnShaft(
velSrcJoint[j]);
1558 auto [degsM_src, mountM] = velDegSOnOwnShaft(
velSrcMotor[j]);
1562 const double jointDegS
1563 = shaftFromMount_vel(degsJ_src, mountJ, j,
false);
1564 const double motorDegS = shaftFromMount_vel(degsM_src, mountM, j,
true);
1568 = this->invertedMotionSenseDirection[j] ? -jointDegS : jointDegS;
1570 = this->invertedMotionSenseDirection[j] ? -motorDegS : motorDegS;
1583 const double tq_per_thousand
1585 const double motorNm = tq_per_thousand * this->ratedMotorTorqueNm[j];
1587 const double signedMotorNm = this->invertedMotionSenseDirection[j] ? -motorNm : motorNm;
1588 this->variables.
jointTorques[j] = signedMotorNm * this->gearRatio[j];
1589 this->variables.
motorCurrents[j] = signedMotorNm / this->torqueConstants[j];
1610 const uint32_t back = this->tsLastRaw[j] - raw;
1613 this->tsWraps[j] += 1u;
1617 this->tsLastRaw[j] = raw;
1619 const uint64_t us_ext
1647 case VOCAB_CM_FORCE_IDLE:
1649 case VOCAB_CM_POSITION:
1651 case VOCAB_CM_VELOCITY:
1653 case VOCAB_CM_TORQUE:
1654 case VOCAB_CM_CURRENT:
1656 case VOCAB_CM_POSITION_DIRECT:
1670 return (cstFlavor == VOCAB_CM_CURRENT) ? VOCAB_CM_CURRENT : VOCAB_CM_TORQUE;
1681 return VOCAB_CM_IDLE;
1683 return VOCAB_CM_POSITION;
1685 return VOCAB_CM_VELOCITY;
1687 return VOCAB_CM_POSITION_DIRECT;
1689 return VOCAB_CM_VELOCITY;
1691 return VOCAB_CM_TORQUE;
1693 return VOCAB_CM_UNKNOWN;
1703 return "VOCAB_CM_IDLE";
1704 case VOCAB_CM_FORCE_IDLE:
1705 return "VOCAB_CM_FORCE_IDLE";
1706 case VOCAB_CM_POSITION:
1707 return "VOCAB_CM_POSITION";
1708 case VOCAB_CM_VELOCITY:
1709 return "VOCAB_CM_VELOCITY";
1710 case VOCAB_CM_TORQUE:
1711 return "VOCAB_CM_TORQUE";
1712 case VOCAB_CM_CURRENT:
1713 return "VOCAB_CM_CURRENT";
1714 case VOCAB_CM_POSITION_DIRECT:
1715 return "VOCAB_CM_POSITION_DIRECT";
1734 return "Continuous over current (device internal)";
1736 return "Short circuit (device internal)";
1738 return "Load level fault (I2t, thermal state)";
1740 return "Load level warning (I2t, thermal state)";
1742 return "Phase failure";
1744 return "Phase failure L1";
1746 return "Phase failure L2";
1748 return "Phase failure L3";
1750 return "DC link over-voltage";
1752 return "DC link under-voltage";
1754 return "Field circuit interrupted";
1756 return "Excess temperature device";
1758 return "Excess temperature drive";
1762 return "Operating unit";
1764 return "Software reset (watchdog)";
1766 return "Parameter error";
1768 return "Motor blocked";
1772 return "Resolver 1 fault";
1774 return "Resolver 2 fault";
1776 return "Communication";
1778 return "Positioning controller (reference limit)";
1780 return "Sub-synchronous run";
1784 switch (code & 0xFF00)
1787 return "Current/device-internal fault";
1789 return "Motor/output circuit fault";
1791 return "Phase/mains supply issue";
1793 return "DC link voltage issue";
1795 return "Field/armature circuit issue";
1797 return "Excess temperature (device)";
1799 return "Excess temperature (drive)";
1801 return "Control device hardware / limits";
1803 return "Operating unit / safety";
1805 return "Software reset / watchdog";
1807 return "Parameter/configuration error";
1809 return "Motor blocked / mechanical issue";
1811 return "Sensor/encoder fault";
1813 return "Communication/internal";
1815 return "Positioning controller (vendor specific)";
1817 return "Timing/performance warning";
1820 std::snprintf(buf,
sizeof(buf),
"Unknown 0x603F error: 0x%04X", code);
1832 :
yarp::os::PeriodicThread(period, useSysClock,
yarp::os::PeriodicThreadClock::Absolute)
1833 , m_impl(
std::make_unique<
Impl>())
1838 :
yarp::os::PeriodicThread(0.001 ,
1839 yarp::os::ShouldUseSystemClock::Yes,
1840 yarp::os::PeriodicThreadClock::Absolute)
1841 , m_impl(
std::make_unique<
Impl>())
1851 constexpr auto logPrefix =
"[open]";
1856 if (!cfg.check(
"ifname") || !cfg.find(
"ifname").isString())
1858 yCError(CIA402,
"%s: 'ifname' parameter is not a string",
Impl::kClassName.data());
1861 if (!cfg.check(
"num_axes") || !cfg.find(
"num_axes").isInt32())
1863 yCError(CIA402,
"%s: 'num_axes' parameter is not an integer",
Impl::kClassName.data());
1867 if (!cfg.check(
"period") || !cfg.find(
"period").isFloat64())
1869 yCError(CIA402,
"%s: 'period' parameter is not a float64",
Impl::kClassName.data());
1872 const double period = cfg.find(
"period").asFloat64();
1875 yCError(CIA402,
"%s: 'period' parameter must be positive",
Impl::kClassName.data());
1878 this->setPeriod(period);
1879 yCDebug(CIA402,
"%s: using period = %.6f s", logPrefix, period);
1881 m_impl->numAxes =
static_cast<size_t>(cfg.find(
"num_axes").asInt32());
1882 m_impl->firstSlave = cfg.check(
"first_slave", yarp::os::Value(1)).asInt32();
1883 if (cfg.check(
"expected_slave_name"))
1884 m_impl->expectedName = cfg.find(
"expected_slave_name").asString();
1921 yWarning(
"%s: invalid mount '%s' (allowed: none|motor|joint) → 'none'",
1927 auto extractListOfStringFromSearchable = [
this](
const yarp::os::Searchable& cfg,
1933 if (!cfg.check(key))
1939 const yarp::os::Value& v = cfg.find(key);
1942 yCError(CIA402,
"%s: key '%s' is not a list",
Impl::kClassName.data(), key);
1946 const yarp::os::Bottle* lst = v.asList();
1950 "%s: internal error: list for key '%s' is null",
1956 const size_t expected =
static_cast<size_t>(m_impl->numAxes);
1957 const size_t actual =
static_cast<size_t>(lst->size());
1958 if (actual != expected)
1961 "%s: list for key '%s' has incorrect size (%zu), expected (%zu)",
1969 result.reserve(expected);
1970 for (
int i = 0; i < lst->size(); ++i)
1972 const yarp::os::Value& elem = lst->get(i);
1974 if (!elem.isString())
1977 "%s: element %d in list for key '%s' is not a string",
1988 if (!acceptedKeys.empty())
1990 if (
std::find(acceptedKeys.begin(), acceptedKeys.end(), val) == acceptedKeys.end())
1993 "%s: invalid value '%s' in list for key '%s'",
2007 auto extractListOfDoubleFromSearchable = [
this](
const yarp::os::Searchable& cfg,
2012 if (!cfg.check(key))
2018 const yarp::os::Value& v = cfg.find(key);
2021 yCError(CIA402,
"%s: key '%s' is not a list",
Impl::kClassName.data(), key);
2025 const yarp::os::Bottle* lst = v.asList();
2029 "%s: internal error: list for key '%s' is null",
2035 const size_t expected =
static_cast<size_t>(m_impl->numAxes);
2036 const size_t actual =
static_cast<size_t>(lst->size());
2037 if (actual != expected)
2040 "%s: list for key '%s' has incorrect size (%zu), expected (%zu)",
2048 result.reserve(expected);
2049 for (
int i = 0; i < lst->size(); ++i)
2051 const yarp::os::Value& elem = lst->get(i);
2053 if (!elem.isFloat64() && !elem.isInt32() && !elem.isInt64() && !elem.isFloat32())
2056 "%s: element %d in list for key '%s' is not a number",
2064 result.push_back(elem.asFloat64());
2070 auto extractListOfBoolFromSearchable = [
this](
const yarp::os::Searchable& cfg,
2075 if (!cfg.check(key))
2081 const yarp::os::Value& v = cfg.find(key);
2084 yCError(CIA402,
"%s: key '%s' is not a list",
Impl::kClassName.data(), key);
2088 const yarp::os::Bottle* lst = v.asList();
2092 "%s: internal error: list for key '%s' is null",
2098 const size_t expected =
static_cast<size_t>(m_impl->numAxes);
2099 const size_t actual =
static_cast<size_t>(lst->size());
2100 if (actual != expected)
2103 "%s: list for key '%s' has incorrect size (%zu), expected (%zu)",
2111 result.reserve(expected);
2112 for (
int i = 0; i < lst->size(); ++i)
2114 const yarp::os::Value& elem = lst->get(i);
2119 "%s: element %d in list for key '%s' is not a boolean",
2127 result.push_back(elem.asBool());
2139 if (!extractListOfStringFromSearchable(cfg,
"enc1_mount", {
"motor",
"joint"}, enc1MStr))
2141 if (!extractListOfStringFromSearchable(cfg,
"enc2_mount", {
"motor",
"joint",
"none"}, enc2MStr))
2148 if (!extractListOfStringFromSearchable(cfg,
2149 "position_feedback_joint",
2150 {
"6064",
"enc1",
"enc2"},
2153 if (!extractListOfStringFromSearchable(cfg,
2154 "position_feedback_motor",
2155 {
"6064",
"enc1",
"enc2"},
2158 if (!extractListOfStringFromSearchable(cfg,
2159 "velocity_feedback_joint",
2160 {
"606C",
"enc1",
"enc2"},
2163 if (!extractListOfStringFromSearchable(cfg,
2164 "velocity_feedback_motor",
2165 {
"606C",
"enc1",
"enc2"},
2175 constexpr uint16_t kPositionStrategySimplePid = 1u;
2176 constexpr uint16_t kPositionStrategyCascadedPid = 2u;
2177 const bool useSimplePidMode
2178 = cfg.check(
"use_simple_pid_mode") ? cfg.find(
"use_simple_pid_mode").asBool() :
false;
2179 const uint16_t desiredPositionStrategy
2180 = useSimplePidMode ? kPositionStrategySimplePid : kPositionStrategyCascadedPid;
2182 if (!extractListOfDoubleFromSearchable(cfg,
"position_window_deg", positionWindowDeg))
2184 if (!extractListOfDoubleFromSearchable(cfg,
"timing_window_ms", timingWindowMs))
2187 const bool hasMaxJointTorqueCfg = cfg.check(
"max_torque_joint_nm");
2188 if (hasMaxJointTorqueCfg
2189 && !extractListOfDoubleFromSearchable(cfg,
2190 "max_torque_joint_nm",
2191 maxJointTorqueNmFromConfig))
2196 const bool hasSimplePidKpCfg = cfg.check(
"simple_pid_kp_nm_per_deg");
2197 const bool hasSimplePidKdCfg = cfg.check(
"simple_pid_kd_nm_s_per_deg");
2198 const bool programSimplePidGains = hasSimplePidKpCfg || hasSimplePidKdCfg;
2199 if (hasSimplePidKpCfg != hasSimplePidKdCfg)
2202 "%s configuration must provide both simple_pid_kp_nm_per_deg and "
2203 "simple_pid_kd_nm_s_per_deg",
2207 if (programSimplePidGains)
2209 if (!extractListOfDoubleFromSearchable(cfg,
"simple_pid_kp_nm_per_deg", simplePidKpNmPerDeg))
2211 if (!extractListOfDoubleFromSearchable(cfg,
2212 "simple_pid_kd_nm_s_per_deg",
2213 simplePidKdNmSecPerDeg))
2217 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2219 m_impl->enc1Mount.push_back(parseMount(enc1MStr[j]));
2220 m_impl->enc2Mount.push_back(parseMount(enc2MStr[j]));
2221 m_impl->posSrcJoint.push_back(parsePos(posSrcJointStr[j]));
2222 m_impl->posSrcMotor.push_back(parsePos(posSrcMotorStr[j]));
2223 m_impl->velSrcJoint.push_back(parseVel(velSrcJointStr[j]));
2224 m_impl->velSrcMotor.push_back(parseVel(velSrcMotorStr[j]));
2229 const int pdoTimeoutUs = cfg.check(
"pdo_timeout_us") ? cfg.find(
"pdo_timeout_us").asInt32()
2231 const bool enableDc = cfg.check(
"enable_dc") ? cfg.find(
"enable_dc").asBool() :
true;
2232 const int dcShiftNs = cfg.check(
"dc_shift_ns") ? cfg.find(
"dc_shift_ns").asInt32() : 0;
2233 yCInfo(CIA402,
"%s opening EtherCAT manager on interface %s", logPrefix, ifname.
c_str());
2235 if (!extractListOfBoolFromSearchable(cfg,
2236 "inverted_motion_sense_direction",
2237 m_impl->invertedMotionSenseDirection))
2242 for (
size_t i = 0; i < v.size(); ++i)
2244 s += (v[i] ?
"true" :
"false");
2245 if (i + 1 < v.size())
2252 "%s: inverted_motion_sense_direction = [%s]",
2254 vecBoolToString(m_impl->invertedMotionSenseDirection).c_str());
2260 const auto ecErr = m_impl->ethercatManager.init(ifname);
2264 "%s EtherCAT initialization failed with error code %d",
2266 static_cast<int>(ecErr));
2270 if (pdoTimeoutUs > 0)
2272 m_impl->ethercatManager.setPdoTimeoutUs(pdoTimeoutUs);
2274 "%s PDO receive timeout set to %d us",
2276 m_impl->ethercatManager.getPdoTimeoutUs());
2280 "%s using default PDO receive timeout of %d us",
2282 m_impl->ethercatManager.getPdoTimeoutUs());
2286 const uint32_t cycleNs =
static_cast<uint32_t
>(
std::llround(this->getPeriod() * 1e9));
2296 const int s = m_impl->firstSlave + int(j);
2298 auto e = m_impl->ethercatManager.readSDO<uint8_t>(s, 0x2012, 0x09, src);
2302 "%s: failed to read position loop source (joint %zu)",
2312 const int s = m_impl->firstSlave + int(j);
2314 auto e = m_impl->ethercatManager.readSDO<uint8_t>(s, 0x2011, 0x05, src);
2325 auto hasEnc1Pos = [&](
size_t j) {
2326 const int s = m_impl->firstSlave + int(j);
2329 auto hasEnc1Vel = [&](
size_t j) {
2330 const int s = m_impl->firstSlave + int(j);
2333 auto hasEnc2Pos = [&](
size_t j) {
2334 const int s = m_impl->firstSlave + int(j);
2337 auto hasEnc2Vel = [&](
size_t j) {
2338 const int s = m_impl->firstSlave + int(j);
2343 "%s using %zu axes, EtherCAT slaves %d to %d",
2347 m_impl->firstSlave +
int(m_impl->numAxes) - 1);
2353 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2355 m_impl->posLoopSrc[j] = readPosLoopSrc(j);
2356 m_impl->velLoopSrc[j] = readVelLoopSrc(j);
2364 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2366 auto bad = [&](
const char* what) {
2367 yCError(CIA402,
"%s j=%zu invalid configuration: %s", logPrefix, j, what);
2375 if (bad(
"pos_joint=enc1 but enc1 not mounted/mapped"))
2381 if (bad(
"pos_joint=enc2 but enc2 not mounted/mapped"))
2388 if (bad(
"pos_motor=enc1 but enc1 not mounted/mapped"))
2394 if (bad(
"pos_motor=enc2 but enc2 not mounted/mapped"))
2402 if (bad(
"vel_joint=enc1 but enc1 not mounted/mapped"))
2408 if (bad(
"vel_joint=enc2 but enc2 not mounted/mapped"))
2415 if (bad(
"vel_motor=enc1 but enc1 not mounted/mapped"))
2421 if (bad(
"vel_motor=enc2 but enc2 not mounted/mapped"))
2429 if (!m_impl->readEncoderResolutions())
2431 yCError(CIA402,
"%s failed to read encoder resolutions from SDO", logPrefix);
2434 if (!m_impl->readSiVelocityUnits())
2436 yCError(CIA402,
"%s failed to read velocity conversions from SDO", logPrefix);
2439 if (!m_impl->readGearRatios())
2441 yCError(CIA402,
"%s failed to read gear ratios from SDO", logPrefix);
2444 if (!m_impl->readMotorConstants())
2446 yCError(CIA402,
"%s failed to read motor constants from SDO", logPrefix);
2449 if (!m_impl->readTorqueValues())
2451 yCError(CIA402,
"%s failed to read torque values from SDO", logPrefix);
2455 if (hasMaxJointTorqueCfg)
2457 if (!m_impl->configureMaxTorqueFromJointNm(maxJointTorqueNmFromConfig))
2460 "%s failed to configure max torque (0x6072) from max_torque_joint_nm",
2467 "%s max_torque_joint_nm not provided: using drive 0x6072 values",
2472 uint16_t currentStrategy = 0;
2473 if (!m_impl->getPositionControlStrategy(currentStrategy))
2475 yCError(CIA402,
"%s failed to get current control strategy", logPrefix);
2479 "%s current control strategy is %u",
2481 static_cast<unsigned>(currentStrategy));
2485 "%s requested position strategy %u (%s)",
2487 static_cast<unsigned>(desiredPositionStrategy),
2488 useSimplePidMode ?
"simple PID" :
"cascaded PID");
2490 if (!m_impl->setPositionControlStrategy(desiredPositionStrategy))
2492 yCError(CIA402,
"%s failed to configure position control strategy", logPrefix);
2497 if (!m_impl->getPositionControlStrategy(currentStrategy))
2499 yCError(CIA402,
"%s failed to get current control strategy", logPrefix);
2503 "%s current control strategy after configuration is %u",
2505 static_cast<unsigned>(currentStrategy));
2507 const bool enableSimplePidGainsIo = (desiredPositionStrategy == kPositionStrategySimplePid);
2508 if (!enableSimplePidGainsIo && programSimplePidGains)
2511 "%s simple PID gains provided but position strategy is not simple PID; skipping "
2512 "0x2012 gains programming",
2516 if (enableSimplePidGainsIo && programSimplePidGains)
2518 if (!m_impl->configureSimplePidGains(simplePidKpNmPerDeg, simplePidKdNmSecPerDeg))
2520 yCError(CIA402,
"%s failed to program simple PID gains", logPrefix);
2523 }
else if (enableSimplePidGainsIo)
2526 "%s skipping simple PID gains programming (config keys not provided)",
2532 bool simplePidGainsAvailable =
false;
2533 if (enableSimplePidGainsIo)
2536 if (!m_impl->readSimplePidGains(readKp, readKd))
2538 yCError(CIA402,
"%s failed to read back simple PID gains", logPrefix);
2541 simplePidGainsAvailable =
true;
2543 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2546 "%s j=%zu simple PID gains: Kp=%.6f Nm/deg, Kd=%.6f Nm·s/deg",
2557 if (!extractListOfDoubleFromSearchable(cfg,
2558 "pos_limit_min_deg",
2559 m_impl->limits.minPositionLimitDeg))
2561 yCError(CIA402,
"%s failed to parse pos_limit_min_deg", logPrefix);
2564 if (!extractListOfDoubleFromSearchable(cfg,
2565 "pos_limit_max_deg",
2566 m_impl->limits.maxPositionLimitDeg))
2568 yCError(CIA402,
"%s failed to parse pos_limit_max_deg", logPrefix);
2571 if (!extractListOfBoolFromSearchable(cfg,
2572 "use_position_limits_from_config",
2573 m_impl->limits.usePositionLimitsFromConfig))
2575 yCError(CIA402,
"%s failed to parse use_position_limits_from_config", logPrefix);
2579 if (m_impl->limits.minPositionLimitDeg.size() != m_impl->numAxes
2580 || m_impl->limits.maxPositionLimitDeg.size() != m_impl->numAxes
2581 || m_impl->limits.usePositionLimitsFromConfig.size() != m_impl->numAxes)
2584 "%s position limit lists must have exactly %zu elements",
2590 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2592 const bool inv = m_impl->invertedMotionSenseDirection[j];
2594 if (m_impl->limits.usePositionLimitsFromConfig[j])
2597 const int32_t lowerCounts
2598 = m_impl->jointDegToTargetCounts(j, m_impl->limits.minPositionLimitDeg[j]);
2599 const int32_t upperCounts
2600 = m_impl->jointDegToTargetCounts(j, m_impl->limits.maxPositionLimitDeg[j]);
2602 int32_t minCounts = 0;
2603 int32_t maxCounts = 0;
2606 minCounts = lowerCounts;
2607 maxCounts = upperCounts;
2611 minCounts = -upperCounts;
2612 maxCounts = -lowerCounts;
2615 if (!m_impl->setPositionCountsLimits(j, minCounts, maxCounts))
2617 yCError(CIA402,
"%s j=%zu failed to set position limits", logPrefix, j);
2623 const int s = m_impl->firstSlave +
static_cast<int>(j);
2624 int32_t minCounts = 0;
2625 int32_t maxCounts = 0;
2626 auto e1 = m_impl->ethercatManager.readSDO<int32_t>(s, 0x607D, 0x01, minCounts);
2627 auto e2 = m_impl->ethercatManager.readSDO<int32_t>(s, 0x607D, 0x02, maxCounts);
2631 yCError(CIA402,
"%s j=%zu failed to read position limits from SDO", logPrefix, j);
2636 double minDeg = 0.0;
2637 double maxDeg = 0.0;
2640 minDeg = m_impl->targetCountsToJointDeg(j, minCounts);
2641 maxDeg = m_impl->targetCountsToJointDeg(j, maxCounts);
2645 minDeg = m_impl->targetCountsToJointDeg(j, maxCounts);
2646 maxDeg = m_impl->targetCountsToJointDeg(j, minCounts);
2649 m_impl->limits.minPositionLimitDeg[j] = minDeg;
2650 m_impl->limits.maxPositionLimitDeg[j] = maxDeg;
2657 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2659 const int slave = m_impl->firstSlave +
static_cast<int>(j);
2660 auto* rx = m_impl->ethercatManager.getRxPDO(slave);
2663 yCError(CIA402,
"%s invalid slave index %d for axis %zu", logPrefix, slave, j);
2666 rx->Controlword = 0x0000;
2673 yCError(CIA402,
"%s initial EtherCAT send/receive after SDO reading failed", logPrefix);
2680 for (
int j = 0; j < m_impl->numAxes; ++j)
2682 if (!m_impl->setPositionWindowDeg(j, positionWindowDeg[j], timingWindowMs[j]))
2684 yCError(CIA402,
"%s j=%d failed to set position window", logPrefix, j);
2693 m_impl->variables.resizeContainers(m_impl->numAxes);
2694 m_impl->setPoints.
resize(m_impl->numAxes);
2695 m_impl->setPoints.reset();
2696 m_impl->controlModeState.resize(m_impl->numAxes);
2697 m_impl->sm.resize(m_impl->numAxes);
2698 m_impl->velLatched.assign(m_impl->numAxes,
false);
2699 m_impl->trqLatched.assign(m_impl->numAxes,
false);
2700 m_impl->currLatched.assign(m_impl->numAxes,
false);
2701 m_impl->posLatched.assign(m_impl->numAxes,
false);
2702 m_impl->torqueSeedNm.assign(m_impl->numAxes, 0.0);
2703 m_impl->tsLastRaw.assign(m_impl->numAxes, 0u);
2704 m_impl->tsWraps.assign(m_impl->numAxes, 0u);
2705 m_impl->ppState.ppRefSpeedDegS.assign(m_impl->numAxes, 0.0);
2706 m_impl->ppState.ppRefAccelerationDegSS.assign(m_impl->numAxes, 0.0);
2707 m_impl->ppState.ppHaltRequested.assign(m_impl->numAxes,
false);
2709 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2711 m_impl->sm[j] = std::make_unique<CiA402::StateMachine>();
2712 m_impl->sm[j]->reset();
2716 if (!extractListOfStringFromSearchable(cfg,
"axes_names", {}, m_impl->variables.jointNames))
2719 constexpr double initialPositionVelocityDegs = 10;
2720 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2725 m_impl->ppState.ppRefSpeedDegS[j] = initialPositionVelocityDegs;
2727 m_impl->setSDORefSpeed(j, initialPositionVelocityDegs);
2730 yCInfo(CIA402,
"%s opened %zu axes, initialization complete", logPrefix, m_impl->numAxes);
2736 const auto opErr = m_impl->ethercatManager.goOperational();
2739 yCError(CIA402,
"%s failed to enter OPERATIONAL state", logPrefix);
2746 = m_impl->ethercatManager.enableDCSync0(cycleNs, dcShiftNs);
2749 yCError(CIA402,
"%s failed to enable DC SYNC0", logPrefix);
2754 yWarning(
"%s DC synchronization disabled by configuration", logPrefix);
2765 yCError(CIA402,
"%s initial send/receive in OPERATIONAL failed", logPrefix);
2769 (void)m_impl->readFeedback();
2771 bool outOfLimits =
false;
2772 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2774 const double posDeg = m_impl->variables.jointPositions[j];
2775 const double minDeg = m_impl->limits.minPositionLimitDeg[j];
2776 const double maxDeg = m_impl->limits.maxPositionLimitDeg[j];
2778 const bool belowMin = (posDeg < minDeg);
2779 const bool aboveMax = (posDeg > maxDeg);
2781 if (belowMin || aboveMax)
2783 const char* axisLabel =
nullptr;
2784 if (j < m_impl->variables.jointNames.size()
2785 && !m_impl->variables.jointNames[j].empty())
2787 axisLabel = m_impl->variables.jointNames[j].c_str();
2791 "%s joint %zu%s%s%s out of limits: pos=%.6f deg, min=%.6f, max=%.6f. "
2792 "Move the joint within limits before starting.",
2795 axisLabel ?
" (" :
"",
2796 axisLabel ? axisLabel :
"",
2797 axisLabel ?
")" :
"",
2808 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2810 const int slave = m_impl->firstSlave +
static_cast<int>(j);
2811 auto* rx = m_impl->ethercatManager.getRxPDO(slave);
2814 rx->Controlword = 0x0000;
2819 (void)m_impl->ethercatManager.sendReceive();
2829 yCError(CIA402,
"%s failed to start device thread", logPrefix);
2834 "%s device thread started (position strategy=%u: %s)",
2836 static_cast<unsigned>(desiredPositionStrategy),
2837 useSimplePidMode ?
"simple PID" :
"cascaded PID");
2839 if (simplePidGainsAvailable)
2841 yCInfo(CIA402,
"%s simple PID gains:", logPrefix);
2842 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2844 const char* axisLabel =
nullptr;
2845 if (j < m_impl->variables.jointNames.size() && !m_impl->variables.jointNames[j].empty())
2847 axisLabel = m_impl->variables.jointNames[j].c_str();
2850 const char*
open = axisLabel ?
" (" :
"";
2851 const char* label = axisLabel ? axisLabel :
"";
2852 const char*
close = axisLabel ?
")" :
"";
2855 "%s j=%zu%s%s%s Kp=%.6f Nm/deg, Kd=%.6f Nm\xC2\xB7s/deg",
2890 constexpr auto logPrefix =
"[run]";
2902 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2904 const int slaveIdx = m_impl->firstSlave +
static_cast<int>(j);
2906 auto tx = m_impl->ethercatManager.getTxView(slaveIdx);
2908 const int tgt = m_impl->controlModeState.target[j];
2911 if (m_impl->controlModeState.active[j] == VOCAB_CM_HW_FAULT
2912 && tgt == VOCAB_CM_FORCE_IDLE)
2914 const auto cmd = m_impl->sm[j]->faultReset();
2918 m_impl->setPoints.reset((
int)j);
2919 m_impl->velLatched[j] = m_impl->trqLatched[j] = m_impl->currLatched[j] =
false;
2939 if (cmd.writeOpMode)
2949 m_impl->setSetPoints();
2957 "%s sendReceive() failed, expected_wkc=%d got_wkc=%d",
2959 m_impl->ethercatManager.getExpectedWorkingCounter(),
2960 m_impl->ethercatManager.getWorkingCounter());
2961 m_impl->ethercatManager.dumpDiagnostics();
2967 m_impl->readFeedback();
2975 for (
size_t j = 0; j < m_impl->numAxes; ++j)
2977 const int slaveIdx = m_impl->firstSlave +
static_cast<int>(j);
2978 auto tx = m_impl->ethercatManager.getTxView(slaveIdx);
2979 bool flavourChanged =
false;
2992 int newActive = VOCAB_CM_IDLE;
2995 newActive = VOCAB_CM_HW_FAULT;
2996 }
else if (!hwInhibit && enabled)
2998 const int activeOp = m_impl->sm[j]->getActiveOpMode();
3004 int flavor = m_impl->controlModeState.cstFlavor[j];
3006 if (flavor != VOCAB_CM_CURRENT && flavor != VOCAB_CM_TORQUE)
3008 flavor = VOCAB_CM_TORQUE;
3014 = (flavor != m_impl->controlModeState.prevCstFlavor[j])
3015 || (m_impl->controlModeState.prevCstFlavor[j] == VOCAB_CM_UNKNOWN);
3024 if (newActive == VOCAB_CM_IDLE || newActive == VOCAB_CM_HW_FAULT
3025 || newActive == VOCAB_CM_FORCE_IDLE)
3027 m_impl->setPoints.reset(j);
3028 m_impl->velLatched[j] = m_impl->trqLatched[j] = m_impl->posLatched[j]
3029 = m_impl->currLatched[j] =
false;
3032 m_impl->controlModeState.target[j] = VOCAB_CM_IDLE;
3037 if (m_impl->controlModeState.active[j] != newActive || flavourChanged)
3040 m_impl->velLatched[j] = m_impl->trqLatched[j] = m_impl->posLatched[j]
3041 = m_impl->currLatched[j] =
false;
3042 m_impl->setPoints.reset(j);
3045 m_impl->controlModeState.active[j] = newActive;
3046 m_impl->controlModeState.prevCstFlavor[j] = m_impl->controlModeState.cstFlavor[j];
3051 constexpr double printingIntervalSec = 30.0;
3055 m_impl->runTimeAccumSec += dtSec;
3056 m_impl->runTimeSamples += 1;
3057 const auto windowSec
3060 if (windowSec >= printingIntervalSec)
3063 = (m_impl->runTimeSamples > 0)
3064 ? (m_impl->runTimeAccumSec /
static_cast<double>(m_impl->runTimeSamples)) * 1000.0
3066 yCInfoThrottle(CIA402,
3067 printingIntervalSec,
3068 "%s %.1fs window: avg run() time = %.3f ms (frequency=%.1f Hz) over %zu "
3069 "cycles (window=%.1fs) - Expected period=%.3f ms (%.1f Hz)",
3071 printingIntervalSec,
3073 (m_impl->runTimeSamples > 0) ? (1000.0 / avgMs) : 0.0,
3074 m_impl->runTimeSamples,
3076 this->getPeriod() * 1000.0,
3077 1.0 / this->getPeriod());
3078 m_impl->runTimeAccumSec = 0.0;
3079 m_impl->runTimeSamples = 0;
3080 m_impl->avgWindowStart = tEnd;
3095 *num =
static_cast<int>(m_impl->numAxes);
3101 yCError(CIA402,
"%s: resetMotorEncoder() not implemented",
Impl::kClassName.data());
3107 yCError(CIA402,
"%s: resetMotorEncoders() not implemented",
Impl::kClassName.data());
3114 "%s: setMotorEncoderCountsPerRevolution() not implemented",
3121 yCError(CIA402,
"%s: setMotorEncoder() not implemented",
Impl::kClassName.data());
3127 yCError(CIA402,
"%s: setMotorEncoders() not implemented",
Impl::kClassName.data());
3138 if (m >=
static_cast<int>(m_impl->numAxes))
3149 *cpr =
static_cast<double>(m_impl->enc1Res[m]);
3151 *cpr =
static_cast<double>(m_impl->enc2Res[m]);
3165 if (m >=
static_cast<int>(m_impl->numAxes))
3172 *v = m_impl->variables.motorEncoders[m];
3179 if (encs ==
nullptr)
3186 std::memcpy(encs, m_impl->variables.motorEncoders.data(), m_impl->numAxes *
sizeof(
double));
3194 if (encs ==
nullptr || time ==
nullptr)
3201 std::memcpy(encs, m_impl->variables.motorEncoders.data(), m_impl->numAxes *
sizeof(
double));
3202 std::memcpy(time, m_impl->variables.feedbackTime.data(), m_impl->numAxes *
sizeof(
double));
3209 if (encs ==
nullptr || time ==
nullptr)
3214 if (m >=
static_cast<int>(m_impl->numAxes))
3221 *encs = m_impl->variables.motorEncoders[m];
3222 *time = m_impl->variables.feedbackTime[m];
3234 if (m >=
static_cast<int>(m_impl->numAxes))
3241 *sp = m_impl->variables.motorVelocities[m];
3248 if (spds ==
nullptr)
3256 m_impl->variables.motorVelocities.data(),
3257 m_impl->numAxes *
sizeof(
double));
3269 if (m >=
static_cast<int>(m_impl->numAxes))
3276 *acc = m_impl->variables.motorAccelerations[m];
3283 if (accs ==
nullptr)
3291 m_impl->variables.motorAccelerations.data(),
3292 m_impl->numAxes *
sizeof(
double));
3302 if (encs ==
nullptr || time ==
nullptr)
3310 m_impl->variables.jointPositions.data(),
3311 m_impl->numAxes *
sizeof(
double));
3312 std::memcpy(time, m_impl->variables.feedbackTime.data(), m_impl->numAxes *
sizeof(
double));
3319 if (encs ==
nullptr || time ==
nullptr)
3324 if (j >=
static_cast<int>(m_impl->numAxes))
3331 *encs = m_impl->variables.jointPositions[j];
3332 *time = m_impl->variables.feedbackTime[j];
3344 *ax =
static_cast<int>(m_impl->numAxes);
3350 yCError(CIA402,
"%s: resetEncoder() not implemented",
Impl::kClassName.data());
3356 yCError(CIA402,
"%s: resetEncoders() not implemented",
Impl::kClassName.data());
3362 yCError(CIA402,
"%s: setEncoder() not implemented",
Impl::kClassName.data());
3368 yCError(CIA402,
"%s: setEncoders() not implemented",
Impl::kClassName.data());
3379 if (j >=
static_cast<int>(m_impl->numAxes))
3386 *v = m_impl->variables.jointPositions[j];
3393 if (encs ==
nullptr)
3401 m_impl->variables.jointPositions.data(),
3402 m_impl->numAxes *
sizeof(
double));
3414 if (j >=
static_cast<int>(m_impl->numAxes))
3421 *sp = m_impl->variables.jointVelocities[j];
3428 if (spds ==
nullptr)
3436 m_impl->variables.jointVelocities.data(),
3437 m_impl->numAxes *
sizeof(
double));
3444 if (spds ==
nullptr)
3449 if (j >=
static_cast<int>(m_impl->numAxes))
3456 *spds = m_impl->variables.jointAccelerations[j];
3463 if (accs ==
nullptr)
3471 m_impl->variables.jointAccelerations.data(),
3472 m_impl->numAxes *
sizeof(
double));
3483 if (j >=
static_cast<int>(m_impl->numAxes))
3492 name = m_impl->variables.jointNames[j];
3498 if (axis >=
static_cast<int>(m_impl->numAxes))
3500 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), axis);
3503 type = yarp::dev::JointTypeEnum::VOCAB_JOINTTYPE_REVOLUTE;
3511 if (mode ==
nullptr)
3516 if (j >=
static_cast<int>(m_impl->numAxes))
3524 *mode = m_impl->controlModeState.active[j];
3531 if (modes ==
nullptr)
3539 std::memcpy(modes, m_impl->controlModeState.active.data(), m_impl->numAxes *
sizeof(
int));
3546 if (modes ==
nullptr || joints ==
nullptr)
3553 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n);
3559 for (
int k = 0; k < n; ++k)
3561 if (joints[k] >=
static_cast<int>(m_impl->numAxes))
3563 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
3566 modes[k] = m_impl->controlModeState.active[joints[k]];
3574 if (j >=
static_cast<int>(m_impl->numAxes))
3581 yCError(CIA402,
"%s: control mode %d not supported",
Impl::kClassName.data(), mode);
3586 m_impl->controlModeState.target[j] = mode;
3587 if (mode == VOCAB_CM_CURRENT || mode == VOCAB_CM_TORQUE)
3589 m_impl->controlModeState.cstFlavor[j] = mode;
3597 if (modes ==
nullptr || joints ==
nullptr)
3604 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n);
3608 for (
int k = 0; k < n; ++k)
3610 if (joints[k] >=
static_cast<int>(m_impl->numAxes))
3612 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
3615 m_impl->controlModeState.target[joints[k]] = modes[k];
3617 if (modes[k] == VOCAB_CM_CURRENT || modes[k] == VOCAB_CM_TORQUE)
3619 m_impl->controlModeState.cstFlavor[joints[k]] = modes[k];
3627 if (modes ==
nullptr)
3633 std::memcpy(m_impl->controlModeState.target.data(), modes, m_impl->numAxes *
sizeof(
int));
3635 for (
size_t j = 0; j < m_impl->numAxes; ++j)
3637 if (m_impl->controlModeState.target[j] == VOCAB_CM_CURRENT
3638 || m_impl->controlModeState.target[j] == VOCAB_CM_TORQUE)
3640 m_impl->controlModeState.cstFlavor[j] = m_impl->controlModeState.target[j];
3655 if (j >=
static_cast<int>(m_impl->numAxes))
3662 *t = m_impl->variables.jointTorques[j];
3677 std::memcpy(t, m_impl->variables.jointTorques.data(), m_impl->numAxes *
sizeof(
double));
3689 if (j >=
static_cast<int>(m_impl->numAxes))
3696 *t = m_impl->setPoints.jointTorques[j];
3710 std::memcpy(t, m_impl->setPoints.jointTorques.data(), m_impl->numAxes *
sizeof(
double));
3717 if (j >=
static_cast<int>(m_impl->numAxes))
3726 if (m_impl->controlModeState.active[j] != VOCAB_CM_TORQUE)
3729 "%s: setRefTorque rejected: TORQUE mode is not active for the joint %d",
3737 m_impl->setPoints.jointTorques[j] = t;
3738 m_impl->setPoints.hasTorqueSP[j] =
true;
3752 for (
size_t j = 0; j < m_impl->numAxes; ++j)
3754 if (m_impl->controlModeState.active[j] != VOCAB_CM_TORQUE)
3757 "%s: setRefTorques rejected: TORQUE mode is not active for the joint "
3767 std::memcpy(m_impl->setPoints.jointTorques.data(), t, m_impl->numAxes *
sizeof(
double));
3768 std::fill(m_impl->setPoints.hasTorqueSP.begin(), m_impl->setPoints.hasTorqueSP.end(),
true);
3774 if (t ==
nullptr || joints ==
nullptr)
3781 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n_joint);
3788 for (
int k = 0; k < n_joint; ++k)
3790 if (joints[k] >=
static_cast<int>(m_impl->numAxes))
3792 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
3795 if (m_impl->controlModeState.active[joints[k]] != VOCAB_CM_TORQUE)
3798 "%s: setRefTorques rejected: TORQUE mode is not active for the joint %d",
3807 for (
int k = 0; k < n_joint; ++k)
3809 m_impl->setPoints.jointTorques[joints[k]] = t[k];
3810 m_impl->setPoints.hasTorqueSP[joints[k]] =
true;
3817 if (min ==
nullptr || max ==
nullptr)
3822 if (j >=
static_cast<int>(m_impl->numAxes))
3829 *min = -m_impl->maxMotorTorqueNm[j] * m_impl->gearRatio[j];
3830 *max = m_impl->maxMotorTorqueNm[j] * m_impl->gearRatio[j];
3837 if (min ==
nullptr || max ==
nullptr)
3843 for (
size_t j = 0; j < m_impl->numAxes; ++j)
3845 min[j] = -m_impl->maxMotorTorqueNm[j] * m_impl->gearRatio[j];
3846 max[j] = m_impl->maxMotorTorqueNm[j] * m_impl->gearRatio[j];
3853 if (j >=
static_cast<int>(m_impl->numAxes))
3862 if (m_impl->controlModeState.active[j] != VOCAB_CM_VELOCITY)
3865 "%s: velocityMove rejected: VELOCITY mode is not active for the joint %d",
3875 m_impl->setPoints.jointVelocities[j] = spd;
3876 m_impl->setPoints.hasVelSP[j] =
true;
3882 if (spds ==
nullptr)
3891 for (
size_t j = 0; j < m_impl->numAxes; ++j)
3893 if (m_impl->controlModeState.active[j] != VOCAB_CM_VELOCITY)
3896 "%s: velocityMove rejected: VELOCITY mode is not active for the joint "
3906 std::memcpy(m_impl->setPoints.jointVelocities.data(), spds, m_impl->numAxes *
sizeof(
double));
3907 std::fill(m_impl->setPoints.hasVelSP.begin(), m_impl->setPoints.hasVelSP.end(),
true);
3918 if (joint >=
static_cast<int>(m_impl->numAxes))
3920 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joint);
3925 *vel = m_impl->setPoints.jointVelocities[joint];
3932 if (spds ==
nullptr)
3940 m_impl->setPoints.jointVelocities.data(),
3941 m_impl->numAxes *
sizeof(
double));
3948 if (vels ==
nullptr || joints ==
nullptr)
3955 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n_joint);
3961 for (
int k = 0; k < n_joint; ++k)
3963 if (joints[k] >=
static_cast<int>(m_impl->numAxes))
3965 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
3968 vels[k] = m_impl->setPoints.jointVelocities[joints[k]];
3980 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
3982 yCError(CIA402,
"%s: setRefAcceleration: joint %d out of range",
Impl::kClassName.data(), j);
3989 accDegS2 = -accDegS2;
3993 constexpr double maxAcc = 10.0;
3994 if (accDegS2 > maxAcc)
3997 "%s: setRefAcceleration: joint %d: acceleration %.2f deg/s^2 too high, "
3998 "saturating to %.2f deg/s^2",
4007 int controlMode = -1;
4010 controlMode = m_impl->controlModeState.active[j];
4013 if (controlMode != VOCAB_CM_POSITION)
4019 const int s = m_impl->firstSlave + j;
4020 const int32_t acc =
static_cast<int32_t
>(
std::llround(accDegS2 * m_impl->degSToVel[j]));
4023 m_impl->ppState.ppRefAccelerationDegSS[j] = accDegS2;
4027 (void)m_impl->ethercatManager.writeSDO<int32_t>(s, 0x6083, 0x00, acc);
4028 (void)m_impl->ethercatManager.writeSDO<int32_t>(s, 0x6084, 0x00, acc);
4029 (void)m_impl->ethercatManager.writeSDO<int32_t>(s, 0x6085, 0x00, acc);
4038 yCError(CIA402,
"%s: setRefAccelerations: null pointer",
Impl::kClassName.data());
4042 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4056 if (j >=
static_cast<int>(m_impl->numAxes))
4062 int controlMode = -1;
4065 controlMode = m_impl->controlModeState.active[j];
4068 if (controlMode == VOCAB_CM_POSITION)
4072 *acc = m_impl->ppState.ppRefAccelerationDegSS[j];
4082 if (accs ==
nullptr)
4088 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4090 int controlMode = -1;
4093 controlMode = m_impl->controlModeState.active[j];
4096 if (controlMode == VOCAB_CM_POSITION)
4100 accs[j] = m_impl->ppState.ppRefAccelerationDegSS[j];
4115 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4117 yCError(CIA402,
"%s: stop: joint %d out of range",
Impl::kClassName.data(), j);
4121 int controlMode = -1;
4124 controlMode = m_impl->controlModeState.active[j];
4127 if (controlMode == VOCAB_CM_POSITION)
4130 m_impl->ppState.ppHaltRequested[j] =
true;
4137 m_impl->setPoints.jointVelocities[j] = 0.0;
4138 m_impl->setPoints.hasVelSP[j] =
true;
4148 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4150 if (m_impl->controlModeState.active[j] == VOCAB_CM_POSITION)
4153 m_impl->ppState.ppHaltRequested[j] =
true;
4159 std::fill(m_impl->setPoints.jointVelocities.begin(),
4160 m_impl->setPoints.jointVelocities.end(),
4162 std::fill(m_impl->setPoints.hasVelSP.begin(), m_impl->setPoints.hasVelSP.end(),
true);
4169 if (spds ==
nullptr || joints ==
nullptr)
4176 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n_joint);
4183 for (
int k = 0; k < n_joint; ++k)
4185 if (joints[k] >=
static_cast<int>(m_impl->numAxes))
4187 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
4190 if (m_impl->controlModeState.active[joints[k]] != VOCAB_CM_VELOCITY)
4193 "%s: velocityMove rejected: VELOCITY mode is not active for the joint "
4203 for (
int k = 0; k < n_joint; ++k)
4205 m_impl->setPoints.jointVelocities[joints[k]] = spds[k];
4206 m_impl->setPoints.hasVelSP[joints[k]] =
true;
4216 if (accs ==
nullptr || joints ==
nullptr)
4223 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n_joint);
4233 if (accs ==
nullptr || joints ==
nullptr)
4245 if (joints ==
nullptr)
4252 yCError(CIA402,
"%s: invalid number of joints %d",
Impl::kClassName.data(), n_joint);
4258 for (
int k = 0; k < n_joint; ++k)
4260 if (joints[k] >=
static_cast<int>(m_impl->numAxes))
4262 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
4265 m_impl->setPoints.jointVelocities[joints[k]] = 0.0;
4266 m_impl->setPoints.hasVelSP[joints[k]] =
true;
4274 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4276 yCError(CIA402,
"%s: getLastJointFault: joint %d out of range",
Impl::kClassName.data(), j);
4280 const int slave = m_impl->firstSlave + j;
4284 auto err = m_impl->ethercatManager.readSDO<uint16_t>(slave, 0x603F, 0x00, code);
4288 "%s: getLastJointFault: SDO read 0x603F:00 failed (joint %d)",
4294 fault =
static_cast<int>(code);
4295 message = m_impl->describe603F(code);
4299 char report[8] = {0};
4300 auto err2 = m_impl->ethercatManager.readSDO(slave, 0x203F, 0x01, report);
4305 while (len <
sizeof(report) && report[len] !=
'\0')
4311 bool printable =
true;
4314 const unsigned char c =
static_cast<unsigned char>(report[i]);
4315 if (c < 0x20 || c > 0x7E)
4324 message +=
" — report: ";
4325 message.
append(report, len);
4328 char hex[3 * 8 + 1] = {0};
4330 for (
std::size_t i = 0; i < len && pos <= static_cast<int>(
sizeof(hex)) - 3; ++i)
4334 static_cast<unsigned char>(report[i]),
4335 (i + 1 < len) ?
" " :
"");
4336 message +=
" — report: [";
4347 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4349 yCError(CIA402,
"%s: positionMove: joint %d out of range",
Impl::kClassName.data(), j);
4353 int controlMode = -1;
4356 controlMode = m_impl->controlModeState.active[j];
4359 if (controlMode != VOCAB_CM_POSITION)
4362 "%s: positionMove rejected: POSITION mode not active for joint %d",
4369 m_impl->setPoints.ppJointTargetsDeg[j] = refDeg;
4370 m_impl->setPoints.ppTargetCounts[j] = m_impl->jointDegToTargetCounts(
size_t(j), refDeg);
4371 m_impl->setPoints.ppIsRelative[j] =
false;
4372 m_impl->setPoints.ppHasPosSP[j] =
true;
4373 m_impl->setPoints.ppPulseHi[j] =
true;
4388 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4389 if (m_impl->controlModeState.active[j] != VOCAB_CM_POSITION)
4392 "%s: positionMove rejected: POSITION mode not active on joint %zu",
4400 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4402 m_impl->setPoints.ppJointTargetsDeg[j] = refsDeg[j];
4403 m_impl->setPoints.ppTargetCounts[j] = m_impl->jointDegToTargetCounts(j, refsDeg[j]);
4404 m_impl->setPoints.ppIsRelative[j] =
false;
4405 m_impl->setPoints.ppHasPosSP[j] =
true;
4406 m_impl->setPoints.ppPulseHi[j] =
true;
4413 if (!joints || !refsDeg || n <= 0)
4420 for (
int k = 0; k < n; ++k)
4422 if (joints[k] < 0 || joints[k] >=
static_cast<int>(m_impl->numAxes))
4424 if (m_impl->controlModeState.active[joints[k]] != VOCAB_CM_POSITION)
4427 "%s: positionMove rejected: POSITION mode not active on joint %d",
4435 for (
int k = 0; k < n; ++k)
4437 const int j = joints[k];
4438 m_impl->setPoints.ppJointTargetsDeg[j] = refsDeg[k];
4439 m_impl->setPoints.ppTargetCounts[j] = m_impl->jointDegToTargetCounts(
size_t(j), refsDeg[k]);
4440 m_impl->setPoints.ppIsRelative[j] =
false;
4441 m_impl->setPoints.ppHasPosSP[j] =
true;
4442 m_impl->setPoints.ppPulseHi[j] =
true;
4449 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4453 if (m_impl->controlModeState.active[j] != VOCAB_CM_POSITION)
4456 "%s: relativeMove rejected: POSITION mode not active for joint %d",
4463 m_impl->setPoints.ppJointTargetsDeg[j] += deltaDeg;
4464 m_impl->setPoints.ppTargetCounts[j] = m_impl->jointDegToTargetCounts(
size_t(j), deltaDeg);
4465 m_impl->setPoints.ppIsRelative[j] =
true;
4466 m_impl->setPoints.ppHasPosSP[j] =
true;
4467 m_impl->setPoints.ppPulseHi[j] =
true;
4477 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4478 if (m_impl->controlModeState.active[j] != VOCAB_CM_POSITION)
4482 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4484 m_impl->setPoints.ppJointTargetsDeg[j] += deltasDeg[j];
4485 m_impl->setPoints.ppTargetCounts[j] = m_impl->jointDegToTargetCounts(j, deltasDeg[j]);
4486 m_impl->setPoints.ppIsRelative[j] =
true;
4487 m_impl->setPoints.ppHasPosSP[j] =
true;
4488 m_impl->setPoints.ppPulseHi[j] =
true;
4495 if (!joints || !deltasDeg || n <= 0)
4499 for (
int k = 0; k < n; ++k)
4500 if (m_impl->controlModeState.active[joints[k]] != VOCAB_CM_POSITION)
4504 for (
int k = 0; k < n; ++k)
4506 const int j = joints[k];
4507 m_impl->setPoints.ppJointTargetsDeg[j] += deltasDeg[k];
4508 m_impl->setPoints.ppTargetCounts[j]
4509 = m_impl->jointDegToTargetCounts(
size_t(j), deltasDeg[k]);
4510 m_impl->setPoints.ppIsRelative[j] =
true;
4511 m_impl->setPoints.ppHasPosSP[j] =
true;
4512 m_impl->setPoints.ppPulseHi[j] =
true;
4519 if (flag ==
nullptr)
4521 yCError(CIA402,
"%s: checkMotionDone: null pointer",
Impl::kClassName.data());
4525 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4527 yCError(CIA402,
"%s: checkMotionDone: joint %d out of range",
Impl::kClassName.data(), j);
4532 *flag = m_impl->variables.targetReached[j];
4538 if (flag ==
nullptr)
4540 yCError(CIA402,
"%s: checkMotionDone: null pointer",
Impl::kClassName.data());
4545 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4547 flag[j] = m_impl->variables.targetReached[j];
4555 if (!joints || !flag || n <= 0)
4558 for (
int k = 0; k < n; ++k)
4570 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4572 yCError(CIA402,
"%s: setRefSpeed: joint %d out of range",
Impl::kClassName.data(), j);
4580 cm = m_impl->controlModeState.active[j];
4582 if (cm != VOCAB_CM_POSITION)
4585 "%s: setRefSpeed: POSITION mode not active for joint %d, not writing SDO",
4594 m_impl->ppState.ppRefSpeedDegS[j] = spDegS;
4597 m_impl->setSDORefSpeed(j, spDegS);
4604 if (spDegS ==
nullptr)
4606 yCError(CIA402,
"%s: setRefSpeeds: null pointer",
Impl::kClassName.data());
4609 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4611 this->
setRefSpeed(
static_cast<int>(j), spDegS[j]);
4618 if (!joints || !spDegS || n <= 0)
4620 yCError(CIA402,
"%s: setRefSpeeds: invalid args",
Impl::kClassName.data());
4624 for (
int k = 0; k < n; ++k)
4633 if (!ref || j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4640 *ref = m_impl->ppState.ppRefSpeedDegS[j];
4647 yCError(CIA402,
"%s: getRefSpeeds: null pointer",
Impl::kClassName.data());
4651 std::memcpy(spds, m_impl->ppState.ppRefSpeedDegS.data(), m_impl->numAxes *
sizeof(
double));
4657 if (!joints || !spds || n <= 0)
4659 yCError(CIA402,
"%s: getRefSpeeds: invalid args",
Impl::kClassName.data());
4663 for (
int k = 0; k < n; ++k)
4665 spds[k] = m_impl->ppState.ppRefSpeedDegS[joints[k]];
4672 if (!ref || j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4674 yCError(CIA402,
"%s: getTargetPosition: invalid args",
Impl::kClassName.data());
4679 *ref = m_impl->setPoints.ppJointTargetsDeg[j];
4685 if (refs ==
nullptr)
4687 yCError(CIA402,
"%s: getTargetPositions: null pointer",
Impl::kClassName.data());
4692 std::memcpy(refs, m_impl->setPoints.ppJointTargetsDeg.data(), m_impl->numAxes *
sizeof(
double));
4698 if (!joints || !refs || n <= 0)
4700 yCError(CIA402,
"%s: getTargetPositions: invalid args",
Impl::kClassName.data());
4704 for (
int k = 0; k < n; ++k)
4706 refs[k] = m_impl->setPoints.ppJointTargetsDeg[joints[k]];
4715 if (j < 0 || j >=
static_cast<int>(m_impl->numAxes))
4717 yCError(CIA402,
"%s: setPosition: joint %d out of range",
Impl::kClassName.data(), j);
4723 if (m_impl->controlModeState.active[j] != VOCAB_CM_POSITION_DIRECT)
4726 "%s: setPosition rejected: POSITION_DIRECT mode is not active for joint %d",
4734 m_impl->setPoints.positionDirectJointTargetsDeg[j] = refDeg;
4735 m_impl->setPoints.positionDirectTargetCounts[j]
4736 = m_impl->jointDegToTargetCounts(
static_cast<size_t>(j), refDeg);
4742 if (refs ==
nullptr)
4744 yCError(CIA402,
"%s: setPositions: null pointer",
Impl::kClassName.data());
4750 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4752 if (m_impl->controlModeState.active[j] != VOCAB_CM_POSITION_DIRECT)
4755 "%s: setPositions rejected: POSITION_DIRECT mode is not active for joint "
4765 for (
size_t j = 0; j < m_impl->numAxes; ++j)
4767 m_impl->setPoints.positionDirectJointTargetsDeg[j] = refs[j];
4768 m_impl->setPoints.positionDirectTargetCounts[j]
4769 = m_impl->jointDegToTargetCounts(j, refs[j]);
4776 if (!joints || !refs)
4778 yCError(CIA402,
"%s: setPositions: null pointer",
Impl::kClassName.data());
4784 "%s: setPositions: invalid number of joints %d",
4792 for (
int k = 0; k < n_joint; ++k)
4794 if (joints[k] < 0 || joints[k] >=
static_cast<int>(m_impl->numAxes))
4797 "%s: setPositions: joint %d out of range",
4802 if (m_impl->controlModeState.active[joints[k]] != VOCAB_CM_POSITION_DIRECT)
4805 "%s: setPositions rejected: POSITION_DIRECT mode is not active for joint "
4815 for (
int k = 0; k < n_joint; ++k)
4817 const int j = joints[k];
4818 m_impl->setPoints.positionDirectJointTargetsDeg[j] = refs[k];
4819 m_impl->setPoints.positionDirectTargetCounts[j]
4820 = m_impl->jointDegToTargetCounts(
static_cast<size_t>(j), refs[k]);
4829 yCError(CIA402,
"%s: getRefPosition: null pointer",
Impl::kClassName.data());
4832 if (joint < 0 || joint >=
static_cast<int>(m_impl->numAxes))
4834 yCError(CIA402,
"%s: getRefPosition: joint %d out of range",
Impl::kClassName.data(), joint);
4839 *ref = m_impl->setPoints.positionDirectJointTargetsDeg[joint];
4847 yCError(CIA402,
"%s: getRefPositions: null pointer",
Impl::kClassName.data());
4853 m_impl->setPoints.positionDirectJointTargetsDeg.data(),
4854 m_impl->numAxes *
sizeof(
double));
4860 if (!joints || !refs)
4862 yCError(CIA402,
"%s: getRefPositions: null pointer",
Impl::kClassName.data());
4868 "%s: getRefPositions: invalid number of joints %d",
4875 for (
int k = 0; k < n_joint; ++k)
4877 if (joints[k] < 0 || joints[k] >=
static_cast<int>(m_impl->numAxes))
4880 "%s: getRefPositions: joint %d out of range",
4885 refs[k] = m_impl->setPoints.positionDirectJointTargetsDeg[joints[k]];
4894 yCError(CIA402,
"%s: getNumberOfMotors: null pointer",
Impl::kClassName.data());
4897 *num = m_impl->numAxes;
4908 if (m < 0 || m >=
static_cast<int>(m_impl->numAxes))
4914 *val = m_impl->variables.driveTemperatures[m];
4920 if (vals ==
nullptr)
4926 std::memcpy(vals, m_impl->variables.driveTemperatures.data(), m_impl->numAxes *
sizeof(
double));
4934 "%s: The getTemperatureLimit function is not implemented",
4943 "%s: The setTemperatureLimit function is not implemented",
4955 if (m < 0 || m >=
static_cast<int>(m_impl->numAxes))
4960 *val = m_impl->gearRatio[m];
4967 yCError(CIA402,
"%s: The setGearboxRatio function is not implemented",
Impl::kClassName.data());
4973 if (curr ==
nullptr)
4978 if (m < 0 || m >=
static_cast<int>(m_impl->numAxes))
4980 yCError(CIA402,
"%s: getCurrent: motor %d out of range",
Impl::kClassName.data(), m);
4984 *curr = m_impl->variables.motorCurrents[m];
4990 if (currs ==
nullptr)
4996 std::memcpy(currs, m_impl->variables.motorCurrents.data(), m_impl->numAxes *
sizeof(
double));
5002 if (min ==
nullptr || max ==
nullptr)
5004 yCError(CIA402,
"%s: getCurrentRange: null pointer",
Impl::kClassName.data());
5007 if (m < 0 || m >=
static_cast<int>(m_impl->numAxes))
5009 yCError(CIA402,
"%s: getCurrentRange: motor %d out of range",
Impl::kClassName.data(), m);
5013 *min = -m_impl->maxCurrentsA[m];
5014 *max = m_impl->maxCurrentsA[m];
5020 if (min ==
nullptr || max ==
nullptr)
5022 yCError(CIA402,
"%s: getCurrentRanges: null pointer",
Impl::kClassName.data());
5026 for (
size_t m = 0; m < m_impl->numAxes; ++m)
5028 min[m] = -m_impl->maxCurrentsA[m];
5029 max[m] = m_impl->maxCurrentsA[m];
5036 if (currs ==
nullptr)
5038 yCError(CIA402,
"%s: setRefCurrents: null pointer",
Impl::kClassName.data());
5045 for (
size_t j = 0; j < m_impl->numAxes; ++j)
5047 if (m_impl->controlModeState.active[j] != VOCAB_CM_CURRENT)
5050 "%s: setRefCurrents rejected: CURRENT mode is not active for the joint "
5060 std::memcpy(m_impl->setPoints.motorCurrents.data(), currs, m_impl->numAxes *
sizeof(
double));
5061 std::fill(m_impl->setPoints.hasCurrentSP.begin(), m_impl->setPoints.hasCurrentSP.end(),
true);
5067 if (m < 0 || m >=
static_cast<int>(m_impl->numAxes))
5069 yCError(CIA402,
"%s: setRefCurrent: motor %d out of range",
Impl::kClassName.data(), m);
5076 if (m_impl->controlModeState.active[m] != VOCAB_CM_CURRENT)
5079 "%s: setRefCurrent rejected: CURRENT mode is not active for the joint %d",
5087 m_impl->setPoints.motorCurrents[m] = curr;
5088 m_impl->setPoints.hasCurrentSP[m] =
true;
5095 if (currs ==
nullptr || motors ==
nullptr)
5097 yCError(CIA402,
"%s: setRefCurrents: null pointer",
Impl::kClassName.data());
5103 "%s: setRefCurrents: invalid number of motors %d",
5108 for (
int k = 0; k < n_motor; ++k)
5110 if (motors[k] < 0 || motors[k] >=
static_cast<int>(m_impl->numAxes))
5113 "%s: setRefCurrents: motor %d out of range",
5123 for (
int k = 0; k < n_motor; ++k)
5125 if (m_impl->controlModeState.active[motors[k]] != VOCAB_CM_CURRENT)
5128 "%s: setRefCurrents rejected: CURRENT mode is not active for the joint %d",
5137 for (
int k = 0; k < n_motor; ++k)
5139 m_impl->setPoints.motorCurrents[motors[k]] = currs[k];
5140 m_impl->setPoints.hasCurrentSP[motors[k]] =
true;
5147 if (currs ==
nullptr)
5149 yCError(CIA402,
"%s: getRefCurrents: null pointer",
Impl::kClassName.data());
5154 std::memcpy(currs, m_impl->setPoints.motorCurrents.data(), m_impl->numAxes *
sizeof(
double));
5160 if (curr ==
nullptr)
5162 yCError(CIA402,
"%s: getRefCurrent: null pointer",
Impl::kClassName.data());
5165 if (m < 0 || m >=
static_cast<int>(m_impl->numAxes))
5167 yCError(CIA402,
"%s: getRefCurrent: motor %d out of range",
Impl::kClassName.data(), m);
5172 *curr = m_impl->setPoints.motorCurrents[m];
5178 if (axis < 0 || axis >=
static_cast<int>(m_impl->numAxes))
5180 yCError(CIA402,
"%s: setLimits: axis %d out of range",
Impl::kClassName.data(), axis);
5186 if (!(min < 0.0 || max < 0.0) && (min >= max))
5189 "%s: setLimits: invalid limits [min=%g, max=%g]",
5198 m_impl->limits.maxPositionLimitDeg[axis] = max;
5199 m_impl->limits.minPositionLimitDeg[axis] = min;
5204 const bool inv = m_impl->invertedMotionSenseDirection[axis];
5205 const bool lowerLimitDisabled = (min < 0.0);
5206 const bool upperLimitDisabled = (max < 0.0);
5208 const auto lowerLimitCounts
5209 = lowerLimitDisabled ? 0 : m_impl->jointDegToTargetCounts(
size_t(axis), min);
5210 const auto upperLimitCounts
5211 = upperLimitDisabled ? 0 : m_impl->jointDegToTargetCounts(
size_t(axis), max);
5213 int32_t minCounts = 0;
5214 int32_t maxCounts = 0;
5227 return m_impl->setPositionCountsLimits(axis, minCounts, maxCounts);
5232 if (min ==
nullptr || max ==
nullptr)
5237 if (axis < 0 || axis >=
static_cast<int>(m_impl->numAxes))
5239 yCError(CIA402,
"%s: getLimits: axis %d out of range",
Impl::kClassName.data(), axis);
5244 *min = m_impl->limits.minPositionLimitDeg[axis];
5245 *max = m_impl->limits.maxPositionLimitDeg[axis];
5252 constexpr auto logPrefix =
"[setVelLimits] ";
5253 yCError(CIA402,
"%s: The setVelLimits function is not implemented", logPrefix);
5260 constexpr auto logPrefix =
"[getVelLimits] ";
5261 yCError(CIA402,
"%s: The getVelLimits function is not implemented", logPrefix);
5273 *mode = m_impl->dummyInteractionMode;
5279 yarp::dev::InteractionModeEnum* modes)
5281 if (!joints || !modes || n_joints <= 0)
5287 for (
int k = 0; k < n_joints; ++k)
5289 if (joints[k] < 0 || joints[k] >=
static_cast<int>(m_impl->numAxes))
5291 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
5294 modes[k] = m_impl->dummyInteractionMode;
5301 if (modes ==
nullptr)
5307 for (
size_t j = 0; j < m_impl->numAxes; ++j)
5309 modes[j] = m_impl->dummyInteractionMode;
5316 if (axis < 0 || axis >=
static_cast<int>(m_impl->numAxes))
5318 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), axis);
5324 "%s: The setInteractionMode function is not implemented",
5331 yarp::dev::InteractionModeEnum* modes)
5333 if (!joints || !modes || n_joints <= 0)
5339 for (
int k = 0; k < n_joints; ++k)
5341 if (joints[k] < 0 || joints[k] >=
static_cast<int>(m_impl->numAxes))
5343 yCError(CIA402,
"%s: joint %d out of range",
Impl::kClassName.data(), joints[k]);
5350 "%s: The setInteractionModes function is not implemented",
5357 if (modes ==
nullptr)
5364 "%s: The setInteractionModes function is not implemented",
Minimal EtherCAT master built on SOEM for CiA-402 devices.
TxView getTxView(int slaveIndex) const noexcept
Obtain a typed, bounds-checked view over the TxPDO (slave→master).
const RxPDO * getRxPDO(int slaveIndex) const noexcept
Access the RxPDO (master→slave) buffer for a given slave.
@ NoError
Operation completed successfully.
Error writeSDO(int slaveIndex, uint16_t idx, uint8_t subIdx, const T &value) noexcept
Write an SDO value to a slave (blocking call).
static bool isOperationEnabled(uint16_t sw)
static bool isFault(uint16_t sw)
static bool isFaultReactionActive(uint16_t sw)
T get(TxField f, T fallback={}) const
Read a field from the TxPDO image with a typed accessor.
bool setGearboxRatio(int m, const double val) override
Sets the gearbox ratio of a specific motor.
bool setRefAcceleration(int j, double acc) override
(Unused in CSV mode) Sets the reference acceleration for a specific joint.
bool getTargetPosition(int j, double *ref) override
Gets the target position for a specific joint.
bool getCurrentRange(int m, double *min, double *max) override
Gets the allowable current range for a specific motor.
bool resetMotorEncoders() override
Resets all motor encoders to zero.
bool setRefCurrent(int m, double curr) override
Sets the reference current for a specific motor.
bool setEncoder(int j, double val) override
Sets the value of a specific encoder.
bool getVelLimits(int axis, double *min, double *max) override
Gets the velocity limits for a specific axis.
void run() override
Runs the periodic thread.
bool setMotorEncoder(int m, const double val) override
Sets the value of a specific motor encoder.
bool getRefVelocity(const int joint, double *vel) override
Gets the reference velocity for a specific joint.
bool setMotorEncoders(const double *vals) override
Sets the values of all motor encoders.
bool getNumberOfMotors(int *num) override
Gets the number of motors controlled by the device.
bool getEncoderAcceleration(int j, double *spds) override
Gets the acceleration of a specific encoder.
bool getEncoderSpeed(int j, double *sp) override
Gets the speed of a specific encoder.
bool getEncoders(double *encs) override
Gets the values of all encoders.
bool getEncoderSpeeds(double *spds) override
Gets the speeds of all encoders.
bool setVelLimits(int axis, double min, double max) override
Sets the velocity limits for a specific axis.
bool positionMove(int j, double ref) override
Commands an absolute position move for a specific joint using Profile Position Mode (PP).
bool getMotorEncoder(int m, double *v) override
Gets the value of a specific motor encoder.
bool getEncoderTimed(int j, double *encs, double *time) override
Gets the value and timestamp of a specific encoder.
bool getAxisName(int axis, std::string &name) override
Gets the name of a specific axis.
bool setPosition(int j, double ref) override
Streams a single joint reference in Cyclic Synchronous Position mode.
bool relativeMove(int j, double delta) override
Commands a relative position move for a specific joint.
bool getLastJointFault(int j, int &fault, std::string &message) override
Gets the last joint fault for a specific joint.
bool getJointType(int axis, yarp::dev::JointTypeEnum &type) override
Gets the type of a specific axis.
bool getRefPosition(const int joint, double *ref) override
Returns the most recent position-direct command for a joint.
bool getMotorEncoderCountsPerRevolution(int m, double *cpr) override
Gets the counts per revolution for a specific motor encoder.
~CiA402MotionControl() override
Destructor.
bool resetEncoders() override
Resets all encoders to zero.
bool getTargetPositions(double *refs) override
Gets the target positions for all joints.
bool setRefCurrents(const double *currs) override
Sets the reference currents for all motors.
bool getTorques(double *t) override
Gets the measured torques for all joints.
bool getRefCurrent(int m, double *curr) override
Gets the last commanded reference current for a specific motor.
bool getRefCurrents(double *currs) override
Gets the last commanded reference currents for all motors.
bool stop() override
Stops motion for all joints.
bool setRefTorques(const double *t) override
Sets the reference torques for all joints.
bool getControlMode(int j, int *mode) override
Gets the control mode of a specific joint.
bool getEncoderAccelerations(double *accs) override
Gets the accelerations of all encoders.
bool getRefSpeed(int j, double *ref) override
Gets the current reference speed (profile velocity) for a specific joint.
bool setLimits(int axis, double min, double max) override
Sets the position limits for a specific axis.
bool setInteractionModes(int n_joints, int *joints, yarp::dev::InteractionModeEnum *modes) override
Sets the interaction modes of a subset of joints.
bool setTemperatureLimit(int m, const double temp) override
Sets the temperature limit of a specific motor.
bool getTorque(int j, double *t) override
Gets the measured torque for a specific joint.
bool getRefPositions(double *refs) override
Returns the position-direct references for all joints.
bool getLimits(int axis, double *min, double *max) override
Gets the position limits for a specific axis.
bool velocityMove(int j, double sp) override
Commands a velocity move for a specific joint.
bool setMotorEncoderCountsPerRevolution(int m, const double cpr) override
Sets the counts per revolution for a specific motor encoder.
bool getRefVelocities(double *vels) override
Gets the reference velocities for all joints.
bool getEncodersTimed(double *encs, double *time) override
Gets the values and timestamps of all encoders.
bool getTorqueRanges(double *min, double *max) override
Gets the torque ranges for all joints.
bool getControlModes(int *modes) override
Gets the control modes of all joints.
bool resetEncoder(int j) override
Resets the specified encoder to zero.
bool setControlModes(const int n, const int *joints, int *modes) override
Sets the control modes of a subset of joints.
bool getMotorEncoderSpeed(int m, double *sp) override
Gets the speed of a specific motor encoder.
bool getMotorEncoders(double *encs) override
Gets the values of all motor encoders.
bool setPositions(const int n_joint, const int *joints, const double *refs) override
Streams references for a subset of joints.
bool setRefSpeed(int j, double sp) override
Sets the reference speed (profile velocity) for a specific joint.
bool getMotorEncoderAcceleration(int m, double *acc) override
Gets the acceleration of a specific motor encoder.
bool getEncoder(int j, double *v) override
Gets the value of a specific encoder.
bool getMotorEncoderTimed(int m, double *encs, double *time) override
Gets the value and timestamp of a specific motor encoder.
bool getCurrentRanges(double *min, double *max) override
Gets the allowable current ranges for all motors.
bool setControlMode(const int j, const int mode) override
Sets the control mode of a specific joint.
bool getTemperatureLimit(int m, double *temp) override
Gets the temperature limit of a specific motor.
bool getInteractionMode(int axis, yarp::dev::InteractionModeEnum *mode) override
Gets the interaction mode of a specific axis.
bool getRefAcceleration(int j, double *acc) override
(Unused in CSV mode) Gets the reference acceleration for a specific joint.
bool getCurrents(double *currs) override
Gets the measured currents of all motors.
bool checkMotionDone(int j, bool *flag) override
Checks if a specific joint has completed its motion.
CiA402MotionControl()
Default constructor.
bool getGearboxRatio(int m, double *val) override
Gets the gearbox ratio of a specific motor.
bool open(yarp::os::Searchable &config) override
Opens the device driver.
bool getTemperature(int m, double *val) override
Gets the temperature of a specific motor.
bool setRefSpeeds(const double *spds) override
Sets the reference speeds (profile velocities) for all joints.
bool getInteractionModes(int n_joints, int *joints, yarp::dev::InteractionModeEnum *modes) override
Gets the interaction modes of a subset of joints.
bool setRefAccelerations(const double *accs) override
(Unused in CSV mode) Sets the reference accelerations for all joints.
bool getMotorEncodersTimed(double *encs, double *time) override
Gets the values and timestamps of all motor encoders.
bool getMotorEncoderAccelerations(double *accs) override
Gets the accelerations of all motor encoders.
bool getRefTorque(int j, double *t) override
Gets the reference torque for a specific joint.
bool setEncoders(const double *vals) override
Sets the values of all encoders.
bool close() override
Closes the device driver.
bool getNumberOfMotorEncoders(int *num) override
Gets the number of motor encoders.
bool getCurrent(int m, double *curr) override
Gets the measured current of a specific motor.
bool getAxes(int *ax) override
Gets the number of axes (encoders).
bool setRefTorque(int j, double t) override
Sets the reference torque for a specific joint.
bool getTemperatures(double *vals) override
Gets the temperatures of all motors.
bool getMotorEncoderSpeeds(double *spds) override
Gets the speeds of all motor encoders.
bool resetMotorEncoder(int m) override
Resets the specified motor encoder to zero.
bool getRefTorques(double *t) override
Gets the reference torques for all joints.
bool setInteractionMode(int axis, yarp::dev::InteractionModeEnum mode) override
Sets the interaction mode of a specific axis.
bool getTorqueRange(int j, double *min, double *max) override
Gets the torque range for a specific joint.
bool getRefAccelerations(double *accs) override
(Unused in CSV mode) Gets the reference accelerations for all joints.
bool getRefSpeeds(double *spds) override
Gets the current reference speeds (profile velocities) for all joints.
T duration_cast(T... args)
@ Velocity606C
0x606C: Velocity actual value
@ Enc2Pos2113_02
0x2113:02 Vendor enc2 position
@ SBC_6621_02
0x6621:02 Safe Brake Control status (exposed via PDO)
@ Enc2Vel2113_03
0x2113:03 Vendor enc2 velocity
@ OpModeDisplay
0x6061: Modes of operation display
@ Timestamp20F0
0x20F0: Timestamp (if supported)
@ Statusword
0x6041: Statusword
@ TemperatureDrive
0x2031:01 Temperature (if supported)
@ Position6064
0x6064: Position actual value
@ Torque6077
0x6077: Torque actual value
@ Enc1Pos2111_02
0x2111:02 Vendor enc1 position
@ Enc1Vel2111_03
0x2111:03 Vendor enc1 velocity
@ STO_6621_01
0x6621:01 Safe Torque Off status (exposed via PDO)
Process Data Object written from master to slave (RxPDO).
int8_t OpMode
0x6060: Modes of operation
uint16_t Controlword
0x6040: Controlword
std::vector< int > prevCstFlavor
std::vector< int > active
std::vector< int > cstFlavor
void resize(std::size_t n)
std::vector< int > target
std::vector< double > minPositionLimitDeg
std::vector< double > maxPositionLimitDeg
std::vector< bool > usePositionLimitsFromConfig
std::vector< double > ppRefSpeedDegS
std::vector< double > ppRefAccelerationDegSS
std::vector< bool > ppHaltRequested
std::vector< bool > ppPulseHi
void resize(std::size_t n)
std::vector< bool > hasCurrentSP
std::vector< double > positionDirectJointTargetsDeg
std::vector< double > jointVelocities
std::vector< bool > ppHasPosSP
std::vector< double > ppJointTargetsDeg
std::vector< bool > hasTorqueSP
std::vector< bool > ppIsRelative
std::vector< bool > hasVelSP
std::vector< bool > ppPulseCoolDown
std::vector< int32_t > ppTargetCounts
std::vector< double > motorCurrents
std::vector< double > jointTorques
std::vector< int32_t > positionDirectTargetCounts
std::vector< uint8_t > STO
std::vector< double > driveTemperatures
std::vector< double > jointAccelerations
std::vector< double > feedbackTime
std::vector< std::string > jointNames
std::vector< double > motorCurrents
std::vector< double > jointTorques
std::vector< double > jointPositions
std::vector< bool > targetReached
std::vector< double > motorVelocities
std::vector< uint8_t > SBC
std::vector< double > jointVelocities
std::vector< double > motorEncoders
void resizeContainers(std::size_t numAxes)
std::vector< double > motorAccelerations
static constexpr std::string_view kClassName
std::vector< double > maxCurrentsA
bool setPositionControlStrategy(uint16_t strategyValue)
static int ciaOpToYarp(int op)
std::vector< double > velToDegS
ControlModeState controlModeState
std::vector< double > ratedMotorTorqueNm
std::vector< double > gearRatio
std::vector< Mount > enc2Mount
static std::string_view yarpToString(int op)
std::vector< VelSrc > velSrcMotor
std::vector< PosSrc > posSrcMotor
std::vector< bool > trqLatched
yarp::dev::InteractionModeEnum dummyInteractionMode
std::vector< double > gearRatioInv
bool setPositionWindowDeg(int j, double winDeg, double winTime_ms)
std::vector< VelSrc > velSrcJoint
std::mutex CiA402MotionControlMutex
std::size_t runTimeSamples
std::vector< bool > posLatched
static constexpr uint64_t TIMESTAMP_WRAP_PERIOD_US
std::vector< double > torqueSeedNm
static std::tuple< double, double, const char * > decode60A9(uint32_t v)
std::string describe603F(uint16_t code)
VariablesReadFromMotors variables
bool readSiVelocityUnits()
std::vector< bool > currLatched
static constexpr uint32_t TIMESTAMP_WRAP_HALF_RANGE
int32_t jointDegToTargetCounts(size_t j, double jointDeg) const
std::vector< uint32_t > tsLastRaw
std::vector< double > torqueConstants
std::chrono::steady_clock::time_point avgWindowStart
std::vector< Mount > enc1Mount
double loopCountsToJointDeg(std::size_t j, double counts) const
PositionProfileState ppState
std::vector< double > maxMotorTorqueNm
std::vector< double > enc2ResInv
std::vector< std::unique_ptr< CiA402::StateMachine > > sm
std::vector< uint32_t > enc2Res
std::vector< uint32_t > enc1Res
bool readMotorConstants()
std::vector< uint64_t > tsWraps
std::vector< double > degSToVel
void setSDORefSpeed(int j, double spDegS)
static int yarpToCiaOp(int cm)
std::vector< double > enc1ResInv
std::vector< int > lastActiveMode
bool readSimplePidGains(std::vector< double > &kpNmPerDeg, std::vector< double > &kdNmSecPerDeg)
struct yarp::dev::CiA402MotionControl::Impl::Limits limits
std::vector< SensorSrc > posLoopSrc
bool readEncoderResolutions()
std::vector< SensorSrc > velLoopSrc
std::vector< PosSrc > posSrcJoint
bool configureSimplePidGains(const std::vector< double > &kpNmPerDeg, const std::vector< double > &kdNmSecPerDeg)
bool configureMaxTorqueFromJointNm(const std::vector< double > &maxJointTorqueNm)
::CiA402::EthercatManager ethercatManager
bool setPositionCountsLimits(int axis, int32_t minCounts, int32_t maxCounts)
static constexpr double MICROSECONDS_TO_SECONDS
bool getPositionControlStrategy(uint16_t &strategyValue)
int ciaOpToYarpWithFlavor(int op, int cstFlavor)
std::vector< bool > invertedMotionSenseDirection
double targetCountsToJointDeg(size_t j, int32_t counts) const
std::vector< bool > velLatched