diff --git a/build/.gitkeep b/build/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/source/RobotAPI/interface/units/RobotUnit/NJointJointSpaceDMPController.ice b/source/RobotAPI/interface/units/RobotUnit/NJointJointSpaceDMPController.ice index 0feba2e5ae20c2fc586b0b2eb4b876220d1870fa..ee6a6782e55eed678c05913df78bf38ee0678e5e 100644 --- a/source/RobotAPI/interface/units/RobotUnit/NJointJointSpaceDMPController.ice +++ b/source/RobotAPI/interface/units/RobotUnit/NJointJointSpaceDMPController.ice @@ -42,6 +42,8 @@ module armarx double phaseKp = 1; double timeDuration = 10; + double maxJointVel = 10; + bool isPhaseStop = false; }; @@ -50,7 +52,7 @@ module armarx void learnDMPFromFiles(Ice::StringSeq trajfiles); bool isFinished(); void runDMP(Ice::DoubleSeq goals, double tau); - void setTemporalFactor(double tau); + void setSpeed(double times); void showMessages(); }; diff --git a/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice b/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice index 737dd63f2aa882c2a869e162f148cae309852191..86cffecf07d78f4e49cc765159a177704d51f115 100644 --- a/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice +++ b/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice @@ -450,6 +450,7 @@ module armarx Ice::FloatSeq handCOM; float handMass; float angularKp; + float frictionCone; }; @@ -469,5 +470,99 @@ module armarx double getCanVal(); }; + class NJointAnomalyDetectionAdaptiveWipingControllerConfig extends NJointControllerConfig + { + + // dmp configuration + int kernelSize = 100; + double dmpAmplitude = 1; + double timeDuration = 10; + + double phaseL = 10; + double phaseK = 10; + float phaseDist0 = 50; + float phaseDist1 = 10; + double phaseKpPos = 1; + double phaseKpOri = 0.1; + double posToOriRatio = 100; + + + // velocity controller configuration + string nodeSetName = ""; + + float maxJointTorque; + Ice::FloatSeq desiredNullSpaceJointValues; + + Ice::FloatSeq Kpos; + Ice::FloatSeq Dpos; + Ice::FloatSeq Kori; + Ice::FloatSeq Dori; + Ice::FloatSeq Knull; + Ice::FloatSeq Dnull; + + string forceSensorName = "FT R"; + string forceFrameName = "ArmR8_Wri2"; + float forceFilter = 0.8; + float waitTimeForCalibration = 0.1; + + // anomaly detection and friction estimation + int velocityHorizon = 100; + int frictionHorizon = 100; + + // pid params + bool isForceControlEnabled; + bool isRotControlEnabled; + bool isTorqueControlEnabled; + bool isLCRControlEnabled; + Ice::FloatSeq pidForce; + Ice::FloatSeq pidRot; + Ice::FloatSeq pidTorque; + Ice::FloatSeq pidLCR; + + float minimumReactForce = 0; + float forceDeadZone; + float velFilter; + + float maxLinearVel; + float maxAngularVel; + + Ice::FloatSeq ws_x; + Ice::FloatSeq ws_y; + Ice::FloatSeq ws_z; + + float adaptCoeff; + float reactThreshold; + float dragForceDeadZone; + float adaptForceCoeff; + float changePositionTolerance; + float changeTimerThreshold; + + Ice::FloatSeq ftOffset; + Ice::FloatSeq handCOM; + float handMass; + + float ftCommandFilter; + float frictionCone; + float fricEstiFilter; + float velNormThreshold; + float maxInteractionForce; + }; + + + interface NJointAnomalyDetectionAdaptiveWipingControllerInterface extends NJointControllerInterface + { + void learnDMPFromFiles(Ice::StringSeq trajfiles); + void learnDMPFromTrajectory(TrajectoryBase trajectory); + + bool isFinished(); + void runDMP(Ice::DoubleSeq goals, double tau); + + void setSpeed(double times); + void setGoals(Ice::DoubleSeq goals); + void setAmplitude(double amplitude); + void setTargetForceInRootFrame(float force); + + double getCanVal(); + }; }; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt index 9eb9f1d1b14210df8d849947c92def273b48a212..c7878d1ad5f066b68f31693a6d6c51dd3237c451 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt @@ -22,7 +22,6 @@ set(LIB_HEADERS) list(APPEND LIB_HEADERS - DMPController/NJointJointSpaceDMPController.h DMPController/NJointJSDMPController.h DMPController/NJointTSDMPController.h DMPController/NJointCCDMPController.h @@ -32,10 +31,9 @@ set(LIB_HEADERS) DMPController/NJointPeriodicTSDMPForwardVelController.h DMPController/NJointPeriodicTSDMPCompliantController.h DMPController/NJointAdaptiveWipingController.h - + DMPController/NJointAnomalyDetectionAdaptiveWipingController.h ) list(APPEND LIB_FILES - DMPController/NJointJointSpaceDMPController.cpp DMPController/NJointJSDMPController.cpp DMPController/NJointTSDMPController.cpp DMPController/NJointCCDMPController.cpp @@ -45,7 +43,7 @@ set(LIB_HEADERS) DMPController/NJointPeriodicTSDMPForwardVelController.cpp DMPController/NJointPeriodicTSDMPCompliantController.cpp DMPController/NJointAdaptiveWipingController.cpp - + DMPController/NJointAnomalyDetectionAdaptiveWipingController.cpp ) list(APPEND LIBS ${DMP_LIBRARIES} DMPController) diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp index d19f4e5856fd718aae44d98650d787c8d7efead8..324b543ac056640e5f5c8ebe3ee54b70db577456 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp @@ -74,6 +74,7 @@ namespace armarx dori << cfg->Dori[0], cfg->Dori[1], cfg->Dori[2]; kpf = cfg->Kpf; + // forcePID.reset(new PIDController(cfg->kpf, )); knull.setZero(targets.size()); dnull.setZero(targets.size()); @@ -267,7 +268,16 @@ namespace armarx Eigen::VectorXf currentTwist = jacobi * qvel_filtered; Eigen::VectorXf targetVel(6); + Eigen::Vector3f axis; + axis.setZero(); targetVel.setZero(); + Eigen::Vector3f forceInToolFrame; + forceInToolFrame << 0, 0, 0; + + Eigen::Vector3f torqueInToolFrame; + torqueInToolFrame << 0, 0, 0; + + float angle = 0; if (firstRun || !dmpRunning) { lastPosition = currentPose.block<2, 1>(0, 3); @@ -275,12 +285,13 @@ namespace armarx firstRun = false; filteredForce.setZero(); Eigen::Vector3f currentForce = forceSensor->force - forceOffset; + Eigen::Matrix3f forceFrameOri = rtGetRobot()->getRobotNode(cfg->forceFrameName)->getPoseInRootFrame().block<3, 3>(0, 0); Eigen::Vector3f gravityInForceFrame = forceFrameOri.transpose() * gravityInRoot; Eigen::Vector3f handGravity = handMass * gravityInForceFrame; currentForce = currentForce - handGravity; - currentForceOffset = 0.1 * currentForceOffset + 0.9 * currentForce; + currentForceOffset = 0.1 * currentForceOffset + 0.9 * currentForce; origHandOri = currentPose.block<3, 3>(0, 0); toolTransform = origHandOri.transpose(); targetVel.setZero(); @@ -302,9 +313,10 @@ namespace armarx currentForce = currentForce - handGravity; filteredForce = (1 - cfg->forceFilter) * filteredForce + cfg->forceFilter * currentForce; - // Eigen::Vector3f currentTorque = forceSensor->torque - torqueOffset; - // Eigen::Vector3f handTorque = handCOM.cross(gravityInForceFrame); - // currentTorque = currentTorque - handTorque; + Eigen::Vector3f currentTorque = forceSensor->torque - torqueOffset; + Eigen::Vector3f handTorque = handCOM.cross(gravityInForceFrame); + currentTorque = currentTorque - handTorque; + filteredTorque = (1 - cfg->forceFilter) * filteredTorque + cfg->forceFilter * currentTorque; for (size_t i = 0; i < 3; ++i) { @@ -319,12 +331,15 @@ namespace armarx } filteredForceInRoot = forceFrameOri * filteredForce; + filteredTorqueInRoot = forceFrameOri * filteredTorque; float targetForce = user2rtData.getUpToDateReadBuffer().targetForce; Eigen::Matrix3f currentHandOri = currentPose.block<3, 3>(0, 0); Eigen::Matrix3f currentToolOri = currentHandOri * toolTransform; - Eigen::Vector3f forceInToolFrame = currentToolOri.transpose() * filteredForceInRoot; + forceInToolFrame = currentToolOri.transpose() * filteredForceInRoot; + torqueInToolFrame = currentToolOri.transpose() * filteredTorqueInRoot; + float desiredZVel = kpf * (targetForce - forceInToolFrame(2)); targetVel(2) -= desiredZVel; targetVel.block<3, 1>(0, 0) = currentToolOri * targetVel.block<3, 1>(0, 0); @@ -340,20 +355,40 @@ namespace armarx if (filteredForceInRoot.norm() > fabs(cfg->minimumReactForce)) { - Eigen::Vector3f desiredToolDir = filteredForceInRoot.normalized();// / filteredForceInRoot.norm(); + Eigen::Vector3f toolYDir; + toolYDir << 0, 1.0, 0; + Eigen::Vector3f toolYDirInRoot = currentToolOri * toolYDir; + Eigen::Vector3f projectedFilteredForceInRoot = filteredForceInRoot - filteredForceInRoot.dot(toolYDirInRoot) * toolYDirInRoot; + Eigen::Vector3f desiredToolDir = projectedFilteredForceInRoot.normalized();// / projectedFilteredForceInRoot.norm(); currentToolDir.normalize(); - Eigen::Vector3f axis = currentToolDir.cross(desiredToolDir); - float angle = acosf(currentToolDir.dot(desiredToolDir)); + axis = currentToolDir.cross(desiredToolDir); + axis = axis.normalized(); + angle = acosf(currentToolDir.dot(desiredToolDir)); + - if (fabs(angle) < M_PI / 2) + if (fabs(angle) < M_PI / 2 && fabs(angle) > cfg->frictionCone) { - Eigen::AngleAxisf desiredToolRot(angle, axis); + // sigmoid function + float adaptedAngularKp = cfg->angularKp / (1 + exp(10 * (angle - M_PI / 4))); + float angularKp = fmin(adaptedAngularKp, cfg->angularKp); + + // test axis + Eigen::Vector3f fixedAxis; + if (axis(1) > 0) + { + fixedAxis << 0.0, 1.0, 0.0; + } + else + { + fixedAxis << 0.0, -1.0, 0.0; + } + Eigen::AngleAxisf desiredToolRot(angle - cfg->frictionCone, fixedAxis); Eigen::Matrix3f desiredRotMat = desiredToolRot * Eigen::Matrix3f::Identity(); Eigen::Vector3f angularDiff = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); - targetVel.tail(3) = cfg->angularKp * angularDiff; + targetVel.tail(3) = angularKp * angularDiff; Eigen::Vector3f desiredRPY = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); Eigen::Vector3f checkedToolDir = desiredRotMat * currentToolDir; @@ -443,11 +478,17 @@ namespace armarx debugRT.getWriteBuffer().globalPose = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystem(targetPose); debugRT.getWriteBuffer().currentPose = currentPose; debugRT.getWriteBuffer().filteredForceInRoot = filteredForceInRoot; - debugRT.getWriteBuffer().filteredForce = filteredForce; + debugRT.getWriteBuffer().rotationAxis = axis; + debugRT.getWriteBuffer().filteredForce = forceInToolFrame; debugRT.getWriteBuffer().globalFilteredForce = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystemVec(filteredForceInRoot); debugRT.getWriteBuffer().targetVel = targetVel; debugRT.getWriteBuffer().adaptK = adaptK; debugRT.getWriteBuffer().isPhaseStop = isPhaseStop; + debugRT.getWriteBuffer().rotAngle = angle; + debugRT.getWriteBuffer().currentTwist = currentTwist; + debugRT.getWriteBuffer().filteredTorque = torqueInToolFrame; + + debugRT.commitWrite(); rt2CtrlData.getWriteBuffer().currentPose = currentPose; @@ -614,15 +655,26 @@ namespace armarx datafields["current_z"] = new Variant(currentPoseDebug(2, 3)); Eigen::Vector3f filteredForce = debugRT.getUpToDateReadBuffer().filteredForce; - datafields["filteredforce_x"] = new Variant(filteredForce(0)); - datafields["filteredforce_y"] = new Variant(filteredForce(1)); - datafields["filteredforce_z"] = new Variant(filteredForce(2)); + datafields["filteredforceInTool_x"] = new Variant(filteredForce(0)); + datafields["filteredforceInTool_y"] = new Variant(filteredForce(1)); + datafields["filteredforceInTool_z"] = new Variant(filteredForce(2)); + + Eigen::Vector3f filteredTorque = debugRT.getUpToDateReadBuffer().filteredTorque; + datafields["filteredtorqueInTool_x"] = new Variant(filteredTorque(0)); + datafields["filteredtorqueInTool_y"] = new Variant(filteredTorque(1)); + datafields["filteredtorqueInTool_z"] = new Variant(filteredTorque(2)); + Eigen::Vector3f filteredForceInRoot = debugRT.getUpToDateReadBuffer().filteredForceInRoot; datafields["filteredForceInRoot_x"] = new Variant(filteredForceInRoot(0)); datafields["filteredForceInRoot_y"] = new Variant(filteredForceInRoot(1)); datafields["filteredForceInRoot_z"] = new Variant(filteredForceInRoot(2)); + Eigen::Vector3f rotationAxis = debugRT.getUpToDateReadBuffer().rotationAxis; + datafields["rotationAxis_x"] = new Variant(rotationAxis(0)); + datafields["rotationAxis_y"] = new Variant(rotationAxis(1)); + datafields["rotationAxis_z"] = new Variant(rotationAxis(2)); + Eigen::Vector3f reactForce = debugRT.getUpToDateReadBuffer().reactForce; datafields["reactForce_x"] = new Variant(reactForce(0)); datafields["reactForce_y"] = new Variant(reactForce(1)); @@ -632,6 +684,18 @@ namespace armarx datafields["targetVel_x"] = new Variant(targetVel(0)); datafields["targetVel_y"] = new Variant(targetVel(1)); datafields["targetVel_z"] = new Variant(targetVel(2)); + datafields["targetVel_ro"] = new Variant(targetVel(3)); + datafields["targetVel_pi"] = new Variant(targetVel(4)); + datafields["targetVel_ya"] = new Variant(targetVel(5)); + + Eigen::VectorXf currentTwist = debugRT.getUpToDateReadBuffer().currentTwist; + datafields["currentTwist_x"] = new Variant(currentTwist(0)); + datafields["currentTwist_y"] = new Variant(currentTwist(1)); + datafields["currentTwist_z"] = new Variant(currentTwist(2)); + datafields["currentTwist_ro"] = new Variant(currentTwist(3)); + datafields["currentTwist_pi"] = new Variant(currentTwist(4)); + datafields["currentTwist_ya"] = new Variant(currentTwist(5)); + Eigen::Vector3f adaptK = debugRT.getUpToDateReadBuffer().adaptK; datafields["adaptK_x"] = new Variant(adaptK(0)); @@ -641,6 +705,7 @@ namespace armarx datafields["deltaT"] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); datafields["PhaseStop"] = new Variant(debugRT.getUpToDateReadBuffer().isPhaseStop); + datafields["rotAngle"] = new Variant(debugRT.getUpToDateReadBuffer().rotAngle); // datafields["targetVel_rx"] = new Variant(targetVel(3)); diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h index 9bfd26e64ad5551f5997634189034bd158e9b43e..7e1949ebe6ef33d71702eee37a72805b7da17736 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h @@ -16,6 +16,8 @@ #include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h> #include <RobotAPI/libraries/core/Trajectory.h> +#include <RobotAPI/libraries/core/PIDController.h> + namespace armarx { @@ -109,6 +111,9 @@ namespace armarx Eigen::Matrix4f targetPose; Eigen::Vector3f filteredForce; Eigen::Vector3f filteredForceInRoot; + Eigen::Vector3f filteredTorque; + + Eigen::Vector3f rotationAxis; Eigen::Vector3f reactForce; Eigen::Vector3f adaptK; @@ -119,6 +124,9 @@ namespace armarx Eigen::Matrix4f globalPose; Eigen::Vector3f globalFilteredForce; Eigen::Vector3f currentToolDir; + Eigen::VectorXf currentTwist; + + float rotAngle; }; TripleBuffer<DebugRTData> debugRT; @@ -181,11 +189,15 @@ namespace armarx Eigen::VectorXf nullSpaceJointsVec; const SensorValueForceTorque* forceSensor; + PIDControllerPtr forcePID; + Eigen::Vector3f filteredForce; Eigen::Vector3f filteredTorque; Eigen::Vector3f forceOffset; Eigen::Vector3f currentForceOffset; + Eigen::Vector3f torqueOffset; + Eigen::Vector3f currentTorqueOffset; float handMass; Eigen::Vector3f handCOM; Eigen::Vector3f gravityInRoot; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c200c425ec99ee1ff3c6fa4e97bc921f7f1e7d9a --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.cpp @@ -0,0 +1,931 @@ +#include "NJointAnomalyDetectionAdaptiveWipingController.h" + +namespace armarx +{ + NJointControllerRegistration<NJointAnomalyDetectionAdaptiveWipingController> registrationControllerNJointAnomalyDetectionAdaptiveWipingController("NJointAnomalyDetectionAdaptiveWipingController"); + + NJointAnomalyDetectionAdaptiveWipingController::NJointAnomalyDetectionAdaptiveWipingController(const RobotUnitPtr& robUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) + { + useSynchronizedRtRobot(); + cfg = NJointAnomalyDetectionAdaptiveWipingControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION(!cfg->nodeSetName.empty()); + VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); + + ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName); + for (size_t i = 0; i < rns->getSize(); ++i) + { + std::string jointName = rns->getNode(i)->getName(); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Torque1DoF); + const SensorValueBase* sv = useSensorValue(jointName); + targets.push_back(ct->asA<ControlTarget1DoFActuatorTorque>()); + + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + + velocitySensors.push_back(velocitySensor); + positionSensors.push_back(positionSensor); + }; + + tcp = rns->getTCP(); + // set tcp controller + nodeSetName = cfg->nodeSetName; + ik.reset(new VirtualRobot::DifferentialIK(rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + + TaskSpaceDMPControllerConfig taskSpaceDMPConfig; + taskSpaceDMPConfig.motionTimeDuration = cfg->timeDuration; + taskSpaceDMPConfig.DMPKernelSize = cfg->kernelSize; + taskSpaceDMPConfig.DMPAmplitude = cfg->dmpAmplitude; + taskSpaceDMPConfig.DMPMode = "Linear"; + taskSpaceDMPConfig.DMPStyle = "Periodic"; + taskSpaceDMPConfig.phaseStopParas.goDist = cfg->phaseDist0; + taskSpaceDMPConfig.phaseStopParas.backDist = cfg->phaseDist1; + taskSpaceDMPConfig.phaseStopParas.Kpos = cfg->phaseKpPos; + taskSpaceDMPConfig.phaseStopParas.Kori = cfg->phaseKpOri; + taskSpaceDMPConfig.phaseStopParas.mm2radi = cfg->posToOriRatio; + taskSpaceDMPConfig.phaseStopParas.maxValue = cfg->phaseL; + taskSpaceDMPConfig.phaseStopParas.slop = cfg->phaseK; + taskSpaceDMPConfig.phaseStopParas.Dpos = 0; + taskSpaceDMPConfig.phaseStopParas.Dori = 0; + + lastCanVal = cfg->timeDuration; + + dmpCtrl.reset(new TaskSpaceDMPController("periodicDMP", taskSpaceDMPConfig, false)); + + NJointAnomalyDetectionAdaptiveWipingControllerControlData initData; + initData.targetTSVel.resize(6); + for (size_t i = 0; i < 6; ++i) + { + initData.targetTSVel(i) = 0; + } + reinitTripleBuffer(initData); + + firstRun = true; + dmpRunning = false; + + // anomaly detection + velocityHorizon = cfg->velocityHorizon; + + // friction estimation + frictionHorizon = cfg->frictionHorizon; + estimatedFriction << 0.0, 0.0; + lastForceInToolXY.setZero(); + + ARMARX_CHECK_EQUAL(cfg->Kpos.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Dpos.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Kori.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Dori.size(), 3); + + kpos << cfg->Kpos[0], cfg->Kpos[1], cfg->Kpos[2]; + dpos << cfg->Dpos[0], cfg->Dpos[1], cfg->Dpos[2]; + kori << cfg->Kori[0], cfg->Kori[1], cfg->Kori[2]; + dori << cfg->Dori[0], cfg->Dori[1], cfg->Dori[2]; + + isForceControlEnabled = cfg->isForceControlEnabled; + isRotControlEnabled = cfg->isRotControlEnabled; + isTorqueControlEnabled = cfg->isTorqueControlEnabled; + isLCRControlEnabled = cfg->isLCRControlEnabled; + forcePID.reset(new PIDController(cfg->pidForce[0], cfg->pidForce[1], cfg->pidForce[2], cfg->pidForce[3])); + rotPID.reset(new PIDController(cfg->pidRot[0], cfg->pidRot[1], cfg->pidRot[2], cfg->pidRot[3])); + torquePID.reset(new PIDController(cfg->pidTorque[0], cfg->pidTorque[1], cfg->pidTorque[2], cfg->pidTorque[3])); + lcrPID.reset(new PIDController(cfg->pidLCR[0], cfg->pidLCR[1], cfg->pidLCR[2], cfg->pidLCR[3])); + + knull.setZero(targets.size()); + dnull.setZero(targets.size()); + + for (size_t i = 0; i < targets.size(); ++i) + { + knull(i) = cfg->Knull.at(i); + dnull(i) = cfg->Dnull.at(i); + } + + nullSpaceJointsVec.resize(cfg->desiredNullSpaceJointValues.size()); + for (size_t i = 0; i < cfg->desiredNullSpaceJointValues.size(); ++i) + { + nullSpaceJointsVec(i) = cfg->desiredNullSpaceJointValues.at(i); + } + + + const SensorValueBase* svlf = robUnit->getSensorDevice(cfg->forceSensorName)->getSensorValue(); + forceSensor = svlf->asA<SensorValueForceTorque>(); + + + ARMARX_CHECK_EQUAL(cfg->ftOffset.size(), 6); + + currentForceOffset.setZero(); + forceOffset << cfg->ftOffset[0], cfg->ftOffset[1], cfg->ftOffset[2]; + torqueOffset << cfg->ftOffset[3], cfg->ftOffset[4], cfg->ftOffset[5]; + + handMass = cfg->handMass; + handCOM << cfg->handCOM[0], cfg->handCOM[1], cfg->handCOM[2]; + + + filteredForce.setZero(); + filteredTorque.setZero(); + filteredFTCommand.setZero(); + filteredForceInRoot.setZero(); + filteredTorqueInRoot.setZero(); + targetFTInToolFrame.setZero(); + + UserToRTData initUserData; + initUserData.targetForce = 0; + user2rtData.reinitAllBuffers(initUserData); + + oriToolDir << 0, 0, 1; + gravityInRoot << 0, 0, -9.8; + + qvel_filtered.setZero(targets.size()); + + ARMARX_CHECK_EQUAL(cfg->ws_x.size(), 2); + ARMARX_CHECK_EQUAL(cfg->ws_y.size(), 2); + ARMARX_CHECK_EQUAL(cfg->ws_z.size(), 2); + // only for ARMAR-6 (safe-guard) + ARMARX_CHECK_LESS(cfg->ws_x[0], cfg->ws_x[1]); + ARMARX_CHECK_LESS(cfg->ws_x[0], 1000); + ARMARX_CHECK_LESS(-200, cfg->ws_x[1]); + + ARMARX_CHECK_LESS(cfg->ws_y[0], cfg->ws_y[1]); + ARMARX_CHECK_LESS(cfg->ws_y[0], 1200); + ARMARX_CHECK_LESS(0, cfg->ws_y[1]); + + ARMARX_CHECK_LESS(cfg->ws_z[0], cfg->ws_z[1]); + ARMARX_CHECK_LESS(cfg->ws_z[0], 1800); + ARMARX_CHECK_LESS(300, cfg->ws_z[1]); + + adaptK = kpos; + lastDiff = 0; + changeTimer = 0; + } + + void NJointAnomalyDetectionAdaptiveWipingController::onInitNJointController() + { + ARMARX_INFO << "init ..."; + + + RTToControllerData initSensorData; + initSensorData.deltaT = 0; + initSensorData.currentTime = 0; + initSensorData.currentPose = tcp->getPoseInRootFrame(); + initSensorData.currentTwist.setZero(); + initSensorData.isPhaseStop = false; + rt2CtrlData.reinitAllBuffers(initSensorData); + + RTToUserData initInterfaceData; + initInterfaceData.currentTcpPose = tcp->getPoseInRootFrame(); + initInterfaceData.waitTimeForCalibration = 0; + rt2UserData.reinitAllBuffers(initInterfaceData); + + started = false; + + runTask("NJointAnomalyDetectionAdaptiveWipingController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); + + } + + std::string NJointAnomalyDetectionAdaptiveWipingController::getClassName(const Ice::Current&) const + { + return "NJointAnomalyDetectionAdaptiveWipingController"; + } + + void NJointAnomalyDetectionAdaptiveWipingController::controllerRun() + { + if (!started) + { + return; + } + + if (!dmpCtrl) + { + return; + } + + Eigen::VectorXf targetVels(6); + bool isPhaseStop = rt2CtrlData.getUpToDateReadBuffer().isPhaseStop; + if (isPhaseStop) + { + targetVels.setZero(); + } + else + { + + double deltaT = rt2CtrlData.getUpToDateReadBuffer().deltaT; + Eigen::Matrix4f currentPose = rt2CtrlData.getUpToDateReadBuffer().currentPose; + Eigen::VectorXf currentTwist = rt2CtrlData.getUpToDateReadBuffer().currentTwist; + + LockGuardType guard {controllerMutex}; + dmpCtrl->flow(deltaT, currentPose, currentTwist); + + targetVels = dmpCtrl->getTargetVelocity(); + + debugOutputData.getWriteBuffer().latestTargetVelocities["x_vel"] = targetVels(0); + debugOutputData.getWriteBuffer().latestTargetVelocities["y_vel"] = targetVels(1); + debugOutputData.getWriteBuffer().latestTargetVelocities["z_vel"] = targetVels(2); + debugOutputData.getWriteBuffer().latestTargetVelocities["roll_vel"] = targetVels(3); + debugOutputData.getWriteBuffer().latestTargetVelocities["pitch_vel"] = targetVels(4); + debugOutputData.getWriteBuffer().latestTargetVelocities["yaw_vel"] = targetVels(5); + debugOutputData.getWriteBuffer().currentPose["currentPose_x"] = currentPose(0, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_y"] = currentPose(1, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_z"] = currentPose(2, 3); + VirtualRobot::MathTools::Quaternion currentQ = VirtualRobot::MathTools::eigen4f2quat(currentPose); + debugOutputData.getWriteBuffer().currentPose["currentPose_qw"] = currentQ.w; + debugOutputData.getWriteBuffer().currentPose["currentPose_qx"] = currentQ.x; + debugOutputData.getWriteBuffer().currentPose["currentPose_qy"] = currentQ.y; + debugOutputData.getWriteBuffer().currentPose["currentPose_qz"] = currentQ.z; + debugOutputData.getWriteBuffer().currentCanVal = dmpCtrl->debugData.canVal; + debugOutputData.getWriteBuffer().mpcFactor = dmpCtrl->debugData.mpcFactor; + debugOutputData.getWriteBuffer().error = dmpCtrl->debugData.poseError; + debugOutputData.getWriteBuffer().posError = dmpCtrl->debugData.posiError; + debugOutputData.getWriteBuffer().oriError = dmpCtrl->debugData.oriError; + debugOutputData.getWriteBuffer().deltaT = deltaT; + debugOutputData.commitWrite(); + } + getWriterControlStruct().canVal = dmpCtrl->canVal; + getWriterControlStruct().targetTSVel = targetVels; + writeControlStruct(); + + dmpRunning = true; + } + + + void NJointAnomalyDetectionAdaptiveWipingController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + double deltaT = timeSinceLastIteration.toSecondsDouble(); + float dTf = (float)deltaT; + + Eigen::Matrix4f currentPose = tcp->getPoseInRootFrame(); + rt2UserData.getWriteBuffer().currentTcpPose = currentPose; + rt2UserData.getWriteBuffer().waitTimeForCalibration += deltaT; + rt2UserData.commitWrite(); + + Eigen::Vector3f currentToolDir; + currentToolDir.setZero(); + Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All); + + Eigen::VectorXf qpos(positionSensors.size()); + Eigen::VectorXf qvel(velocitySensors.size()); + for (size_t i = 0; i < positionSensors.size(); ++i) + { + qpos(i) = positionSensors[i]->position; + qvel(i) = velocitySensors[i]->velocity; + } + + qvel_filtered = (1 - cfg->velFilter) * qvel_filtered + cfg->velFilter * qvel; + Eigen::VectorXf currentTwist = jacobi * qvel_filtered; + + velocityHorizonList.push_back(currentTwist); + if (velocityHorizonList.size() > velocityHorizon) + { + velocityHorizonList.pop_front(); + } + + + Eigen::VectorXf targetVel(6); + Eigen::Vector3f axis; + Eigen::Vector3f forceInToolFrame; + Eigen::Vector3f torqueInToolFrame; + Eigen::Vector6f targetFTInRootFrame; + Eigen::Vector3f velPInToolFrame; + targetVel.setZero(); + axis.setZero(); + forceInToolFrame.setZero(); + torqueInToolFrame.setZero(); + targetFTInRootFrame.setZero(); + velPInToolFrame.setZero(); + float angle = 0; + + if (firstRun || !dmpRunning) + { + initHandPose = currentPose; + lastPosition = currentPose.block<2, 1>(0, 3); + targetPose = currentPose; + firstRun = false; + filteredForce.setZero(); + Eigen::Vector3f currentForce = forceSensor->force - forceOffset; + + Eigen::Matrix3f forceFrameOri = rtGetRobot()->getRobotNode(cfg->forceFrameName)->getPoseInRootFrame().block<3, 3>(0, 0); + Eigen::Vector3f gravityInForceFrame = forceFrameOri.transpose() * gravityInRoot; + Eigen::Vector3f handGravity = handMass * gravityInForceFrame; + currentForce = currentForce - handGravity; + + currentForceOffset = 0.1 * currentForceOffset + 0.9 * currentForce; + origHandOri = currentPose.block<3, 3>(0, 0); + toolTransform = origHandOri.transpose(); + targetVel.setZero(); + } + else + { + Eigen::Matrix3f currentToolOri = currentPose.block<3, 3>(0, 0) * toolTransform; + + /* -------------------------- get target vel from dmp thread --------------------------------- */ + rtUpdateControlStruct(); + targetVel = rtGetControlStruct().targetTSVel; + targetVel(2) = 0; + targetVel.head(3) = currentToolOri * targetVel.head(3); + targetVel.tail(3) = currentToolOri * targetVel.tail(3); + + double canVal = rtGetControlStruct().canVal; + if (canVal - lastCanVal > 0.9 * cfg->timeDuration) + { + wipingCounter++; + mu = 1.0; + } + lastCanVal = canVal; + + /* -------------------------- force feedback, filter and transform frame --------------------------------- */ + Eigen::Vector3f currentForce = forceSensor->force - forceOffset - currentForceOffset; + + Eigen::Matrix3f forceFrameOri = rtGetRobot()->getRobotNode(cfg->forceFrameName)->getPoseInRootFrame().block<3, 3>(0, 0); + Eigen::Vector3f gravityInForceFrame = forceFrameOri.transpose() * gravityInRoot; + Eigen::Vector3f handGravity = handMass * gravityInForceFrame; + + currentForce = currentForce - handGravity; + filteredForce = (1 - cfg->forceFilter) * filteredForce + cfg->forceFilter * currentForce; + + Eigen::Vector3f currentTorque = forceSensor->torque - torqueOffset; + Eigen::Vector3f handTorque = handCOM.cross(gravityInForceFrame); + currentTorque = currentTorque - handTorque; + filteredTorque = (1 - cfg->forceFilter) * filteredTorque + cfg->forceFilter * currentTorque; + + Eigen::Vector3f forceInRootForFricEst = forceFrameOri * filteredForce; + Eigen::Vector3f forceInToolForFricEst = currentToolOri.transpose() * forceInRootForFricEst; + + for (size_t i = 0; i < 3; ++i) + { + if (fabs(filteredForce(i)) > cfg->forceDeadZone) + { + filteredForce(i) -= (filteredForce(i) / fabs(filteredForce(i))) * cfg->forceDeadZone; + } + else + { + filteredForce(i) = 0; + } + } + + filteredForceInRoot = forceFrameOri * filteredForce; + filteredTorqueInRoot = forceFrameOri * filteredTorque; + float targetForce = user2rtData.getUpToDateReadBuffer().targetForce; + + forceInToolFrame = currentToolOri.transpose() * filteredForceInRoot; + // TODO this is wrong + torqueInToolFrame = currentToolOri.transpose() * filteredTorqueInRoot; + velPInToolFrame = currentToolOri.transpose() * currentTwist.head(3); + // Eigen::Vector3f velRInToolFrame = currentToolOri.transpose() * currentTwist.tail(3); + + /* -------------------------- friction estimation --------------------------------- */ + + Eigen::Vector2f v_xy; + Eigen::Vector2f f_xy; + v_xy << velPInToolFrame(0), velPInToolFrame(1); + f_xy << forceInToolForFricEst(0), forceInToolForFricEst(1); + f_xy = cfg->fricEstiFilter * f_xy + (1 - cfg->fricEstiFilter) * lastForceInToolXY; + lastForceInToolXY = f_xy; + + if (wipingCounter > 0) + { + if (v_xy.norm() > cfg->velNormThreshold && fabs(forceInToolForFricEst(2) - targetForce) < 0.5 * targetForce) + { + recordFrictionNorm.push_back(f_xy.norm()); + recordForceNormToSurface.push_back(forceInToolForFricEst(2)); + } + if (recordFrictionNorm.size() > frictionHorizon) + { + recordFrictionNorm.pop_front(); + recordForceNormToSurface.pop_front(); + float dotProduct = 0.0; + float normSquare = 0.0; + for (size_t i = 0; i < recordFrictionNorm.size(); i++) + { + dotProduct += (recordFrictionNorm[i] * recordForceNormToSurface[i]); + normSquare += (recordForceNormToSurface[i] * recordForceNormToSurface[i]); + } + if (normSquare > 0) + { + float mu_tmp = dotProduct / normSquare; + if (mu_tmp > 0) + { + mu = fmax(fmin(mu, mu_tmp), safeFrictionConeLowerLimit); + } + } + if (v_xy.norm() > cfg->velNormThreshold) + { + estimatedFriction = - v_xy * mu * forceInToolForFricEst(2) / v_xy.norm(); + } + } + } + + /* -------------------------- Force Regulation and Torque PID Controller --------------------------------- */ + + forcePID->update(deltaT, forceInToolFrame(2), targetForce); + torquePID->update(deltaT, torqueInToolFrame(1), 0.0); + + /* -------------------------- Rotation PID Controller --------------------------------- */ + + currentToolDir = currentToolOri * oriToolDir; + for (int i = 3; i < 6; ++i) + { + targetVel(i) = 0; + } + float frictionCone; + if (cfg->frictionCone < 0.0) + { + frictionCone = atan(mu); + + } + else + { + frictionCone = cfg->frictionCone; + } + float adaptedAngularKp = 0.0; + Eigen::Vector3f angularDiff; + angularDiff.setZero(); + // rotation changes + if (filteredForceInRoot.norm() > fabs(cfg->minimumReactForce)) + { + Eigen::Vector3f toolYDir; + toolYDir << 0, 1.0, 0; + Eigen::Vector3f toolYDirInRoot = currentToolOri * toolYDir; + Eigen::Vector3f projectedFilteredForceInRoot = filteredForceInRoot - filteredForceInRoot.dot(toolYDirInRoot) * toolYDirInRoot; + Eigen::Vector3f desiredToolDir = projectedFilteredForceInRoot.normalized();// / projectedFilteredForceInRoot.norm(); + currentToolDir.normalize(); + + axis = currentToolDir.cross(desiredToolDir); + axis = axis.normalized(); + angle = acosf(currentToolDir.dot(desiredToolDir)); + + int sign = 1; + if (axis(1) < 0) + { + sign = -1; + } + + if (fabs(angle) < M_PI / 2 && fabs(angle) > frictionCone) + { + // sigmoid function + adaptedAngularKp = cfg->pidRot[0] / (1 + exp(10 * (angle - M_PI / 4))); + adaptedAngularKp = fmin(adaptedAngularKp, cfg->pidRot[0]); + rotPID->Kp = adaptedAngularKp; + angle -= frictionCone; + angle *= sign; + } + else + { + angle = 0.0; + rotPID->Kp = cfg->pidRot[0]; + } + } + rotPID->update(deltaT, angle, 0.0); + + /* -------------------------- Lose Contact Recover PID Controller --------------------------------- */ + + if (forceInToolFrame(2) > loseContactRatio * targetForce) + { + makingContactCounter++; + if (makingContactCounter > 100) + { + isMakingContact = true; + isLCREnabled = false; + } + else + { + isMakingContact = false; + } + } + if (!isContactedOnce && isMakingContact) + { + isContactedOnce = true; + } + Eigen::Vector3f compensationAxis; + compensationAxis.setZero(); + if (isContactedOnce && fabs(velPInToolFrame(2)) > 10 && frictionCone < 1.0) + { + makingContactCounter = 0; + Eigen::Vector3f v; + v << velPInToolFrame(0), 0.0, 0.0; + Eigen::Vector3f f; + f << 0.0, 0.0, targetForce; + compensationAxis = f.cross(v); + if (compensationAxis.norm() > 0) + { + compensationAxis.normalized(); + } + else + { + compensationAxis.setZero(); + } + forceControlGate *= 0.5; + isLCREnabled = true; + lcrCounter -= 1; + } + else + { + forceControlGate = 1.0; + // TODO: restart vmp controller + } + float velInForceDir = 0.0; + if (lcrCounter < 500) + { + velInForceDir = fabs(velPInToolFrame(2)); + lcrCounter -= 1; + if (lcrCounter == 0) + { + lcrCounter = 500; + } + } + lcrPID->update(deltaT, velInForceDir, 0.0); + + // get PID control commands + if (isLCRControlEnabled) + { + // targetFTInToolFrame.tail(3) += (float)lcrPID->getControlValue() * compensationAxis; + targetVel.tail(3) += (float)lcrPID->getControlValue() * compensationAxis; + } + if (isForceControlEnabled) + { + // targetFTInToolFrame(2) -= (float)forcePID->getControlValue() * forceControlGate; + targetVel(2) -= (float)forcePID->getControlValue(); + targetVel.head(3) = currentToolOri * targetVel.head(3); + } + if (isRotControlEnabled) + { + // targetFTInToolFrame(4) -= (float)rotPID->getControlValue(); + // targetVel.tail(3) = adaptedAngularKp * angularDiff; + targetVel(4) -= (float)rotPID->getControlValue(); + } + if (isTorqueControlEnabled) + { + // targetFTInToolFrame(4) -= (float)torquePID->getControlValue(); + targetVel(4) += (float)torquePID->getControlValue(); + } + // targetFTInRootFrame.head(3) = currentToolOri * targetFTInToolFrame.head(3); + // targetFTInRootFrame.tail(3) = currentToolOri * targetFTInToolFrame.tail(3); + } + + /* -------------------------- VMP Phase Stop --------------------------------- */ + + bool isPhaseStop = false; + + float diff = (targetPose.block<2, 1>(0, 3) - currentPose.block<2, 1>(0, 3)).norm(); + if (diff > cfg->phaseDist0) + { + isPhaseStop = true; + } + + /* -------------------------- Drag Force Adaptation --------------------------------- */ + + if (filteredForceInRoot.block<2, 1>(0, 0).norm() > cfg->dragForceDeadZone) + { + Eigen::Vector2f dragForce = filteredForceInRoot.block<2, 1>(0, 0) - cfg->dragForceDeadZone * filteredForceInRoot.block<2, 1>(0, 0) / filteredForceInRoot.block<2, 1>(0, 0).norm(); + adaptK(0) = fmax(adaptK(0) - dTf * cfg->adaptForceCoeff * dragForce.norm(), 0); + adaptK(1) = fmax(adaptK(1) - dTf * cfg->adaptForceCoeff * dragForce.norm(), 0); + lastDiff = diff; + } + else + { + adaptK(0) = fmin(adaptK(0) + fabs(dTf * cfg->adaptCoeff), kpos(0)); + adaptK(1) = fmin(adaptK(1) + fabs(dTf * cfg->adaptCoeff), kpos(1)); + } + adaptK(2) = kpos(2); + + /* -------------------------- Integrate Target Velocity 2 Target Pose --------------------------------- */ + + targetPose.block<3, 1>(0, 3) = targetPose.block<3, 1>(0, 3) + dTf * targetVel.block<3, 1>(0, 0); + Eigen::Matrix3f rotMat = VirtualRobot::MathTools::rpy2eigen3f(dTf * targetVel(3), dTf * targetVel(4), dTf * targetVel(5)); + targetPose.block<3, 3>(0, 0) = rotMat * targetPose.block<3, 3>(0, 0); + + if (isPhaseStop) + { + Eigen::Vector2f currentXY = currentPose.block<2, 1>(0, 3); + if ((lastPosition - currentXY).norm() < cfg->changePositionTolerance) + { + changeTimer += deltaT; + } + else + { + lastPosition = currentPose.block<2, 1>(0, 3); + changeTimer = 0; + } + + if (changeTimer > cfg->changeTimerThreshold) + { + targetPose(0, 3) = currentPose(0, 3); + targetPose(1, 3) = currentPose(1, 3); + isPhaseStop = false; + changeTimer = 0; + } + } + else + { + changeTimer = 0; + } + + + targetPose(0, 3) = targetPose(0, 3) > cfg->ws_x[0] ? targetPose(0, 3) : cfg->ws_x[0]; + targetPose(0, 3) = targetPose(0, 3) < cfg->ws_x[1] ? targetPose(0, 3) : cfg->ws_x[1]; + + targetPose(1, 3) = targetPose(1, 3) > cfg->ws_y[0] ? targetPose(1, 3) : cfg->ws_y[0]; + targetPose(1, 3) = targetPose(1, 3) < cfg->ws_y[1] ? targetPose(1, 3) : cfg->ws_y[1]; + + targetPose(2, 3) = targetPose(2, 3) > cfg->ws_z[0] ? targetPose(2, 3) : cfg->ws_z[0]; + targetPose(2, 3) = targetPose(2, 3) < cfg->ws_z[1] ? targetPose(2, 3) : cfg->ws_z[1]; + + /* -------------------------- Force/Torque Impedance Controller --------------------------------- */ + + // inverse dynamic controller + jacobi.block<3, 8>(0, 0) = 0.001 * jacobi.block<3, 8>(0, 0); // convert mm to m + + Eigen::Matrix3f diffMat = targetPose.block<3, 3>(0, 0) * currentPose.block<3, 3>(0, 0).transpose(); + Eigen::Vector3f rpy = VirtualRobot::MathTools::eigen3f2rpy(diffMat); + Eigen::Vector6f taskFTControlCommand; + + taskFTControlCommand.head(3) = adaptK.cwiseProduct(targetPose.block<3, 1>(0, 3) - currentPose.block<3, 1>(0, 3)) * 0.001 + + dpos.cwiseProduct(- currentTwist.head(3)) * 0.001 + + targetFTInRootFrame.head(3); + taskFTControlCommand.tail(3) = kori.cwiseProduct(rpy) + + dori.cwiseProduct(- currentTwist.tail(3)) + + targetFTInRootFrame.tail(3); + + filteredFTCommand = cfg->ftCommandFilter * taskFTControlCommand + (1 - cfg->ftCommandFilter) * filteredFTCommand; + + /* -------------------------- NullSpace and Joint Torque Controller --------------------------------- */ + + Eigen::MatrixXf I = Eigen::MatrixXf::Identity(targets.size(), targets.size()); + Eigen::VectorXf nullspaceTorque = knull.cwiseProduct(nullSpaceJointsVec - qpos) - dnull.cwiseProduct(qvel); + Eigen::MatrixXf jtpinv = ik->computePseudoInverseJacobianMatrix(jacobi.transpose(), 2.0); + Eigen::VectorXf jointDesiredTorques = jacobi.transpose() * filteredFTCommand + (I - jacobi.transpose() * jtpinv) * nullspaceTorque; + // if (forceInToolFrame.norm() > cfg->maxInteractionForce) + // { + // jointDesiredTorques.setZero(); + // } + + for (size_t i = 0; i < targets.size(); ++i) + { + targets.at(i)->torque = jointDesiredTorques(i); + + if (!targets.at(i)->isValid()) + { + targets.at(i)->torque = 0.0f; + } + else + { + if (fabs(targets.at(i)->torque) > fabs(cfg->maxJointTorque)) + { + targets.at(i)->torque = fabs(cfg->maxJointTorque) * (targets.at(i)->torque / fabs(targets.at(i)->torque)); + } + } + } + + /* -------------------------- Communication --------------------------------- */ + + debugRT.getWriteBuffer().currentToolDir = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystemVec(currentToolDir); + debugRT.getWriteBuffer().targetPose = targetPose; + debugRT.getWriteBuffer().globalPose = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystem(targetPose); + debugRT.getWriteBuffer().currentPose = currentPose; + debugRT.getWriteBuffer().filteredForceInRoot = filteredForceInRoot; + debugRT.getWriteBuffer().rotationAxis = axis; + debugRT.getWriteBuffer().filteredForce = forceInToolFrame; + debugRT.getWriteBuffer().globalFilteredForce = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystemVec(filteredForceInRoot); + debugRT.getWriteBuffer().targetVel = targetVel; + debugRT.getWriteBuffer().adaptK = adaptK; + debugRT.getWriteBuffer().isPhaseStop = isPhaseStop; + debugRT.getWriteBuffer().rotAngle = angle; + debugRT.getWriteBuffer().currentTwist = currentTwist; + debugRT.getWriteBuffer().filteredTorque = torqueInToolFrame; + debugRT.getWriteBuffer().wipingCounter = wipingCounter; + debugRT.getWriteBuffer().mu = mu; + debugRT.getWriteBuffer().estimatedFriction = estimatedFriction; + debugRT.getWriteBuffer().frictionInToolXY = lastForceInToolXY; + debugRT.getWriteBuffer().velPInTool = velPInToolFrame; + + debugRT.commitWrite(); + + rt2CtrlData.getWriteBuffer().currentPose = currentPose; + rt2CtrlData.getWriteBuffer().currentTwist = currentTwist; + rt2CtrlData.getWriteBuffer().deltaT = deltaT; + rt2CtrlData.getWriteBuffer().currentTime += deltaT; + rt2CtrlData.getWriteBuffer().isPhaseStop = isPhaseStop; + rt2CtrlData.commitWrite(); + + } + + + void NJointAnomalyDetectionAdaptiveWipingController::learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromFiles(fileNames); + + } + + void NJointAnomalyDetectionAdaptiveWipingController::learnDMPFromTrajectory(const TrajectoryBasePtr& trajectory, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + ARMARX_CHECK_EXPRESSION(trajectory); + TrajectoryPtr dmpTraj = TrajectoryPtr::dynamicCast(trajectory); + ARMARX_CHECK_EXPRESSION(dmpTraj); + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromTrajectory(dmpTraj); + + } + + void NJointAnomalyDetectionAdaptiveWipingController::setSpeed(Ice::Double times, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setSpeed(times); + } + + + void NJointAnomalyDetectionAdaptiveWipingController::setGoals(const Ice::DoubleSeq& goals, const Ice::Current& ice) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setGoalPoseVec(goals); + } + + void NJointAnomalyDetectionAdaptiveWipingController::setAmplitude(Ice::Double amp, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setAmplitude(amp); + } + + void NJointAnomalyDetectionAdaptiveWipingController::setTargetForceInRootFrame(float targetForce, const Ice::Current&) + { + LockGuardType guard {controlDataMutex}; + user2rtData.getWriteBuffer().targetForce = targetForce; + user2rtData.commitWrite(); + } + + void NJointAnomalyDetectionAdaptiveWipingController::runDMP(const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&) + { + firstRun = true; + while (firstRun || rt2UserData.getUpToDateReadBuffer().waitTimeForCalibration < cfg->waitTimeForCalibration) + { + usleep(100); + } + + + Eigen::Matrix4f pose = rt2UserData.getUpToDateReadBuffer().currentTcpPose; + + LockGuardType guard {controllerMutex}; + dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); + dmpCtrl->setSpeed(tau); + + ARMARX_IMPORTANT << "run DMP"; + started = true; + dmpRunning = false; + } + + + void NJointAnomalyDetectionAdaptiveWipingController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx& debugDrawer, const DebugObserverInterfacePrx& debugObs) + { + std::string datafieldName; + std::string debugName = "Periodic"; + StringVariantBaseMap datafields; + + Eigen::Matrix4f targetPoseDebug = debugRT.getUpToDateReadBuffer().targetPose; + datafields["target_x"] = new Variant(targetPoseDebug(0, 3)); + datafields["target_y"] = new Variant(targetPoseDebug(1, 3)); + datafields["target_z"] = new Variant(targetPoseDebug(2, 3)); + + Eigen::Matrix4f currentPoseDebug = debugRT.getUpToDateReadBuffer().currentPose; + datafields["current_x"] = new Variant(currentPoseDebug(0, 3)); + datafields["current_y"] = new Variant(currentPoseDebug(1, 3)); + datafields["current_z"] = new Variant(currentPoseDebug(2, 3)); + + Eigen::Vector3f filteredForce = debugRT.getUpToDateReadBuffer().filteredForce; + datafields["filteredforceInTool_x"] = new Variant(filteredForce(0)); + datafields["filteredforceInTool_y"] = new Variant(filteredForce(1)); + datafields["filteredforceInTool_z"] = new Variant(filteredForce(2)); + + Eigen::Vector3f filteredTorque = debugRT.getUpToDateReadBuffer().filteredTorque; + datafields["filteredtorqueInTool_x"] = new Variant(filteredTorque(0)); + datafields["filteredtorqueInTool_y"] = new Variant(filteredTorque(1)); + datafields["filteredtorqueInTool_z"] = new Variant(filteredTorque(2)); + + + Eigen::Vector3f filteredForceInRoot = debugRT.getUpToDateReadBuffer().filteredForceInRoot; + datafields["filteredForceInRoot_x"] = new Variant(filteredForceInRoot(0)); + datafields["filteredForceInRoot_y"] = new Variant(filteredForceInRoot(1)); + datafields["filteredForceInRoot_z"] = new Variant(filteredForceInRoot(2)); + + Eigen::Vector3f rotationAxis = debugRT.getUpToDateReadBuffer().rotationAxis; + datafields["rotationAxis_x"] = new Variant(rotationAxis(0)); + datafields["rotationAxis_y"] = new Variant(rotationAxis(1)); + datafields["rotationAxis_z"] = new Variant(rotationAxis(2)); + + Eigen::Vector3f reactForce = debugRT.getUpToDateReadBuffer().reactForce; + datafields["reactForce_x"] = new Variant(reactForce(0)); + datafields["reactForce_y"] = new Variant(reactForce(1)); + datafields["reactForce_z"] = new Variant(reactForce(2)); + + Eigen::VectorXf targetVel = debugRT.getUpToDateReadBuffer().targetVel; + datafields["targetVel_x"] = new Variant(targetVel(0)); + datafields["targetVel_y"] = new Variant(targetVel(1)); + datafields["targetVel_z"] = new Variant(targetVel(2)); + datafields["targetVel_ro"] = new Variant(targetVel(3)); + datafields["targetVel_pi"] = new Variant(targetVel(4)); + datafields["targetVel_ya"] = new Variant(targetVel(5)); + + Eigen::VectorXf currentTwist = debugRT.getUpToDateReadBuffer().currentTwist; + datafields["currentTwist_x"] = new Variant(currentTwist(0)); + datafields["currentTwist_y"] = new Variant(currentTwist(1)); + datafields["currentTwist_z"] = new Variant(currentTwist(2)); + datafields["currentTwist_ro"] = new Variant(currentTwist(3)); + datafields["currentTwist_pi"] = new Variant(currentTwist(4)); + datafields["currentTwist_ya"] = new Variant(currentTwist(5)); + + + Eigen::Vector3f adaptK = debugRT.getUpToDateReadBuffer().adaptK; + datafields["adaptK_x"] = new Variant(adaptK(0)); + datafields["adaptK_y"] = new Variant(adaptK(1)); + + datafields["canVal"] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + datafields["deltaT"] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + + datafields["PhaseStop"] = new Variant(debugRT.getUpToDateReadBuffer().isPhaseStop); + datafields["rotAngle"] = new Variant(debugRT.getUpToDateReadBuffer().rotAngle); + datafields["wipingCounter"] = new Variant(debugRT.getUpToDateReadBuffer().wipingCounter); + datafields["mu"] = new Variant(debugRT.getUpToDateReadBuffer().mu); + + Eigen::VectorXf estFri = debugRT.getUpToDateReadBuffer().estimatedFriction; + datafields["estimatedFriction_x"] = new Variant(estFri(0)); + datafields["estimatedFriction_y"] = new Variant(estFri(1)); + + Eigen::VectorXf frictionInToolXY = debugRT.getUpToDateReadBuffer().frictionInToolXY; + datafields["frictionInToolXY_x"] = new Variant(frictionInToolXY(0)); + datafields["frictionInToolXY_y"] = new Variant(frictionInToolXY(1)); + + Eigen::VectorXf velPInTool = debugRT.getUpToDateReadBuffer().velPInTool; + datafields["velPInTool_x"] = new Variant(velPInTool(0)); + datafields["velPInTool_y"] = new Variant(velPInTool(1)); + datafields["velPInTool_z"] = new Variant(velPInTool(2)); + + // datafields["targetVel_rx"] = new Variant(targetVel(3)); + // datafields["targetVel_ry"] = new Variant(targetVel(4)); + // datafields["targetVel_rz"] = new Variant(targetVel(5)); + + // auto values = debugOutputData.getUpToDateReadBuffer().latestTargetVelocities; + // for (auto& pair : values) + // { + // datafieldName = pair.first + "_" + debugName; + // datafields[datafieldName] = new Variant(pair.second); + // } + + // auto currentPose = debugOutputData.getUpToDateReadBuffer().currentPose; + // for (auto& pair : currentPose) + // { + // datafieldName = pair.first + "_" + debugName; + // datafields[datafieldName] = new Variant(pair.second); + // } + + // datafieldName = "canVal_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + // datafieldName = "mpcFactor_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().mpcFactor); + // datafieldName = "error_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().error); + // datafieldName = "posError_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().posError); + // datafieldName = "oriError_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().oriError); + // datafieldName = "deltaT_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + + datafieldName = "PeriodicDMP"; + debugObs->setDebugChannel(datafieldName, datafields); + + + // draw force; + Eigen::Matrix4f globalPose = debugRT.getUpToDateReadBuffer().globalPose; + Eigen::Vector3f handPosition = globalPose.block<3, 1>(0, 3); + Eigen::Vector3f forceDir = debugRT.getUpToDateReadBuffer().globalFilteredForce; + + debugDrawer->setArrowVisu("Force", "currentForce", new Vector3(handPosition), new Vector3(forceDir), DrawColor {0, 0, 1, 1}, 10 * forceDir.norm(), 3); + + // draw direction of the tool + Eigen::Vector3f currentToolDir = debugRT.getUpToDateReadBuffer().currentToolDir; + debugDrawer->setArrowVisu("Tool", "Tool", new Vector3(handPosition), new Vector3(currentToolDir), DrawColor {1, 0, 0, 1}, 100, 3); + debugDrawer->setPoseVisu("target", "targetPose", new Pose(globalPose)); + + } + + + + void NJointAnomalyDetectionAdaptiveWipingController::onDisconnectNJointController() + { + ARMARX_INFO << "stopped ..."; + } + + + +} diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.h new file mode 100644 index 0000000000000000000000000000000000000000..7e9e5998a6a7360691d1061d7663c98426cbabd1 --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.h @@ -0,0 +1,257 @@ +#pragma once + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h> +#include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include <VirtualRobot/IK/DifferentialIK.h> +#include <RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.h> +#include <RobotAPI/libraries/core/CartesianVelocityController.h> + +#include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h> +#include <ArmarXCore/core/time/CycleUtil.h> + +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h> +#include <RobotAPI/libraries/core/Trajectory.h> + +#include <RobotAPI/libraries/core/PIDController.h> + +namespace armarx +{ + + TYPEDEF_PTRS_HANDLE(NJointAnomalyDetectionAdaptiveWipingController); + TYPEDEF_PTRS_HANDLE(NJointAnomalyDetectionAdaptiveWipingControllerControlData); + + class NJointAnomalyDetectionAdaptiveWipingControllerControlData + { + public: + Eigen::VectorXf targetTSVel; + double canVal; + }; + + class pidController + { + public: + float Kp = 0, Kd = 0; + float lastError = 0; + float update(float dt, float error) + { + float derivative = (error - lastError) / dt; + float retVal = Kp * error + Kd * derivative; + lastError = error; + return retVal; + } + }; + + /** + * @brief The NJointAnomalyDetectionAdaptiveWipingController class + * @ingroup Library-RobotUnit-NJointControllers + */ + class NJointAnomalyDetectionAdaptiveWipingController : + public NJointControllerWithTripleBuffer<NJointAnomalyDetectionAdaptiveWipingControllerControlData>, + public NJointAnomalyDetectionAdaptiveWipingControllerInterface + { + public: + using ConfigPtrT = NJointAnomalyDetectionAdaptiveWipingControllerConfigPtr; + NJointAnomalyDetectionAdaptiveWipingController(const RobotUnitPtr&, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + // NJointControllerInterface interface + std::string getClassName(const Ice::Current&) const; + + // NJointController interface + + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + // NJointAnomalyDetectionAdaptiveWipingControllerInterface interface + void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&); + void learnDMPFromTrajectory(const TrajectoryBasePtr& trajectory, const Ice::Current&); + bool isFinished(const Ice::Current&) + { + return false; + } + + void setSpeed(Ice::Double times, const Ice::Current&); + void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&); + void setAmplitude(Ice::Double amp, const Ice::Current&); + void runDMP(const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&); + void setTargetForceInRootFrame(Ice::Float force, const Ice::Current&); + double getCanVal(const Ice::Current&) + { + return dmpCtrl->canVal; + } + + protected: + virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); + + void onInitNJointController(); + void onDisconnectNJointController(); + void controllerRun(); + + private: + struct DebugBufferData + { + StringFloatDictionary latestTargetVelocities; + StringFloatDictionary currentPose; + double currentCanVal; + double mpcFactor; + double error; + double phaseStop; + double posError; + double oriError; + double deltaT; + }; + + TripleBuffer<DebugBufferData> debugOutputData; + + + struct DebugRTData + { + Eigen::Matrix4f targetPose; + Eigen::Vector3f filteredForce; + Eigen::Vector3f filteredForceInRoot; + Eigen::Vector3f filteredTorque; + + Eigen::Vector3f rotationAxis; + + Eigen::Vector3f reactForce; + Eigen::Vector3f adaptK; + Eigen::VectorXf targetVel; + Eigen::Matrix4f currentPose; + bool isPhaseStop; + + Eigen::Matrix4f globalPose; + Eigen::Vector3f globalFilteredForce; + Eigen::Vector3f currentToolDir; + Eigen::VectorXf currentTwist; + + float rotAngle; + + int wipingCounter; + float mu; + Eigen::Vector2f estimatedFriction; + Eigen::Vector3f velPInTool; + Eigen::Vector2f frictionInToolXY; + }; + TripleBuffer<DebugRTData> debugRT; + + + struct RTToControllerData + { + double currentTime; + double deltaT; + Eigen::Matrix4f currentPose; + Eigen::VectorXf currentTwist; + bool isPhaseStop; + }; + TripleBuffer<RTToControllerData> rt2CtrlData; + + struct RTToUserData + { + Eigen::Matrix4f currentTcpPose; + float waitTimeForCalibration; + }; + TripleBuffer<RTToUserData> rt2UserData; + + struct UserToRTData + { + float targetForce; + }; + TripleBuffer<UserToRTData> user2rtData; + + + TaskSpaceDMPControllerPtr dmpCtrl; + + std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> positionSensors; + std::vector<ControlTarget1DoFActuatorTorque*> targets; + + // anomaly detection + std::deque<Eigen::VectorXf> velocityHorizonList; + size_t velocityHorizon; + + // velocity ik controller parameters + std::string nodeSetName; + + bool started; + bool firstRun; + bool dmpRunning; + + // friction estimation + float mu = 1.5f; // init friction coefficient + Eigen::Vector2f lastForceInToolXY; + double lastCanVal = 0.0; + int wipingCounter = 0; + std::deque<float> recordFrictionNorm; + std::deque<float> recordForceNormToSurface; + size_t frictionHorizon; + Eigen::Vector2f estimatedFriction; + float safeFrictionConeLowerLimit = 0.2; + + // lose contact detection + float loseContactRatio = 0.2f; + int makingContactCounter = 0; + bool isMakingContact = false; + bool isLCREnabled = false; + bool isContactedOnce = false; + float forceControlGate = 1.0; + int lcrCounter = 500; + + VirtualRobot::DifferentialIKPtr ik; + VirtualRobot::RobotNodePtr tcp; + + NJointAnomalyDetectionAdaptiveWipingControllerConfigPtr cfg; + mutable MutexType controllerMutex; + PeriodicTask<NJointAnomalyDetectionAdaptiveWipingController>::pointer_type controllerTask; + Eigen::Matrix4f targetPose; + Eigen::Matrix4f initHandPose; + + Eigen::Vector3f kpos; + Eigen::Vector3f dpos; + Eigen::Vector3f kori; + Eigen::Vector3f dori; + Eigen::VectorXf knull; + Eigen::VectorXf dnull; + + Eigen::VectorXf nullSpaceJointsVec; + const SensorValueForceTorque* forceSensor; + + // pid controllers + bool isForceControlEnabled; + bool isRotControlEnabled; + bool isTorqueControlEnabled; + bool isLCRControlEnabled; + PIDControllerPtr forcePID; + PIDControllerPtr rotPID; + PIDControllerPtr torquePID; + PIDControllerPtr lcrPID; // lose contact recover pid controller + + // force torque related + Eigen::Vector6f targetFTInToolFrame; + Eigen::Vector3f filteredForce; + Eigen::Vector3f filteredTorque; + Eigen::Vector6f filteredFTCommand; + Eigen::Vector3f forceOffset; + Eigen::Vector3f currentForceOffset; + + Eigen::Vector3f torqueOffset; + Eigen::Vector3f currentTorqueOffset; + float handMass; + Eigen::Vector3f handCOM; + Eigen::Vector3f gravityInRoot; + + Eigen::Vector3f filteredForceInRoot; + Eigen::Vector3f filteredTorqueInRoot; + + Eigen::Matrix3f toolTransform; + Eigen::Vector3f oriToolDir; + Eigen::Matrix3f origHandOri; + Eigen::VectorXf qvel_filtered; + + Eigen::Vector3f adaptK; + float lastDiff; + Eigen::Vector2f lastPosition; + double changeTimer; + + }; +} // namespace armarx diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp index 0a8ba174454a5f6c0b80f40aea0fc4caf75967f1..e149fadcd8125c1abea14feba1f4d099537043c9 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp @@ -1,5 +1,7 @@ #include "NJointJSDMPController.h" +#include <ArmarXCore/core/time/CycleUtil.h> + namespace armarx @@ -14,7 +16,7 @@ namespace armarx NJointJSDMPController::NJointJSDMPController(const RobotUnitPtr&, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) { - NJointJointSpaceDMPControllerConfigPtr cfg = NJointJointSpaceDMPControllerConfigPtr::dynamicCast(config); + cfg = NJointJointSpaceDMPControllerConfigPtr::dynamicCast(config); ARMARX_CHECK_EXPRESSION_W_HINT(cfg, "Needed type: NJointJointSpaceDMPControllerConfigPtr"); for (std::string jointName : cfg->jointNames) @@ -23,8 +25,6 @@ namespace armarx const SensorValueBase* sv = useSensorValue(jointName); targets.insert(std::make_pair(jointName, ct->asA<ControlTarget1DoFActuatorVelocity>())); positionSensors.insert(std::make_pair(jointName, sv->asA<SensorValue1DoFActuatorPosition>())); - torqueSensors.insert(std::make_pair(jointName, sv->asA<SensorValue1DoFActuatorTorque>())); - gravityTorqueSensors.insert(std::make_pair(jointName, sv->asA<SensorValue1DoFGravityTorque>())); velocitySensors.insert(std::make_pair(jointName, sv->asA<SensorValue1DoFActuatorVelocity>())); } if (cfg->jointNames.size() == 0) @@ -32,7 +32,7 @@ namespace armarx ARMARX_ERROR << "cfg->jointNames.size() == 0"; } - dmpPtr.reset(new DMP::UMIDMP(cfg->kernelSize, cfg->DMPKd, cfg->baseMode, cfg->tau)); + dmpPtr.reset(new DMP::UMIDMP(cfg->kernelSize, cfg->DMPKd, cfg->baseMode, 1)); timeDuration = cfg->timeDuration; phaseL = cfg->phaseL; phaseK = cfg->phaseK; @@ -58,86 +58,93 @@ namespace armarx initSensorData.currentState.resize(cfg->jointNames.size()); controllerSensorData.reinitAllBuffers(initSensorData); - deltaT = 0; } void NJointJSDMPController::controllerRun() { - if (!controllerSensorData.updateReadBuffer()) + if (!started) { - return; + for (size_t i = 0; i < dimNames.size(); ++i) + { + targetVels[i] = 0; + } } - - currentState = controllerSensorData.getUpToDateReadBuffer().currentState; - double deltaT = controllerSensorData.getUpToDateReadBuffer().deltaT; - if (canVal > 1e-8) + else { - - double phaseStop = 0; - double error = 0; - std::vector<double> currentPosition; - currentPosition.resize(dimNames.size()); - - for (size_t i = 0; i < currentState.size(); i++) + currentState = controllerSensorData.getUpToDateReadBuffer().currentState; + double deltaT = controllerSensorData.getUpToDateReadBuffer().deltaT; + if (canVal > 1e-8) { - DMP::DMPState currentPos = currentState[i]; - currentPosition[i] = currentPos.pos; - error += pow(currentPos.pos - targetState[i], 2); - } - - double phaseDist; + double phaseStop = 0; + double mpcFactor = 1; + + std::vector<double> currentPosition; + double error = 0; + currentPosition.resize(dimNames.size()); + + for (size_t i = 0; i < currentState.size(); i++) + { + DMP::DMPState currentPos = currentState[i]; + currentPosition[i] = currentPos.pos; + error += pow(currentPos.pos - targetState[i], 2); + } + + if (cfg->isPhaseStop) + { + double phaseDist; + + if (isDisturbance) + { + phaseDist = phaseDist1; + } + else + { + phaseDist = phaseDist0; + } + + error = sqrt(error); + phaseStop = phaseL / (1 + exp(-phaseK * (error - phaseDist))); + mpcFactor = 1 - (phaseStop / phaseL); + + if (mpcFactor < 0.1) + { + isDisturbance = true; + } + + if (mpcFactor > 0.9) + { + isDisturbance = false; + } + } + + canVal -= tau * deltaT * 1 / (1 + phaseStop); + double dmpDeltaT = deltaT / timeDuration; + + currentState = dmpPtr->calculateDirectlyVelocity(currentState, canVal / timeDuration, dmpDeltaT, targetState); + + for (size_t i = 0; i < currentState.size(); ++i) + { + double vel0 = tau * currentState[i].vel / timeDuration; + double vel1 = phaseKp * (targetState[i] - currentPosition[i]); + double vel = mpcFactor * vel0 + (1 - mpcFactor) * vel1; + targetVels[i] = finished ? 0.0f : vel; + + debugOutputData.getWriteBuffer().latestTargetVelocities[dimNames[i]] = vel; + } + + debugOutputData.getWriteBuffer().currentCanVal = canVal; + debugOutputData.getWriteBuffer().mpcFactor = mpcFactor; + debugOutputData.commitWrite(); - if (isDisturbance) - { - phaseDist = phaseDist1; } else { - phaseDist = phaseDist0; - } - - error = sqrt(error); - phaseStop = phaseL / (1 + exp(-phaseK * (error - phaseDist))); - double mpcFactor = 1 - (phaseStop / phaseL); - - if (mpcFactor < 0.1) - { - isDisturbance = true; - } - - if (mpcFactor > 0.9) - { - isDisturbance = false; - } - - canVal -= 1 / tau * deltaT * 1 / (1 + phaseStop); - double dmpDeltaT = deltaT / timeDuration; - dmpPtr->setTemporalFactor(tau); - - currentState = dmpPtr->calculateDirectlyVelocity(currentState, canVal / timeDuration, dmpDeltaT, targetState); - - for (size_t i = 0; i < currentState.size(); ++i) - { - double vel0 = currentState[i].vel / timeDuration; - double vel1 = phaseKp * (targetState[i] - currentPosition[i]); - double vel = mpcFactor * vel0 + (1 - mpcFactor) * vel1; - targetVels[i] = finished ? 0.0f : vel; - - debugOutputData.getWriteBuffer().latestTargetVelocities[dimNames[i]] = vel; - } - - debugOutputData.getWriteBuffer().currentCanVal = canVal; - debugOutputData.getWriteBuffer().mpcFactor = deltaT; - debugOutputData.commitWrite(); - - } - else - { - finished = true; - for (size_t i = 0; i < dimNames.size(); ++i) - { - targetVels[i] = 0; + finished = true; + for (size_t i = 0; i < dimNames.size(); ++i) + { + targetVels[i] = 0; + } } } @@ -154,18 +161,24 @@ namespace armarx DMP::DMPState currentPos; currentPos.pos = (positionSensors.count(jointName) == 1) ? positionSensors[jointName]->position : 0.0f; currentPos.vel = (velocitySensors.count(jointName) == 1) ? velocitySensors[jointName]->velocity : 0.0f; - currentPos.vel *= timeDuration; controllerSensorData.getWriteBuffer().currentState[i] = currentPos; } controllerSensorData.getWriteBuffer().deltaT = timeSinceLastIteration.toSecondsDouble(); - controllerSensorData.getWriteBuffer().currentTime += controllerSensorData.getWriteBuffer().deltaT; + controllerSensorData.getWriteBuffer().currentTime += timeSinceLastIteration.toSecondsDouble(); controllerSensorData.commitWrite(); std::vector<double> targetJointVels = rtGetControlStruct().targetJointVels; for (size_t i = 0; i < dimNames.size(); ++i) { - targets[dimNames[i]]->velocity = targetJointVels[i]; + if (fabs(targetJointVels[i]) > cfg->maxJointVel) + { + targets[dimNames[i]]->velocity = targetJointVels[i] < 0 ? -cfg->maxJointVel : cfg->maxJointVel; + } + else + { + targets[dimNames[i]]->velocity = targetJointVels[i]; + } } @@ -201,40 +214,45 @@ namespace armarx void NJointJSDMPController::runDMP(const Ice::DoubleSeq& goals, double tau, const Ice::Current&) { + while (!rt2UserData.updateReadBuffer()) + { + usleep(100); + } + targetState.clear(); targetState.resize(dimNames.size()); currentState.clear(); currentState.resize(dimNames.size()); for (size_t i = 0; i < dimNames.size(); i++) { - const auto& jointName = dimNames.at(i); DMP::DMPState currentPos; - currentPos.pos = (positionSensors.count(jointName) == 1) ? positionSensors[jointName]->position : 0.0f; - currentPos.vel = (velocitySensors.count(jointName) == 1) ? velocitySensors[jointName]->velocity : 0.0f; + currentPos.pos = rt2UserData.getReadBuffer().qpos[i]; + currentPos.vel = rt2UserData.getReadBuffer().qvel[i]; currentState[i] = currentPos; targetState.push_back(currentPos.pos); } - dmpPtr->prepareExecution(goals, currentState, 1, tau); + + dmpPtr->prepareExecution(goals, currentState, 1, 1); this->goals = goals; - this->tau = tau; - canVal = timeDuration; + canVal = timeDuration * tau; finished = false; isDisturbance = false; - ARMARX_INFO << "run DMP..."; - controllerTask->start(); + ARMARX_INFO << "run DMP"; + started = true; + } void NJointJSDMPController::showMessages(const Ice::Current&) { } - void NJointJSDMPController::setTemporalFactor(double tau, const Ice::Current&) + void NJointJSDMPController::setSpeed(double times, const Ice::Current&) { LockGuardType guard(controllerMutex); - this->tau = tau; + tau = times; } void NJointJSDMPController::rtPreActivateController() @@ -264,7 +282,20 @@ namespace armarx void NJointJSDMPController::onInitNJointController() { ARMARX_INFO << "init ..."; - controllerTask = new PeriodicTask<NJointJSDMPController>(this, &NJointJSDMPController::controllerRun, 0.2); + started = false; + runTask("NJointJSDMPController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); } void NJointJSDMPController::onDisconnectNJointController() diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h index f593d4943bcaae6286a654d8dcfb46823abe3915..1f0e7a64e8d4bf0ba5c527fc12f98ec18e71bff5 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h @@ -23,15 +23,6 @@ namespace armarx std::vector<double> targetJointVels; }; - - // class SimplePID - // { - // public: - // float Kp = 0, Kd = 0; - // float lastError = 0; - // float update(float dt, float error); - // }; - /** * @brief The NJointJSDMPController class * @ingroup Library-RobotUnit-NJointControllers @@ -56,7 +47,7 @@ namespace armarx } void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) override; - void setTemporalFactor(double tau, const Ice::Current&) override; + void setSpeed(double times, const Ice::Current&) override; void runDMP(const Ice::DoubleSeq& goals, double tau, const Ice::Current&) override; @@ -68,7 +59,7 @@ namespace armarx virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; private: - + NJointJointSpaceDMPControllerConfigPtr cfg; struct DebugBufferData { StringFloatDictionary latestTargetVelocities; @@ -86,9 +77,13 @@ namespace armarx }; TripleBuffer<NJointJSDMPControllerSensorData> controllerSensorData; + struct RTToUserData + { + Eigen::VectorXf qpos; + Eigen::VectorXf qvel; + }; + TripleBuffer<RTToUserData> rt2UserData; - std::map<std::string, const SensorValue1DoFActuatorTorque*> torqueSensors; - std::map<std::string, const SensorValue1DoFGravityTorque*> gravityTorqueSensors; std::map<std::string, const SensorValue1DoFActuatorPosition*> positionSensors; std::map<std::string, const SensorValue1DoFActuatorVelocity*> velocitySensors; std::map<std::string, ControlTarget1DoFActuatorVelocity*> targets; @@ -102,8 +97,8 @@ namespace armarx DMP::Vec<DMP::DMPState> currentState; double canVal; double deltaT; - double tau; + double finished; // phaseStop parameters @@ -114,6 +109,7 @@ namespace armarx double phaseKp; bool isDisturbance; + bool started; std::vector<std::string> dimNames; DMP::DVec targetState; std::vector<double> targetVels; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp index 5675efd9521c6092afb4e3e84f1cbb784f58584a..e56bb103a8da8ba235d4a38f331fc964f03a54df 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp @@ -6,6 +6,7 @@ namespace armarx NJointTaskSpaceImpedanceDMPController::NJointTaskSpaceImpedanceDMPController(const RobotUnitPtr& robotUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) { + ARMARX_INFO << "creating impedance dmp controller"; cfg = NJointTaskSpaceImpedanceDMPControllerConfigPtr::dynamicCast(config); useSynchronizedRtRobot(); rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); @@ -104,6 +105,8 @@ namespace armarx controllerSensorData.reinitAllBuffers(initControllerSensorData); firstRun = true; + + ARMARX_INFO << "Finished controller constructor "; } std::string NJointTaskSpaceImpedanceDMPController::getClassName(const Ice::Current&) const