From 1cfdd413dfba3261989679a7a96f514adaa3804b Mon Sep 17 00:00:00 2001 From: JeffGao <jianfenggaobit@gmail.com> Date: Thu, 5 Sep 2019 16:44:22 +0200 Subject: [PATCH] add anomaly detection wiping controller --- .../NJointTaskSpaceDMPController.ice | 95 ++ .../RobotAPINJointControllers/CMakeLists.txt | 4 +- .../NJointAdaptiveWipingController.cpp | 60 +- .../NJointAdaptiveWipingController.h | 11 + ...omalyDetectionAdaptiveWipingController.cpp | 931 ++++++++++++++++++ ...AnomalyDetectionAdaptiveWipingController.h | 257 +++++ 6 files changed, 1343 insertions(+), 15 deletions(-) create mode 100644 source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.cpp create mode 100644 source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAnomalyDetectionAdaptiveWipingController.h diff --git a/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice b/source/RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.ice index 737dd63f2..86cffecf0 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 369cf82eb..e8a66b9b7 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt @@ -50,7 +50,7 @@ set(LIB_HEADERS DMPController/NJointPeriodicTSDMPForwardVelController.h DMPController/NJointPeriodicTSDMPCompliantController.h DMPController/NJointAdaptiveWipingController.h - + DMPController/NJointAnomalyDetectionAdaptiveWipingController.h ) list(APPEND LIB_FILES DMPController/NJointJointSpaceDMPController.cpp @@ -63,7 +63,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 a58e3ae3d..324b543ac 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()); @@ -270,6 +271,13 @@ namespace armarx 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); @@ -277,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(); @@ -304,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) { @@ -321,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); @@ -351,13 +364,13 @@ namespace armarx axis = currentToolDir.cross(desiredToolDir); axis = axis.normalized(); - float angle = acosf(currentToolDir.dot(desiredToolDir)); + angle = acosf(currentToolDir.dot(desiredToolDir)); - if (fabs(angle) < M_PI / 2) + if (fabs(angle) < M_PI / 2 && fabs(angle) > cfg->frictionCone) { // sigmoid function - float adaptedAngularKp = 1 / (1 + exp(0.2 * (angle - M_PI / 4))); + float adaptedAngularKp = cfg->angularKp / (1 + exp(10 * (angle - M_PI / 4))); float angularKp = fmin(adaptedAngularKp, cfg->angularKp); // test axis @@ -370,7 +383,7 @@ namespace armarx { fixedAxis << 0.0, -1.0, 0.0; } - Eigen::AngleAxisf desiredToolRot(angle, fixedAxis); + Eigen::AngleAxisf desiredToolRot(angle - cfg->frictionCone, fixedAxis); Eigen::Matrix3f desiredRotMat = desiredToolRot * Eigen::Matrix3f::Identity(); Eigen::Vector3f angularDiff = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); @@ -466,11 +479,16 @@ namespace armarx debugRT.getWriteBuffer().currentPose = currentPose; debugRT.getWriteBuffer().filteredForceInRoot = filteredForceInRoot; debugRT.getWriteBuffer().rotationAxis = axis; - debugRT.getWriteBuffer().filteredForce = filteredForce; + 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; @@ -637,9 +655,15 @@ 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)); @@ -664,6 +688,15 @@ namespace armarx 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)); @@ -672,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 9c2b8d01d..7e1949ebe 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,8 @@ namespace armarx Eigen::Matrix4f targetPose; Eigen::Vector3f filteredForce; Eigen::Vector3f filteredForceInRoot; + Eigen::Vector3f filteredTorque; + Eigen::Vector3f rotationAxis; Eigen::Vector3f reactForce; @@ -120,6 +124,9 @@ namespace armarx Eigen::Matrix4f globalPose; Eigen::Vector3f globalFilteredForce; Eigen::Vector3f currentToolDir; + Eigen::VectorXf currentTwist; + + float rotAngle; }; TripleBuffer<DebugRTData> debugRT; @@ -182,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 000000000..c200c425e --- /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 000000000..4de83bd73 --- /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; + int 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; + int 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 -- GitLab