diff --git a/source/RobotAPI/components/units/CMakeLists.txt b/source/RobotAPI/components/units/CMakeLists.txt index 7c99e406382161c50b689c9f1a5bea1c64b317ee..fe8179da6045e7307ef47cb9fb78d0ffc7ff9465 100644 --- a/source/RobotAPI/components/units/CMakeLists.txt +++ b/source/RobotAPI/components/units/CMakeLists.txt @@ -86,3 +86,4 @@ set(LIB_FILES armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") add_subdirectory(relays) +add_subdirectory(RobotUnit) diff --git a/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp b/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6e7e8235191760481ccb3e8cfbef38585f7723c6 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp @@ -0,0 +1,724 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "BasicControllers.h" + +#include <boost/algorithm/clamp.hpp> + +#include <ArmarXCore/core/util/algorithm.h> +#include <ArmarXCore/core/logging/Logging.h> + +namespace armarx +{ + float velocityControlWithAccelerationBounds( + float dt, float maxDt, + const float currentV, float targetV, float maxV, + float acceleration, float deceleration, + float directSetVLimit + ) + { + dt = std::min(std::abs(dt), std::abs(maxDt)); + maxV = std::abs(maxV); + acceleration = std::abs(acceleration); + deceleration = std::abs(deceleration); + targetV = boost::algorithm::clamp(targetV, -maxV, maxV); + + //we can have 3 cases: + // 1. we directly set v and ignore acc/dec (if |curr - target| <= limit) + // 2. we need to accelerate (if curr and target v have same sign and |curr| < |target|) + // 3. we need to decellerate (other cases) + + //handle case 1 + const float curverror = targetV - currentV; + if (std::abs(curverror) < directSetVLimit) + { + return targetV; + } + + //handle case 2 + 3 + const bool accelerate = sign(targetV) == sign(currentV) && // v in same direction + std::abs(targetV) > std::abs(currentV); // currently to slow + + const float usedacc = accelerate ? acceleration : -deceleration; + const float maxDeltaV = std::abs(usedacc * dt); + if (maxDeltaV >= std::abs(curverror)) + { + //we change our v too much with the used acceleration + //but we could reach our target with a lower acceleration ->set the target + + return targetV; + } + const float deltaVel = boost::algorithm::clamp(sign(currentV) * usedacc * dt, -maxDeltaV, maxDeltaV); + const float nextV = currentV + deltaVel; + return nextV; + } + + //////////////////////////// + //wip? + float velocityControlWithAccelerationAndPositionBounds(float dt, float maxDt, + float currentV, float targetV, float maxV, + float acceleration, float deceleration, + float directSetVLimit, + float currentPosition, + float positionLimitLoSoft, float positionLimitHiSoft, + float positionLimitLoHard, float positionLimitHiHard) + { + if (currentPosition <= positionLimitLoHard || currentPosition >= positionLimitHiHard) + { + return std::nanf("1"); + } + + float softLimitViolation = 0; + if (currentPosition <= positionLimitLoSoft) + { + softLimitViolation = -1; + } + if (currentPosition >= positionLimitHiSoft) + { + softLimitViolation = 1; + } + + const float upperDt = std::max(std::abs(dt), std::abs(maxDt)); + dt = std::min(std::abs(dt), std::abs(maxDt)); + maxV = std::abs(maxV); + + //we can have 4 cases: + // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them, e.g. if we violated them initially) + // 2. we directly set v and ignore acc/dec (if |curr - target| <= limit) + // 3. we need to accelerate (if curr and target v have same sign and |curr| < |target|) + // 4. we need to decellerate (other cases) + float nextv; + //handle case 1 + const float vsquared = currentV * currentV; + const float brakingDist = sign(currentV) * vsquared / 2.f / std::abs(deceleration); //the braking distance points in the direction of the velocity + + + const float posIfBrakingNow = currentPosition + brakingDist; + if (posIfBrakingNow <= positionLimitLoSoft || posIfBrakingNow >= positionLimitHiSoft) + { + //case 1. -> brake now! (we try to have v=0 at the limit) + const auto limit = posIfBrakingNow <= positionLimitLoSoft ? positionLimitLoSoft : positionLimitHiSoft; + const float wayToGo = limit - currentPosition; + + //decellerate! + // s = v²/(2a) <=> a = v²/(2s) + const float dec = std::abs(vsquared / 2.f / wayToGo); + const float vel = currentV - sign(currentV) * dec * upperDt; + nextv = boost::algorithm::clamp(vel, -maxV, maxV); + if (sign(currentV) != sign(nextv)) + { + //stop now + nextv = 0; + } + } + else + { + //handle 2-3 + nextv = velocityControlWithAccelerationBounds(dt, maxDt, currentV, targetV, maxV, acceleration, deceleration, directSetVLimit); + } + if (softLimitViolation == sign(nextv)) + { + //the area between soft and hard limits is sticky + //the controller can only move out of it (not further in) + return 0; + } + + //the next velocity will not violate the pos bounds harder than they are already + return nextv; + } + + + float positionThroughVelocityControlWithAccelerationBounds( + float dt, float maxDt, + float currentV, float maxV, + float acceleration, float deceleration, + float currentPosition, float targetPosition, + float p + ) + { + dt = std::min(std::abs(dt), std::abs(maxDt)); + maxV = std::abs(maxV); + acceleration = std::abs(acceleration); + deceleration = std::abs(deceleration); + const float signV = sign(currentV); + //we can have 3 cases: + // 1. we use a p controller and ignore acc/dec (if velocity target from p controller < velocity target from ramp controller) + // 2. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) + // the brakingDistance have the same sign and brakingDistance < e + // and currentVel <= maxV) + // 3. we need to decellerate (other cases) + + //handle case 1 + const float positionError = targetPosition - currentPosition; + float newTargetVelPController = positionError * p; + + //handle case 2-3 + const float brakingDistance = signV * currentV * currentV / 2.f / deceleration; //the braking distance points in the direction of the velocity + const float posIfBrakingNow = currentPosition + brakingDistance; + const float posErrorIfBrakingNow = targetPosition - posIfBrakingNow; + + const bool decelerate = + std::abs(currentV) > maxV || // we need to slow down (to stay in [-maxV,maxV] + // std::abs(posIfBrakingNow - targetPosition) <= pControlPosErrorLimit || // we want to hit the target + sign(posErrorIfBrakingNow) != signV; // we are moving away from the target + const float usedacc = decelerate ? -deceleration : acceleration; + const float maxDeltaV = std::abs(usedacc * dt); + const float deltaVel = boost::algorithm::clamp(signV * usedacc * dt, -maxDeltaV, maxDeltaV); + + float newTargetVel = boost::algorithm::clamp(currentV + deltaVel, -maxV, maxV); + bool usePID = std::abs(newTargetVelPController) < std::abs(newTargetVel); + // ARMARX_INFO << deactivateSpam(0.2) << VAROUT(usePID) << VAROUT(dt) << VAROUT(decelerate) << VAROUT(usedacc) << VAROUT(maxDeltaV) << VAROUT(deltaVel) << VAROUT(newTargetVel) << VAROUT(newTargetVelPController) + // << VAROUT(currentPosition) << VAROUT(targetPosition); + if (usePID) + { + return newTargetVelPController; + } + else + { + return newTargetVel; + } + } + + float positionThroughVelocityControlWithAccelerationAndPositionBounds( + float dt, float maxDt, + float currentV, float maxV, + float acceleration, float deceleration, + float currentPosition, float targetPosition, + float p, + float positionLimitLo, float positionLimitHi + + ) + { + dt = std::min(std::abs(dt), std::abs(maxDt)); + maxV = std::abs(maxV); + acceleration = std::abs(acceleration); + deceleration = std::abs(deceleration); + const float signV = sign(currentV); + //we can have 4 cases: + // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them) + // 2. we use a p controller and ignore acc/dec (if |currentPosition - targetPosition| <= pControlPosErrorLimit AND |currentPosition - targetPosition|*p < maxV) + // 4. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) + // and the brakingDistance have the same sign and brakingDistance < e + // and currentVel <= maxV) + // 5. we need to decellerate (other cases) + + //handle case 1 + const float vsquared = currentV * currentV; + const float brakingDistance = signV * vsquared / 2.f / deceleration; //the braking distance points in the direction of the velocity + const float posIfBrakingNow = currentPosition + brakingDistance; + if (posIfBrakingNow <= positionLimitLo || posIfBrakingNow >= positionLimitHi) + { + //case 1. -> brake now! (we try to have v=0 at the limit) + const auto limit = brakingDistance > 0 ? positionLimitHi : positionLimitLo; + const float wayToGo = std::abs(limit - currentPosition); + // s = v²/(2a) <=> a = v²/(2s) + const float dec = std::abs(vsquared / 2.f / wayToGo); + const float vel = currentV - signV * dec * dt; + return vel; + } + + //handle case 2-3 + return positionThroughVelocityControlWithAccelerationBounds( + dt, maxDt, + currentV, maxV, + acceleration, deceleration, + currentPosition, boost::algorithm::clamp(targetPosition, positionLimitLo, positionLimitHi), + p + ); + } + + //////////////////////////// + //wip + + + float positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition( + float dt, float maxDt, + float currentV, float maxV, + float acceleration, float deceleration, + float currentPosition, float targetPosition, + float pControlPosErrorLimit, float p, + float& direction, + float positionPeriodLo, float positionPeriodHi) + { + currentPosition = periodicClamp(currentPosition, positionPeriodLo, positionPeriodHi); + targetPosition = periodicClamp(targetPosition , positionPeriodLo, positionPeriodHi); + + const float brakingDist = brakingDistance(currentV, deceleration); + const float posIfBrakingNow = currentPosition + brakingDist; + const float posIfBrakingNowError = targetPosition - posIfBrakingNow; + const float posError = targetPosition - currentPosition; + if ( + std::abs(posIfBrakingNowError) <= pControlPosErrorLimit || + std::abs(posError) <= pControlPosErrorLimit + ) + { + //this allows slight overshooting (in the limits of the p controller) + return positionThroughVelocityControlWithAccelerationBounds( + dt, maxDt, + currentV, maxV, + acceleration, deceleration, + currentPosition, targetPosition, + p + ); + } + + //we transform the problem with periodic bounds into + //a problem with position bounds + const float positionPeriodLength = std::abs(positionPeriodHi - positionPeriodLo); + + //we shift the positions such that 0 == target + currentPosition = periodicClamp(currentPosition - targetPosition, 0.f, positionPeriodLength); + //how many times will we go ovet the target if we simply bake now? + const float overshoot = std::trunc((currentPosition + brakingDist) / positionPeriodLength); + + if (true || direction == 0) + { + //determine the direction to go (1 == pos vel, -1 == neg vel) + direction = (periodicClamp(currentPosition + brakingDist, 0.f , positionPeriodLength) >= positionPeriodLength / 2) ? 1 : -1; + } + //shift the target away from 0 + targetPosition = (overshoot - std::min(0.f, -direction)) * positionPeriodLength; // - direction * pControlPosErrorLimit; + + //move + return positionThroughVelocityControlWithAccelerationBounds( + dt, maxDt, + currentV, maxV, + acceleration, deceleration, + currentPosition, targetPosition, + p + ); + } + + bool VelocityControllerWithAccelerationBounds::validParameters() const + { + return maxV > 0 && + acceleration > 0 && + deceleration > 0 && + targetV <= maxV && + targetV >= -maxV; + } + + float VelocityControllerWithAccelerationBounds::run() const + { + const float useddt = std::min(std::abs(dt), std::abs(maxDt)); + + //we can have 3 cases: + // 1. we directly set v and ignore acc/dec (if |curr - target| <= limit) + // 2. we need to accelerate (if curr and target v have same sign and |curr| < |target|) + // 3. we need to decellerate (other cases) + + //handle case 1 + const float curverror = targetV - currentV; + if (std::abs(curverror) < directSetVLimit) + { + return targetV; + } + + //handle case 2 + 3 + const bool accelerate = sign(targetV) == sign(currentV) && // v in same direction + std::abs(targetV) > std::abs(currentV); // currently to slow + + const float usedacc = accelerate ? acceleration : -deceleration; + const float maxDeltaV = std::abs(usedacc * useddt); + if (maxDeltaV >= std::abs(curverror)) + { + //we change our v too much with the used acceleration + //but we could reach our target with a lower acceleration ->set the target + + return targetV; + } + const float deltaVel = boost::algorithm::clamp(sign(currentV) * usedacc * useddt, -maxDeltaV, maxDeltaV); + const float nextV = currentV + deltaVel; + return nextV; + } + + float VelocityControllerWithAccelerationBounds::estimateTime() const + { + const float curverror = std::abs(targetV - currentV); + if (curverror < directSetVLimit) + { + return maxDt; + } + return curverror / ((targetV > currentV) ? acceleration : deceleration); + } + + bool VelocityControllerWithAccelerationAndPositionBounds::validParameters() const + { + return VelocityControllerWithAccelerationBounds::validParameters() && + positionLimitLoHard < positionLimitLoSoft && + positionLimitLoSoft < positionLimitHiSoft && + positionLimitHiSoft < positionLimitHiHard; + } + + float VelocityControllerWithAccelerationAndPositionBounds::run() const + { + if (currentPosition <= positionLimitLoHard || currentPosition >= positionLimitHiHard) + { + return std::nanf("1"); + } + + float softLimitViolation = 0; + if (currentPosition <= positionLimitLoSoft) + { + softLimitViolation = -1; + } + if (currentPosition >= positionLimitHiSoft) + { + softLimitViolation = 1; + } + + const float upperDt = std::max(std::abs(dt), std::abs(maxDt)); + + //we can have 4 cases: + // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them, e.g. if we violated them initially) + // 2. we directly set v and ignore acc/dec (if |curr - target| <= limit) + // 3. we need to accelerate (if curr and target v have same sign and |curr| < |target|) + // 4. we need to decellerate (other cases) + float nextv; + //handle case 1 + const float vsquared = currentV * currentV; + const float brakingDist = sign(currentV) * vsquared / 2.f / std::abs(deceleration); //the braking distance points in the direction of the velocity + + const float posIfBrakingNow = currentPosition + brakingDist; + if (posIfBrakingNow <= positionLimitLoSoft || posIfBrakingNow >= positionLimitHiSoft) + { + //case 1. -> brake now! (we try to have v=0 at the limit) + const auto limit = posIfBrakingNow <= positionLimitLoSoft ? positionLimitLoSoft : positionLimitHiSoft; + const float wayToGo = limit - currentPosition; + + //decellerate! + // s = v²/(2a) <=> a = v²/(2s) + const float dec = std::abs(vsquared / 2.f / wayToGo); + const float vel = currentV - sign(currentV) * dec * upperDt; + nextv = boost::algorithm::clamp(vel, -maxV, maxV); + if (sign(currentV) != sign(nextv)) + { + //stop now + nextv = 0; + } + } + else + { + //handle 2-3 + nextv = VelocityControllerWithAccelerationBounds::run(); + } + if (softLimitViolation == sign(nextv)) + { + //the area between soft and hard limits is sticky + //the controller can only move out of it (not further in) + return 0; + } + + //the next velocity will not violate the pos bounds harder than they are already + return nextv; + } + + bool PositionThroughVelocityControllerWithAccelerationBounds::validParameters() const + { + return maxV > 0 && + acceleration > 0 && + deceleration > 0 && + pControlPosErrorLimit > 0 && + pControlVelLimit > 0 && + p > 0; + } + + float PositionThroughVelocityControllerWithAccelerationBounds::run() const + { + const float useddt = std::min(std::abs(dt), std::abs(maxDt)); + const float signV = sign(currentV); + //we can have 3 cases: + // 1. we use a p controller and ignore acc/dec (if |currentPosition - targetPosition| <= pControlPosErrorLimit AND |currentV| < pControlVelLimit) + // 2. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) + // the brakingDistance have the same sign and brakingDistance < e + // and currentVel <= maxV) + // 3. we need to decellerate (other cases) + + //handle case 1 + const float positionError = targetPosition - currentPosition; + if (std::abs(positionError) < pControlPosErrorLimit && std::abs(currentV) < pControlVelLimit) + { + return positionError * p; + } + + //handle case 2-3 + const float brakingDistance = signV * currentV * currentV / 2.f / deceleration; //the braking distance points in the direction of the velocity + const float posIfBrakingNow = currentPosition + brakingDistance; + const float posErrorIfBrakingNow = targetPosition - posIfBrakingNow; + + const bool decelerate = + std::abs(currentV) > maxV || // we need to slow down (to stay in [-maxV,maxV] + std::abs(posIfBrakingNow - targetPosition) <= pControlPosErrorLimit || // we want to hit the target + sign(posErrorIfBrakingNow) != signV; // we are moving away from the target + const float usedacc = decelerate ? -deceleration : acceleration; + const float maxDeltaV = std::abs(usedacc * useddt); + const float deltaVel = boost::algorithm::clamp(signV * usedacc * useddt, -maxDeltaV, maxDeltaV); + return boost::algorithm::clamp(currentV + deltaVel, -maxV, maxV); + } + + float PositionThroughVelocityControllerWithAccelerationBounds::estimateTime() const + { + // const float brakingDistance = sign(currentV) * currentV * currentV / 2.f / deceleration; + const float posError = targetPosition - currentPosition; + const float posIfBrakingNow = currentPosition + brakingDistance(currentV, deceleration); + //we are near the goal if we break now + if (std::abs(posIfBrakingNow - targetPosition) <= pControlPosErrorLimit && currentV <= pControlVelLimit) + { + return std::sqrt(2 * std::abs(brakingDistance(currentV, deceleration)) / deceleration); + } + const float posErrorIfBrakingNow = targetPosition - posIfBrakingNow; + + float t = 0; + float curVel = currentV; + float curPos = currentPosition; + auto curPosEr = [&] {return targetPosition - curPos;}; + // auto curBrake = [&] {return std::abs(brakingDistance(curVel, deceleration));}; + + //check for overshooting of target + if (sign(posError) != sign(posErrorIfBrakingNow)) + { + t = currentV / deceleration; + curVel = 0; + curPos = posIfBrakingNow; + } + //check for to high v + if (std::abs(curVel) > maxV) + { + const float dv = curVel - maxV; + t += dv / deceleration; + curPos += 0.5f / deceleration * dv * dv * sign(curVel); + } + //curBrake()<=curPosEr() && curVel <= maxV + auto tr = trapeze(curVel, acceleration, maxV, deceleration, 0, curPosEr()); + return t + tr.at(0).dt + tr.at(1).dt + tr.at(2).dt; + // //where are we if we acc to max and then dec + // const deltas acc = accetlerateToVelocity(curVel, acceleration, maxV); + // const deltas dec = accetlerateToVelocity(maxV , deceleration, 0); + // const float dist = acc.dx + dec.dx; + // if (std::abs(dist - curPosEr()) <= pControlPosErrorLimit) + // { + // //after this we are at the target + // return t + acc.dt + dec.dt; + // } + // if (dist < curPosEr()) + // { + // //calc how long to acc + dec + // return t + acc.dt + dec.dt; + // } + // if (dist > curPosEr()) + // { + // return t + acc.dt + dec.dt + (dist - curPosEr()) / maxV; + // } + + // const float posErrorAfterBraking = curPosEr - curBrake; + // return t; + + } + + bool PositionThroughVelocityControllerWithAccelerationAndPositionBounds::validParameters() const + { + return PositionThroughVelocityControllerWithAccelerationBounds::validParameters() && + positionLimitLo < positionLimitHi && + targetPosition <= positionLimitHi && + positionLimitLo <= targetPosition; + } + + float PositionThroughVelocityControllerWithAccelerationAndPositionBounds::run() const + { + const float useddt = std::min(std::abs(dt), std::abs(maxDt)); + const float signV = sign(currentV); + //we can have 4 cases: + // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them) + // 2. we use a p controller and ignore acc/dec (if |currentPosition - targetPosition| <= pControlPosErrorLimit AND |currentPosition - targetPosition|*p < maxV) + // 4. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) + // and the brakingDistance have the same sign and brakingDistance < e + // and currentVel <= maxV) + // 5. we need to decellerate (other cases) + + //handle case 1 + const float vsquared = currentV * currentV; + const float brakingDistance = signV * vsquared / 2.f / deceleration; //the braking distance points in the direction of the velocity + const float posIfBrakingNow = currentPosition + brakingDistance; + if (posIfBrakingNow <= positionLimitLo || posIfBrakingNow >= positionLimitHi) + { + //case 1. -> brake now! (we try to have v=0 at the limit) + const auto limit = brakingDistance > 0 ? positionLimitHi : positionLimitLo; + const float wayToGo = std::abs(limit - currentPosition); + // s = v²/(2a) <=> a = v²/(2s) + const float dec = std::abs(vsquared / 2.f / wayToGo); + const float vel = currentV - signV * dec * useddt; + return vel; + } + + //handle case 2-3 + return PositionThroughVelocityControllerWithAccelerationBounds::run(); + } + + std::array<deltas, 3> trapeze(float v0, float acc, float vMax, float dec, float vt, float dx) + { + acc = std::abs(acc); + vMax = std::abs(vMax); + dec = std::abs(dec); + auto calc = [&](float vmax) + { + const deltas dacc = accetlerateToVelocity(v0 , acc, vmax); + const deltas ddec = accetlerateToVelocity(vmax, dec, vt); + const float dxMax = dacc.dx + ddec.dx; + if (sign(dxMax) == sign(dx)) + { + if (abs(dxMax) <= dx) + { + deltas mid; + mid.dx = dxMax - dx; + mid.dv = vMax; + mid.dt = mid.dx / mid.dv; + return std::make_pair(true, std::array<deltas, 3> {dacc, mid, ddec}); + } + //we need a lower vMax (vx) + //in all following formulas # elem {0,t} + //a0=acc , at = dec + //vx=v0+dv0 = vt + dvt -> dv# = vx-v# + //dx=dx0+dxt + //dx#=dv#^2/2/a#+dv#/a#*v# + //dx#=(vx-v#)^2/2/a#+(vx-v#)/a#*v# + //dx#=vx^2*(1/2/a#) + vx*(v#/2 - v#/a#) + (v#^2/2/a# - v#^2/2) + //dx=vx^2*(1/2/a0+1/2/at) + vx*(v0/2 - v0/a0 + vt/2 - vt/at) + (v0^2/2/a0 - v0^2/2 +vt^2/2/at - vt^2/2) + //0 =vx^2*(1/2/a0+1/2/at) + vx*(v0/2 - v0/a0 + vt/2 - vt/at) + (v0^2/2/a0 - v0^2/2 +vt^2/2/at - vt^2/2 -dx) + //div = (1/2/a0 + 1/2/at ) + //p = (v0/2 - v0/a0 + vt/2 - vt/at )/div + //q = (v0^2/2/a0 - v0^2/2 + vt^2/2/at - vt^2/2 -dx)/div + // -> vx1/2 = pq(p,q) + const float a0 = acc; + const float at = dec; + const float div = (1.f / 2.f / a0 + 1.f / 2.f / at); + const float p = (v0 / 2.f - v0 / a0 + vt / 2.f - vt / at) / div; + const float q = (v0 * v0 / 2.f * (1.f / a0 - 1.f) + vt * vt / 2.f * (1.f / at - 1.f) - dx) / div; + const auto vxs = pq(p, q); + //one or both of the results may be invalid + bool vx1Valid = std::isfinite(vxs.first) && std::abs(vxs.first) <= std::abs(vmax); + bool vx2Valid = std::isfinite(vxs.second) && std::abs(vxs.second) <= std::abs(vmax); + switch (vx1Valid + vx2Valid) + { + case 0: + return std::make_pair(false, std::array<deltas, 3>()); + case 1: + { + float vx = vx1Valid ? vxs.first : vxs.second; + const deltas daccvx = accetlerateToVelocity(v0, acc, vx); + const deltas ddecvx = accetlerateToVelocity(vx, dec, vt); + deltas mid; + mid.dx = 0; + mid.dv = vx; + mid.dt = 0; + return std::make_pair(false, std::array<deltas, 3> {daccvx, mid, ddecvx}); + } + case 2: + { + const deltas daccvx1 = accetlerateToVelocity(v0, acc, vxs.first); + const deltas ddecvx1 = accetlerateToVelocity(vxs.first, dec, vt); + const deltas daccvx2 = accetlerateToVelocity(v0, acc, vxs.second); + const deltas ddecvx2 = accetlerateToVelocity(vxs.second, dec, vt); + deltas mid; + mid.dx = 0; + mid.dt = 0; + if (daccvx1.dt + ddecvx1.dt < daccvx2.dt + ddecvx2.dt) + { + mid.dv = vxs.first; + return std::make_pair(false, std::array<deltas, 3> {daccvx1, mid, ddecvx1}); + } + mid.dv = vxs.second; + return std::make_pair(false, std::array<deltas, 3> {daccvx2, mid, ddecvx2}); + } + default: + throw std::logic_error {"unreachable code (bool + bool neither 0,1,2)"}; + } + } + return std::make_pair(false, std::array<deltas, 3>()); + }; + + const auto plusVMax = calc(vMax); + const auto negVMax = calc(-vMax); + switch (plusVMax.first + negVMax.first) + { + case 0: + throw std::invalid_argument("could not find a trapez to reach the goal"); + case 1: + return plusVMax.first ? plusVMax.second : negVMax.second; + case 2: + { + const float dt1 = plusVMax.second.at(0).dt + plusVMax.second.at(1).dt + plusVMax.second.at(2).dt; + const float dt2 = negVMax .second.at(0).dt + negVMax .second.at(1).dt + negVMax .second.at(2).dt; + return dt1 < dt2 ? plusVMax.second : negVMax.second; + } + default: + throw std::logic_error {"unreachable code (bool + bool neither 0,1,2)"}; + } + } + + float PositionThroughVelocityControllerWithAccelerationBoundsAndPeriodicPosition::run() const + { + //run should be static, but we need to adapt the parabs for the subcontroler -> have the subcontroler as value; + PositionThroughVelocityControllerWithAccelerationBounds sub = *this; // slice parent class from this + sub.currentPosition = periodicClamp(currentPosition, positionPeriodLo, positionPeriodHi); + sub.targetPosition = periodicClamp(targetPosition , positionPeriodLo, positionPeriodHi); + + const float brakingDist = brakingDistance(currentV, deceleration); + const float posIfBrakingNow = currentPosition + brakingDist; + const float posIfBrakingNowError = targetPosition - posIfBrakingNow; + const float posError = targetPosition - currentPosition; + if ( + std::abs(posIfBrakingNowError) <= pControlPosErrorLimit || + std::abs(posError) <= pControlPosErrorLimit + ) + { + //this allows slight overshooting (in the limits of the p controller) + return PositionThroughVelocityControllerWithAccelerationBounds::run(); + } + + //we transform the problem with periodic bounds into + //a problem with position bounds + const float positionPeriodLength = std::abs(positionPeriodHi - positionPeriodLo); + + //we shift the positions such that 0 == target + sub.currentPosition = periodicClamp(currentPosition - targetPosition, 0.f, positionPeriodLength); + //how many times will we go ovet the target if we simply bake now? + const float overshoot = std::trunc((currentPosition + brakingDist) / positionPeriodLength); + + ///TODO check if there may be a case requiring the direction + float direction = 0; + if (direction == 0) + { + //determine the direction to go (1 == pos vel, -1 == neg vel) + direction = (periodicClamp(currentPosition + brakingDist, 0.f , positionPeriodLength) >= positionPeriodLength / 2) ? 1 : -1; + } + //shift the target away from 0 + sub.targetPosition = (overshoot - std::min(0.f, -direction)) * positionPeriodLength; // - direction * pControlPosErrorLimit; + + //move + return sub.run(); + } + + + + + +} diff --git a/source/RobotAPI/components/units/RobotUnit/BasicControllers.h b/source/RobotAPI/components/units/RobotUnit/BasicControllers.h new file mode 100644 index 0000000000000000000000000000000000000000..a63c1dbc365795f7d387be4b11e40bc729caf82e --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/BasicControllers.h @@ -0,0 +1,204 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_BasicControllers_H +#define _ARMARX_LIB_RobotAPI_BasicControllers_H + +#include <cmath> +#include <type_traits> +#include <ArmarXCore/core/util/algorithm.h> + +namespace armarx +{ + template<class T, class = typename std::enable_if<std:: is_floating_point<T>::value>::type > + T periodicClamp(T value, T periodLo, T periodHi) + { + float dist = periodHi - periodLo; + return std::fmod(value - periodLo + dist, dist) + periodLo; + } + + template<class T, class = typename std::enable_if<std:: is_floating_point<T>::value>::type > + std::pair<T, T> pq(T p, T q) + { + T a = - p / 2; + T b = std::sqrt(std::pow(p / 2, 2) - q); + return {a + b, a - b}; + } + + struct deltas + { + float dv; + float dx; + float dt; + }; + + inline deltas accetlerateToVelocity(float v0, float acc, float vt) + { + acc = std::abs(acc); + deltas d; + d.dv = vt - v0; + d.dt = std::abs(d.dv / acc); + d.dx = sign(d.dv) * d.dv * d.dv / 2.f / acc + v0 * d.dt; + return d; + } + + + std::array<deltas, 3> trapeze(float v0, float acc, float vMax, float dec, float vt, float dx); + + // inline std::array<deltas, 3> trapezeWithDt(float v0, float acc, float vMax, float dec, float vt, float dx, float dt) + // { + + // } + + /** + * @param v0 The initial velocity. + * @param deceleration The deceleration. + * @return The braking distance given the parameters. + */ + inline float brakingDistance(float v0, float deceleration) + { + return accetlerateToVelocity(v0, deceleration, 0).dx; + } + + + + struct VelocityControllerWithAccelerationBounds + { + float dt; + float maxDt; + float currentV; + float targetV; + float maxV; + float acceleration; + float deceleration; + float directSetVLimit; + + bool validParameters() const; + float run() const; + float estimateTime() const; + }; + + float velocityControlWithAccelerationBounds( + float dt, float maxDt, + const float currentV, float targetV, float maxV, + float acceleration, float deceleration, + float directSetVLimit); + + /** + * @brief Performs velocity control while staying in positional bounds, obeying acceleration / deceleration and staying below a velocity bound. + * + * we can have 4 cases: + * 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them. e.f. if we already violate them or the robot can't brake fast enough) + * 2. we directly set v and ignore acc/dec (if |currentV - targetV| <= directSetVLimit) + * 3. we need to accelerate (if currentV and targetV have same sign and |currentV| < |currentV|) + * 4. we need to decellerate (other cases) + * @param dt The time in seconds until the next call is made. (use the time since the last call as an approximate) + * @param maxDt Limits dt in case the given approximation has a sudden high value. + * @param currentV + * @param targetV + * @param maxV + * @param acceleration + * @param deceleration + * @param directSetVLimit In case |currentV - targetV| <= directSetVLimit this function will return targetV (if the position bounds are not violated) + * @param currentPosition + * @param positionLimitLo + * @param positionLimitHi + * @return The next velocity. + */ + struct VelocityControllerWithAccelerationAndPositionBounds: VelocityControllerWithAccelerationBounds + { + float currentPosition; + float positionLimitLoSoft; + float positionLimitHiSoft; + float positionLimitLoHard; + float positionLimitHiHard; + + bool validParameters() const; + float run() const; + }; + + + + float velocityControlWithAccelerationAndPositionBounds(float dt, float maxDt, + float currentV, float targetV, float maxV, + float acceleration, float deceleration, + float directSetVLimit, + float currentPosition, + float positionLimitLoSoft, float positionLimitHiSoft, + float positionLimitLoHard, float positionLimitHiHard); + + + struct PositionThroughVelocityControllerWithAccelerationBounds + { + float dt; + float maxDt; + float currentV; + float maxV; + float acceleration; + float deceleration; + float currentPosition; + float targetPosition; + float pControlPosErrorLimit; + float pControlVelLimit; + float p; + + bool validParameters() const; + float run() const; + float estimateTime() const; + }; + float positionThroughVelocityControlWithAccelerationBounds(float dt, float maxDt, + float currentV, float maxV, + float acceleration, float deceleration, + float currentPosition, float targetPosition, + float p); + + struct PositionThroughVelocityControllerWithAccelerationAndPositionBounds: PositionThroughVelocityControllerWithAccelerationBounds + { + float positionLimitLo; + float positionLimitHi; + + bool validParameters() const; + float run() const; + }; + float positionThroughVelocityControlWithAccelerationAndPositionBounds(float dt, float maxDt, + float currentV, float maxV, + float acceleration, float deceleration, + float currentPosition, float targetPosition, float p, + float positionLimitLo, float positionLimitHi); + + struct PositionThroughVelocityControllerWithAccelerationBoundsAndPeriodicPosition: PositionThroughVelocityControllerWithAccelerationBounds + { + // float direction; + float positionPeriodLo; + float positionPeriodHi; + float run() const; + }; + float positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition(float dt, float maxDt, + float currentV, float maxV, + float acceleration, float deceleration, + float currentPosition, float targetPosition, + float pControlPosErrorLimit, float p, + float& direction, + float positionPeriodLo, float positionPeriodHi); +} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a84c20b4f17ec336de52cbb4ef8c44592a6a6bf0 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt @@ -0,0 +1,91 @@ +set(LIB_NAME RobotUnit) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +set(LIBS + ArmarXCoreInterfaces + ArmarXCore + ArmarXGuiInterfaces + RobotAPIUnits + RobotAPIInterfaces +) + +set(LIB_FILES + SyntaxCheck.cpp + RobotUnit.cpp + BasicControllers.cpp + + ControlTargets/ControlTargetBase.cpp + + Units/RobotUnitSubUnit.cpp + Units/ForceTorqueSubUnit.cpp +# Units/HapticSubUnit.cpp + Units/InertialMeasurementSubUnit.cpp + Units/KinematicSubUnit.cpp + Units/PlatformSubUnit.cpp + + LVL0Controllers/LVL0Controller.cpp + + LVL1Controllers/LVL1Controller.cpp + LVL1Controllers/LVL1TrajectoryController.cpp + LVL1Controllers/LVL1KinematicUnitPassThroughController.cpp + LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.cpp + +# LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.cpp +# LVL1Controllers/LVL1KinematicUnitPositionController.cpp +# LVL1Controllers/LVL1KinematicUnitTorqueController.cpp +# LVL1Controllers/LVL1KinematicUnitVelocityController.cpp + + Devices/SensorDevice.cpp + Devices/ControlDevice.cpp +) +set(LIB_HEADERS + util.h + structs.h + Constants.h + ControlModes.h + RobotUnit.h + BasicControllers.h + + ControlTargets/ControlTargetBase.h + ControlTargets/ControlTarget1DoFActuator.h + ControlTargets/ControlTargetHolonomicPlatformVelocity.h + + SensorValues/SensorValueBase.h + SensorValues/SensorValue1DoFActuator.h + SensorValues/SensorValueIMU.h + SensorValues/SensorValueForceTorque.h + SensorValues/SensorValueHolonomicPlatform.h + SensorValues/SensorValueRTThreadTimings.h + + Units/RobotUnitSubUnit.h + Units/ForceTorqueSubUnit.h +# Units/HapticSubUnit.h + Units/InertialMeasurementSubUnit.h + Units/KinematicSubUnit.h + Units/PlatformSubUnit.h + + LVL0Controllers/LVL0Controller.h + + LVL1Controllers/LVL1Controller.h + LVL1Controllers/LVL1Controller.hpp + LVL1Controllers/LVL1TrajectoryController.h + LVL1Controllers/LVL1KinematicUnitPassThroughController.h + LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.h +# LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.h +# LVL1Controllers/LVL1KinematicUnitPositionController.h +# LVL1Controllers/LVL1KinematicUnitTorqueController.h +# LVL1Controllers/LVL1KinematicUnitVelocityController.h + + Devices/DeviceBase.h + Devices/SensorDevice.h + Devices/ControlDevice.h + + Devices/RTThreadTimingsSensorDevice.h +) + +add_subdirectory(test) + +armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") + diff --git a/source/RobotAPI/libraries/RTRobotUnit/Constants.h b/source/RobotAPI/components/units/RobotUnit/Constants.h similarity index 100% rename from source/RobotAPI/libraries/RTRobotUnit/Constants.h rename to source/RobotAPI/components/units/RobotUnit/Constants.h diff --git a/source/RobotAPI/libraries/RTRobotUnit/ControlModes.h b/source/RobotAPI/components/units/RobotUnit/ControlModes.h similarity index 64% rename from source/RobotAPI/libraries/RTRobotUnit/ControlModes.h rename to source/RobotAPI/components/units/RobotUnit/ControlModes.h index dc2e580a42bf4301aea8f1fcfc41d965d1865591..8a98aec0f01a7628f1525554bd2438fadfc4a2df 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/ControlModes.h +++ b/source/RobotAPI/components/units/RobotUnit/ControlModes.h @@ -30,15 +30,17 @@ namespace armarx namespace ControlModes { //'normal' actor modes - static const std::string PositionMode = "PositionMode"; - static const std::string TorqueMode = "TorqueMode"; - static const std::string VelocityMode = "VelocityMode"; + static const std::string Position1DoF = "ControlMode_Position1DoF"; + static const std::string Torque1DoF = "ControlMode_Torque1DoF"; + static const std::string Velocity1DoF = "ControlMode_Velocity1DoF"; //modes for holonomic platforms - static const std::string HolonomicPlatformVelocityMode = "HolonomicPlatformVelocityMode"; + static const std::string HolonomicPlatformVelocity = "ControlMode_HolonomicPlatformVelocity"; + static const std::string HolonomicPlatformRelativePosition = "ControlMode_HolonomicPlatformRelativePosition"; //special sentinel modes - static const std::string EmergencyStopMode = "EmergencyStopMode"; + static const std::string EmergencyStop = "ControlMode_EmergencyStop"; + static const std::string StopMovement = "ControlMode_StopMovement"; } } #endif diff --git a/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h new file mode 100644 index 0000000000000000000000000000000000000000..260854a0018d359dce37c2f7f7bf92be0649e34e --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h @@ -0,0 +1,62 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::ActuatorVelocityTarget + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_ControlTarget1DoFActuator_H +#define _ARMARX_LIB_RobotAPI_ControlTarget1DoFActuator_H + +#include "ControlTargetBase.h" +#include <ArmarXCore/observers/variant/Variant.h> + +namespace armarx +{ +#define make_ControlTarget1DoFActuator(name,varname,cmode) \ + class name: public ControlTargetBase \ + { \ + public: \ + float varname = ControllerConstants::ValueNotSetNaN; \ + virtual const std::string& getControlMode() const override \ + { \ + return cmode; \ + } \ + virtual void reset() override \ + { \ + varname = ControllerConstants::ValueNotSetNaN; \ + } \ + virtual bool isValid() const override \ + { \ + return std::isfinite(varname); \ + } \ + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override \ + { \ + return {{#varname, {new TimedVariant{varname,timestamp}}}}; \ + } \ + DETAIL_ControlTargetBase_DEFAULT_METHOD_IMPLEMENTATION \ + } + + make_ControlTarget1DoFActuator(ControlTarget1DoFActuatorPosition, position, ControlModes::Position1DoF); + make_ControlTarget1DoFActuator(ControlTarget1DoFActuatorVelocity, velocity, ControlModes::Velocity1DoF); + make_ControlTarget1DoFActuator(ControlTarget1DoFActuatorTorque , torque , ControlModes::Torque1DoF); + +#undef make_ControlTarget1DoFActuator +} + +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/HapticDataUnit.h b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetBase.cpp similarity index 71% rename from source/RobotAPI/libraries/RTRobotUnit/DataUnits/HapticDataUnit.h rename to source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetBase.cpp index de02fbe004fb45c8b8f08eafeac404bae1d9200c..2a48abf52d71f29ac6a9ea09837c96cbba8fb4fc 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/HapticDataUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetBase.cpp @@ -13,27 +13,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI::ArmarXObjects::HapticDataUnit + * @package RobotAPI::ArmarXObjects::ControlTargetBase * @author Raphael ( ufdrv at student dot kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#ifndef _ARMARX_LIB_RobotAPI_HapticDataUnit_H -#define _ARMARX_LIB_RobotAPI_HapticDataUnit_H +#include "ControlTargetBase.h" namespace armarx { - class HapticDataUnitInterface - { - public: - }; - - class HapticDataUnitPtrProvider: virtual public HapticDataUnitInterface - { - public: - }; } - -#endif diff --git a/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetBase.h b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetBase.h new file mode 100644 index 0000000000000000000000000000000000000000..69f6111757e3d13cfdce324c72dbc743da6b7b2e --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetBase.h @@ -0,0 +1,131 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::ControlTargetBase + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_JointControlTargetBase_H +#define _ARMARX_LIB_RobotAPI_JointControlTargetBase_H + +#include <memory> +#include <string> +#include <map> + +#include <ArmarXCore/observers/variant/TimedVariant.h> +#include <RobotAPI/interface/units/KinematicUnitInterface.h> + +#include "../util.h" +#include "../Constants.h" +#include "../ControlModes.h" + +namespace armarx +{ + /** + * @defgroup Library-RobotUnit RobotUnit + * @ingroup RobotAPI + * A description of the library RobotUnit. + * + * @class ControlTargetBase + * @ingroup Library-RobotUnit + * @brief Brief description of class JointControlTargetBase. + * + * Detailed description of class ControlTargetBase. + */ + class ControlTargetBase + { + public: + virtual void copyTo(ControlTargetBase* target) const = 0; + virtual std::unique_ptr<ControlTargetBase> clone() const = 0; + + virtual const std::string& getControlMode() const = 0; + virtual std::string getControlTargetType(bool withoutNamespaceSpecifier = false) const = 0; + virtual void reset() = 0; + virtual bool isValid() const = 0; + + /// @brief used to send the data to the DebugObserverTopic and to other Components (e.g. GUI widgets) + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const + { + return {}; + } + + template<class T> + bool isA() const + { + return asA<T>(); + } + + template<class T> + const T* asA() const + { + return dynamic_cast<const T*>(this); + } + + template<class T> + T* asA() + { + return dynamic_cast<T*>(this); + } + + template<class T, class = typename std::enable_if<std::is_base_of<ControlTargetBase, T>::value>::type> + void copyTo(std::unique_ptr<T>& target) const + { + copyTo(target.get()); + } + + virtual ~ControlTargetBase() = default; + }; + +#define DETAIL_ControlTargetBase_DEFAULT_METHOD_IMPLEMENTATION \ + virtual void copyTo(ControlTargetBase* target) const override \ + { \ + using this_type = typename std::decay<decltype(*this)>::type; \ + *(target->asA<this_type>()) = *this; \ + } \ + virtual std::unique_ptr<ControlTargetBase> clone() const override \ + { \ + using this_type = typename std::decay<decltype(*this)>::type; \ + std::unique_ptr<ControlTargetBase> c {new this_type}; \ + ControlTargetBase::copyTo(c); \ + return c; \ + } \ + virtual std::string getControlTargetType(bool withoutNamespaceSpecifier = false) const override \ + { \ + return getTypeName(*this, withoutNamespaceSpecifier); \ + } + +#define make_DummyControlTarget(Suffix,ControlMode) \ + class DummyControlTarget##Suffix : public ControlTargetBase \ + { \ + public: \ + virtual const std::string& getControlMode() const \ + { \ + return ControlMode; \ + } \ + virtual void reset() {} \ + virtual bool isValid() const \ + { \ + return true; \ + } \ + DETAIL_ControlTargetBase_DEFAULT_METHOD_IMPLEMENTATION \ + } + make_DummyControlTarget(EmergencyStop, ControlModes::EmergencyStop); + make_DummyControlTarget(StopMovement, ControlModes::StopMovement); +#undef make_DummyControlTarget +} +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/Targets/HolonomicPlatformVelocityTarget.h b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetHolonomicPlatformVelocity.h similarity index 53% rename from source/RobotAPI/libraries/RTRobotUnit/Targets/HolonomicPlatformVelocityTarget.h rename to source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetHolonomicPlatformVelocity.h index 09b0923a876d1d5f6392a5bf19c28404c0892024..04572f1ce5c42e58824d9dcc9dd816ceef93e72f 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/Targets/HolonomicPlatformVelocityTarget.h +++ b/source/RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetHolonomicPlatformVelocity.h @@ -13,40 +13,41 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI::ArmarXObjects::HolonomicPlatformVelocityTarget + * @package RobotAPI::ArmarXObjects::ControlTargetHolonomicPlatformVelocity * @author Raphael ( ufdrv at student dot kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#ifndef _ARMARX_LIB_RobotAPI_HolonomicPlatformVelocityTarget_H -#define _ARMARX_LIB_RobotAPI_HolonomicPlatformVelocityTarget_H +#ifndef _ARMARX_LIB_RobotAPI_ControlTargetHolonomicPlatformVelocity_H +#define _ARMARX_LIB_RobotAPI_ControlTargetHolonomicPlatformVelocity_H -#include "TargetBase.h" +#include "ControlTargetBase.h" +#include <ArmarXCore/observers/variant/Variant.h> namespace armarx { /** - * @defgroup Library-RTRobotUnit RTRobotUnit + * @defgroup Library-RobotUnit RobotUnit * @ingroup RobotAPI - * A description of the library RTRobotUnit. + * A description of the library RobotUnit. * - * @class HolonomicPlatformVelocityTarget - * @ingroup Library-RTRobotUnit - * @brief Brief description of class HolonomicPlatformVelocityTarget. + * @class ControlTargetHolonomicPlatformVelocity + * @ingroup Library-RobotUnit + * @brief Brief description of class ControlTargetHolonomicPlatformVelocity. * - * Detailed description of class HolonomicPlatformVelocityTarget. + * Detailed description of class ControlTargetHolonomicPlatformVelocity. */ - class HolonomicPlatformVelocityTarget: public TargetBase + class ControlTargetHolonomicPlatformVelocity: public ControlTargetBase { public: float velocityX = ControllerConstants::ValueNotSetNaN; float velocityY = ControllerConstants::ValueNotSetNaN; float velocityRotation = ControllerConstants::ValueNotSetNaN; - virtual std::string getControlMode() const override + virtual const std::string& getControlMode() const override { - return ControlModes::HolonomicPlatformVelocityMode; + return ControlModes::HolonomicPlatformVelocity; } virtual void reset() override { @@ -58,6 +59,17 @@ namespace armarx { return std::isfinite(velocityX) && std::isfinite(velocityY) && std::isfinite(velocityRotation); } + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"velocityX", {new TimedVariant{velocityX ,timestamp}}}, + {"velocityY", {new TimedVariant{velocityY ,timestamp}}}, + {"velocityRotation", {new TimedVariant{velocityRotation,timestamp}}}, + }; + } + + DETAIL_ControlTargetBase_DEFAULT_METHOD_IMPLEMENTATION }; } diff --git a/source/RobotAPI/components/units/RobotUnit/Devices/ControlDevice.cpp b/source/RobotAPI/components/units/RobotUnit/Devices/ControlDevice.cpp new file mode 100644 index 0000000000000000000000000000000000000000..31a251ad1a3b1a427dc8a45ec9030c993a896d28 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Devices/ControlDevice.cpp @@ -0,0 +1,132 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "ControlDevice.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +namespace armarx +{ + const ControlDevicePtr ControlDevice::NullPtr + { + nullptr + }; + + LVL0Controller* ControlDevice::getLVL0EmergencyStopController() + { + ARMARX_CHECK_EXPRESSION_W_HINT( + lVL0EmergencyStopController, + "ControlDevice::getLVL0EmergencyStopController called for a ControlDevice without a LVL0Controller with the ControlMode ControlModes::EmergencyStop" + << " (add a LVL0Controller with this ControlMode)" + ); + return lVL0EmergencyStopController; + } + + LVL0Controller* ControlDevice::getLVL0StopMovementController() + { + ARMARX_CHECK_EXPRESSION_W_HINT( + lVL0StopMovementController, + "ControlDevice::getLVL0StopMovementController called for a ControlDevice without a LVL0Controller with the ControlMode ControlModes::StopMovement" + << " (add a LVL0Controller with this ControlMode)" + ); + return lVL0StopMovementController; + } + + void ControlDevice::rtSetActiveLVL0Controller(LVL0Controller* lvl0) + { + ARMARX_CHECK_EXPRESSION_W_HINT(lvl0, "Called ControlDevice::rtSetActiveLVL0Controller with a nullptr (Don't do this)") + if (activelvl0Controller == lvl0) + { + return; + } + if (lvl0->parent != this) + { + throw std::logic_error + { + "ControlDevice(" + getDeviceName() + ")::rtSetActiveLVL0Controller: tried to switch to a lvl0 controller from a different ControlDevice " + + "(You should only call ControlDevice::rtSetActiveLVL0Controller with LVL0Controllers added to the ControlDevice via ControlDevice::addLVL0Controller)" + }; + } + if (activelvl0Controller) + { + activelvl0Controller->rtDeactivate(); + } + lvl0->rtActivate(); + activelvl0Controller = lvl0; + } + + void ControlDevice::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + auto activeLvl0 = rtGetActiveLVL0Controller(); + ARMARX_CHECK_EXPRESSION(activeLvl0); + activeLvl0->rtRun(sensorValuesTimestamp, timeSinceLastIteration); + rtWriteTargetValues(sensorValuesTimestamp, timeSinceLastIteration); + } + + void ControlDevice::addLVL0Controller(LVL0Controller* lvl0) + { + if (!lvl0) + { + throw std::invalid_argument + { + "ControlDevice(" + getDeviceName() + ")::addLVL0Controller: lvl0 controller is nullptr (Don't try nullptrs as LVL0Controller)" + }; + } + if (lvl0->parent) + { + throw std::logic_error + { + "ControlDevice(" + getDeviceName() + ")::addLVL0Controller: tried to add a lvl0 controller multiple times" + + "(Don't try to add a LVL0Controller multiple to a ControlDevice)" + }; + } + const std::string& mode = lvl0->getControlMode(); + if (lvl0Controllers.has(mode)) + { + throw std::invalid_argument + { + "ControlDevice(" + getDeviceName() + ")::addLVL0Controller: lvl0 controller for mode " + mode + " was already added" + + "(Don't try to add multiple LVL0Controller with the same ControlMode)" + }; + } + lvl0->parent = this; + lvl0->rtResetTarget(); + if (mode == ControlModes::EmergencyStop) + { + lVL0EmergencyStopController = lvl0; + } + if (mode == ControlModes::StopMovement) + { + lVL0StopMovementController = lvl0; + } + if (!lvl0->getControlTarget()) + { + throw std::logic_error + { + "ControlDevice(" + getDeviceName() + ")::addLVL0Controller: The LVL0Controller has no ControlTarget. (mode = " + mode + ")" + + "(Don't try to add LVL0Controller without a ControlTarget)" + }; + } + lvl0Controllers.add(mode, std::move(lvl0)); + } + +} diff --git a/source/RobotAPI/components/units/RobotUnit/Devices/ControlDevice.h b/source/RobotAPI/components/units/RobotUnit/Devices/ControlDevice.h new file mode 100644 index 0000000000000000000000000000000000000000..5e1079ae54735346cef1b1e7653ac9e98e5c64b2 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Devices/ControlDevice.h @@ -0,0 +1,148 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_ControlDevice_H +#define _ARMARX_LIB_RobotAPI_ControlDevice_H + +#include <string> +#include <memory> +#include <atomic> + +#include"DeviceBase.h" +#include "../util.h" +#include "../ControlModes.h" +#include "../LVL0Controllers/LVL0Controller.h" + +namespace armarx +{ + TYPEDEF_PTRS_SHARED(ControlDevice); + + namespace ControlDeviceTags + { + using namespace DeviceTags; + const static std::string PeriodicPosition = "ControlDeviceTag_PeriodicPosition"; + const static std::string CreateNoDefaultController = "ControlDeviceTag_CreateNoDefaultController"; + const static std::string ForcePositionThroughVelocity = "ControlDeviceTag_ForcePositionThroughVelocity"; + } + + /** + * @brief The ControlDevice class represents a logical unit accepting commands via its LVL0Controllers. + * + * It holds a set of LVL0Controllers with different Controll modes. + * It always has a LVL0Controller with mode ControlModes::EmergencyStop which is used in case of an error. + * It always has a LVL0Controller with mode ControlModes::StopMovement which is used when the controled device should stop any movement. + * + * It holds a set of tags. + * These tags are used by different components to create default controllers, check for abilities or present output to the user. + */ + class ControlDevice: public virtual DeviceBase + { + public: + /// @brief A static const nullptr in case a const ref to a nullptr needs to be returned + static const ControlDevicePtr NullPtr; + + /// @brief Create a ControlDevice with the given name + ControlDevice(const std::string& name): DeviceBase {name} {} + //controller management + /// @return the lVL0EmergencyStopController of this ControlDevice + LVL0Controller* rtGetLVL0EmergencyStopController() + { + return lVL0EmergencyStopController; + } + /// @return the lVL0EmergencyStopController of this ControlDevice + LVL0Controller* getLVL0EmergencyStopController(); + /// @return the lVL0StopMovementController of this ControlDevice + LVL0Controller* rtGetLVL0StopMovementController() + { + return lVL0StopMovementController; + } + /// @return the lVL0StopMovementController of this ControlDevice + LVL0Controller* getLVL0StopMovementController(); + /// @return the active LVL0Controller of this ControlDevice + LVL0Controller* rtGetActiveLVL0Controller() + { + return activelvl0Controller; + } + /// @return all LVL0Controllers of this ControlDevice + const std::vector<LVL0Controller*>& rtGetLVL0Controllers() + { + return lvl0Controllers.values(); + } + std::vector<const LVL0Controller*> getLVL0Controllers() const + { + return {lvl0Controllers.values().begin(), lvl0Controllers.values().end()}; + } + /// @return the LVL0Controller for the given mode of this ControlDevice (or nullptr if there is no controller for this mode) + LVL0Controller* getLVL0Controller(const std::string& mode) + { + return lvl0Controllers.at(mode, nullptr); + } + const LVL0Controller* getLVL0Controller(const std::string& mode) const + { + return lvl0Controllers.at(mode, nullptr); + } + LVL0Controller* getLVL0Controller(std::size_t i) + { + return lvl0Controllers.at(i); + } + const LVL0Controller* getLVL0Controller(std::size_t i) const + { + return lvl0Controllers.at(i); + } + bool hasLVL0Controller(const std::string& mode) const + { + return lvl0Controllers.has(mode); + } + const std::vector<std::string>& getControlModes() const + { + return lvl0Controllers.keys(); + } + /** + * @brief Activates the given LVL0Controller for this device + * @throw std::logic_error if the lvl0 controller does not belong to this ControlDevice + */ + virtual void rtSetActiveLVL0Controller(LVL0Controller* lvl0); + /** + * @brief runs the active LVL0 Controller and write the target values into the control device + * @param sensorValuesTimestamp + * @param timeSinceLastIteration + * @see writeTargetValues() + */ + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + virtual void rtWriteTargetValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) {} + protected: + /** + * @brief adds the lvl0 controller to this ControlDevice + * @throw std::logic_error if the LVL0Controller was already added to a ControlDevice + * @throw std::invalid_argument if a LVL0Controller with the same control mode was already added to this device + */ + void addLVL0Controller(LVL0Controller* lvl0); + private: + KeyValueVector<std::string, LVL0Controller*> lvl0Controllers; + LVL0Controller* activelvl0Controller {nullptr}; + LVL0Controller* lVL0EmergencyStopController {nullptr}; + LVL0Controller* lVL0StopMovementController {nullptr}; + }; +} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Devices/DeviceBase.h b/source/RobotAPI/components/units/RobotUnit/Devices/DeviceBase.h new file mode 100644 index 0000000000000000000000000000000000000000..51d8dbbba1d66fa74e7550b4fea445e830267165 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Devices/DeviceBase.h @@ -0,0 +1,69 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_DeviceBase_H +#define _ARMARX_LIB_RobotAPI_DeviceBase_H + +#include "../util.h" + +namespace armarx +{ + + namespace DeviceTags + { + } + class DeviceBase + { + public: + /// @brief Create a Device with the given name + DeviceBase(const std::string& name): deviceName {name} {} + virtual ~DeviceBase() = default; + /// @return The Device's name + const std::string& getDeviceName() const + { + return deviceName; + } + //tags + /// @return Thes set of tags for this Device + const std::set<std::string>& getTags() const + { + return tags; + } + /// @return Whether the device has the given tag + bool hasTag(const std::string& tag) const + { + return getTags().count(tag); + } + + protected: + /// @brief adds the given tag to the Device + void addDeviceTag(const std::string& tag) + { + tags.emplace(tag); + } + private: + std::set<std::string> tags; + const std::string deviceName; + }; +} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Devices/RTThreadTimingsSensorDevice.h b/source/RobotAPI/components/units/RobotUnit/Devices/RTThreadTimingsSensorDevice.h new file mode 100644 index 0000000000000000000000000000000000000000..61dec2e7a9582d41c98845d2f4775295de310012 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Devices/RTThreadTimingsSensorDevice.h @@ -0,0 +1,147 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_RTThreadTimingsSensorDevice_H +#define _ARMARX_LIB_RobotAPI_RTThreadTimingsSensorDevice_H + +#include"SensorDevice.h" +#include "../SensorValues/SensorValueRTThreadTimings.h" + +namespace armarx +{ + TYPEDEF_PTRS_SHARED(RTThreadTimingsSensorDevice); + + class RTThreadTimingsSensorDevice: virtual public SensorDevice + { + public: + RTThreadTimingsSensorDevice(const std::string& name): DeviceBase(name), SensorDevice(name) {} + virtual const SensorValueBase* getSensorValue() const = 0; + + virtual void rtMarkRtLoopStart() = 0; + virtual void rtMarkRtLoopPreSleep() = 0; + + virtual void rtMarkRtBusSendReceiveStart() = 0; + virtual void rtMarkRtBusSendReceiveEnd() = 0; + protected: + friend class RobotUnit; + + virtual void rtMarkRtSwitchControllerSetupStart() = 0; + virtual void rtMarkRtSwitchControllerSetupEnd() = 0; + + virtual void rtMarkRtRunLVL1ControllersStart() = 0; + virtual void rtMarkRtRunLVL1ControllersEnd() = 0; + + virtual void rtMarkRtHandleInvalidTargetsStart() = 0; + virtual void rtMarkRtHandleInvalidTargetsEnd() = 0; + + virtual void rtMarkRtRunLVL0ControllersStart() = 0; + virtual void rtMarkRtRunLVL0ControllersEnd() = 0; + + + virtual void rtMarkRtReadSensorDeviceValuesStart() = 0; + virtual void rtMarkRtReadSensorDeviceValuesEnd() = 0; + + virtual void rtMarkRtUpdateSensorAndControlBufferStart() = 0; + virtual void rtMarkRtUpdateSensorAndControlBufferEnd() = 0; + + virtual void rtMarkRtResetAllTargetsStart() = 0; + virtual void rtMarkRtResetAllTargetsEnd() = 0; + + }; + + template<class SensorValueType = SensorValueRTThreadTimings> + class RTThreadTimingsSensorDeviceImpl: public RTThreadTimingsSensorDevice + { + public: + static_assert( + std::is_base_of<SensorValueRTThreadTimings, SensorValueType>::value, + "The template parameter of RTThreadTimingsSensorDeviceImpl should be derived from SensorValueRTThreadTimings" + ); + RTThreadTimingsSensorDeviceImpl(const std::string& name): DeviceBase(name), SensorDevice(name), RTThreadTimingsSensorDevice(name) {} + virtual const SensorValueBase* getSensorValue() const + { + return &value; + } + + SensorValueType value; + + virtual void rtMarkRtLoopStart() override + { + std::chrono::microseconds now = MicroNow(); + value.rtLoopRoundTripTime = now - rtLoopStart; + rtLoopStart = now; + } + virtual void rtMarkRtLoopPreSleep() override + { + value.rtLoopDuration = MicroNow() - rtLoopStart; + } + virtual void rtMarkRtBusSendReceiveStart() override + { + std::chrono::microseconds now = MicroNow(); + value.rtBusSendReceiveRoundTripTime = now - rtBusSendReceiveStart; + rtBusSendReceiveStart = now; + } + virtual void rtMarkRtBusSendReceiveEnd() override + { + value.rtBusSendReceiveDuration = MicroNow() - rtBusSendReceiveStart; + } + + static std::chrono::microseconds MicroNow() + { + return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()); + } + protected: +#define make_markRT_X_Start_End(name) \ + virtual void rtMarkRt##name##Start() override \ + { \ + std::chrono::microseconds now = MicroNow(); \ + value.rt##name##RoundTripTime = now - rt##name##Start; \ + rt##name##Start=now; \ + } \ + virtual void rtMarkRt##name##End() override \ + { \ + value.rt##name##Duration=MicroNow() - rt##name##Start; \ + } + + make_markRT_X_Start_End(SwitchControllerSetup) + make_markRT_X_Start_End(RunLVL1Controllers) + make_markRT_X_Start_End(HandleInvalidTargets) + make_markRT_X_Start_End(RunLVL0Controllers) + make_markRT_X_Start_End(ReadSensorDeviceValues) + make_markRT_X_Start_End(UpdateSensorAndControlBuffer) + make_markRT_X_Start_End(ResetAllTargets) +#undef make_markRT_X_Start_End + protected: + std::chrono::microseconds rtSwitchControllerSetupStart; + std::chrono::microseconds rtRunLVL1ControllersStart; + std::chrono::microseconds rtHandleInvalidTargetsStart; + std::chrono::microseconds rtRunLVL0ControllersStart; + std::chrono::microseconds rtBusSendReceiveStart; + std::chrono::microseconds rtReadSensorDeviceValuesStart; + std::chrono::microseconds rtUpdateSensorAndControlBufferStart; + std::chrono::microseconds rtResetAllTargetsStart; + std::chrono::microseconds rtLoopStart; + }; + +} + +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/IMUDataUnit.h b/source/RobotAPI/components/units/RobotUnit/Devices/SensorDevice.cpp similarity index 74% rename from source/RobotAPI/libraries/RTRobotUnit/DataUnits/IMUDataUnit.h rename to source/RobotAPI/components/units/RobotUnit/Devices/SensorDevice.cpp index 3e7433cd1f1d9d69ede38d384433a4238a6cae09..20ca86f2e8077738ba8c80546b53e242d7c70352 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/IMUDataUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/Devices/SensorDevice.cpp @@ -13,27 +13,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI::ArmarXObjects::IMUDataUnit + * @package RobotAPI * @author Raphael ( ufdrv at student dot kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#ifndef _ARMARX_LIB_RobotAPI_IMUDataUnit_H -#define _ARMARX_LIB_RobotAPI_IMUDataUnit_H +#include "SensorDevice.h" namespace armarx { - class IMUDataUnitInterface + const SensorDevicePtr SensorDevice::NullPtr { - public: + nullptr }; - class IMUDataUnitPtrProvider: virtual public IMUDataUnitInterface - { - public: - }; } - -#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Devices/SensorDevice.h b/source/RobotAPI/components/units/RobotUnit/Devices/SensorDevice.h new file mode 100644 index 0000000000000000000000000000000000000000..d8b4a5dca04e0e575674367066ff16096825df10 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Devices/SensorDevice.h @@ -0,0 +1,60 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_SensorDevice_H +#define _ARMARX_LIB_RobotAPI_SensorDevice_H + +#include"DeviceBase.h" +#include "../SensorValues/SensorValueBase.h" +#include "../util.h" + +#include <IceUtil/Time.h> + +namespace armarx +{ + TYPEDEF_PTRS_SHARED(SensorDevice); + + namespace SensorDeviceTags + { + using namespace DeviceTags; + } + + class SensorDevice: public virtual DeviceBase + { + public: + /// @brief A static const nullptr in case a const ref to a nullptr needs to be returned + static const SensorDevicePtr NullPtr; + /// @brief Create a SensorDevice with the given name + SensorDevice(const std::string& name): DeviceBase {name} {} + /// @return The SensorDevice's sensor value + virtual const SensorValueBase* getSensorValue() const = 0; + + std::string getSensorValueType(bool withoutNamespaceSpecifier = false) const + { + return getSensorValue()->getSensorValueType(withoutNamespaceSpecifier); + } + + virtual void rtReadSensorValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) {} + }; +} + +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL0Controllers/LVL0Controller.cpp b/source/RobotAPI/components/units/RobotUnit/LVL0Controllers/LVL0Controller.cpp similarity index 79% rename from source/RobotAPI/libraries/RTRobotUnit/LVL0Controllers/LVL0Controller.cpp rename to source/RobotAPI/components/units/RobotUnit/LVL0Controllers/LVL0Controller.cpp index f1314225db47875c050245ae3a477a13cad453ef..8b25b9df97a86c596914313b13d6e6aaf0ec1973 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL0Controllers/LVL0Controller.cpp +++ b/source/RobotAPI/components/units/RobotUnit/LVL0Controllers/LVL0Controller.cpp @@ -22,7 +22,12 @@ #include "LVL0Controller.h" +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> using namespace armarx; - +ControlDevice& LVL0Controller::getParent() const +{ + ARMARX_CHECK_EXPRESSION_W_HINT(parent, "This LVL0Controller is not owned by a ControlDevice"); + return *parent; +} diff --git a/source/RobotAPI/components/units/RobotUnit/LVL0Controllers/LVL0Controller.h b/source/RobotAPI/components/units/RobotUnit/LVL0Controllers/LVL0Controller.h new file mode 100644 index 0000000000000000000000000000000000000000..1a16efbdf8b7fe91c3e991ff10e6930ef633638e --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL0Controllers/LVL0Controller.h @@ -0,0 +1,99 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::LVL0Controller + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_LVL0Controller_H +#define _ARMARX_LIB_RobotAPI_LVL0Controller_H + +#include "../ControlTargets/ControlTargetBase.h" +#include "../util.h" + +#include <memory> +#include <atomic> + +namespace armarx +{ + class ControlDevice; + + class LVL0Controller + { + public: + virtual ~LVL0Controller() = default; + + virtual ControlTargetBase* getControlTarget() = 0; + + virtual const ControlTargetBase* getControlTarget() const + { + return const_cast<const ControlTargetBase*>(const_cast<LVL0Controller*>(this)->getControlTarget()); + } + + virtual void rtResetTarget() + { + getControlTarget()->reset(); + } + + virtual bool rtIsTargetVaild() const + { + return getControlTarget()->isValid(); + } + + virtual const std::string& getControlMode() const + { + return getControlTarget()->getControlMode(); + } + ControlDevice& getParent() const; + + protected: + ControlDevice& rtGetParent() const + { + return *parent; + } + //called by the owning ControlDevice + /// @brief called when this LVL0Controller is run + virtual void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) = 0; + /// @brief called when this LVL0Controller is activated + virtual void rtPreActivateController() + { + + } + virtual void rtPostDeactivateController() + { + + } + /// @brief called when this LVL0Controller is deactivated + private: + friend class ControlDevice; + std::atomic_bool activated {false}; + ControlDevice* parent {nullptr}; + void rtActivate() + { + rtPreActivateController(); + activated = true; + } + void rtDeactivate() + { + activated = false; + rtPostDeactivateController(); + rtResetTarget(); + } + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.cpp b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e77d64f87c9eeb12a8f99c5ba87d92b025a3b75 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.cpp @@ -0,0 +1,199 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::LVL1Controller + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "LVL1Controller.h" + +#include <ArmarXCore/core/util/algorithm.h> + +#include "../RobotUnit.h" + +using namespace armarx; + +const LVL1ControllerPtr LVL1Controller::NullPtr +{ + nullptr +}; + +bool LVL1Controller::isControllerActive(const Ice::Current&) const +{ + return isActive; +} +bool LVL1Controller::isControllerRequested(const Ice::Current&) const +{ + return isRequested; +} + +bool LVL1Controller::hasControllerError(const Ice::Current&) const +{ + return deactivatedBecauseOfError; +} + +std::string LVL1Controller::getInstanceName(const Ice::Current&) const +{ + return getName(); +} + +LVL1ControllerDescription LVL1Controller::getControllerDescription(const Ice::Current&) const +{ + LVL1ControllerDescription d; + d.className = getClassName(); + ARMARX_CHECK_EXPRESSION(getProxy(-1)); + d.controller = LVL1ControllerInterfacePrx::uncheckedCast(getProxy(-1)); + d.controlModeAssignment = controlDeviceControlModeMap; + d.instanceName = getInstanceName(); + return d; +} + +StringStringDictionary LVL1Controller::getControlDeviceUsedControlModeMap(const Ice::Current&) const +{ + return controlDeviceControlModeMap; +} + +boost::optional<std::vector<char> > LVL1Controller::isNotInConflictWith(const std::vector<char>& used) const +{ + ARMARX_CHECK_EXPRESSION(used.size() == controlDeviceUsedBitmap.size()); + auto result = used; + for (std::size_t i = 0; i < controlDeviceUsedBitmap.size(); ++i) + { + if (controlDeviceUsedBitmap.at(i)) + { + if (used.at(i)) + { + return boost::optional<std::vector<char>>(); + } + result.at(i) = true; + } + + } + return {true, std::move(result)}; +} + +LVL1ControllerStatus LVL1Controller::getControllerStatus(const Ice::Current&) const +{ + LVL1ControllerStatus s; + s.active = isActive; + s.instanceName = getInstanceName(); + s.requested = isRequested; + s.error = deactivatedBecauseOfError; + s.timestampUSec = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + return s; +} + +LVL1ControllerDescriptionWithStatus LVL1Controller::getControllerDescriptionWithStatus(const Ice::Current&) const +{ + LVL1ControllerDescriptionWithStatus ds; + ds.description = getControllerDescription(); + ds.status = getControllerStatus(); + return ds; +} + +RobotUnitInterfacePrx LVL1Controller::getRobotUnit(const Ice::Current&) const +{ + ARMARX_CHECK_EXPRESSION(robotUnit); + return RobotUnitInterfacePrx::uncheckedCast(robotUnit->getProxy(-1)); +} + +void LVL1Controller::activateController(const Ice::Current&) const +{ + ARMARX_CHECK_EXPRESSION(robotUnit); + robotUnit->activateController(getInstanceName()); +} + +void LVL1Controller::deactivateController(const Ice::Current&) const +{ + ARMARX_CHECK_EXPRESSION(robotUnit); + robotUnit->deactivateController(getInstanceName()); +} + +void LVL1Controller::publish(const SensorAndControl& sac, const DebugDrawerInterfacePrx& draw, const DebugObserverInterfacePrx& observer) +{ + const bool active = isActive; + if (publishActive != active) + { + publishActive = active; + if (active) + { + onPublishActivation(draw, observer); + } + else + { + onPublishDeactivation(draw, observer); + } + } + if (active) + { + onPublish(sac, draw, observer); + } +} + +void LVL1Controller::rtActivateController() +{ + if (!isActive) + { + deactivatedBecauseOfError = false; + ARMARX_INFO << "activating lvl1controller " << getInstanceName() << " " << this; + rtPreActivateController(); + isActive = true; + } + else + { + ARMARX_INFO << "lvl1controller " << getInstanceName() << " is already active " << this; + } +} + +void LVL1Controller::rtDeactivateController() +{ + if (isActive) + { + isActive = false; + ARMARX_INFO << "deactivating lvl1controller " << getInstanceName() << " " << this; + rtPostDeactivateController(); + } + else + { + ARMARX_INFO << "lvl1controller " << getInstanceName() << " is already deactivated " << this; + } +} + +void LVL1Controller::rtDeactivateControllerBecauseOfError() +{ + deactivatedBecauseOfError = true; + rtDeactivateController(); +} + +void LVL1Controller::robotUnitInit(RobotUnit* ru, std::size_t ctrlId, StringStringDictionary ctrlDeviceControlModeMap, std::vector<char> ctrlDeviceUsedBitmap, std::vector<std::size_t> ctrlDeviceUsedIndices) +{ + ARMARX_CHECK_EXPRESSION(ru); + ARMARX_CHECK_EXPRESSION(ctrlDeviceControlModeMap.size()); + ARMARX_CHECK_EXPRESSION(ctrlId != std::numeric_limits<std::size_t>::max()); + ARMARX_CHECK_EXPRESSION_W_HINT(ru->getNumberOfControlDevices() == ctrlDeviceUsedBitmap.size(), + "ru->getNumberOfControlDevices() == ctrlDeviceUsedBitmap.size() ->" + + std::to_string(ru->getNumberOfControlDevices()) + " > " + std::to_string(ctrlDeviceUsedBitmap.size())); + ARMARX_CHECK_EXPRESSION(ctrlDeviceControlModeMap.size() == ctrlDeviceUsedIndices.size()); + + robotUnit = ru; + id = ctrlId; + controlDeviceControlModeMap = std::move(ctrlDeviceControlModeMap); + controlDeviceUsedBitmap = std::move(ctrlDeviceUsedBitmap); + controlDeviceUsedIndices = std::move(ctrlDeviceUsedIndices); +} + diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.h b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.h similarity index 50% rename from source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.h rename to source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.h index 53c36a59d564b4d199e1b89c8f7a644f52485c08..9ac907a6342aa65a23c0d959bf547cd34367a3ab 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.h +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.h @@ -27,48 +27,78 @@ #include <atomic> #include <mutex> #include <functional> +#include <unordered_set> + +#include <boost/optional.hpp> #include <ArmarXCore/core/Component.h> #include <ArmarXCore/core/util/Registrar.h> #include <ArmarXCore/core/util/TripleBuffer.h> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.h> +#include <ArmarXGui/interface/WidgetDescription.h> + +#include <RobotAPI/interface/units/RobotUnit/LVL1Controller.h> +#include <RobotAPI/interface/visualization/DebugDrawerInterface.h> +#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> -#include "../Targets/TargetBase.h" +#include "../ControlTargets/ControlTargetBase.h" //units -#include "../DataUnits/ForceTorqueDataUnit.h" -#include "../DataUnits/HapticDataUnit.h" -#include "../DataUnits/IMUDataUnit.h" -#include "../DataUnits/KinematicDataUnit.h" -#include "../DataUnits/PlatformDataUnit.h" +#include "../Devices/ControlDevice.h" +#include "../Devices/SensorDevice.h" + +#include "../util.h" +#include "../structs.h" + +namespace VirtualRobot +{ + class Robot; +} namespace armarx { - class LVL1ControllerDataProviderInterface: virtual public Ice::Object + TYPEDEF_PTRS_HANDLE(LVL1Controller); + TYPEDEF_PTRS_HANDLE(LVL1ControllerDescriptionProviderInterface); + + class RobotUnit; + + class LVL1ControllerDescriptionProviderInterface: virtual public Ice::Object { public: - virtual const ForceTorqueDataUnitInterface* getRTForceTorqueDataUnit() const = 0; - virtual const HapticDataUnitInterface* getRTHapticDataUnit() const = 0; - virtual const IMUDataUnitInterface* getRTIMUDataUnit() const = 0; - virtual const KinematicDataUnitInterface* getRTKinematicDataUnit() const = 0; - virtual const PlatformDataUnitInterface* getRTPlatformDataUnit() const = 0; + virtual ~LVL1ControllerDescriptionProviderInterface() = default; - virtual TargetBase* getJointTarget(const std::string& jointName, const std::string& controlMode) = 0; + virtual ConstSensorDevicePtr getSensorDevice(const std::string& sensorDeviceName) const = 0; + virtual const SensorValueBase* getSensorValue(const std::string& sensorDeviceName) const + { + auto sd = getSensorDevice(sensorDeviceName); + return sd ? sd->getSensorValue() : nullptr; + } + virtual ConstControlDevicePtr getControlDevice(const std::string& deviceName) = 0; + virtual ControlTargetBase* getControlTarget(const std::string& deviceName, const std::string& controlMode) = 0; virtual std::string getName() const = 0; + + template<class T> + const T* getSensorValue(const std::string& sensorDeviceName) const + { + return dynamic_cast<T*>(getSensorValue(sensorDeviceName)); + } + template<class T> + T* getControlTarget(const std::string& deviceName, const std::string& controlMode) + { + return dynamic_cast<T*>(getControlTarget(deviceName, controlMode)); + } }; - using LVL1ControllerDataProviderInterfacePtr = IceInternal::Handle<LVL1ControllerDataProviderInterface>; /** - * @defgroup Library-RTRobotUnit RTRobotUnit + * @defgroup Library-RobotUnit RobotUnit * @ingroup RobotAPI - * A description of the library RTRobotUnit. + * A description of the library RobotUnit. */ /** - * @ingroup Library-RTRobotUnit + * @ingroup Library-RobotUnit * @brief A high level controller writing its results into targes. * * This is the abstract base class for all LVL1Controllers. @@ -136,27 +166,70 @@ namespace armarx */ class LVL1Controller: virtual public LVL1ControllerInterface, - virtual public Component + virtual public ManagedIceObject { public: + //add these static functions if you want to provide a gui for construction + // static WidgetDescription::WidgetPtr GenerateConfigDescription + // ( + // const boost::shared_ptr<VirtualRobot::Robot>&, + // const std::map<std::string, ConstControlDevicePtr>& controlDevices, + // const std::map<std::string, ConstSensorDevicePtr>& sensorDevices + // ) + // { + // return a nullptr / Widget if you want remote configuration but do not need any parameters + // } + // static LVL1ControllerConfigPtr GenerateConfigFromVariants(const StringVariantBaseMap&) + // { + // return the config that is passed to your controller + // } + using GenerateConfigDescriptionFunctionSignature = WidgetDescription::WidgetPtr(*) + ( + const boost::shared_ptr<VirtualRobot::Robot>&, + const std::map<std::string, ConstControlDevicePtr>& controlDevices, + const std::map<std::string, ConstSensorDevicePtr>& sensorDevices + ); + using GenerateConfigFromVariantsFunctionSignature = LVL1ControllerConfigPtr(*)(const StringVariantBaseMap&); + + static const LVL1ControllerPtr NullPtr; + + virtual ~LVL1Controller() = default; //ice interface (must not be called in the rt thread) - virtual bool isControllerActive(const Ice::Current& = GlobalIceCurrent) const final + virtual bool isControllerActive(const Ice::Current& = GlobalIceCurrent) const final; + virtual bool isControllerRequested(const Ice::Current& = GlobalIceCurrent) const final; + virtual bool hasControllerError(const Ice::Current& = GlobalIceCurrent) const final; + + virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override = 0; + virtual std::string getInstanceName(const Ice::Current& = GlobalIceCurrent) const final; + + virtual LVL1ControllerDescription getControllerDescription(const Ice::Current& = GlobalIceCurrent) const final; + virtual LVL1ControllerStatus getControllerStatus(const Ice::Current& = GlobalIceCurrent) const final; + virtual LVL1ControllerDescriptionWithStatus getControllerDescriptionWithStatus(const Ice::Current& = GlobalIceCurrent) const final; + virtual RobotUnitInterfacePrx getRobotUnit(const Ice::Current& = GlobalIceCurrent) const final; + + virtual void activateController(const Ice::Current& = GlobalIceCurrent) const final; + virtual void deactivateController(const Ice::Current& = GlobalIceCurrent) const final; + + virtual WidgetDescription::StringWidgetDictionary getFunctionDescriptions(const Ice::Current& = GlobalIceCurrent) const override { - return isActive; + return {}; } - virtual std::string getInstanceName(const Ice::Current& = GlobalIceCurrent) const final + virtual void callDescribedFunction(const std::string&, const StringVariantBaseMap&, const Ice::Current& = GlobalIceCurrent) override { - return getName(); } - virtual StringStringDictionary getJointControlModeMap(const Ice::Current& = GlobalIceCurrent) const final + + std::size_t getId() const { - return jointControlModeMap; + return id; } - virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override = 0; - //c++ interface (local calls) (must not be called in the rt thread) - //c++ interface (local calls) (can only be called in the rt thread) + + //c++ interface (local calls) (must be called in the rt thread) virtual void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) = 0; - virtual void rtSwapBufferAndRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) = 0; + virtual void rtSwapBufferAndRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + rtRun(sensorValuesTimestamp, timeSinceLastIteration); + } + /** * @brief This function is called before the controller is activated. * You can use it to activate a thread again (DO NOT SPAWN NEW THREADS!) e.g. via a std::atomic_bool. @@ -168,23 +241,78 @@ namespace armarx */ virtual void rtPostDeactivateController() {} - bool rtUsesJoint(std::size_t jointindex) const + bool rtUsesControlDevice(std::size_t deviceIndex) const { - return std::find(jointIndices.begin(), jointIndices.end(), jointindex) == jointIndices.end(); + return controlDeviceUsedBitmap.at(deviceIndex); } - const std::vector<std::size_t>& rtGetJointIndices() const + + std::size_t rtGetId() const + { + return id; + } + + //used control devices + virtual StringStringDictionary getControlDeviceUsedControlModeMap(const Ice::Current& = GlobalIceCurrent) const final; + const std::vector<char>& getControlDeviceUsedBitmap() const + { + return controlDeviceUsedBitmap; + } + const std::vector<std::size_t>& rtGetControlDeviceUsedIndices() const + { + return controlDeviceUsedIndices; + } + const std::vector<std::size_t>& getControlDeviceUsedIndices() const + { + return controlDeviceUsedIndices; + } + + //check for conflict + boost::optional<std::vector<char>> isNotInConflictWith(const LVL1ControllerPtr& other) const { - return jointIndices; + return isNotInConflictWith(other->getControlDeviceUsedBitmap()); } + boost::optional<std::vector<char>> isNotInConflictWith(const std::vector<char>& used) const; + + template<class ItT> + static boost::optional<std::vector<char>> AreNotInConflict(ItT first, ItT last); + + //publish thread hooks + virtual void onPublishActivation(const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) {} + virtual void onPublishDeactivation(const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) {} + virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) {} private: - friend class RobotUnit; + void publish(const SensorAndControl& sac, const DebugDrawerInterfacePrx& draw, const DebugObserverInterfacePrx& observer); + void rtActivateController(); void rtDeactivateController(); + void rtDeactivateControllerBecauseOfError(); + + //firends + friend class RobotUnitLVL1ControllerAccess; + + void robotUnitInit( + RobotUnit* ru, + std::size_t ctrlId, + StringStringDictionary ctrlDeviceControlModeMap, + std::vector<char> ctrlDeviceUsedBitmap, + std::vector<std::size_t> ctrlDeviceUsedIndices + ); + + RobotUnit* robotUnit; + std::size_t id = std::numeric_limits<std::size_t>::max(); + StringStringDictionary controlDeviceControlModeMap; + std::vector<char> controlDeviceUsedBitmap; + std::vector<std::size_t> controlDeviceUsedIndices; //this data is filled by the robot unit to provide convenience functions std::atomic_bool isActive {false}; - JointNameToControlModeDictionary jointControlModeMap; - std::vector<std::size_t> jointIndices; + std::atomic_bool isRequested {false}; + std::atomic_bool deactivatedBecauseOfError {false}; + + bool publishActive {false}; + + bool statusReportedActive {false}; + bool statusReportedRequested {false}; // ManagedIceObject interface protected: @@ -192,108 +320,50 @@ namespace armarx { return getClassName(); } - }; - using LVL1ControllerPtr = IceInternal::Handle<LVL1Controller>; - - using LVL1ControllerRegistry = Registrar<std::function<LVL1ControllerPtr(LVL1ControllerDataProviderInterfacePtr robotUnit, LVL1ControllerConfigPtr config)>>; - template<class ControllerType> - struct LVL1ControllerRegistration - { - LVL1ControllerRegistration(const std::string& name) + public: + struct IdComp { - LVL1ControllerRegistry::registerElement(name, [](LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config) + template<class PtrT1, class PtrT2> + bool operator()(PtrT1 l, PtrT2 r) const { - ARMARX_CHECK_EXPRESSION_W_HINT(prov, "DataProvider is NULL!"); - return new ControllerType(prov, config); - }); - } - }; - - /** - * @ingroup Library-RTRobotUnit - * @brief Brief description of class LVL1Controller. - * - * Detailed description of class LVL1Controller. - * - * \code{cpp} - //in ice - module armarx - { - struct SomeControllerConfig extends LVL1ControllerConfig - { - float paramSetViaConfig; + //null is bigger than any other + if (!l) + { + return false; + } + if (!r) + { + return true; + } + return l->id < r->id; + } }; - interface SomeControllerInterface extends LVL1ControllerInterface + // ManagedIceObject interface + protected: + virtual void onInitComponent() override { - void setSomeParamChangedViaIce(float param); - }; + } + virtual void onConnectComponent() override + { + } }; - - //in c++ - #include <ArmarXCore/core/exceptions/local/ExpressionException.h> - namespace armarx + class LVL1ControllerRegistryEntry { - struct SomeControlDataStruct - { - float parameterChangedViaIceCalls; - }; - - class SomeController : virtual public LVL1Controller<SomeControlDataStruct>, - public SomeControllerInterface - { - ActuatorVelocityTargetPtr targetJointXPtr; - std::vector<const float*> jointValuePtrs; - float someParamSetViaConfig; - SomeController(RobotUnitPtr robotUnit, LVL1ControllerConfigPtr config): - LVL1Controller<SomeControlDataStruct>(SomeControlDataStruct(1)) //initial parameter value - { - //cast the config to your config type - SomeControlDataStructPtr someConfig = SomeControlDataStructPtr::dynamicCast(config); - ARMARX_CHECK_EXPRESSION_W_HINT(someConfig, - "The provided config has the wrong type! The type is " << config->ice_id() - << " instead of " << SomeControllerConfig::ice_staticId()); - //read the config - someParamSetViaConfig = someConfig->parameterSetViaIceCalls - //make sure the used units are supported - ARMARX_CHECK_EXPRESSION_W_HINT(robotUnit->getRTKinematicDataUnit(), "The RobotUnit " << robotUnit->getName() << " has no kinematic data unit"); - //get pointers to sensor values from units - jointValuePtrs = robotUnit->getRTKinematicDataUnit()->getJointAngles({"jointX", "jointY"}); - - //get pointers for the results of this controller - targetJointXPtr = dynamic_cast<ActuatorVelocityTarget*>(robotUnit->getJointTarget("jointX", ControlModes::VelocityMode)); - ARMARX_CHECK_EXPRESSION_W_HINT(targetJointXPtr, "The Joint jointX does not support the mode " + ControlModes::VelocityMode); - } - - SomeController(ActuatorVelocityTargetPtr targetPtr, ControllerConfigPtr config): - targetJointXPtr{targetPtr} - { + private: + friend class RobotUnit; + virtual LVL1ControllerPtr create(LVL1ControllerDescriptionProviderInterfacePtr, const LVL1ControllerConfigPtr&, const boost::shared_ptr<VirtualRobot::Robot>&) const = 0; + virtual WidgetDescription::WidgetPtr GenerateConfigDescription(const boost::shared_ptr<VirtualRobot::Robot>&, const std::map<std::string, ConstControlDevicePtr>&, const std::map<std::string, ConstSensorDevicePtr>&) const = 0; + virtual LVL1ControllerConfigPtr GenerateConfigFromVariants(const StringVariantBaseMap&) const = 0; + virtual bool hasRemoteConfiguration() const = 0; + }; + using LVL1ControllerRegistry = Registrar<std::unique_ptr<LVL1ControllerRegistryEntry>>; - } + template<class ControllerType> + struct LVL1ControllerRegistration; - virtual void run(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override - { - pid.update( - rtGetControlStruct().parameterChangedViaIceCalls, - jointValuePtrs.at(0), - timeSinceLastIteration.toMicroSeconds()); - *targetJointXPtr = pid.getValue() + someParamSetViaConfig; - } - virtual void setSomeParamChangedViaIce(float param, const Ice::Current& = GlobalIceCurrent) override - { - std::lock_guard<std::recursive_mutex> lock(controlDataMutex); - getWriterControlStruct().parameterChangedViaIceCalls = param; - writeControlStruct(); - } - } - //register the controller - //this line has to appear in some cpp of your lib to do its registration (including it is enough) - LVL1ControllerRegistration<SomeController> registrationSomeController("SomeController"); - } - * \endcode - */ template <typename ControlDataStruct> class LVL1ControllerWithTripleBuffer: virtual public LVL1Controller { @@ -354,27 +424,6 @@ namespace armarx }; template <typename ControlDataStruct> using LVL1ControllerWithTripleBufferPtr = IceInternal::Handle<LVL1ControllerWithTripleBuffer<ControlDataStruct>>; - - - struct PlatformCartesianVelocity - { - float vx; - float vy; - float vAngle; - PlatformCartesianVelocity() : vx(0), vy(0), vAngle(0) - { } - }; - - - class AbstractLvl1PlatformVelocityController : public virtual LVL1ControllerWithTripleBuffer<PlatformCartesianVelocity> - { - - public: - virtual void setTarget(float vx, float vy, float vAngle) = 0; - }; - - typedef boost::shared_ptr <AbstractLvl1PlatformVelocityController> AbstractLvl1PlatformVelocityControllerPtr; - - } +#include "LVL1Controller.hpp" #endif diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.hpp b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cede05a859b7a698f0c7da9906ad898ed21cd30b --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1Controller.hpp @@ -0,0 +1,135 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::LVL1Controller + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_LVL1Controller_HPP +#define _ARMARX_LIB_RobotAPI_LVL1Controller_HPP + +#include <ArmarXCore/core/util/TemplateMetaProgramming.h> + +namespace armarx +{ + template<class ItT> + boost::optional<std::vector<char>> LVL1Controller::AreNotInConflict(ItT first, ItT last) + { + if (first == last) + { + return {true, std::vector<char>{}}; + } + std::size_t n = (*first)->getControlDeviceUsedBitmap().size(); + std::vector<char> inuse(n, false); + while (first != last) + { + auto r = (*first)->isNotInConflictWith(inuse); + if (!r) + { + return r; + } + inuse = std::move(r.get()); + ++first; + } + return {true, std::move(inuse)}; + } + + namespace detail + { + template<class LVL1ControllerT, class = void, class = void> + class LVL1ControllerRegistryEntryHelper : public LVL1ControllerRegistryEntry + { + virtual LVL1ControllerPtr create(LVL1ControllerDescriptionProviderInterfacePtr prov, const LVL1ControllerConfigPtr& config, const boost::shared_ptr<VirtualRobot::Robot>& rob) const final + { + ARMARX_CHECK_EXPRESSION_W_HINT(prov, "DataProvider is NULL!"); + return new LVL1ControllerT(prov, config, rob); + } + virtual WidgetDescription::WidgetPtr GenerateConfigDescription(const boost::shared_ptr<VirtualRobot::Robot>&, const std::map<std::string, ConstControlDevicePtr>&, const std::map<std::string, ConstSensorDevicePtr>&) const final + { + ARMARX_CHECK_EXPRESSION(!"This function should never be called"); + } + virtual LVL1ControllerConfigPtr GenerateConfigFromVariants(const StringVariantBaseMap&) const + { + ARMARX_CHECK_EXPRESSION(!"This function should never be called"); + } + virtual bool hasRemoteConfiguration() const + { + return false; + } + }; + + ARMARX_META_MAKE_HAS_MEMBER_FNC_CHECK(hasGenerateConfigDescription, GenerateConfigDescription, LVL1Controller::GenerateConfigDescriptionFunctionSignature); + ARMARX_META_MAKE_HAS_MEMBER_FNC_CHECK(hasGenerateConfigFromVariants, GenerateConfigFromVariants, LVL1Controller::GenerateConfigFromVariantsFunctionSignature); + +#define ARMARX_ASSERT_LVL1CONTROLLER_HAS_CONSTRUCTION_GUI(T) \ + static_assert \ + ( \ + ::armarx::detail::hasGenerateConfigDescription<T>::value && \ + ::armarx::detail::hasGenerateConfigFromVariants<T>::value, \ + #T " does not offer a construction gui" \ + ) + + template<class LVL1ControllerT> + class LVL1ControllerRegistryEntryHelper < + LVL1ControllerT, + // meta::void_t<decltype(LVL1ControllerT::GenerateConfigDescription)>, + typename std::enable_if < hasGenerateConfigDescription<LVL1ControllerT>::value >::type, + // meta::void_t<decltype(LVL1ControllerT::GenerateConfigFromVariants) + typename std::enable_if < hasGenerateConfigFromVariants<LVL1ControllerT>::value >::type + > : public LVL1ControllerRegistryEntry + { + virtual LVL1ControllerPtr create(LVL1ControllerDescriptionProviderInterfacePtr prov, const LVL1ControllerConfigPtr& config, const boost::shared_ptr<VirtualRobot::Robot>& rob) const final + { + ARMARX_CHECK_EXPRESSION_W_HINT(prov, "DataProvider is NULL!"); + return new LVL1ControllerT(prov, config, rob); + } + virtual WidgetDescription::WidgetPtr GenerateConfigDescription( + const boost::shared_ptr<VirtualRobot::Robot>& robot, + const std::map<std::string, ConstControlDevicePtr>& controlDevices, + const std::map<std::string, ConstSensorDevicePtr>& sensorDevices + ) const final + { + WidgetDescription::WidgetPtr cfg = LVL1ControllerT::GenerateConfigDescription(robot, controlDevices, sensorDevices); + if (!cfg) + { + cfg = new WidgetDescription::Widget; + } + return cfg; + } + virtual LVL1ControllerConfigPtr GenerateConfigFromVariants(const StringVariantBaseMap& variants) const + { + return LVL1ControllerT::GenerateConfigFromVariants(variants); + } + virtual bool hasRemoteConfiguration() const + { + return true; + } + }; + } + + using LVL1ControllerRegistry = Registrar<std::unique_ptr<LVL1ControllerRegistryEntry>>; + template<class ControllerType> + struct LVL1ControllerRegistration + { + LVL1ControllerRegistration(const std::string& name) + { + LVL1ControllerRegistry::registerElement(name, std::unique_ptr<LVL1ControllerRegistryEntry>(new detail::LVL1ControllerRegistryEntryHelper<ControllerType>)); + } + }; +} +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.cpp b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.cpp similarity index 50% rename from source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.cpp rename to source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.cpp index 5d3805bd03eec6bf416a6cc4e4b96e22dd023039..6874ebde43fc50adf2d02044f470c23541adcb93 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.cpp @@ -13,30 +13,26 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI::ArmarXObjects::LVL1HolonomicPlatformVelocityPassThroughController + * @package RobotAPI::ArmarXObjects::LVL1HolonomicPlatformUnitVelocityPassThroughController * @author Raphael ( ufdrv at student dot kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#include "LVL1HolonomicPlatformVelocityPassThroughController.h" +#include "LVL1HolonomicPlatformUnitVelocityPassThroughController.h" #include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughControllerConfig.h> - using namespace armarx; -LVL1HolonomicPlatformVelocityPassThroughController::LVL1HolonomicPlatformVelocityPassThroughController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config) +LVL1HolonomicPlatformUnitVelocityPassThroughController::LVL1HolonomicPlatformUnitVelocityPassThroughController(LVL1ControllerDescriptionProviderInterfacePtr prov, LVL1ControllerConfigPtr config, const VirtualRobot::RobotPtr&) { - LVL1HolonomicPlatformVelocityPassThroughControllerConfigPtr cfg = LVL1HolonomicPlatformVelocityPassThroughControllerConfigPtr::dynamicCast(config); - ARMARX_CHECK_EXPRESSION_W_HINT(cfg, - "The provided config has the wrong type! The type is " << config->ice_id() - << " instead of " << LVL1HolonomicPlatformVelocityPassThroughControllerConfig::ice_staticId()); + LVL1HolonomicPlatformUnitVelocityPassThroughControllerConfigPtr cfg = LVL1HolonomicPlatformUnitVelocityPassThroughControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION_W_HINT(cfg, "The provided config has the wrong type! The type is " << config->ice_id()); - target = dynamic_cast<HolonomicPlatformVelocityTarget*>(prov->getJointTarget(cfg->platformName, ControlModes::HolonomicPlatformVelocityMode)); - ARMARX_CHECK_EXPRESSION_W_HINT(target, "The actuator " << cfg->platformName << " has no controll mode " << ControlModes::HolonomicPlatformVelocityMode); + target = prov->getControlTarget(cfg->platformName, ControlModes::HolonomicPlatformVelocity)->asA<ControlTargetHolonomicPlatformVelocity>(); + ARMARX_CHECK_EXPRESSION_W_HINT(target, "The actuator " << cfg->platformName << " has no control mode " << ControlModes::HolonomicPlatformVelocity); initialSettings.velocityX = cfg->initialVelocityX; initialSettings.velocityY = cfg->initialVelocityY; @@ -44,16 +40,15 @@ LVL1HolonomicPlatformVelocityPassThroughController::LVL1HolonomicPlatformVelocit reinitTripleBuffer(initialSettings); } -void LVL1HolonomicPlatformVelocityPassThroughController::rtRun(const IceUtil::Time&, const IceUtil::Time&) +void LVL1HolonomicPlatformUnitVelocityPassThroughController::rtRun(const IceUtil::Time&, const IceUtil::Time&) { target->velocityX = rtGetControlStruct().velocityX; target->velocityY = rtGetControlStruct().velocityY; target->velocityRotation = rtGetControlStruct().velocityRotation; } - -void LVL1HolonomicPlatformVelocityPassThroughController::setVelocites(float velocityX, float velocityY, - float velocityRotation, const Ice::Current&) +void LVL1HolonomicPlatformUnitVelocityPassThroughController::setVelocites(float velocityX, float velocityY, + float velocityRotation) { LockGuardType guard {controlDataMutex}; getWriterControlStruct().velocityX = velocityX; @@ -62,6 +57,5 @@ void LVL1HolonomicPlatformVelocityPassThroughController::setVelocites(float velo writeControlStruct(); } - -LVL1ControllerRegistration<LVL1HolonomicPlatformVelocityPassThroughController> -registrationLVL1HolonomicPlatformVelocityPassThroughController("LVL1HolonomicPlatformVelocityPassThroughController"); +LVL1ControllerRegistration<LVL1HolonomicPlatformUnitVelocityPassThroughController> +registrationLVL1HolonomicPlatformUnitVelocityPassThroughController("LVL1HolonomicPlatformUnitVelocityPassThroughController"); diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.h b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.h new file mode 100644 index 0000000000000000000000000000000000000000..31f0dbbf5d0190cb90a50ce298204cd2a0e52c5a --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.h @@ -0,0 +1,79 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::LVL1HolonomicPlatformUnitVelocityPassThroughController + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_LVL1HolonomicPlatformUnitVelocityPassThroughController_H +#define _ARMARX_LIB_RobotAPI_LVL1HolonomicPlatformUnitVelocityPassThroughController_H + +#include <VirtualRobot/Robot.h> + +#include "LVL1Controller.h" +#include "../ControlTargets/ControlTargetHolonomicPlatformVelocity.h" +#include "../util.h" + +namespace armarx +{ + + TYPEDEF_PTRS_HANDLE(LVL1HolonomicPlatformUnitVelocityPassThroughControllerConfig); + class LVL1HolonomicPlatformUnitVelocityPassThroughControllerConfig: virtual public LVL1ControllerConfig + { + public: + std::string platformName; + float initialVelocityX; + float initialVelocityY; + float initialVelocityRotation; + }; + + TYPEDEF_PTRS_HANDLE(LVL1HolonomicPlatformUnitVelocityPassThroughController); + class LVL1HolonomicPlatformUnitVelocityPassThroughControllerControlData + { + public: + float velocityX = 0; + float velocityY = 0; + float velocityRotation = 0; + }; + + TYPEDEF_PTRS_HANDLE(LVL1HolonomicPlatformUnitVelocityPassThroughController); + class LVL1HolonomicPlatformUnitVelocityPassThroughController: + virtual public LVL1ControllerWithTripleBuffer<LVL1HolonomicPlatformUnitVelocityPassThroughControllerControlData> + { + public: + LVL1HolonomicPlatformUnitVelocityPassThroughController(LVL1ControllerDescriptionProviderInterfacePtr prov, LVL1ControllerConfigPtr config, const VirtualRobot::RobotPtr&); + + virtual void rtRun(const IceUtil::Time&, const IceUtil::Time&) override; + + //for the platform unit + void setVelocites(float velocityX, float velocityY, float velocityRotation); + + //ice interface + virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override + { + return "LVL1HolonomicPlatformUnitVelocityPassThroughController"; + } + protected: + virtual void onInitComponent() override {} + virtual void onConnectComponent() override {} + + ControlTargetHolonomicPlatformVelocity* target; + LVL1HolonomicPlatformUnitVelocityPassThroughControllerControlData initialSettings; + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1KinematicUnitPassThroughController.cpp b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1KinematicUnitPassThroughController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..138f6033c6cfaefb31ab28d3c4a1689eba71680a --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1KinematicUnitPassThroughController.cpp @@ -0,0 +1,64 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitPassThroughController + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "LVL1KinematicUnitPassThroughController.h" +using namespace armarx; + +LVL1ControllerRegistration<LVL1KinematicUnitPassThroughController> registrationControllerLVL1KinematicUnitPassThroughController("LVL1KinematicUnitPassThroughController"); + +LVL1KinematicUnitPassThroughController::LVL1KinematicUnitPassThroughController(LVL1ControllerDescriptionProviderInterfacePtr prov, const LVL1ControllerConfigPtr& config, const VirtualRobot::RobotPtr&) +{ + LVL1KinematicUnitPassThroughControllerConfigPtr cfg = LVL1KinematicUnitPassThroughControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION_W_HINT(cfg, "The provided config has the wrong type! The type is " << config->ice_id()); + + const SensorValueBase* sv = prov->getSensorValue(cfg->deviceName); + ControlTargetBase* ct = prov->getControlTarget(cfg->deviceName, cfg->controlMode); + //get sensor + if (cfg->controlMode == ControlModes::Position1DoF) + { + ARMARX_CHECK_EXPRESSION(sv->asA<SensorValue1DoFActuatorPosition>()); + sensor = &(sv->asA<SensorValue1DoFActuatorPosition>()->position); + ARMARX_CHECK_EXPRESSION(ct->asA<ControlTarget1DoFActuatorPosition>()); + target = &(ct->asA<ControlTarget1DoFActuatorPosition>()->position); + sensorToControlOnActivateFactor = 1; + } + else if (cfg->controlMode == ControlModes::Velocity1DoF) + { + ARMARX_CHECK_EXPRESSION(sv->asA<SensorValue1DoFActuatorVelocity>()); + sensor = &(sv->asA<SensorValue1DoFActuatorVelocity>()->velocity); + ARMARX_CHECK_EXPRESSION(ct->asA<ControlTarget1DoFActuatorVelocity>()); + target = &(ct->asA<ControlTarget1DoFActuatorVelocity>()->velocity); + sensorToControlOnActivateFactor = 1; + } + else if (cfg->controlMode == ControlModes::Torque1DoF) + { + ARMARX_CHECK_EXPRESSION(sv->asA<SensorValue1DoFActuatorTorque>()); + sensor = &(sv->asA<SensorValue1DoFActuatorTorque>()->torque); + ARMARX_CHECK_EXPRESSION(ct->asA<ControlTarget1DoFActuatorTorque>()); + target = &(ct->asA<ControlTarget1DoFActuatorTorque>()->torque); + sensorToControlOnActivateFactor = 0; + } + else + { + throw InvalidArgumentException {"Unsupported control mode: " + cfg->controlMode}; + } +} diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1KinematicUnitPassThroughController.h b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1KinematicUnitPassThroughController.h new file mode 100644 index 0000000000000000000000000000000000000000..bf8e62a5e90e839ff63035ee41dc3fb9417ef664 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1KinematicUnitPassThroughController.h @@ -0,0 +1,94 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitPassThroughController + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_LVL1KinematicUnitPassThroughController_H +#define _ARMARX_LIB_RobotAPI_LVL1KinematicUnitPassThroughController_H + +#include <atomic> + +#include <VirtualRobot/Robot.h> + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include "LVL1Controller.h" +#include "../RobotUnit.h" +#include "../SensorValues/SensorValue1DoFActuator.h" + +#include "../ControlTargets/ControlTarget1DoFActuator.h" + +namespace armarx +{ + TYPEDEF_PTRS_HANDLE(LVL1KinematicUnitPassThroughControllerConfig); + class LVL1KinematicUnitPassThroughControllerConfig : virtual public LVL1ControllerConfig + { + public: + std::string deviceName; + std::string controlMode; + }; + + + TYPEDEF_PTRS_HANDLE(LVL1KinematicUnitPassThroughController); + class LVL1KinematicUnitPassThroughController: + virtual public LVL1Controller + { + public: + inline LVL1KinematicUnitPassThroughController(LVL1ControllerDescriptionProviderInterfacePtr prov, const LVL1ControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + inline virtual void rtRun(const IceUtil::Time&, const IceUtil::Time&) override + { + *target = control; + } + inline virtual void rtPreActivateController() override + { + ARMARX_IMPORTANT << "<<<<<<<<<<<<<<<<<<<<<< ACT " << this; + control = (*sensor) * sensorToControlOnActivateFactor; + } + virtual void rtPostDeactivateController() override + { + ARMARX_IMPORTANT << "<<<<<<<<<<<<<<<<<<<<<< DEC " << this; + } + + //ice interface + inline virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override + { + return "LVL1KinematicUnitPassThroughController"; + } + + void set(float val) + { + control = val; + } + + protected: + std::atomic<float> control {0}; + float* target {nullptr}; + const float* sensor + { + nullptr + }; + float sensorToControlOnActivateFactor {0}; + + virtual void onInitComponent() override {} + virtual void onConnectComponent() override {} + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1TrajectoryController.cpp b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1TrajectoryController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a79e673031739340335fcc01e65881acf05b514 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1TrajectoryController.cpp @@ -0,0 +1,140 @@ +#include "LVL1TrajectoryController.h" +#include <ArmarXCore/core/time/TimeUtil.h> +#include <VirtualRobot/TimeOptimalTrajectory/TimeOptimalTrajectory.h> + + +namespace armarx +{ + + LVL1ControllerRegistration<LVL1TrajectoryController> registrationControllerLVL1TrajectoryController("LVL1TrajectoryController"); + + + LVL1TrajectoryController::LVL1TrajectoryController(LVL1ControllerDescriptionProviderInterfacePtr prov, const LVL1ControllerConfigPtr& config, const VirtualRobot::RobotPtr&) + { + cfg = LVL1TrajectoryControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION_W_HINT(cfg, "Needed type: LVL1TrajectoryControllerConfigPtr"); + // trajectory = TrajectoryPtr::dynamicCast(cfg->trajectory); + + for (size_t i = 0; i < cfg->jointNames.size(); i++) + { + auto jointName = cfg->jointNames.at(i); + ControlTargetBase* ct = prov->getControlTarget(jointName, ControlModes::Velocity1DoF); + const SensorValueBase* sv = prov->getSensorValue(jointName); + targets.push_back(ct->asA<ControlTarget1DoFActuatorVelocity>()); + sensors.push_back(sv->asA<SensorValue1DoFActuatorPosition>()); + PIDs.push_back(PIDControllerPtr(new PIDController(cfg->PID_p, + cfg->PID_i, cfg->PID_d, + cfg->maxVelocity))); + } + + + } + + std::string LVL1TrajectoryController::getClassName(const Ice::Current&) const + { + return "LVL1TrajectoryController"; + } + + void LVL1TrajectoryController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + TIMING_START(TrajectoryControl); + const Trajectory& trajectory = *rtGetControlStruct().trajectory; + if (startTime.toMicroSeconds() == 0) + { + startTime = sensorValuesTimestamp; + } + auto timeSinceStart = sensorValuesTimestamp - startTime; + auto positions = trajectory.getStates(timeSinceStart.toSecondsDouble(), 0); + auto velocities = trajectory.getStates(timeSinceStart.toSecondsDouble(), 1); + // size_t dim = trajectory->dim(); + for (size_t d = 0; d < targets.size(); ++d) + { + ARMARX_CHECK_EXPRESSION(d < targets.size()); + ARMARX_CHECK_EXPRESSION(d < PIDs.size()); + ARMARX_CHECK_EXPRESSION(d < sensors.size()); + ARMARX_CHECK_EXPRESSION(d < positions.size()); + ARMARX_CHECK_EXPRESSION(d < velocities.size()); + PIDControllerPtr& pid = PIDs.at(d); + pid->update(timeSinceLastIteration.toSecondsDouble(), + sensors.at(d)->position, + positions.at(d)); + double newVel = pid->getControlValue(); + newVel += velocities.at(1); + targets.at(d)->velocity = newVel; + } + TIMING_END(TrajectoryControl); + + } + + void LVL1TrajectoryController::setTrajectory(const TrajectoryBasePtr& t, const Ice::Current&) + { + TIMING_START(TrajectoryOptimization); + ARMARX_CHECK_EXPRESSION(t); + TrajectoryPtr trajectory = TrajectoryPtr::dynamicCast(t); + ARMARX_CHECK_EXPRESSION(trajectory); + size_t trajectoryDimensions = trajectory->dim(); + ARMARX_CHECK_EXPRESSION(trajectoryDimensions == targets.size()); + std::list<Eigen::VectorXd> pathPoints; + float timeStep = cfg->preSamplingStepMs / 1000; + Eigen::VectorXd maxVelocities = Eigen::VectorXd::Constant(trajectoryDimensions, cfg->maxVelocity); + Eigen::VectorXd maxAccelerations = Eigen::VectorXd::Constant(trajectoryDimensions, cfg->maxAcceleration); + for (const Trajectory::TrajData & element : *trajectory) + { + Eigen::VectorXd waypoint(trajectoryDimensions); + for (size_t i = 0; i < trajectoryDimensions; ++i) + { + waypoint(i) = element.getPosition(i); + } + pathPoints.emplace_back(waypoint); + } + + VirtualRobot::Path p(pathPoints, cfg->maxDeviation); + VirtualRobot::TimeOptimalTrajectory timeOptimalTraj(p, maxVelocities, maxAccelerations); + + + TrajectoryPtr newTraj = new Trajectory(); + + Ice::DoubleSeq newTimestamps; + double duration = timeOptimalTraj.getDuration(); + for (double t = 0.0; t < duration; t += timeStep) + { + newTimestamps.push_back(t); + } + newTimestamps.push_back(duration); + + for (size_t d = 0; d < trajectoryDimensions; d++) + { + // Ice::DoubleSeq position; + // for (double t = 0.0; t < duration; t += timeStep) + // { + // position.push_back(timeOptimalTraj.getPosition(t)[d]); + // } + // position.push_back(timeOptimalTraj.getPosition(duration)[d]); + // newTraj->addDimension(position, newTimestamps, trajectory->getDimensionName(d)); + + Ice::DoubleSeq derivs; + for (double t = 0.0; t < duration; t += timeStep) + { + derivs.clear(); + derivs.push_back(timeOptimalTraj.getPosition(t)[d]); + derivs.push_back(timeOptimalTraj.getVelocity(t)[d]); + newTraj->addDerivationsToDimension(d, t, derivs); + } + derivs.clear(); + derivs.push_back(timeOptimalTraj.getPosition(duration)[d]); + derivs.push_back(timeOptimalTraj.getVelocity(duration)[d]); + newTraj->addDerivationsToDimension(d, duration, derivs); + } + newTraj->setDimensionNames(trajectory->getDimensionNames()); + TIMING_END(TrajectoryOptimization); + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().trajectory = newTraj; + writeControlStruct(); + } + + void LVL1TrajectoryController::rtPreActivateController() + { + startTime = IceUtil::Time(); + } + +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1TrajectoryController.h b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1TrajectoryController.h new file mode 100644 index 0000000000000000000000000000000000000000..5b3ab3e477d29f738c79192ca81d96b2d259360a --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/LVL1Controllers/LVL1TrajectoryController.h @@ -0,0 +1,53 @@ +#ifndef ARMARX_LVL1TRAJECTORYCONTROLLER_H +#define ARMARX_LVL1TRAJECTORYCONTROLLER_H + +#include "LVL1Controller.h" +#include <VirtualRobot/Robot.h> +#include <RobotAPI/interface/units/RobotUnit/LVL1TrajectoryController.h> +#include <RobotAPI/libraries/core/PIDController.h> +#include <RobotAPI/libraries/core/Trajectory.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> + + +namespace armarx +{ + TYPEDEF_PTRS_HANDLE(LVL1HolonomicPlatformUnitVelocityPassThroughController); + class LVL1HolonomicPlatformUnitVelocityPassThroughControllerControlData + { + public: + TrajectoryPtr trajectory = TrajectoryPtr(new Trajectory()); + }; + + class LVL1TrajectoryController : + public LVL1ControllerWithTripleBuffer<LVL1HolonomicPlatformUnitVelocityPassThroughControllerControlData>, + public LVL1TrajectoryControllerInterface + { + public: + LVL1TrajectoryController(LVL1ControllerDescriptionProviderInterfacePtr prov, const LVL1ControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + // LVL1ControllerInterface interface + std::string getClassName(const Ice::Current&) const override; + + // LVL1Controller interface + void rtPreActivateController() override; + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override; + + // LVL1TrajectoryControllerInterface interface + void setTrajectory(const TrajectoryBasePtr& t, const Ice::Current&) override; + private: + // TrajectoryPtr trajectory; + IceUtil::Time startTime; + std::vector<PIDControllerPtr> PIDs; + std::vector<ControlTarget1DoFActuatorVelocity*> targets; + std::vector<const SensorValue1DoFActuatorPosition*> sensors; + LVL1TrajectoryControllerConfigPtr cfg; + virtual void onInitComponent() override {} + virtual void onConnectComponent() override {} + + + }; + +} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d3c98080aba74788b01c116447a758dd063dcd5 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp @@ -0,0 +1,2263 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::RobotUnit + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "RobotUnit.h" + +#include <algorithm> + +#include <VirtualRobot/XML/RobotIO.h> +#include <VirtualRobot/Nodes/ForceTorqueSensor.h> +#include <VirtualRobot/Robot.h> +#include <VirtualRobot/RobotNodeSet.h> + +#include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> +#include <ArmarXCore/core/system/DynamicLibrary.h> +#include <ArmarXCore/observers/variant/TimestampVariant.h> + +#include "Units/ForceTorqueSubUnit.h" +#include "Units/InertialMeasurementSubUnit.h" +#include "Units/KinematicSubUnit.h" +#include "Units/PlatformSubUnit.h" + + +namespace armarx +{ + namespace detail + { + template<class TimeT = std::chrono::microseconds> + struct TimerTag + { + //assume it is std::chrono + + using ClockT = std::chrono::high_resolution_clock; + const ClockT::time_point beg; + + TimerTag() : beg {ClockT::now()} {} + + TimeT stop() + { + return std::chrono::duration_cast<TimeT>(ClockT::now() - beg); + } + }; + + template<> + struct TimerTag<TimestampVariant> : TimerTag<> + { + TimestampVariant stop() + { + return {TimerTag<std::chrono::microseconds>::stop().count()}; + } + }; + + template<> + struct TimerTag<IceUtil::Time> : TimerTag<> + { + TimestampVariant stop() + { + return IceUtil::Time::microSeconds(TimerTag<std::chrono::microseconds>::stop().count()); + } + }; + + template <class Fun, class TimeT> + TimeT operator*(TimerTag<TimeT>&& t, Fun&& fn) + { + fn(); + return t.stop(); + } + + //for virtual time + + template<class TimeT = IceUtil::Time> + struct VirtualTimerTag; + + template<> + struct VirtualTimerTag<TimestampVariant> + { + const IceUtil::Time beg; + VirtualTimerTag() : beg {TimeUtil::GetTime()} {} + TimestampVariant stop() + { + return {TimeUtil::GetTime() - beg}; + } + }; + + template<> + struct VirtualTimerTag<IceUtil::Time> + { + const IceUtil::Time beg; + VirtualTimerTag() : beg {TimeUtil::GetTime()} {} + TimestampVariant stop() + { + return TimeUtil::GetTime() - beg; + } + }; + + template <class Fun, class TimeT> + TimeT operator*(VirtualTimerTag<TimeT>&& t, Fun&& fn) + { + fn(); + return t.stop(); + } + } +} +#define ARMARX_TIMER(...) ::armarx::detail::TimerTag<__VA_ARGS__>{} *[&] +#define ARMARX_VIRTUAL_TIMER(...) ::armarx::detail::VirtualTimerTag<__VA_ARGS__>{} *[&] + + +namespace std +{ + std::string to_string(armarx::RobotUnitState s) + { + switch (s) + { + case armarx::RobotUnitState::PreComponentInitialization: + return "PreComponentInitialization"; + case armarx::RobotUnitState::InitializingDevices: + return "InitializingDevices"; + case armarx::RobotUnitState::InitializingUnits: + return "InitializingUnits"; + case armarx::RobotUnitState::WaitingForRTThreadInitialization: + return "WaitingForRTThreadInitialization"; + case armarx::RobotUnitState::Running: + return "Running"; + case armarx::RobotUnitState::Exiting: + return "Exiting"; + } + throw std::invalid_argument {"Unknown state " + std::to_string(static_cast<std::size_t>(s))}; + } +} + +namespace armarx +{ + + namespace detail + { + std::function<std::string(const std::string&)> makeDebugObserverNameFixer(const std::string& prefix) + { + return + [prefix](const std::string & name) + { + std::string s = prefix + name; + std::replace(s.begin(), s.end(), '.', '_'); + std::replace(s.begin(), s.end(), ':', '_'); + return s; + }; + } + } + + + const std::string RobotUnit::rtThreadTimingsSensorDeviceName = "RTThreadTimings"; + + const std::set<RobotUnitState> RobotUnit::devicesReadyStates {RobotUnitState::InitializingUnits, RobotUnitState::WaitingForRTThreadInitialization, RobotUnitState::Running}; + RobotUnit::GuardType RobotUnit::getGuard() const + { + ARMARX_CHECK_EXPRESSION_W_HINT(std::this_thread::get_id() != rtThreadId, "Atempted to lock mutex in RT thread"); + return GuardType {dataMutex}; + } + + std::chrono::microseconds RobotUnit::MicroNow() + { + return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()); + } + + void RobotUnit::checkLVL1ControllerClassName(const std::string& className) const + { + if (!LVL1ControllerRegistry::has(className)) + { + std::stringstream ss; + ss << "Requested controller class '" << className << "' unknown! Known classes:" << LVL1ControllerRegistry::getKeys() + << " (If this class exists in a different lib then load it via loadLibFromPath(path) or loadLibFromPackage(package, lib))"; + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + } + + void RobotUnit::addControlDevice(const ControlDevicePtr& cd) + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingDevices, __FUNCTION__); + controlDevices.add(cd->getDeviceName(), cd); + ARMARX_INFO << "added ControlDevice " << cd->getDeviceName(); + } + + void RobotUnit::addSensorDevice(const SensorDevicePtr& sd) + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingDevices, __FUNCTION__); + if (!sd) + { + std::stringstream ss; + ss << "RobotUnit::addSensorDevice: SensorDevice is nullptr"; + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + if (!sd->getSensorValue()) + { + std::stringstream ss; + ss << "RobotUnit::addSensorDevice: SensorDevice " << sd->getDeviceName() << " has null SensorValue (this is not allowed)"; + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + if (sd->getDeviceName() == rtThreadTimingsSensorDeviceName) + { + if (!std::dynamic_pointer_cast<RTThreadTimingsSensorDevice>(sd)) + { + throw InvalidArgumentException + { + "You tried to add a SensorDevice with the name " + sd->getDeviceName() + " which does not derive from RTThreadTimingsSensorDevice. (Don't do this)" + }; + } + sensorDevices.add(sd->getDeviceName(), sd); //this checks if we already added such a device (do this before setting timingSensorDevice) + rtThreadTimingsSensorDevice = std::dynamic_pointer_cast<RTThreadTimingsSensorDevice>(sd); + } + else + { + sensorDevices.add(sd->getDeviceName(), sd); + } + ARMARX_INFO << "added SensorDevice " << sd->getDeviceName() << " (valuetype = " << sd->getSensorValueType() << ")"; + } + + RTThreadTimingsSensorDevicePtr RobotUnit::createRTThreadTimingSensorDevice() const + { + return std::make_shared<RTThreadTimingsSensorDeviceImpl<>>(rtThreadTimingsSensorDeviceName); + } + + void RobotUnit::finishDeviceInitialization() + { + auto beg = MicroNow(); + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingDevices, __FUNCTION__); + if (!sensorDevices.has(rtThreadTimingsSensorDeviceName)) + { + addSensorDevice(createRTThreadTimingSensorDevice()); + } + state = RobotUnitState::InitializingUnits; + //check added devices + ARMARX_DEBUG << "checking ControlDevices:"; + for (const ControlDevicePtr& controlDevice : controlDevices.values()) + { + ARMARX_CHECK_EXPRESSION(controlDevice); + ARMARX_DEBUG << "----" << controlDevice->getDeviceName(); + if (!controlDevice->hasLVL0Controller(ControlModes::EmergencyStop)) + { + std::stringstream s; + s << "ControlDevice " << controlDevice->getDeviceName() + << " has no LVL0Controller with ControlMode " << ControlModes::EmergencyStop + << " (to fix this, add a LVL0Controller with this ControlMode to the ControlDevice).\nAvailable controllers: " + << controlDevice->getControlModes(); + ARMARX_ERROR << "--------" << s.str(); + throw LogicError {s.str()}; + + } + if (!controlDevice->hasLVL0Controller(ControlModes::StopMovement)) + { + std::stringstream s; + s << "ControlDevice " << controlDevice->getDeviceName() + << " has no LVL0Controller with ControlMode \"" << ControlModes::StopMovement + << "\" (to fix this, add a LVL0Controller with this ControlMode to the ControlDevice) \nAvailable controllers: " + << controlDevice->getControlModes(); + ARMARX_ERROR << "--------" << s.str(); + throw LogicError {s.str()}; + } + } + ARMARX_DEBUG << "checking sensor devices:"; + for (const SensorDevicePtr& sensorDevice : sensorDevices.values()) + { + ARMARX_CHECK_EXPRESSION(sensorDevice); + ARMARX_DEBUG << "----" << sensorDevice->getDeviceName(); + if (!sensorDevice->getSensorValue()) + { + std::stringstream s; + s << "SensorDevice " << sensorDevice->getSensorValue() << " has null SensorValue"; + ARMARX_ERROR << "--------" << s.str(); + throw LogicError {s.str()}; + } + ARMARX_CHECK_EXPRESSION(sensorDevice); + } + + ARMARX_DEBUG << "initializing buffers:"; + { + //ctrl buffers + { + ARMARX_DEBUG << "----initializing controller buffers"; + controllersActivated.reinitAllBuffers(LVL0AndLVL1Controllers {getControllerBufferSize()}); + LVL0AndLVL1Controllers ctrlinitReq(getControllerBufferSize()); + for (std::size_t i = 0 ; i < getControllerBufferSize(); ++i) + { + ctrlinitReq.lvl0Controllers.at(i) = controlDevices.at(i)->rtGetLVL0StopMovementController(); + } + controllersRequested.reinitAllBuffers(ctrlinitReq); + controllersRequested.commitWrite();//to make sure the diry bit is activated (to trigger rt switch) + } + //sensor/control buffer + { + ARMARX_DEBUG << "----initializing SensorValue/ControlTartget buffer"; + SensorAndControl scinitRead; + SensorAndControl scinitHidden; + SensorAndControl scinitWrite; + scinitRead.sensors.reserve(getNumberOfSensorDevices()); + scinitHidden.sensors.reserve(getNumberOfSensorDevices()); + scinitWrite.sensors.reserve(getNumberOfSensorDevices()); + ARMARX_DEBUG << "--------cloning SensorValues"; + for (ConstSensorDevicePtr s : sensorDevices.values()) + { + ARMARX_DEBUG << "------------of device " << s->getDeviceName() << "(type = " << s->getSensorValueType() << ")"; + scinitRead.sensors.emplace_back(s->getSensorValue()->clone()); + scinitHidden.sensors.emplace_back(s->getSensorValue()->clone()); + scinitWrite.sensors.emplace_back(s->getSensorValue()->clone()); + } + scinitRead.control.reserve(getControllerBufferSize()); + scinitHidden.control.reserve(getControllerBufferSize()); + scinitWrite.control.reserve(getControllerBufferSize()); + ARMARX_DEBUG << "--------cloning ControlTargets"; + for (ConstControlDevicePtr c : controlDevices.values()) + { + ARMARX_DEBUG << "------------of device " << c->getDeviceName(); + scinitRead.control.emplace_back(); + scinitHidden.control.emplace_back(); + scinitWrite.control.emplace_back(); + auto& scinitReadCD = scinitRead.control.back(); + auto& scinitHiddenCD = scinitHidden.control.back(); + auto& scinitWriteCD = scinitWrite.control.back(); + scinitReadCD.reserve(c->getLVL0Controllers().size()); + scinitHiddenCD.reserve(c->getLVL0Controllers().size()); + scinitWriteCD.reserve(c->getLVL0Controllers().size()); + for (const LVL0Controller* lvl0 : c->getLVL0Controllers()) + { + ARMARX_DEBUG << "----------------mode " << lvl0->getControlMode() << "(type = " << lvl0->getControlTarget()->getControlTargetType() << ")"; + scinitReadCD.emplace_back(lvl0->getControlTarget()->clone()); + scinitHiddenCD.emplace_back(lvl0->getControlTarget()->clone()); + scinitWriteCD.emplace_back(lvl0->getControlTarget()->clone()); + } + } + sensorAndControl.reinitAllBuffers(std::move(scinitWrite), std::move(scinitHidden), std::move(scinitRead)); + } + } + ARMARX_DEBUG << "copying device ptrs to const ptr map"; + { + for (const auto& dev : controlDevices.values()) + { + controlDevicesConstPtr[dev->getDeviceName()] = dev; + } + for (const auto& dev : sensorDevices.values()) + { + sensorDevicesConstPtr[dev->getDeviceName()] = dev; + } + } + ARMARX_VERBOSE << "ControlDevices:\n" << controlDevices.keys(); + ARMARX_VERBOSE << "SensorDevices:\n" << sensorDevices.keys(); + ARMARX_INFO << "finishDeviceInitialization...done! " << (MicroNow() - beg).count() << " us"; + } + + void RobotUnit::initializeDefaultUnits() + { + auto beg = MicroNow(); + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + ARMARX_INFO << "initializing default units"; + initializeKinematicUnit(); + initializePlatformUnit(); + initializeForceTorqueUnit(); + initializeInertialMeasurementUnit(); + } + ARMARX_INFO << "initializing default units...done! " << (MicroNow() - beg).count() << " us"; + } + + void RobotUnit::initializeKinematicUnit() + { + using UnitT = KinematicSubUnit; + using IfaceT = KinematicUnitInterface; + + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + //check if unit is already added + if (getUnit(IfaceT::ice_staticId())) + { + return; + } + //add ctrl et al + std::map<std::string, UnitT::ActuatorData> devs; + for (const ControlDevicePtr& controlDevice : controlDevices.values()) + { + const auto& controlDeviceName = controlDevice->getDeviceName(); + if (!controlDevice->hasTag(ControlDeviceTags::CreateNoDefaultController)) + { + UnitT::ActuatorData ad; + ad.name = controlDeviceName; + ad.sensorDeviceIndex = sensorDevices.has(controlDeviceName) ? sensorDevices.index(controlDeviceName) : std::numeric_limits<std::size_t>::max(); + /// TODO use better kinematic unit controllers (posThroughVel + ramps) +#define initializeKinematicUnit_tmp_helper(mode, CtargT, ctrl) \ + { \ + const auto& controlMode = mode; \ + LVL1KinematicUnitPassThroughControllerPtr& pt = ctrl; \ + LVL0Controller* lvl0 = controlDevice->getLVL0Controller(controlMode); \ + if (lvl0 && lvl0->getControlTarget()->isA<CtargT>()) \ + { \ + LVL1KinematicUnitPassThroughControllerConfigPtr config = new LVL1KinematicUnitPassThroughControllerConfig; \ + config->controlMode=controlMode; \ + config->deviceName=controlDeviceName; \ + LVL1ControllerPtr lvl1 = createController( \ + "LVL1KinematicUnitPassThroughController", \ + "LVL1KU_PTCtrl_"+controlDeviceName+"_"+controlMode, \ + config \ + ); \ + pt = LVL1KinematicUnitPassThroughControllerPtr::dynamicCast(lvl1); \ + ARMARX_CHECK_EXPRESSION(pt); \ + } \ + } + initializeKinematicUnit_tmp_helper(ControlModes::Position1DoF, ControlTarget1DoFActuatorPosition, ad.ctrlPos) + initializeKinematicUnit_tmp_helper(ControlModes::Velocity1DoF, ControlTarget1DoFActuatorVelocity, ad.ctrlVel) + initializeKinematicUnit_tmp_helper(ControlModes::Torque1DoF , ControlTarget1DoFActuatorTorque , ad.ctrlTor) +#undef initializeKinematicUnit_tmp_helper + + + if (ad.ctrlPos || ad.ctrlTor || ad.ctrlVel) + { + devs[controlDeviceName] = std::move(ad); + } + } + } + ARMARX_CHECK_EXPRESSION_W_HINT(!devs.empty(), "no 1DoF ControlDevice found with matching SensorDevice"); + //add it + const std::string configName = getProperty<std::string>("KinematicUnitName"); + const std::string& configDomain = "ArmarX"; + const std::string confPre = configDomain + "." + configName + "."; + Ice::PropertiesPtr properties = getIceProperties()->clone(); + //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); + //fill properties + properties->setProperty(confPre + "RobotNodeSetName" , robotNodeSetName); + properties->setProperty(confPre + "RobotFileName" , robotFileName); + properties->setProperty(confPre + "RobotFileNameProject", robotProjectName); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties , configName, configDomain); + //fill devices (sensor + controller) + unit->setupData(getProperty<std::string>("RobotFileName").getValue(), robot->clone(robot->getName()), std::move(devs), this); + //add + addUnit(unit); + } + + void RobotUnit::initializePlatformUnit() + { + using UnitT = PlatformSubUnit; + using IfaceT = PlatformUnitInterface; + + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + //check if unit is already added + if (getUnit(IfaceT::ice_staticId())) + { + return; + } + //is there a platform dev? + if (getRobotPlatformName().empty()) + { + ARMARX_INFO << "no platform unit created since no platform name was given"; + return; + } + if (!controlDevices.has(getRobotPlatformName())) + { + ARMARX_WARNING << "no platform unit created since the platform control device with name '" << getRobotPlatformName() << "' was not found"; + return; + } + const ControlDevicePtr& controlDevice = controlDevices.at(robotPlatformName); + const SensorDevicePtr& sensorDevice = sensorDevices.at(robotPlatformName); + LVL0Controller* lvl0vel = controlDevice->getLVL0Controller(ControlModes::HolonomicPlatformVelocity); + ARMARX_CHECK_EXPRESSION(lvl0vel); + ARMARX_CHECK_EXPRESSION(sensorDevice->getSensorValue()->asA<SensorValueHolonomicPlatform>()); + //add it + const std::string configName = getProperty<std::string>("PlatformUnitName"); + const std::string& configDomain = "ArmarX"; + const std::string confPre = configDomain + "." + configName + "."; + Ice::PropertiesPtr properties = getIceProperties()->clone(); + //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); + //fill properties + properties->setProperty(confPre + "PlatformName", robotPlatformName); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties , configName, configDomain); + //config + LVL1HolonomicPlatformUnitVelocityPassThroughControllerConfigPtr config = new LVL1HolonomicPlatformUnitVelocityPassThroughControllerConfig; + config->initialVelocityX = 0; + config->initialVelocityY = 0; + config->initialVelocityRotation = 0; + config->platformName = robotPlatformName; + auto ctrl = LVL1HolonomicPlatformUnitVelocityPassThroughControllerPtr::dynamicCast( + createController("LVL1HolonomicPlatformUnitVelocityPassThroughController", configName + "_VelPTContoller", config) + ); + ARMARX_CHECK_EXPRESSION(ctrl); + unit->pt = ctrl; + unit->platformSensorIndex = sensorDevices.index(robotPlatformName); + //add + addUnit(unit); + } + + void RobotUnit::initializeForceTorqueUnit() + { + using UnitT = ForceTorqueSubUnit; + using IfaceT = ForceTorqueUnitInterface; + + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + //check if unit is already added + if (getUnit(IfaceT::ice_staticId())) + { + return; + } + //check if it is required + auto sensors = getRobot()->getSensors<VirtualRobot::ForceTorqueSensor>(); + std::vector<ForceTorqueSubUnit::DeviceData> ftdevs; + ftdevs.reserve(sensors.size()); + for (const auto& ft : sensors) + { + auto deviceName = ft->getName(); + if (sensorDevices.has(deviceName)) + { + const SensorDevicePtr& sensorDevice = sensorDevices.at(deviceName); + if (sensorDevice->getSensorValue()->isA<SensorValueForceTorque>()) + { + ForceTorqueSubUnit::DeviceData dat; + dat.sensorIndex = sensorDevices.index(deviceName); + dat.deviceName = std::move(deviceName); + dat.frame = ft->getRobotNode()->getName(); + ftdevs.emplace_back(std::move(dat)); + } + else + { + ARMARX_WARNING << "ForceTorqueSensorDevice " << deviceName << " has no SensorValueForceTorque"; + } + } + else + { + ARMARX_WARNING << "ForceTorqueSensor with name " << deviceName << " has no corresponding sensor device"; + } + } + if (ftdevs.empty()) + { + ARMARX_WARNING << "no force torque unit created since there are no force torque sensor devices"; + return; + } + //add it + const std::string configName = getProperty<std::string>("ForceTorqueUnitName"); + const std::string& configDomain = "ArmarX"; + const std::string confPre = configDomain + "." + configName + "."; + Ice::PropertiesPtr properties = getIceProperties()->clone(); + //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); + //fill properties + properties->setProperty(confPre + "AgentName", robot->getName()); + properties->setProperty(confPre + "ForceTorqueTopicName", getProperty<std::string>("ForceTorqueTopicName").getValue()); + ARMARX_INFO << "creating unit " << configName << " using these properties: " << properties->getPropertiesForPrefix(""); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties , configName, configDomain); + unit->devs = ftdevs; + //add + addUnit(unit); + } + + void RobotUnit::initializeInertialMeasurementUnit() + { + using UnitT = InertialMeasurementSubUnit; + using IfaceT = InertialMeasurementUnitInterface; + + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + //check if unit is already added + if (getUnit(IfaceT::ice_staticId())) + { + return; + } + //check if it is required + std::map<std::string, std::size_t> imudevs; + for (auto i : getIndices(sensorDevices.keys())) + { + const SensorDevicePtr& sensorDevice = sensorDevices.at(i); + if (sensorDevice->getSensorValue()->isA<SensorValueIMU>()) + { + imudevs[sensorDevice->getDeviceName()] = i; + } + } + if (imudevs.empty()) + { + ARMARX_IMPORTANT << "no inertial measurement unit created since there are no imu sensor devices"; + return; + } + //add it + const std::string configName = getProperty<std::string>("InertialMeasurementUnitName"); + const std::string& configDomain = "ArmarX"; + const std::string confPre = configDomain + "." + configName + "."; + Ice::PropertiesPtr properties = getIceProperties()->clone(); + //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); + //fill properties + properties->setProperty(confPre + "IMUTopicName", getProperty<std::string>("IMUTopicName").getValue()); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties , configName, configDomain); + unit->devs = imudevs; + //add + addUnit(unit); + } + + void RobotUnit::addUnit(ManagedIceObjectPtr unit) + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + getArmarXManager()->addObjectAsync(unit, ""); + units.emplace_back(std::move(unit)); + } + + void RobotUnit::finishUnitInitialization() + { + + auto beg = MicroNow(); + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + state = RobotUnitState::WaitingForRTThreadInitialization; + //start publisher + int pubtimestep = getProperty<std::size_t>("PublishPeriodMs"); + if (!pubtimestep) + { + pubtimestep = 1; + } + publishNewSensorDataTime = TimeUtil::GetTime(); + publisherTask = new PublisherTaskT([&] {publish();}, pubtimestep, false, getName() + "_PublisherTask"); + ARMARX_INFO << "starting publisher with timestep " << pubtimestep; + publisherTask->start(); + ARMARX_INFO << "finishUnitInitialization...done! " << (MicroNow() - beg).count() << " us"; + } + + void RobotUnit::initRTThread() + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::WaitingForRTThreadInitialization, __FUNCTION__); + rtThreadId = std::this_thread::get_id(); + state = RobotUnitState::Running; + } + + bool RobotUnit::rtSwitchControllerSetup() + { + rtThreadTimingsSensorDevice->rtMarkRtSwitchControllerSetupStart(); + if (!rtRequestedControllersChanged()) + { + rtThreadTimingsSensorDevice->rtMarkRtSwitchControllerSetupEnd(); + return false; + } + + //handle lvl1 + { + //"merge" + std::size_t n = rtGetControllerBufferSize(); + std::size_t idxAct = 0; + std::size_t idxReq = 0; + for (std::size_t i = 0; i < n; ++i) + { + //skip nullptrs in act + while (idxAct < n && !rtGetActivatedLVL1Controllers().at(idxAct)) + { + ++idxAct; + } + const LVL1ControllerPtr& req = idxReq < n ? rtGetRequestedLVL1Controllers().at(idxReq) : nullptr; //may be null + const LVL1ControllerPtr& act = idxAct < n ? rtGetActivatedLVL1Controllers().at(idxAct) : nullptr; //may be null if it is the last + std::size_t reqId = req ? req->rtGetId() : std::numeric_limits<std::size_t>::max(); + std::size_t actId = act ? act->rtGetId() : std::numeric_limits<std::size_t>::max(); + + if (reqId < actId) + { + //new ctrl + RtActivateLVL1Controller(req); + ++idxReq; + } + else if (reqId > actId) + { + RtDeactivateLVL1Controller(act); + ++idxAct; + } + else //if(reqId == actId) + { + //same ctrl or both null ctrl + ++idxReq; + ++idxAct; + } + if (idxAct >= n && !req) + { + break; + } + } + rtGetActivatedLVL1Controllers() = rtGetRequestedLVL1Controllers(); + } + + //handle lvl0 + { + for (std::size_t i = 0; i < rtGetControllerBufferSize(); ++i) + { + auto& controlDevice = controlDevices.at(i); + const auto requestedLVL0 = rtGetRequestedLVL0Controllers().at(i); + controlDevice->rtSetActiveLVL0Controller(requestedLVL0); + rtGetActivatedLVL0Controllers().at(i) = requestedLVL0; + } + } + rtWriteActivatedControllers(); + rtThreadTimingsSensorDevice->rtMarkRtSwitchControllerSetupEnd(); + return true; + } + + void RobotUnit::rtDeactivateAssignedLVL1Controller(std::size_t ctrlDevIndex, bool writeActivatedControlers) + { + std::size_t lvl1Index = std::numeric_limits<std::size_t>::max(); + for (std::size_t i = 0; i < rtGetControllerBufferSize(); ++i) + { + const LVL1ControllerPtr& lvl1 = rtGetActivatedLVL1Controllers().at(i); + if (lvl1 && lvl1->rtUsesControlDevice(ctrlDevIndex)) + { + //found corresponding lvl1 + lvl1Index = i; + break; + } + } + ARMARX_CHECK_EXPRESSION_W_HINT( + lvl1Index < rtGetControllerBufferSize(), + lvl1Index << " < " << rtGetControllerBufferSize() << + ": no lvl1 controller controls this device (name = " + << controlDevices.at(ctrlDevIndex)->getDeviceName() + << ", ControlMode = " << rtGetActivatedLVL0Controllers().at(ctrlDevIndex)->getControlMode() << ")!" << "\n" + << VAROUT(rtGetActivatedLVL0Controllers()) << "\n" + << VAROUT(rtGetActivatedLVL1Controllers()) + ); + const LVL1ControllerPtr& lvl1 = rtGetActivatedLVL1Controllers().at(lvl1Index); + RtDeactivateLVL1ControllerBecauseOfError(lvl1); + for (auto ctrlDevIdx : lvl1->rtGetControlDeviceUsedIndices()) + { + const ControlDevicePtr& controlDevice = controlDevices.at(ctrlDevIdx); + LVL0Controller* es = controlDevice->rtGetLVL0EmergencyStopController(); + controlDevice->rtSetActiveLVL0Controller(es); + rtGetActivatedLVL0Controllers().at(ctrlDevIdx) = es; + } + rtGetActivatedLVL1Controllers().at(lvl1Index) = nullptr; + + if (writeActivatedControlers) + { + ARMARX_IMPORTANT << "SWITCH CTRLS deac"; + rtWriteActivatedControllers(); + } + } + + void RobotUnit::rtRunLVL1Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + rtThreadTimingsSensorDevice->rtMarkRtRunLVL1ControllersStart(); + for (LVL1Controller* lvl1 : rtGetActivatedLVL1Controllers()) + { + try + { + if (lvl1) + { + lvl1->rtSwapBufferAndRun(sensorValuesTimestamp, timeSinceLastIteration); + } + } + catch (...) + { + ARMARX_INFO << "Lvl1 Controller " << lvl1->getInstanceName() << " threw an exception and is now deactivated: " << GetHandledExceptionString(); + RtSetLVL1ControllerActive(lvl1, 0, true); + } + } + rtThreadTimingsSensorDevice->rtMarkRtRunLVL1ControllersEnd(); + } + + void RobotUnit::rtRunLVL0Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + rtThreadTimingsSensorDevice->rtMarkRtRunLVL0ControllersStart(); + for (const ControlDevicePtr& device : this->controlDevices.values()) + { + device->rtRun(sensorValuesTimestamp, timeSinceLastIteration); + } + rtThreadTimingsSensorDevice->rtMarkRtRunLVL0ControllersEnd(); + } + + void RobotUnit::rtReadSensorDeviceValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + rtThreadTimingsSensorDevice->rtMarkRtReadSensorDeviceValuesStart(); + for (const SensorDevicePtr& device : this->sensorDevices.values()) + { + device->rtReadSensorValues(sensorValuesTimestamp, timeSinceLastIteration); + } + rtThreadTimingsSensorDevice->rtMarkRtReadSensorDeviceValuesEnd(); + } + + void RobotUnit::rtHandleInvalidTargets() + { + rtThreadTimingsSensorDevice->rtMarkRtHandleInvalidTargetsStart(); + bool oneIsInvalid = 0; + for (std::size_t i = 0; i < rtGetControllerBufferSize(); ++i) + { + if (!rtGetActivatedLVL0Controllers().at(i)->rtIsTargetVaild()) + { + rtDeactivateAssignedLVL1Controller(i, false); + oneIsInvalid = true; + } + } + if (oneIsInvalid) + { + ARMARX_VERBOSE << "ONE IS INVALID"; + rtWriteActivatedControllers(); + } + rtThreadTimingsSensorDevice->rtMarkRtHandleInvalidTargetsEnd(); + } + + void RobotUnit::rtResetAllTargets() + { + rtThreadTimingsSensorDevice->rtMarkRtResetAllTargetsStart(); + for (const ControlDevicePtr& controlDev : controlDevices.values()) + { + controlDev->rtGetActiveLVL0Controller()->rtResetTarget(); + } + rtThreadTimingsSensorDevice->rtMarkRtResetAllTargetsEnd(); + } + + ConstSensorDevicePtr RobotUnit::getSensorDevice(const std::string& sensorDeviceName) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return sensorDevices.at(sensorDeviceName, SensorDevice::NullPtr); + } + + ConstControlDevicePtr RobotUnit::getControlDevice(const std::string& deviceName) + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return controlDevices.at(deviceName, ControlDevice::NullPtr); + } + + ControlTargetBase* RobotUnit::getControlTarget(const std::string& deviceName, const std::string& controlMode) + { + static const std::string fname = __FUNCTION__; + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + if (std::this_thread::get_id() != initLVL1ThreadId) + { + throw std::logic_error + { + fname + ": Must be only called during instantiation of a LVL1 controller from the instantiated controler " + + "(Don't try to call this function outside of a LVL1Controller's constructor)" + }; + } + if (initLVL1UsedControlTargets.find(deviceName) != initLVL1UsedControlTargets.end()) + { + throw std::logic_error + { + fname + ": Must not request two ControlTargets for the same device. (got = " + + initLVL1UsedControlTargets.at(deviceName) + ", requested " + controlMode + ") " + + "(You can only have a ControlDevice in one ControlMode. " + + "To check all available ControlModes for this device, get the ControlDevice via getControlDevice(" + deviceName + ") and querry it)" + }; + } + // we should allow these modes for debugging reasons + there is no real reason to forbid them + //if (controlMode == ControlModes::EmergencyStop) + //{ + // throw std::invalid_argument {"RobotUnit::getControlTarget: Can't get target for the control mode " + ControlModes::EmergencyStop}; + //} + //if (controlMode == ControlModes::StopMovement) + //{ + // throw std::invalid_argument {"RobotUnit::getControlTarget: Can't get target for the control mode " + ControlModes::StopMovement}; + //} + ControlDevicePtr controlDevice = controlDevices.at(deviceName, ControlDevice::NullPtr); + if (!controlDevice) + { + return nullptr; + } + if (!controlDevice->hasLVL0Controller(controlMode)) + { + return nullptr; + } + //there is a device and a target as was requested: + initLVL1UsedControlTargets[deviceName] = controlMode; + return controlDevice->getLVL0Controller(controlMode)->getControlTarget(); + } + + LVL1ControllerInterfacePrx RobotUnit::createController(const std::string& className, const std::string& instanceName, const LVL1ControllerConfigPtr& config, const Ice::Current&) + { + //no lock required + return LVL1ControllerInterfacePrx::uncheckedCast(createController(className, instanceName, config)->getProxy(-1, true)); + } + + LVL1ControllerInterfacePrx RobotUnit::createControllerFromVariantConfig(const std::string& className, const std::string& instanceName, const StringVariantBaseMap& variants, const Ice::Current&) + { + //no lock required + checkLVL1ControllerClassName(className); + if (!LVL1ControllerRegistry::get(className)->hasRemoteConfiguration()) + { + std::stringstream ss; + ss << "Requested controller class '" << className << "' allows no remote configuration" << LVL1ControllerRegistry::getKeys() + << " (Implement 'static WidgetDescription::WidgetPtr " << className << "::GenerateConfigDescription()'" + << " and 'static LVL1ControllerConfigPtr " << className << "::GenerateConfigFromVariants(const StringVariantBaseMap&)' to allow remote configuration"; + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + return createController(className, instanceName, LVL1ControllerRegistry::get(className)->GenerateConfigFromVariants(variants), GlobalIceCurrent/*to select ice overload*/); + } + + LVL1ControllerPtr RobotUnit::createController(const std::string& className, const std::string& instanceName, const LVL1ControllerConfigPtr& config) + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + if (instanceName.empty()) + { + ARMARX_ERROR << "The instance name is empty! (give a unique name)"; + throw InvalidArgumentException {"The instance name is empty! (give a unique name)"}; + } + //check if we would be able to create the class + checkLVL1ControllerClassName(className); + auto& factory = LVL1ControllerRegistry::get(className); + + //check if the instance name is already in use + if (lvl1Controllers.count(instanceName)) + { + std::stringstream ss; + ss << "There already is a controller instance with the name '" << instanceName << "'. Use a different instance name instead." + << " Other used instance names are " << getLVL1ControllerNames(); + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + //create the controller + ARMARX_CHECK_EXPRESSION(initLVL1UsedControlTargets.empty());//in case someone broke things + initLVL1ThreadId = std::this_thread::get_id(); + ARMARX_ON_SCOPE_EXIT + { + initLVL1UsedControlTargets.clear(); + initLVL1ThreadId = std::thread::id{}; + }; + LVL1ControllerPtr lvl1 = factory->create(LVL1ControllerDescriptionProviderInterfacePtr::dynamicCast(RobotUnitPtr {this}), config, robot); + + std::vector<char> ctrlDeviceUsedBitmap(getNumberOfControlDevices(), false); + std::vector<std::size_t> ctrlDeviceUsedIndices; + ctrlDeviceUsedIndices.reserve(getNumberOfControlDevices()); + for (const auto& cd2targ : initLVL1UsedControlTargets) + { + auto idx = controlDevices.index(cd2targ.first); + ctrlDeviceUsedBitmap.at(idx) = true; + ctrlDeviceUsedIndices.emplace_back(idx); + } + InitLVL1Controler(lvl1, this, lvl1ControllerNextId++, initLVL1UsedControlTargets, std::move(ctrlDeviceUsedBitmap), std::move(ctrlDeviceUsedIndices)); + getArmarXManager()->addObjectAsync(lvl1, instanceName); + lvl1Controllers[instanceName] = lvl1; + listenerPrx->lvl1ControllerAdded(instanceName); + return lvl1; + } + + void RobotUnit::setActivateControllersRequest(std::set<LVL1ControllerPtr, LVL1Controller::IdComp>&& ctrls) + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + //erase nullptr + ctrls.erase(nullptr); + ARMARX_CHECK_EXPRESSION(ctrls.size() <= getControllerBufferSize()); + if (std::set<LVL1ControllerPtr, LVL1Controller::IdComp> {getRequestedLVL1Controllers().begin(), getRequestedLVL1Controllers().end()} == ctrls) + { + //redirty the flag to swap in the same set again + controllersRequested.commitWrite(); + return; + } + LVL0AndLVL1Controllers request {getControllerBufferSize()}; + std::size_t idx = 0; + for (const LVL1ControllerPtr& lvl1 : ctrls) + { + request.lvl1Controllers.at(idx++) = lvl1.get(); + //add lvl0 for this ctrl + for (const auto& cd2cm : lvl1->getControlDeviceUsedControlModeMap()) + { + std::size_t lvl0idx = controlDevices.index(cd2cm.first); + if (request.lvl0Controllers.at(lvl0idx)) + { + std::stringstream ss; + ss << "RobotUnit:setActivateControllersRequest controllers to activate are in conflict!\ncontrollers:"; + for (const auto& ctrl : ctrls) + { + ss << "\n" << ctrl->getInstanceName(); + } + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + request.lvl0Controllers.at(lvl0idx) = controlDevices.at(lvl0idx)->getLVL0Controller(cd2cm.second); + } + } + for (auto i : getIndices(request.lvl0Controllers)) + { + LVL0Controller*& lvl0 = request.lvl0Controllers.at(i); + if (!lvl0) + { + LVL0Controller* lvl0Old = getRequestedLVL0Controllers().at(i); + if (lvl0Old == controlDevices.at(i)->getLVL0StopMovementController()) + { + //don't replace this controller with emergency stop + lvl0 = lvl0Old; + } + else + { + //no one controls this device -> emergency stop + lvl0 = controlDevices.at(i)->getLVL0EmergencyStopController(); + } + } + } + writeRequestedControllers(std::move(request)); + ARMARX_INFO << "requested controllers:\n" << getRequestedLVL1ControllerNames(); + } + + void RobotUnit::activateController(const std::string& name, const Ice::Current&) + { + activateControllers({name}); + } + + void RobotUnit::activateControllers(const Ice::StringSeq& names, const Ice::Current&) + { + ARMARX_DEBUG << "requesting controller activation for:\n" << names; + if (names.empty()) + { + return; + } + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + auto ctrlsToActVec = getLVL1ControllersNotNull(names); //also checks if these controllers exist + ARMARX_DEBUG << "all requested controllers exist" << std::flush; + std::set<LVL1ControllerPtr, LVL1Controller::IdComp> ctrlsToAct {ctrlsToActVec.begin(), ctrlsToActVec.end()}; + ARMARX_CHECK_EXPRESSION(!ctrlsToAct.count(nullptr)); + std::set<LVL1ControllerPtr, LVL1Controller::IdComp> ctrls {getRequestedLVL1Controllers().begin(), getRequestedLVL1Controllers().end()}; + ctrls.erase(nullptr); + //check for conflict + std::vector<char> inuse; + //check requested controllers + { + auto r = LVL1Controller::AreNotInConflict(ctrlsToAct.begin(), ctrlsToAct.end()); + if (!r) + { + std::stringstream ss; + ss << "RobotUnit::activateControllers: requested controllers are in conflict!\ncontrollers:\n" << names; + ARMARX_ERROR << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + inuse = std::move(r.get()); + } + ARMARX_DEBUG << "all requested controllers are conflict free" << std::flush; + ARMARX_DEBUG << "inuse field \n" << inuse; + //add already active controllers if they are conflict free + { + if (ctrls.empty()) + { + ARMARX_DEBUG << "no already requested LVL1Controllers"; + } + for (const LVL1ControllerPtr& lvl1 : ctrls) + { + if (ctrlsToAct.count(lvl1)) + { + continue; + } + auto r = lvl1->isNotInConflictWith(inuse); + if (r) + { + ARMARX_DEBUG << "keeping already requested LVL1Controller '" << lvl1->getInstanceName() << "' in list of requested controllers"; + ctrlsToAct.insert(lvl1); + inuse = std::move(r.get()); + } + else + { + ARMARX_INFO << "removing already requested LVL1Controller '" << lvl1->getInstanceName() << "' from list of requested controllers"; + } + } + ARMARX_DEBUG << "inuse field \n" << inuse; + } + setActivateControllersRequest(std::move(ctrlsToAct)); + } + + void RobotUnit::deactivateController(const std::string& name, const Ice::Current&) + { + deactivateControllers(Ice::StringSeq {name}); + } + + void RobotUnit::deactivateControllers(const Ice::StringSeq& names, const Ice::Current&) + { + if (names.empty()) + { + return; + } + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + auto ctrlsDeacVec = getLVL1ControllersNotNull(names); //also checks if these controllers exist + std::set<LVL1ControllerPtr, LVL1Controller::IdComp> ctrls {getRequestedLVL1Controllers().begin(), getRequestedLVL1Controllers().end()}; + for (const auto& lvl1ToDeactivate : ctrlsDeacVec) + { + ctrls.erase(lvl1ToDeactivate); + } + setActivateControllersRequest(std::move(ctrls)); + } + + void RobotUnit::switchControllerSetup(const Ice::StringSeq& newSetup, const Ice::Current&) + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + auto ctrlsToActVec = getLVL1ControllersNotNull(newSetup); //also checks if these controllers exist + setActivateControllersRequest({ctrlsToActVec.begin(), ctrlsToActVec.end()}); + } + + void RobotUnit::publish() + { + auto begPublish = Now(); + static const int spamdelay = 1; + std::set<RobotUnitState> skipStates + { + RobotUnitState::PreComponentInitialization, + RobotUnitState::InitializingDevices, + RobotUnitState::InitializingUnits, + RobotUnitState::WaitingForRTThreadInitialization + }; + if (skipStates.count(state)) + { + ARMARX_DEBUG << deactivateSpam(spamdelay) << "skipping publishing in state " << state; + return; + } + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::Running, __FUNCTION__); + auto debugObserverBatchPrx = debugObserverPrx->ice_batchOneway(); + //swap buffers in + const bool haveActivatedControllersChanged = activatedControllersChanged(); + const bool haveSensorAndControlValsChanged = sensorAndControlBufferChanged(); + //setup datastructures + const auto& sensorAndControlBuffer = getSensorAndControlBuffer(); + const auto& activatedControllers = getActivatedControllers(); + const auto& requestedControllers = getRequestedControllers(); + + const auto timestamp = sensorAndControlBuffer.sensorValuesTimestamp; + + const auto numSensorDevices = getNumberOfSensorDevices(); + const auto numControlDevices = getNumberOfControlDevices(); + + auto listenerBatchPrx = listenerPrx->ice_batchOneway(); + const bool publishDebugObserver = !(publishIterationCount % debugObserverSkipIterations); + const bool debugObserverPublishControlTargetsThisIteration = debugObserverPublishControlTargets; + const bool debugObserverPublishSensorValuesThisIteration = debugObserverPublishSensorValues; + StringVariantBaseMap debugObserverMap; + + if (haveSensorAndControlValsChanged) + { + //update units + debugObserverMap["publishTimings_UnitUpdate"] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant) + { + ARMARX_DEBUG << deactivateSpam(spamdelay) << "updating units with new sensor values"; + publishNewSensorDataTime = TimeUtil::GetTime(); + for (ManagedIceObjectPtr& u : units) + { + RobotUnitSubUnitPtr rsu = RobotUnitSubUnitPtr::dynamicCast(u); + if (rsu && rsu->getObjectScheduler() && rsu->getProxy()) + { + debugObserverMap["publishTimings_UnitUpdate_" + rsu->getName()] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant){rsu->update(sensorAndControlBuffer, activatedControllers);}, + timestamp + }; + } + } + }, + timestamp + }; + //publish sensor updates + + debugObserverMap["publishTimings_SensorUpdates"] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant) + { + for (std::size_t sensidx = 0 ; sensidx < numSensorDevices; ++sensidx) + { + const SensorDevice& sensDev = *(sensorDevices.at(sensidx)); + const SensorValueBase& sensVal = *(sensorAndControlBuffer.sensors.at(sensidx)); + auto variants = sensVal.toVariants(timestamp); + + if (!variants.empty()) + { + if (debugObserverPublishSensorValuesThisIteration && publishDebugObserver) + { + transformMapKeys( + variants, debugObserverMap, + detail::makeDebugObserverNameFixer(sensDev.getDeviceName() + "_" + sensVal.getSensorValueType(true) + "_") + ); + } + SensorDeviceStatus status; + status.deviceName = sensDev.getDeviceName(); + status.sensorValue = std::move(variants); + status.timestampUSec = MicroNow().count(); + listenerBatchPrx->sensorDeviceStatusChanged(status); + } + } + }, + timestamp + }; + } + else + { + const double sensSecondsAgo = (TimeUtil::GetTime() - publishNewSensorDataTime).toSecondsDouble(); + if (sensSecondsAgo > 1) + { + ARMARX_WARNING << deactivateSpam(spamdelay) << "RobotUnit::publish: Last sensor value update is " << sensSecondsAgo << " seconds ago!"; + } + } + if (haveSensorAndControlValsChanged || haveActivatedControllersChanged) + { + debugObserverMap["publishTimings_ControlUpdates"] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant) + { + for (std::size_t ctrlidx = 0 ; ctrlidx < numControlDevices; ++ctrlidx) + { + const ControlDevice& ctrlDev = *(controlDevices.at(ctrlidx)); + + StringToStringVariantBaseMapMap variants; + for (const auto& ctrlVal : sensorAndControlBuffer.control.at(ctrlidx)) + { + const auto& controlMode = ctrlVal->getControlMode(); + variants[controlMode] = ctrlVal->toVariants(timestamp); + if ( + haveSensorAndControlValsChanged && + !variants[controlMode].empty() && + debugObserverPublishControlTargetsThisIteration && + publishDebugObserver + ) + { + transformMapKeys( + variants[controlMode], debugObserverMap, + detail::makeDebugObserverNameFixer(ctrlDev.getDeviceName() + "_" + controlMode + "_") + ); + } + } + ControlDeviceStatus status; + const auto activeLVL0 = activatedControllers.lvl0Controllers.at(ctrlidx); + status.activeControlMode = activeLVL0 ? activeLVL0->getControlMode() : std::string {"!!LVL0Controller is nullptr!!"}; + status.deviceName = ctrlDev.getDeviceName(); + ARMARX_CHECK_EXPRESSION(requestedControllers.lvl0Controllers.at(ctrlidx)); + status.requestedControlMode = requestedControllers.lvl0Controllers.at(ctrlidx)->getControlMode(); + status.controlTargetValues = std::move(variants); + status.timestampUSec = MicroNow().count(); + listenerBatchPrx->controlDeviceStatusChanged(status); + } + }, + timestamp + }; + } + //call publish hook + publish LVL1Controller changes + debugObserverMap["publishTimings_LVL1Updates"] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant) + { + for (const LVL1ControllerPtr& lvl1 : getMapValues(lvl1Controllers)) + { + debugObserverMap["publishTimings_LVL1Updates_" + lvl1->getInstanceName()] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant) + { + //run some hook for active (used for visu) + PublishLVL1Controller(lvl1, sensorAndControlBuffer, debugDrawerPrx, debugObserverBatchPrx); + if ( + GetLVL1ControllerStatusReportedActive(lvl1) != lvl1->isControllerActive() || + GetLVL1ControllerStatusReportedRequested(lvl1) != lvl1->isControllerRequested() + ) + { + UpdateLVL1ControllerStatusReported(lvl1); + listenerBatchPrx->lVl1ControllerStatusChanged(lvl1->getControllerStatus()); + } + }, + timestamp + }; + } + }, + timestamp + }; + + //report new class names + debugObserverMap["publishTimings_ClassNameUpdates"] = new TimedVariant + { + ARMARX_TIMER(TimestampVariant) + { + const auto classNames = LVL1ControllerRegistry::getKeys(); + if (lastReportedClasses.size() != classNames.size()) + { + for (const auto& name : classNames) + { + if (!lastReportedClasses.count(name)) + { + listenerBatchPrx->lvl1ControllerClassAdded(name); + lastReportedClasses.emplace(name); + } + } + } + }, + timestamp + }; + debugObserverMap["publishTimings_RobotUnitListenerFlush"] = new TimedVariant {ARMARX_TIMER(TimestampVariant){listenerBatchPrx->ice_flushBatchRequests();}, timestamp}; + + if (publishDebugObserver) + { + debugObserverMap["publishTimings_LastDebugObserverFlush"] = new TimedVariant {TimestampVariant{lastDebugObserverFlush.count()}, timestamp}; + debugObserverMap["publishTimings_LastPublishLoop"] = new TimedVariant {TimestampVariant{lastPublishLoop.count()}, timestamp}; + debugObserverBatchPrx->setDebugChannel(getName(), debugObserverMap); + lastDebugObserverFlush = ARMARX_TIMER() + { + debugObserverBatchPrx->ice_flushBatchRequests(); + }; + } + else + { + lastDebugObserverFlush = std::chrono::microseconds {0}; + } + + //warn if there are unset lvl0 controllers + { + const auto& lvl0s = getActivatedLVL0Controllers(); + if (std::any_of(lvl0s.begin(), lvl0s.end(), [](const LVL0Controller * lvl0) + { + return !lvl0; + })) + { + ARMARX_WARNING << "RobotUnit::publish: Some activated LVL0Controllers are reported to be nullptr! " + << "(did you forget to call rtSwitchControllerSetup()?)"; + } + } + ++publishIterationCount; + lastPublishLoop = MicroToNow(begPublish); + } + + void RobotUnit::removeAllUnits() + { + for (ManagedIceObjectPtr& unit : units) + { + checkRefCountIsOne(unit, unit->getName()); + getArmarXManager()->removeObjectBlocking(unit); + checkRefCountIsOne(unit, unit->getName()); + } + units.clear(); + } + + void RobotUnit::finishRunning() + { + if (state == RobotUnitState::Exiting) + { + ARMARX_ERROR << "RobotUnit::finishRunning called multiple times!"; + return; + } + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::Running, __FUNCTION__); + state = RobotUnitState::Exiting; + //const ptr maps + controlDevicesConstPtr.clear(); + sensorDevicesConstPtr.clear(); + //publisher + publisherTask->stop(); + while (publisherTask->isFunctionExecuting() || publisherTask->isRunning()) + { + ARMARX_FATAL << deactivateSpam(0.1) << "PUBLISHER TASK IS RUNNING EVEN AFTER IT WAS STOPPED!"; + } + checkRefCountIsOne(publisherTask, "publisherTask"); + publisherTask = nullptr; + //RT + joinRTThread(); + //units + removeAllUnits(); + //lvl1 + for (auto& n2lvl1 : lvl1Controllers) + { + LVL1ControllerPtr& lvl1 = n2lvl1.second; + getArmarXManager()->removeObjectBlocking(lvl1); + checkRefCountIsOne(lvl1, lvl1->getName()); + } + lvl1Controllers.clear(); + //clear devices + sensorDevices.clear(); + controlDevices.clear(); + //robot + if (robot.use_count() != 1) + { + ARMARX_WARNING << "The robot's smart pointer use count is " << robot.use_count(); + } + robot.reset(); + ARMARX_INFO << "RobotUnit finished running"; + } + + bool RobotUnit::loadLibFromPath(const std::string& path, const Ice::Current&) + { + std::string absPath; + if (ArmarXDataPath::getAbsolutePath(path, absPath)) + { + return loadLibFromAbsolutePath(absPath); + } + else + { + ARMARX_ERROR << "Could not find library " + path; + return false; + } + } + + bool RobotUnit::loadLibFromPackage(const std::string& package, const std::string& libname, const Ice::Current&) + { + CMakePackageFinder finder(package); + if (!finder.packageFound()) + { + ARMARX_ERROR << "Could not find package '" << package << "'"; + return false; + } + + for (auto libDirPath : Split(finder.getLibraryPaths(), ";")) + { + boost::filesystem::path fullPath = libDirPath; + fullPath /= "lib" + libname + "." + DynamicLibrary::GetSharedLibraryFileExtension(); + if (!boost::filesystem::exists(fullPath)) + { + fullPath = libDirPath; + fullPath /= libname; + if (!boost::filesystem::exists(fullPath)) + { + continue; + } + } + if (loadLibFromAbsolutePath(fullPath.string())) + { + return true; + } + } + ARMARX_ERROR << "Could not find library " << libname << " in package " << package; + return false; + } + + bool RobotUnit::loadLibFromAbsolutePath(const std::string& path) + { + auto guard = getGuard(); + if (loadedLibs.count(path)) + { + return true; + } + DynamicLibraryPtr lib(new DynamicLibrary()); + try + { + lib->load(path); + } + catch (...) + { + handleExceptions(); + return false; + } + + if (lib->isLibraryLoaded()) + { + loadedLibs[path] = lib; + } + else + { + ARMARX_ERROR << "Could not load lib " + path + ": " + lib->getErrorMessage(); + return false; + } + return true; + } + + Ice::StringSeq RobotUnit::getLVL1ControllerClassNames(const Ice::Current&) const + { + return LVL1ControllerRegistry::getKeys(); + } + + LVL1ControllerClassDescription RobotUnit::getLVL1ControllerClassDescription(const std::string& className, const Ice::Current&) const + { + while (state == RobotUnitState::PreComponentInitialization || state == RobotUnitState::InitializingDevices) + { + //this phase should only last short so busy waiting is ok + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + checkLVL1ControllerClassName(className); + LVL1ControllerClassDescription data; + data.className = className; + if (LVL1ControllerRegistry::get(className)->hasRemoteConfiguration()) + { + data.configDescription = LVL1ControllerRegistry::get(className)->GenerateConfigDescription(robot, controlDevicesConstPtr, sensorDevicesConstPtr); + } + return data; + } + + LVL1ControllerClassDescriptionSeq RobotUnit::getLVL1ControllerClassDescriptions(const Ice::Current&) const + { + while (state == RobotUnitState::PreComponentInitialization || state == RobotUnitState::InitializingDevices) + { + //this phase should only last short so busy waiting is ok + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + auto guard = getGuard(); + LVL1ControllerClassDescriptionSeq r; + r.reserve(LVL1ControllerRegistry::getKeys().size()); + for (const auto& key : LVL1ControllerRegistry::getKeys()) + { + r.emplace_back(getLVL1ControllerClassDescription(key)); + } + return r; + } + + Ice::StringSeq RobotUnit::getLVL1ControllerNames(const Ice::Current&) const + { + auto guard = getGuard(); + return getMapKeys(lvl1Controllers); + } + + Ice::StringSeq RobotUnit::getRequestedLVL1ControllerNames(const Ice::Current&) const + { + auto guard = getGuard(); + return GetNonNullNames(getRequestedLVL1Controllers()); + } + + Ice::StringSeq RobotUnit::getActivatedLVL1ControllerNames(const Ice::Current&) const + { + auto guard = getGuard(); + return GetNonNullNames(getActivatedLVL1Controllers()); + } + + LVL1ControllerInterfacePrx RobotUnit::getLVL1Controller(const std::string& name, const Ice::Current&) const + { + LVL1ControllerPtr ctrl; + { + auto guard = getGuard(); + auto it = lvl1Controllers.find(name); + if (it == lvl1Controllers.end()) + { + return nullptr; + } + ctrl = it->second; + } + return LVL1ControllerInterfacePrx::uncheckedCast(ctrl->getProxy(-1, true)); + } + + LVL1ControllerStatus RobotUnit::getLVL1ControllerStatus(const std::string& name, const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return getLVL1ControllerNotNull(name)->getControllerStatus(); + } + + LVL1ControllerStatusSeq RobotUnit::getLVL1ControllerStatuses(const Ice::Current&) const + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + LVL1ControllerStatusSeq r; + r.reserve(lvl1Controllers.size()); + for (const auto& lvl1 : lvl1Controllers) + { + r.emplace_back(lvl1.second->getControllerStatus()); + } + return r; + } + + LVL1ControllerDescription RobotUnit::getLVL1ControllerDescription(const std::string& name, const Ice::Current&) const + { + LVL1ControllerPtr lvl1; + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + lvl1 = getLVL1ControllerNotNull(name); + } + return lvl1->getControllerDescription(); + } + + LVL1ControllerDescriptionSeq RobotUnit::getLVL1ControllerDescriptions(const Ice::Current&) const + { + std::map<std::string, LVL1ControllerPtr> lvl1ControllersCopy; + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + lvl1ControllersCopy = lvl1Controllers; + } + LVL1ControllerDescriptionSeq r; + r.reserve(lvl1ControllersCopy.size()); + for (const auto& lvl1 : lvl1ControllersCopy) + { + r.emplace_back(lvl1.second->getControllerDescription()); + } + return r; + } + + LVL1ControllerDescriptionWithStatus RobotUnit::getLVL1ControllerDescriptionWithStatus(const std::string& name, const Ice::Current&) const + { + LVL1ControllerPtr lvl1; + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + lvl1 = getLVL1ControllerNotNull(name); + } + return lvl1->getControllerDescriptionWithStatus(); + } + + LVL1ControllerDescriptionWithStatusSeq RobotUnit::getLVL1ControllerDescriptionsWithStatuses(const Ice::Current&) const + { + std::map<std::string, LVL1ControllerPtr> lvl1ControllersCopy; + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + lvl1ControllersCopy = lvl1Controllers; + } + LVL1ControllerDescriptionWithStatusSeq r; + r.reserve(lvl1ControllersCopy.size()); + for (const auto& lvl1 : lvl1ControllersCopy) + { + r.emplace_back(lvl1.second->getControllerDescriptionWithStatus()); + } + return r; + } + + Ice::StringSeq RobotUnit::getControlDeviceNames(const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return controlDevices.keys(); + } + + ControlDeviceDescription armarx::RobotUnit::RobotUnit::getControlDeviceDescription(std::size_t idx) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + const ControlDevicePtr& controlDevice = controlDevices.at(idx); + ControlDeviceDescription data; + data.deviceName = controlDevice->getDeviceName(); + data.tags.assign(controlDevice->getTags().begin(), controlDevice->getTags().end()); + for (const auto& lvl0 : controlDevice->getLVL0Controllers()) + { + data.contolModeToTargetType[lvl0->getControlMode()] = lvl0->getControlTarget()->getControlTargetType(); + } + return data; + } + + ControlDeviceDescription RobotUnit::getControlDeviceDescription(const std::string& name, const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + if (!controlDevices.has(name)) + { + std::stringstream ss; + ss << "RobotUnit::getControlDeviceDescription: There is no ControlDevice '" << name + << "'. There are these ControlDevices: " << controlDevices.keys(); + throw InvalidArgumentException {ss.str()}; + } + return getControlDeviceDescription(controlDevices.index(name)); + } + + ControlDeviceDescriptionSeq RobotUnit::getControlDeviceDescriptions(const Ice::Current&) const + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + ControlDeviceDescriptionSeq r; + r.reserve(getNumberOfControlDevices()); + for (auto idx : getIndices(controlDevices.values())) + { + r.emplace_back(getControlDeviceDescription(idx)); + } + return r; + } + + ControlDeviceStatus armarx::RobotUnit::RobotUnit::getControlDeviceStatus(std::size_t idx) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + const ControlDevicePtr& controlDevice = controlDevices.at(idx); + ControlDeviceStatus status; + const auto activeLVL0 = getActivatedLVL0Controllers().at(idx); + status.activeControlMode = activeLVL0 ? activeLVL0->getControlMode() : std::string {"!!LVL0Controller is nullptr!!"}; + status.deviceName = controlDevice->getDeviceName(); + ARMARX_CHECK_EXPRESSION(getRequestedLVL0Controllers().at(idx)); + status.requestedControlMode = getRequestedLVL0Controllers().at(idx)->getControlMode(); + for (const auto& targ : getSensorAndControlBuffer().control.at(idx)) + { + status.controlTargetValues[targ->getControlMode()] = targ->toVariants(getSensorAndControlBuffer().sensorValuesTimestamp); + } + status.timestampUSec = MicroNow().count(); + return status; + } + + ControlDeviceStatus RobotUnit::getControlDeviceStatus(const std::string& name, const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + if (!controlDevices.has(name)) + { + std::stringstream ss; + ss << "RobotUnit::getControlDeviceStatus: There is no ControlDevice '" << name + << "'. There are these ControlDevices: " << controlDevices.keys(); + throw InvalidArgumentException {ss.str()}; + } + return getControlDeviceStatus(controlDevices.index(name)); + } + + ControlDeviceStatusSeq RobotUnit::getControlDeviceStatuses(const Ice::Current&) const + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + ControlDeviceStatusSeq r; + r.reserve(getNumberOfControlDevices()); + for (auto idx : getIndices(controlDevices.values())) + { + r.emplace_back(getControlDeviceStatus(idx)); + } + return r; + } + + Ice::StringSeq RobotUnit::getSensorDeviceNames(const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return sensorDevices.keys(); + } + + SensorDeviceDescription armarx::RobotUnit::RobotUnit::getSensorDeviceDescription(std::size_t idx) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + const SensorDevicePtr& sensorDevice = sensorDevices.at(idx); + SensorDeviceDescription data; + data.deviceName = sensorDevice->getDeviceName(); + data.tags.assign(sensorDevice->getTags().begin(), sensorDevice->getTags().end()); + data.sensorValueType = sensorDevice->getSensorValueType(); + return data; + } + + SensorDeviceDescription RobotUnit::getSensorDeviceDescription(const std::string& name, const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + if (!sensorDevices.has(name)) + { + std::stringstream ss; + ss << "RobotUnit::getSensorDeviceDescription: There is no SensorDevice '" << name + << "'. There are these SensorDevices: " << sensorDevices.keys(); + throw InvalidArgumentException {ss.str()}; + } + return getSensorDeviceDescription(sensorDevices.index(name)); + } + + SensorDeviceDescriptionSeq RobotUnit::getSensorDeviceDescriptions(const Ice::Current&) const + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + SensorDeviceDescriptionSeq r; + r.reserve(getNumberOfSensorDevices()); + for (auto idx : getIndices(sensorDevices.values())) + { + r.emplace_back(getSensorDeviceDescription(idx)); + } + return r; + } + + SensorDeviceStatus armarx::RobotUnit::RobotUnit::getSensorDeviceStatus(std::size_t idx) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + const SensorDevicePtr& sensorDevice = sensorDevices.at(idx); + SensorDeviceStatus status; + status.deviceName = sensorDevice->getDeviceName(); + status.sensorValue = getSensorAndControlBuffer().sensors.at(idx)->toVariants(getSensorAndControlBuffer().sensorValuesTimestamp); + status.timestampUSec = MicroNow().count(); + return status; + } + + SensorDeviceStatus RobotUnit::getSensorDeviceStatus(const std::string& name, const Ice::Current&) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + if (!sensorDevices.has(name)) + { + std::stringstream ss; + ss << "RobotUnit::getSensorDeviceStatus: There is no SensorDevice '" << name + << "'. There are these SensorDevices: " << sensorDevices.keys(); + throw InvalidArgumentException {ss.str()}; + } + return getSensorDeviceStatus(sensorDevices.index(name)); + } + + SensorDeviceStatusSeq RobotUnit::getSensorDeviceStatuses(const Ice::Current&) const + { + auto guard = getGuard(); + if (!areDevicesReady()) + { + return {}; + } + SensorDeviceStatusSeq r; + r.reserve(getNumberOfSensorDevices()); + for (auto idx : getIndices(sensorDevices.values())) + { + r.emplace_back(getSensorDeviceStatus(idx)); + } + return r; + } + + std::vector<armarx::LVL1ControllerPtr> RobotUnit::getLVL1ControllersNotNull(const std::vector<std::string>& names) const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + std::vector<LVL1ControllerPtr> ctrl; + ctrl.reserve(names.size()); + for (const auto& name : names) + { + auto it = lvl1Controllers.find(name); + if (it == lvl1Controllers.end()) + { + std::stringstream ss; + ss << "RobotUnit: there is no LVL1Controller with name '" << name + << "'. Existing LVL1Controllers are: " << getLVL1ControllerNames(); + throw InvalidArgumentException {ss.str()}; + } + if (!it->second) + { + std::stringstream ss; + ss << "RobotUnit: The LVL1Controller with name '" << name + << "'. Is a nullptr! This should never be the case (invariant)! \nMap:\n" << lvl1Controllers; + ARMARX_FATAL << ss.str(); + throw InvalidArgumentException {ss.str()}; + } + ctrl.emplace_back(it->second); + } + return ctrl; + } + LVL1ControllerPtr RobotUnit::getLVL1ControllerNotNull(const std::string& name) const + { + return getLVL1ControllersNotNull(std::vector<std::string> {name}).at(0); + } + + StringLVL1ControllerPrxDictionary RobotUnit::getAllLVL1Controllers(const Ice::Current&) const + { + std::map<std::string, LVL1ControllerPtr> lvl1ControllersCopy; + { + auto guard = getGuard(); + //copy to keep lock retention time low + lvl1ControllersCopy = lvl1Controllers; + } + StringLVL1ControllerPrxDictionary result; + for (const auto& pair : lvl1ControllersCopy) + { + result[pair.first] = LVL1ControllerInterfacePrx::uncheckedCast(pair.second->getProxy(-1, true)); + } + return result; + } + + Ice::ObjectProxySeq RobotUnit::getUnits(const Ice::Current&) const + { + std::vector<ManagedIceObjectPtr> unitsCopy; + { + auto guard = getGuard(); + //copy to keep lock retention time low + unitsCopy = units; + } + Ice::ObjectProxySeq r; + r.reserve(unitsCopy.size()); + for (const ManagedIceObjectPtr& unit : unitsCopy) + { + r.emplace_back(unit->getProxy(-1, true)); + } + return r; + } + + const ManagedIceObjectPtr& RobotUnit::getUnit(const std::string& staticIceId) const + { + auto guard = getGuard(); + for (const ManagedIceObjectPtr& unit : units) + { + if (unit->ice_isA(staticIceId)) + { + return unit; + } + } + return ManagedIceObject::NullPtr; + } + + Ice::ObjectPrx RobotUnit::getUnit(const std::string& staticIceId, const Ice::Current&) const + { + //no lock required + ManagedIceObjectPtr unit = getUnit(staticIceId); + if (unit) + { + return unit->getProxy(-1, true); + } + return nullptr; + } + + KinematicUnitInterfacePrx RobotUnit::getKinematicUnit(const Ice::Current&) const + { + //no lock required + return KinematicUnitInterfacePrx::uncheckedCast(getUnit(KinematicUnitInterface::ice_staticId(), GlobalIceCurrent)); + } + + ForceTorqueUnitInterfacePrx RobotUnit::getForceTorqueUnit(const Ice::Current&) const + { + //no lock required + return ForceTorqueUnitInterfacePrx::uncheckedCast(getUnit(ForceTorqueUnitInterface::ice_staticId(), GlobalIceCurrent)); + } + + InertialMeasurementUnitInterfacePrx RobotUnit::getInertialMeasurementUnit(const Ice::Current&) const + { + //no lock required + return InertialMeasurementUnitInterfacePrx::uncheckedCast(getUnit(InertialMeasurementUnitInterface::ice_staticId(), GlobalIceCurrent)); + } + + PlatformUnitInterfacePrx RobotUnit::getPlatformUnit(const Ice::Current&) const + { + //no lock required + return PlatformUnitInterfacePrx::uncheckedCast(getUnit(PlatformUnitInterface::ice_staticId(), GlobalIceCurrent)); + } + + const VirtualRobot::RobotPtr& RobotUnit::getRobot() const + { + auto guard = getGuard(); + throwIfStateIsNot({RobotUnitState::InitializingDevices, RobotUnitState::InitializingUnits, RobotUnitState::WaitingForRTThreadInitialization, RobotUnitState::Running}, __FUNCTION__); + ARMARX_CHECK_EXPRESSION(robot); + return robot; + } + + std::string RobotUnit::getRobotName() const + { + auto guard = getGuard(); + throwIfStateIsNot({RobotUnitState::InitializingDevices, RobotUnitState::InitializingUnits, RobotUnitState::WaitingForRTThreadInitialization, RobotUnitState::Running}, __FUNCTION__); + return getRobot()->getName(); + } + + std::size_t RobotUnit::getNumberOfControlDevices() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return controlDevices.size(); + } + + std::size_t RobotUnit::getNumberOfSensorDevices() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return sensorDevices.size(); + } + + const DebugDrawerInterfacePrx& RobotUnit::getDebugDrawerProxy() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return debugDrawerPrx; + } + + std::string RobotUnit::getListenerTopicName(const Ice::Current&) const + { + auto guard = getGuard(); + return robotUnitListenerTopicName; + } + + const RobotUnitListenerPrx& RobotUnit::getListenerProxy() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return listenerPrx; + } + + PropertyDefinitionsPtr RobotUnit::createPropertyDefinitions() + { + return PropertyDefinitionsPtr(new RobotUnitPropertyDefinitions(getConfigIdentifier())); + } + + void RobotUnit::icePropertiesUpdated(const std::set<std::string>& changedProperties) + { + + if (changedProperties.count("DebugObserverPublishSensorValues")) + { + debugObserverPublishSensorValues = getProperty<bool>("DebugObserverPublishSensorValues").getValue(); + } + if (changedProperties.count("DebugObserverPublishControlTargets")) + { + debugObserverPublishControlTargets = getProperty<bool>("DebugObserverPublishControlTargets").getValue(); + } + if (changedProperties.count("DebugObserverPrintEveryNthIterations")) + { + debugObserverSkipIterations = getProperty<std::uint64_t>("DebugObserverPrintEveryNthIterations").getValue(); + } + } + + void RobotUnit::onInitComponent() + { + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::PreComponentInitialization, __FUNCTION__); + robotUnitListenerTopicName = getProperty<std::string>("RobotUnitListenerTopicName").getValue(); + offeringTopic(getListenerTopicName()); + offeringTopic(getProperty<std::string>("DebugDrawerUpdatesTopicName").getValue()); + + debugObserverPublishSensorValues = getProperty<bool>("DebugObserverPublishSensorValues").getValue(); + debugObserverPublishControlTargets = getProperty<bool>("DebugObserverPublishControlTargets").getValue(); + + //load robot + { + robotNodeSetName = getProperty<std::string>("RobotNodeSetName").getValue(); + robotProjectName = getProperty<std::string>("RobotFileNameProject").getValue(); + robotFileName = getProperty<std::string>("RobotFileName").getValue(); + robotPlatformName = getProperty<std::string>("PlatformName").getValue(); + debugObserverSkipIterations = getProperty<std::uint64_t>("DebugObserverPrintEveryNthIterations").getValue(); + if (debugObserverSkipIterations < 1) + { + debugObserverSkipIterations = 1; + } + Ice::StringSeq includePaths; + if (!robotProjectName.empty()) + { + CMakePackageFinder finder(robotProjectName); + Ice::StringSeq projectIncludePaths; + auto pathsString = finder.getDataDir(); + boost::split(projectIncludePaths, + pathsString, + boost::is_any_of(";,"), + boost::token_compress_on); + includePaths.insert(includePaths.end(), projectIncludePaths.begin(), projectIncludePaths.end()); + } + if (!ArmarXDataPath::getAbsolutePath(robotFileName, robotFileName, includePaths)) + { + throw UserException("Could not find robot file " + robotFileName); + } + // read the robot + try + { + robot = VirtualRobot::RobotIO::loadRobot(robotFileName, VirtualRobot::RobotIO::eFull); + } + catch (VirtualRobot::VirtualRobotException& e) + { + throw UserException(e.what()); + } + ARMARX_INFO << "Loaded robot:" + << "\n\tProject : " << robotProjectName + << "\n\tRobot file : " << robotFileName + << "\n\tRobotNodeSet : " << robotNodeSetName + << "\n\tNodeNames : " << robot->getRobotNodeSet(robotNodeSetName)->getNodeNames(); + } + ARMARX_CHECK_EXPRESSION(robot); + state = RobotUnitState::InitializingDevices; + } + onInitRobotUnit(); + } + + void RobotUnit::onConnectComponent() + { + listenerPrx = getTopic<RobotUnitListenerPrx>(getListenerTopicName()); + debugDrawerPrx = getTopic<DebugDrawerInterfacePrx>(getProperty<std::string>("DebugDrawerUpdatesTopicName").getValue()); + debugObserverPrx = getTopic<DebugObserverInterfacePrx>(getProperty<std::string>("DebugObserverTopicName").getValue()); + onConnectRobotUnit(); + } + + void RobotUnit::onDisconnectComponent() + { + onDisconnectRobotUnit(); + } + + void RobotUnit::onExitComponent() + { + onExitRobotUnit(); + finishRunning(); + } + + bool RobotUnit::rtRequestedControllersChanged() const + { + return controllersRequested.updateReadBuffer(); + } + + const LVL0AndLVL1Controllers& RobotUnit::rtGetRequestedControllers() const + { + return controllersRequested.getReadBuffer(); + } + + const std::vector<armarx::LVL0Controller*>& RobotUnit::rtGetRequestedLVL0Controllers() const + { + return rtGetRequestedControllers().lvl0Controllers; + } + + const std::vector<armarx::LVL1Controller*>& RobotUnit::rtGetRequestedLVL1Controllers() const + { + return rtGetRequestedControllers().lvl1Controllers; + } + + void RobotUnit::rtWriteActivatedControllers() + { + controllersActivated.commitWrite(); + } + + LVL0AndLVL1Controllers& RobotUnit::rtGetActivatedControllers() + { + return controllersActivated.getWriteBuffer(); + } + + std::vector<armarx::LVL0Controller*>& RobotUnit::rtGetActivatedLVL0Controllers() + { + return rtGetActivatedControllers().lvl0Controllers; + } + + std::vector<armarx::LVL1Controller*>& RobotUnit::rtGetActivatedLVL1Controllers() + { + return rtGetActivatedControllers().lvl1Controllers; + } + + void RobotUnit::rtUpdateSensorAndControlBuffer(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + rtThreadTimingsSensorDevice->rtMarkRtUpdateSensorAndControlBufferStart(); + SensorAndControl& sc = sensorAndControl.getWriteBuffer(); + sc.sensorValuesTimestamp = sensorValuesTimestamp; + sc.timeSinceLastIteration = timeSinceLastIteration; + for (std::size_t sensIdx = 0; sensIdx < rtGetNumberOfSensorDevices(); ++sensIdx) + { + sensorDevices.at(sensIdx)->getSensorValue()->copyTo(sc.sensors.at(sensIdx)); + } + for (std::size_t ctrlIdx = 0; ctrlIdx < controlDevices.size(); ++ctrlIdx) + { + ControlDevice& controlDevice = *controlDevices.at(ctrlIdx); + auto& tv = sc.control.at(ctrlIdx); + for (std::size_t targIdx = 0; targIdx < controlDevice.rtGetLVL0Controllers().size(); ++targIdx) + { + LVL0Controller& lvl0 = *controlDevice.rtGetLVL0Controllers().at(targIdx); + lvl0.getControlTarget()->copyTo(tv.at(targIdx)); + } + } + sensorAndControl.commitWrite(); + rtThreadTimingsSensorDevice->rtMarkRtUpdateSensorAndControlBufferEnd(); + } + + void RobotUnit::writeRequestedControllers(LVL0AndLVL1Controllers &&setOfControllers) + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + //check lvl1 + const auto& lvl1s = setOfControllers.lvl1Controllers; + std::set<LVL1ControllerPtr> lvl1set {lvl1s.begin(), lvl1s.end()}; + lvl1set.erase(nullptr); + //# lvl1 + const std::size_t nlvl1s = std::count_if(lvl1s.begin(), lvl1s.end(), [](const LVL1ControllerPtr & p) + { + return p; + }); + + ARMARX_CHECK_EXPRESSION(nlvl1s == lvl1set.size()); + ARMARX_CHECK_EXPRESSION(lvl1s.size() == getControllerBufferSize()); + //first nlvl1s not null + ARMARX_CHECK_EXPRESSION(std::all_of(lvl1s.begin(), lvl1s.begin() + nlvl1s, [](LVL1Controller * p) + { + return p; + })); + //last getNumberOfControlDevices()-nlvl1s null + ARMARX_CHECK_EXPRESSION(std::all_of(lvl1s.begin() + nlvl1s, lvl1s.end(), [](LVL1Controller * p) + { + return !p; + })); + //conflict free and sorted + ARMARX_CHECK_EXPRESSION(std::is_sorted(lvl1s.begin(), lvl1s.end(), LVL1Controller::IdComp {})); + ARMARX_CHECK_EXPRESSION(LVL1Controller::AreNotInConflict(lvl1s.begin(), lvl1s.begin() + nlvl1s)); + + //check lvl0 + const auto& lvl0s = setOfControllers.lvl0Controllers; + ARMARX_CHECK_EXPRESSION(lvl0s.size() == getControllerBufferSize()); + ARMARX_CHECK_EXPRESSION(std::all_of(lvl0s.begin(), lvl0s.end(), [](LVL0Controller * p) + { + return p; + })); + + //set requested state + for (const auto& name2lvl1 : lvl1Controllers) + { + const LVL1ControllerPtr& lvl1 = name2lvl1.second; + SetLVL1ControllerRequested(lvl1, lvl1set.count(lvl1)); + } + + controllersRequested.getWriteBuffer() = std::move(setOfControllers); + controllersRequested.commitWrite(); + } + + const LVL0AndLVL1Controllers& RobotUnit::getRequestedControllers() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return controllersRequested.getWriteBuffer(); + } + + const std::vector<armarx::LVL0Controller*>& RobotUnit::getRequestedLVL0Controllers() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return getRequestedControllers().lvl0Controllers; + } + + const std::vector<armarx::LVL1Controller*>& RobotUnit::getRequestedLVL1Controllers() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return getRequestedControllers().lvl1Controllers; + } + + bool RobotUnit::activatedControllersChanged() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return controllersActivated.updateReadBuffer(); + } + + const LVL0AndLVL1Controllers& RobotUnit::getActivatedControllers() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return controllersActivated.getReadBuffer(); + } + + const std::vector<armarx::LVL0Controller*>& RobotUnit::getActivatedLVL0Controllers() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return getActivatedControllers().lvl0Controllers; + } + + const std::vector<armarx::LVL1Controller*>& RobotUnit::getActivatedLVL1Controllers() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return getActivatedControllers().lvl1Controllers; + } + + bool RobotUnit::sensorAndControlBufferChanged() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return sensorAndControl.updateReadBuffer(); + } + + const SensorAndControl& RobotUnit::getSensorAndControlBuffer() const + { + auto guard = getGuard(); + throwIfDevicesNotReady(__FUNCTION__); + return sensorAndControl.getReadBuffer(); + } + + std::ostream& operator<<(std::ostream& o, RobotUnitState s) + { + o << std::to_string(s); + return o; + } +} diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnit.h b/source/RobotAPI/components/units/RobotUnit/RobotUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..c545dea662726e58738dfe64cdebd8cab40e6ca3 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnit.h @@ -0,0 +1,665 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::RobotUnit + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_UNIT_RobotAPI_RobotUnit_H +#define _ARMARX_UNIT_RobotAPI_RobotUnit_H + +#include <thread> +#include <atomic> +#include <chrono> +#include <mutex> +#include <unordered_map> +#include <memory> + +#include <ArmarXCore/core/util/OnScopeExit.h> +#include <ArmarXCore/core/Component.h> +#include <ArmarXCore/core/ArmarXManager.h> +#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/util/algorithm.h> +#include <ArmarXCore/core/util/TripleBuffer.h> +#include <ArmarXCore/core/services/tasks/PeriodicTask.h> +#include <ArmarXCore/observers/DebugObserver.h> +#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> + +#include "LVL0Controllers/LVL0Controller.h" +#include "LVL1Controllers/LVL1Controller.h" +#include "Devices/ControlDevice.h" +#include "Devices/SensorDevice.h" +#include "Devices/RTThreadTimingsSensorDevice.h" +#include "util.h" +#include "structs.h" + +namespace armarx +{ + TYPEDEF_PTRS_HANDLE(RobotSubUnitUpdateInterface); + class RobotSubUnitUpdateInterface: virtual public ManagedIceObject {}; + + TYPEDEF_PTRS_HANDLE(RobotUnit); + + class DynamicLibrary; + typedef boost::shared_ptr<DynamicLibrary> DynamicLibraryPtr; + /** + * @class RobotUnitPropertyDefinitions + * @brief + */ + class RobotUnitPropertyDefinitions: + public armarx::ComponentPropertyDefinitions + { + public: + RobotUnitPropertyDefinitions(std::string prefix): + armarx::ComponentPropertyDefinitions(prefix) + { + defineOptionalProperty<std::string>("RobotUnitListenerTopicName", "RobotUnitListenerTopic", "The topic receiving events for RobotUnitListener"); + defineOptionalProperty<std::string>("DebugDrawerUpdatesTopicName", "DebugDrawerUpdates", "The topic receiving events for debug drawing"); + defineOptionalProperty<std::size_t>("PublishPeriodMs", 10, "Milliseconds between each publish"); + + defineRequiredProperty<std::string>("RobotFileName", "Robot file name, e.g. robot_model.xml"); + defineOptionalProperty<std::string>("RobotFileNameProject", "", "Project in which the robot filename is located (if robot is loaded from an external project)"); + + defineOptionalProperty<std::string>("KinematicUnitName", "KinematicUnit", "The name of the created kinematic unit"); + defineOptionalProperty<std::string>("RobotNodeSetName", "Robot", "Robot node set name as defined in robot xml file, e.g. 'LeftArm'"); + + defineOptionalProperty<std::string>("PlatformUnitName", "PlatformUnit", "The name of the created platform unit"); + defineOptionalProperty<std::string>("PlatformName", "Platform", "Name of the platform (will publish values on PlatformName + 'State')"); + + defineOptionalProperty<std::string>("ForceTorqueUnitName", "ForceTorqueUnit", "The name of the created force troque unit (empty = default)"); + defineOptionalProperty<std::string>("ForceTorqueTopicName", "ForceTorqueValues", "Name of the topic on which the force torque sensor values are provided"); + + defineOptionalProperty<std::string>("InertialMeasurementUnitName", "InertialMeasurementUnit", "The name of the created inertial measurement unit (empty = default)"); + defineOptionalProperty<std::string>("IMUTopicName", "IMUValues", "Name of the IMU Topic."); + + defineOptionalProperty<bool>("DebugObserverPublishSensorValues", true, "Whether sensor values are send to the debug observer topic", PropertyDefinitionBase::eModifiable); + defineOptionalProperty<bool>("DebugObserverPublishControlTargets", true, "Whether control targets are send to the debug observer topic", PropertyDefinitionBase::eModifiable); + defineOptionalProperty<std::string>("DebugObserverTopicName", "DebugObserver", "The topic where updates are send to"); + defineOptionalProperty<std::uint64_t>("DebugObserverPrintEveryNthIterations", 1, "Only every nth iteration data is send to the debug observer", PropertyDefinitionBase::eModifiable); + } + }; + + /// @brief The current state of the multi step initialization of a RobotUnit + enum class RobotUnitState : std::size_t + { + PreComponentInitialization, + InitializingDevices, + //ok to use devices/create controllers + InitializingUnits, + WaitingForRTThreadInitialization, + Running, + Exiting + }; +} +namespace std +{ + std::string to_string(armarx::RobotUnitState s); +} + +namespace armarx +{ + std::ostream& operator<<(std::ostream& o, RobotUnitState s); + + /** + * @brief This class is a friend of LVL1Controller. + * + * It encapsulates all access to privates of a LVL1Controller + * to reduce the amount of code which could break invariants + * in LVL1Controller and to collect all such code in one place + * to make this problem more manageable. + * + * Also the private functions of LVL1Controller should not be made public to prevent + * other classes (e.g. KinematicSubUnit) or derived LVL1Controllers + * from accessing them + * + */ + class RobotUnitLVL1ControllerAccess + { + protected: + static void RtActivateLVL1Controller(const LVL1ControllerPtr& lvl1) + { + lvl1->rtActivateController(); + } + static void RtDeactivateLVL1Controller(const LVL1ControllerPtr& lvl1) + { + lvl1->rtDeactivateController(); + } + static void RtDeactivateLVL1ControllerBecauseOfError(const LVL1ControllerPtr& lvl1) + { + lvl1->rtDeactivateControllerBecauseOfError(); + } + + static void RtSetLVL1ControllerActive(const LVL1ControllerPtr& lvl1, bool activate, bool deactivateBecauseOfError = false) + { + if (activate) + { + RtActivateLVL1Controller(lvl1); + } + else + { + if (deactivateBecauseOfError) + { + RtDeactivateLVL1ControllerBecauseOfError(lvl1); + } + else + { + RtDeactivateLVL1Controller(lvl1); + } + } + } + static void InitLVL1Controler( + LVL1ControllerPtr lvl1, + RobotUnit* ru, + std::size_t ctrlId, + StringStringDictionary ctrlDeviceControlModeMap, + std::vector<char> ctrlDeviceUsedBitmap, + std::vector<std::size_t> ctrlDeviceUsedIndices + ) + { + lvl1->robotUnitInit(ru, ctrlId, std::move(ctrlDeviceControlModeMap), std::move(ctrlDeviceUsedBitmap), std::move(ctrlDeviceUsedIndices)); + } + static void PublishLVL1Controller( + LVL1ControllerPtr lvl1, + const SensorAndControl& sac, + const DebugDrawerInterfacePrx& draw, + const DebugObserverInterfacePrx& observer + ) + { + lvl1 ->publish(sac, draw, observer); + } + bool GetLVL1ControllerStatusReportedActive(LVL1ControllerPtr lvl1) + { + return lvl1->statusReportedActive; + } + bool GetLVL1ControllerStatusReportedRequested(LVL1ControllerPtr lvl1) + { + return lvl1->statusReportedRequested; + } + void UpdateLVL1ControllerStatusReported(LVL1ControllerPtr lvl1) + { + lvl1->statusReportedActive = lvl1->isActive; + lvl1->statusReportedRequested = lvl1->isRequested; + } + void SetLVL1ControllerRequested(LVL1ControllerPtr lvl1, bool requested) + { + lvl1->isRequested = requested; + } + }; + + class RobotUnit : + virtual public Component, + virtual public RobotUnitInterface, + virtual public LVL1ControllerDescriptionProviderInterface, + virtual public RobotUnitLVL1ControllerAccess + { + protected: + using PublisherTaskT = SimplePeriodicTask<std::function<void(void)>>; + public: + using MutexType = std::recursive_mutex; + using GuardType = std::unique_lock<MutexType>; + //using GuardType = messaging_unique_lock<MutexType>; + using ClockT = std::chrono::high_resolution_clock; + using TimePointT = std::chrono::high_resolution_clock::time_point; + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // data + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + //devices + private: + /// data may only be added during and oly be used after State::InitializingDevices + KeyValueVector<std::string, ControlDevicePtr> controlDevices; + /// data may only be added during and oly be used after State::InitializingDevices + KeyValueVector<std::string, SensorDevicePtr > sensorDevices; + + std::map<std::string, ConstControlDevicePtr> controlDevicesConstPtr; + std::map<std::string, ConstSensorDevicePtr> sensorDevicesConstPtr; + + RTThreadTimingsSensorDevicePtr rtThreadTimingsSensorDevice; + static const std::string rtThreadTimingsSensorDeviceName; + //triple buffers + private: + /// @brief Controllers the RT thread is supposed to activate (direction nonRT -> RT) (some lvl1 controllers may be null) + /// data may only be used after State::InitializingDevices + /// all LVL1Controller have to be sorted by id (null controllers are at the end) + WriteBufferedTripleBuffer<LVL0AndLVL1Controllers> controllersRequested; + /// @brief Controllers the RT thread is currently running (direction RT -> nonRT) (some lvl1 controllers may be null) + /// data may only be used after State::InitializingDevices + /// all LVL1Controller are sorted by id (null controllers may be anywhere) + WriteBufferedTripleBuffer<LVL0AndLVL1Controllers> controllersActivated; + /// @brief Transfers the current sensor values and control targets (direction RT -> nonRT) + /// data may only be used after State::InitializingDevices + TripleBuffer<SensorAndControl> sensorAndControl; + + //(instantiating) lvl1 controllers + private: + /// data may only be used after State::InitializingDevices + std::thread::id initLVL1ThreadId; + /// data may only be used after State::InitializingDevices + std::map<std::string, std::string> initLVL1UsedControlTargets; + /// @brief Holds all currently loaded LVL1 controllers (index: [instancename])(May not be accessed in rt.) + std::map<std::string, LVL1ControllerPtr> lvl1Controllers; + std::size_t lvl1ControllerNextId {0}; + + //listeners + private: + std::string robotUnitListenerTopicName; + RobotUnitListenerPrx listenerPrx; + DebugDrawerInterfacePrx debugDrawerPrx; + + //publishing + private: + std::uint64_t publishIterationCount {0}; + + IceUtil::Time publishNewSensorDataTime; + PublisherTaskT::pointer_type publisherTask; + std::set<std::string> lastReportedClasses; + + std::atomic_bool debugObserverPublishSensorValues; + std::atomic_bool debugObserverPublishControlTargets; + std::atomic<std::uint64_t> debugObserverSkipIterations; + + std::chrono::microseconds lastDebugObserverFlush; + std::chrono::microseconds lastPublishLoop; + DebugObserverInterfacePrx debugObserverPrx; + + + //other + private: + /// @brief The RobotUnit's state + std::atomic<RobotUnitState> state {RobotUnitState::PreComponentInitialization}; + std::map<std::string, DynamicLibraryPtr> loadedLibs; + std::vector<ManagedIceObjectPtr> units; + std::thread::id rtThreadId; + mutable MutexType dataMutex; + static const std::set<RobotUnitState> devicesReadyStates; + + //robot + private: + boost::shared_ptr<VirtualRobot::Robot> robot; + std::string robotNodeSetName; + std::string robotProjectName; + std::string robotFileName; + std::string robotPlatformName; + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // util + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + public: + RobotUnitState getRobotUnitState() const + { + return state; + } + virtual std::string getName() const override + { + return Component::getName(); + } + protected: + template<class ExceptT = LogicError> + inline void throwIfStateIsNot(const std::set<RobotUnitState>& sinit, const std::string& fnc) const; + template<class ExceptT = LogicError> + inline void throwIfStateIsNot(RobotUnitState s, const std::string& fnc) const; + template<class ExceptT = LogicError> + inline void throwIfDevicesNotReady(const std::string& fnc) const; + + template<class Cont> + static Ice::StringSeq GetNonNullNames(const Cont& c); + + template<class PtrT> + void checkRefCountIsOne(const PtrT& p, const std::string& name) const; + GuardType getGuard() const; + inline const KeyValueVector<std::string, SensorDevicePtr>& getSensorDevices() const; + inline const KeyValueVector<std::string, ControlDevicePtr>& getControlDevices() const; + + void checkLVL1ControllerClassName(const std::string& className) const; + + //time + static TimePointT Now() + { + return ClockT::now(); + } + static std::chrono::microseconds MicroToNow(TimePointT last) + { + return std::chrono::duration_cast<std::chrono::microseconds>(Now() - last); + } + static std::chrono::microseconds MicroNow(); + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // other + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + public: + /// @see PropertyUser::createPropertyDefinitions() + virtual armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; + virtual std::string getDefaultName() const override + { + return "RobotUnit"; + } + // PropertyUser interface + public: + virtual void icePropertiesUpdated(const std::set<std::string>& changedProperties) override; + protected: + virtual void onInitRobotUnit() {} + virtual void onConnectRobotUnit() {} + virtual void onDisconnectRobotUnit() {} + /// @brief called in RobotUnit::onExitComponent() before calling RobotUnit::finishRunning + virtual void onExitRobotUnit() {} + virtual void onInitComponent() final; + virtual void onConnectComponent() final; + virtual void onDisconnectComponent() final; + virtual void onExitComponent() final; + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // TripleBuffer + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + protected: + //rt requested + bool rtRequestedControllersChanged() const; + const LVL0AndLVL1Controllers& rtGetRequestedControllers() const; + const std::vector<LVL0Controller*>& rtGetRequestedLVL0Controllers() const; + const std::vector<LVL1Controller*>& rtGetRequestedLVL1Controllers() const; + // rt activated + void rtWriteActivatedControllers(); + LVL0AndLVL1Controllers& rtGetActivatedControllers(); + std::vector<LVL0Controller*>& rtGetActivatedLVL0Controllers(); + std::vector<LVL1Controller*>& rtGetActivatedLVL1Controllers(); + // rt sensor and control + //declaration further down + //void rtUpdateSensorAndControlBuffer(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + // non rt requested + void writeRequestedControllers(LVL0AndLVL1Controllers&& setOfControllers); + const LVL0AndLVL1Controllers& getRequestedControllers() const; + const std::vector<LVL0Controller*>& getRequestedLVL0Controllers() const; + const std::vector<LVL1Controller*>& getRequestedLVL1Controllers() const; + // non rt activated + bool activatedControllersChanged() const; + const LVL0AndLVL1Controllers& getActivatedControllers() const; + const std::vector<LVL0Controller*>& getActivatedLVL0Controllers() const; + const std::vector<LVL1Controller*>& getActivatedLVL1Controllers() const; + // non rt sensor and control + bool sensorAndControlBufferChanged() const; + const SensorAndControl& getSensorAndControlBuffer() const; + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // State: InitializingDevices + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + protected: + void addControlDevice(const ControlDevicePtr& cd); + void addSensorDevice(const SensorDevicePtr& sd); + + virtual RTThreadTimingsSensorDevicePtr createRTThreadTimingSensorDevice() const; + + void finishDeviceInitialization(); + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // State: InitializingUnits + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + protected: + virtual void initializeDefaultUnits(); + virtual void initializeKinematicUnit(); + virtual void initializePlatformUnit(); + virtual void initializeForceTorqueUnit(); + virtual void initializeInertialMeasurementUnit(); + void addUnit(ManagedIceObjectPtr unit); + void finishUnitInitialization(); + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // State: Running (RT) + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + virtual void initRTThread(); + + //switching helper (rt) + //is in the base class RobotUnitLVL1ControllerAccess that is a friend of LVL1Controller + //static void RtSetLVL1ControllerActive(const LVL1ControllerPtr& lvl1, bool activate = 1, bool deactivateBecauseOfError = false); + void rtDeactivateAssignedLVL1Controller(std::size_t ctrlDevIndex, bool writeActivatedControlers = true); + + //run (these functions should be called in the control loop) + bool rtSwitchControllerSetup(); + void rtRunLVL1Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + void rtHandleInvalidTargets(); + /** + * @brief Runs LVL0 controllers and writes target values to ControlDevices + * @param sensorValuesTimestamp + * @param timeSinceLastIteration + */ + void rtRunLVL0Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + void rtReadSensorDeviceValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + //rtBusSendReceive call this robot specific function here + void rtUpdateSensorAndControlBuffer(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + void rtResetAllTargets(); + + RTThreadTimingsSensorDevice& rtGetRTThreadTimingsSensorDevice() + { + return *rtThreadTimingsSensorDevice; + } + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // State: InitializingUnits + Running (NON RT) + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + public: + //create lvl1 controllers + virtual ConstSensorDevicePtr getSensorDevice(const std::string& sensorDeviceName) const; + virtual ConstControlDevicePtr getControlDevice(const std::string& deviceName); + virtual ControlTargetBase* getControlTarget(const std::string& deviceName, const std::string& controlMode) override; + virtual LVL1ControllerInterfacePrx createController(const std::string& className, const std::string& instanceName, const LVL1ControllerConfigPtr& config, const Ice::Current&) override; + virtual LVL1ControllerInterfacePrx createControllerFromVariantConfig(const std::string& className, const std::string& instanceName, const StringVariantBaseMap& variants, const Ice::Current&) override; + LVL1ControllerPtr createController(const std::string& className, const std::string& instanceName, const LVL1ControllerConfigPtr& config); + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // State: Running (NON RT) + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + public: + //switching + virtual void activateController(const std::string& name, const Ice::Current& = GlobalIceCurrent) override; + virtual void activateControllers(const Ice::StringSeq& names, const Ice::Current& = GlobalIceCurrent) override; + virtual void deactivateController(const std::string& name, const Ice::Current& = GlobalIceCurrent) override; + virtual void deactivateControllers(const Ice::StringSeq& names, const Ice::Current& = GlobalIceCurrent) override; + virtual void switchControllerSetup(const Ice::StringSeq& newSetup, const Ice::Current& = GlobalIceCurrent) override; + protected: + void setActivateControllersRequest(std::set<LVL1ControllerPtr, LVL1Controller::IdComp>&& ctrls); + // publishing + protected: + virtual void publish(); + + //shutting down + protected: + ///@brief joint your thread in this hook. (used by RobotUnit::finishRunning()) + virtual void joinRTThread() = 0; + ///@brief delete all units (used by RobotUnit::finishRunning()) + virtual void removeAllUnits(); + private: + void finishRunning(); + + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // Lib loading + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + public: + virtual bool loadLibFromPath(const std::string& path, const Ice::Current& = GlobalIceCurrent) override; + virtual bool loadLibFromPackage(const std::string& package, const std::string& lib, const Ice::Current& = GlobalIceCurrent) override; + private: + bool loadLibFromAbsolutePath(const std::string& path); + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + // querry data + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // + public: + //lvl1 + virtual Ice::StringSeq getLVL1ControllerNames(const Ice::Current& = GlobalIceCurrent) const override; + virtual Ice::StringSeq getRequestedLVL1ControllerNames(const Ice::Current& = GlobalIceCurrent) const override; + virtual Ice::StringSeq getActivatedLVL1ControllerNames(const Ice::Current& = GlobalIceCurrent) const override; + + virtual LVL1ControllerInterfacePrx getLVL1Controller(const std::string& name, const Ice::Current&) const override; + virtual StringLVL1ControllerPrxDictionary getAllLVL1Controllers(const Ice::Current&) const override; + std::vector<LVL1ControllerPtr> getLVL1ControllersNotNull(const std::vector<std::string>& names) const; + LVL1ControllerPtr getLVL1ControllerNotNull(const std::string& name) const; + + virtual LVL1ControllerStatus getLVL1ControllerStatus(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual LVL1ControllerStatusSeq getLVL1ControllerStatuses(const Ice::Current&) const override; + + virtual LVL1ControllerDescription getLVL1ControllerDescription(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual LVL1ControllerDescriptionSeq getLVL1ControllerDescriptions(const Ice::Current&) const override; + + virtual LVL1ControllerDescriptionWithStatus getLVL1ControllerDescriptionWithStatus(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual LVL1ControllerDescriptionWithStatusSeq getLVL1ControllerDescriptionsWithStatuses(const Ice::Current&) const override; + //control devices + virtual Ice::StringSeq getControlDeviceNames(const Ice::Current&) const override; + ControlDeviceDescription getControlDeviceDescription(std::size_t cdidx) const; + virtual ControlDeviceDescription getControlDeviceDescription(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual ControlDeviceDescriptionSeq getControlDeviceDescriptions(const Ice::Current&) const override; + ControlDeviceStatus getControlDeviceStatus(std::size_t cdidx) const; + virtual ControlDeviceStatus getControlDeviceStatus(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual ControlDeviceStatusSeq getControlDeviceStatuses(const Ice::Current&) const override; + + //sensor devices + virtual Ice::StringSeq getSensorDeviceNames(const Ice::Current&) const override; + SensorDeviceDescription getSensorDeviceDescription(std::size_t cdidx) const; + virtual SensorDeviceDescription getSensorDeviceDescription(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual SensorDeviceDescriptionSeq getSensorDeviceDescriptions(const Ice::Current&) const override; + SensorDeviceStatus getSensorDeviceStatus(std::size_t idx) const; + virtual SensorDeviceStatus getSensorDeviceStatus(const std::string& name, const Ice::Current& = GlobalIceCurrent) const override; + virtual SensorDeviceStatusSeq getSensorDeviceStatuses(const Ice::Current&) const override; + + //lvl1controller classes + virtual Ice::StringSeq getLVL1ControllerClassNames(const Ice::Current& = GlobalIceCurrent) const override; + virtual LVL1ControllerClassDescription getLVL1ControllerClassDescription(const std::string& className, const Ice::Current& = GlobalIceCurrent) const override; + virtual LVL1ControllerClassDescriptionSeq getLVL1ControllerClassDescriptions(const Ice::Current&) const override; + + //units + virtual Ice::ObjectProxySeq getUnits(const Ice::Current& = GlobalIceCurrent) const override; + virtual Ice::ObjectPrx getUnit(const std::string& staticIceId, const Ice::Current&) const override; + const ManagedIceObjectPtr& getUnit(const std::string& staticIceId) const; + virtual KinematicUnitInterfacePrx getKinematicUnit(const Ice::Current& = GlobalIceCurrent) const override; + virtual ForceTorqueUnitInterfacePrx getForceTorqueUnit(const Ice::Current& = GlobalIceCurrent) const override; + virtual InertialMeasurementUnitInterfacePrx getInertialMeasurementUnit(const Ice::Current& = GlobalIceCurrent) const override; + virtual PlatformUnitInterfacePrx getPlatformUnit(const Ice::Current& = GlobalIceCurrent) const override; + + //other + const boost::shared_ptr<VirtualRobot::Robot>& getRobot() const; + std::string getRobotName() const; + const std::string& getRobotNodetSeName() const + { + return robotNodeSetName; + } + const std::string& getRobotProjectName() const + { + return robotProjectName; + } + const std::string& getRobotFileName() const + { + return robotFileName; + } + const std::string& getRobotPlatformName() const + { + return robotPlatformName; + } + + + std::size_t getNumberOfControlDevices() const; + std::size_t getNumberOfSensorDevices() const; + std::size_t rtGetControllerBufferSize() const + { + return controlDevices.size(); + } + std::size_t rtGetNumberOfSensorDevices() const + { + return sensorDevices.size(); + } + std::size_t getControllerBufferSize() const + { + return getNumberOfControlDevices(); + } + + const DebugDrawerInterfacePrx& getDebugDrawerProxy() const; + + virtual std::string getListenerTopicName(const Ice::Current& = GlobalIceCurrent) const override; + + bool areDevicesReady() const + { + return devicesReadyStates.count(state); + } + protected: + const RobotUnitListenerPrx& getListenerProxy() const; + }; + + const KeyValueVector<std::string, SensorDevicePtr>& RobotUnit::getSensorDevices() const + { + return sensorDevices; + } + + const KeyValueVector<std::string, ControlDevicePtr>& RobotUnit::getControlDevices() const + { + return controlDevices; + } + + template<class ExceptT> + void RobotUnit::throwIfStateIsNot(const std::set<RobotUnitState>& sinit, const std::string& fnc) const + { + std::set<RobotUnitState> sset {sinit}; + if (! sset.count(state)) + { + std::stringstream ss; + ss << fnc << ": Can't be called if state is not in ("; + bool fst = true; + for (RobotUnitState st : sset) + { + if (!fst) + { + ss << ", "; + } + ss << st; + fst = false; + } + ss << " (current state " << getRobotUnitState() << ")"; + ARMARX_ERROR << ss.str(); + throw ExceptT {ss.str()}; + } + } + template<class ExceptT> + void RobotUnit::throwIfStateIsNot(RobotUnitState s, const std::string& fnc) const + { + throwIfStateIsNot<ExceptT>(std::set<RobotUnitState> {s}, fnc); + } + template<class ExceptT> + void RobotUnit::throwIfDevicesNotReady(const std::string& fnc) const + { + throwIfStateIsNot<ExceptT>(devicesReadyStates, fnc); + } + + template<class Cont> + Ice::StringSeq RobotUnit::GetNonNullNames(const Cont& c) + { + Ice::StringSeq result; + result.reserve(c.size()); + for (const auto& e : c) + { + if (e) + { + result.emplace_back(e->getName()); + } + } + return result; + } + + template<class PtrT> + void RobotUnit::checkRefCountIsOne(const PtrT& p, const std::string& name) const + { + const auto uc = p->__getRef(); + if (uc != 1) + { + ARMARX_WARNING << "Pointer " << name << " should have usecount 1 but instead hase usecount " << uc; + } + } +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h new file mode 100644 index 0000000000000000000000000000000000000000..487f25e47a7851e9f4f25b2febb446a4842a0fbb --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h @@ -0,0 +1,129 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef _ARMARX_LIB_RobotAPI_SensorValue1DoFActuator_H +#define _ARMARX_LIB_RobotAPI_SensorValue1DoFActuator_H + +#include "SensorValueBase.h" + +#include <ArmarXCore/core/util/algorithm.h> + +namespace armarx +{ + class SensorValue1DoFActuatorPosition : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float position; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return {{"position", {new TimedVariant{position, timestamp}}}}; + } + }; + class SensorValue1DoFActuatorVelocity : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float velocity; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return {{"velocity", {new TimedVariant{velocity, timestamp}}}}; + } + }; + class SensorValue1DoFActuatorAcceleration : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float acceleration; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return {{"acceleration", {new TimedVariant{acceleration, timestamp}}}}; + } + }; + class SensorValue1DoFActuatorTorque : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float torque; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return {{"torque", {new TimedVariant{torque, timestamp}}}}; + } + }; + + + class SensorValue1DoFActuator : + virtual public SensorValue1DoFActuatorPosition, + virtual public SensorValue1DoFActuatorVelocity, + virtual public SensorValue1DoFActuatorAcceleration, + virtual public SensorValue1DoFActuatorTorque + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + auto map = SensorValue1DoFActuatorPosition::toVariants(timestamp); + mergeMaps(map, SensorValue1DoFActuatorVelocity::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + mergeMaps(map, SensorValue1DoFActuatorAcceleration::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + mergeMaps(map, SensorValue1DoFActuatorTorque::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + return map; + } + }; + + + class SensorValue1DoFActuatorCurrent : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float motorCurrent; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return {{"motorCurrent", {new TimedVariant{motorCurrent, timestamp}}}}; + } + }; + class SensorValue1DoFActuatorMotorTemperature : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float motorTemperature; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return {{"motorTemperature", {new TimedVariant{motorTemperature, timestamp}}}}; + } + }; + + class SensorValue1DoFRealActuator : + virtual public SensorValue1DoFActuator, + virtual public SensorValue1DoFActuatorCurrent, + virtual public SensorValue1DoFActuatorMotorTemperature + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + auto map = SensorValue1DoFActuator::toVariants(timestamp); + mergeMaps(map, SensorValue1DoFActuatorCurrent::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + mergeMaps(map, SensorValue1DoFActuatorMotorTemperature::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + return map; + } + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueBase.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueBase.h new file mode 100644 index 0000000000000000000000000000000000000000..24b5ecf955b92d5c7ced332bb560776589d20ae2 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueBase.h @@ -0,0 +1,105 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef _ARMARX_LIB_RobotAPI_SensorValueBase_H +#define _ARMARX_LIB_RobotAPI_SensorValueBase_H + +#include <typeinfo> +#include <memory> +#include <string> +#include <map> + +#include <ArmarXCore/observers/variant/TimedVariant.h> + +#include "../util.h" + +namespace armarx +{ + class SensorValueBase + { + public: + virtual void copyTo(SensorValueBase* target) const = 0; + virtual std::unique_ptr<SensorValueBase> clone() const = 0; + virtual std::string getSensorValueType(bool withoutNamespaceSpecifier) const = 0; + + /// @brief used to send the data to the DebugObserverTopic and to other Components (e.g. GUI widgets) + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const + { + return {}; + } + + template<class T> + bool isA() const + { + return dynamic_cast<const T*>(this); + } + + template<class T> + const T* asA() const + { + return dynamic_cast<const T*>(this); + } + + template<class T> + T* asA() + { + return dynamic_cast<T*>(this); + } + + template<class T, class = typename std::enable_if<std::is_base_of<SensorValueBase, T>::value>::type> + void copyTo(std::unique_ptr<T>& target) const + { + copyTo(target.get()); + } + + virtual ~SensorValueBase() = default; + }; +} + +#define DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION \ + using SensorValueBase = ::armarx::SensorValueBase; \ + virtual void copyTo(SensorValueBase* target) const override \ + { \ + using this_type = typename std::decay<decltype(*this)>::type; \ + *(target->asA<this_type>()) = *this; \ + } \ + virtual std::unique_ptr<SensorValueBase> clone() const override \ + { \ + using this_type = typename std::decay<decltype(*this)>::type; \ + std::unique_ptr<SensorValueBase> c {new this_type}; \ + SensorValueBase::copyTo(c); \ + return c; \ + } \ + virtual std::string getSensorValueType(bool withoutNamespaceSpecifier = false) const override \ + { \ + return getTypeName(*this, withoutNamespaceSpecifier); \ + } + +namespace armarx +{ + class SensorValueDummy : public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + }; +} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h new file mode 100644 index 0000000000000000000000000000000000000000..04588cb5561bed5e9c6d17628c486c5ff0ac41b8 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h @@ -0,0 +1,48 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef _ARMARX_LIB_RobotAPI_SensorValueForceTorque_H +#define _ARMARX_LIB_RobotAPI_SensorValueForceTorque_H + +#include "Eigen/Core" +#include "SensorValueBase.h" + +#include <RobotAPI/libraries/core/Pose.h> + +namespace armarx +{ + class SensorValueForceTorque : public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + Eigen::Vector3f torque; + Eigen::Vector3f force; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"torque", {new TimedVariant{Vector3{torque}, timestamp}}}, + {"force", {new TimedVariant{Vector3{force }, timestamp}}}, + }; + } + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueHolonomicPlatform.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueHolonomicPlatform.h new file mode 100644 index 0000000000000000000000000000000000000000..8206fac66c3841c95c7c4874bce97fe4ea613ff6 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueHolonomicPlatform.h @@ -0,0 +1,145 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef _ARMARX_LIB_RobotAPI_SensorValueHolonomicPlatformVelocity_H +#define _ARMARX_LIB_RobotAPI_SensorValueHolonomicPlatformVelocity_H + +#include "SensorValueBase.h" + +#include <ArmarXCore/core/util/algorithm.h> + +namespace armarx +{ + class SensorValueHolonomicPlatformVelocity : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float velocityX; + float velocityY; + float velocityRotation; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"velocityX", {new TimedVariant(velocityX,timestamp)}}, + {"velocityY", {new TimedVariant(velocityY,timestamp)}}, + {"velocityRotation", {new TimedVariant(velocityRotation,timestamp)}}, + }; + } + }; + + class SensorValueHolonomicPlatformAcceleration : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float accelerationX; + float accelerationY; + float accelerationRotation; + void deriveAccelerationFromVelocityDelta(float dvx, float dvy, float dvrot, float dt) + { + accelerationX = dvx / dt; + accelerationY = dvy / dt; + accelerationRotation = dvrot / dt; + } + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"accelerationX", {new TimedVariant(accelerationX,timestamp)}}, + {"accelerationY", {new TimedVariant(accelerationY,timestamp)}}, + {"accelerationRotation", {new TimedVariant(accelerationRotation,timestamp)}}, + }; + } + }; + + class SensorValueHolonomicPlatformRelativePosition : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float relativePositionX; + float relativePositionY; + float relativePositionRotation; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"relativePositionX", {new TimedVariant(relativePositionX,timestamp)}}, + {"relativePositionY", {new TimedVariant(relativePositionY,timestamp)}}, + {"relativePositionRotation", {new TimedVariant(relativePositionRotation,timestamp)}}, + }; + } + }; + + class SensorValueHolonomicPlatformAbsolutePosition : virtual public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float absolutePositionX; + float absolutePositionY; + float absolutePositionRotation; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"absolutePositionX", {new TimedVariant(absolutePositionX,timestamp)}}, + {"absolutePositionY", {new TimedVariant(absolutePositionY,timestamp)}}, + {"absolutePositionRotation", {new TimedVariant(absolutePositionRotation,timestamp)}}, + }; + } + }; + + class SensorValueHolonomicPlatform : + virtual public SensorValueHolonomicPlatformVelocity, + virtual public SensorValueHolonomicPlatformAcceleration, + virtual public SensorValueHolonomicPlatformRelativePosition + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + + void setVelocitiesAndDeriveAcceleration(float vx, float vy, float vrot, float dt) + { + deriveAccelerationFromVelocityDelta(vx - velocityX, vy - velocityY, vrot - velocityRotation, dt); + velocityX = vx; + velocityY = vy; + velocityRotation = vrot; + } + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + auto map = SensorValueHolonomicPlatformVelocity::toVariants(timestamp); + mergeMaps(map, SensorValueHolonomicPlatformAcceleration::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + mergeMaps(map, SensorValueHolonomicPlatformRelativePosition::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + return map; + } + }; + + class SensorValueHolonomicPlatformWithAbsolutePosition : virtual public SensorValueHolonomicPlatform, virtual public SensorValueHolonomicPlatformAbsolutePosition + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + auto map = SensorValueHolonomicPlatform::toVariants(timestamp); + mergeMaps(map, SensorValueHolonomicPlatformAbsolutePosition::toVariants(timestamp), MergeMapsMode::OverrideNoValues); + return map; + } + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueIMU.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueIMU.h new file mode 100644 index 0000000000000000000000000000000000000000..87801a24024a5358e4b751924dcaa3cbd6c7c494 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueIMU.h @@ -0,0 +1,55 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef _ARMARX_LIB_RobotAPI_SensorValueIMU_H +#define _ARMARX_LIB_RobotAPI_SensorValueIMU_H + +#include <Eigen/Core> +#include <Eigen/Geometry> + +#include "SensorValueBase.h" + +#include <ArmarXCore/core/util/algorithm.h> +#include <RobotAPI/libraries/core/Pose.h> + +namespace armarx +{ + class SensorValueIMU : public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + Eigen::Vector3f linearVelocity; + Eigen::Vector3f angularVelocity; + Eigen::Vector3f linearAcceleration; + Eigen::Quaternionf orientation; + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"linearVelocity", {new TimedVariant{Vector3{linearVelocity},timestamp}}}, + {"angularVelocity", {new TimedVariant{Vector3{angularVelocity},timestamp}}}, + {"linearAcceleration", {new TimedVariant{Vector3{linearAcceleration},timestamp}}}, + {"orientation", {new TimedVariant{Quaternion{orientation},timestamp}}}, + }; + } + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueRTThreadTimings.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueRTThreadTimings.h new file mode 100644 index 0000000000000000000000000000000000000000..a685736ab6d9dc324c839a5ee56f035b5c5a5614 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValueRTThreadTimings.h @@ -0,0 +1,91 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#ifndef _ARMARX_LIB_RobotAPI_SensorValueRTThreadTimings_H +#define _ARMARX_LIB_RobotAPI_SensorValueRTThreadTimings_H + +#include "SensorValueBase.h" + +#include <chrono> + +#include <ArmarXCore/observers/variant/TimestampVariant.h> + +namespace armarx +{ + class SensorValueRTThreadTimings : public SensorValueBase + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + + std::chrono::microseconds rtSwitchControllerSetupDuration; + std::chrono::microseconds rtSwitchControllerSetupRoundTripTime; + + std::chrono::microseconds rtRunLVL1ControllersDuration; + std::chrono::microseconds rtRunLVL1ControllersRoundTripTime; + + std::chrono::microseconds rtHandleInvalidTargetsDuration; + std::chrono::microseconds rtHandleInvalidTargetsRoundTripTime; + + std::chrono::microseconds rtRunLVL0ControllersDuration; + std::chrono::microseconds rtRunLVL0ControllersRoundTripTime; + + std::chrono::microseconds rtBusSendReceiveDuration; + std::chrono::microseconds rtBusSendReceiveRoundTripTime; + + std::chrono::microseconds rtReadSensorDeviceValuesDuration; + std::chrono::microseconds rtReadSensorDeviceValuesRoundTripTime; + + std::chrono::microseconds rtUpdateSensorAndControlBufferDuration; + std::chrono::microseconds rtUpdateSensorAndControlBufferRoundTripTime; + + std::chrono::microseconds rtResetAllTargetsDuration; + std::chrono::microseconds rtResetAllTargetsRoundTripTime; + + std::chrono::microseconds rtLoopDuration; + std::chrono::microseconds rtLoopRoundTripTime; + + virtual std::map<std::string, VariantBasePtr> toVariants(const IceUtil::Time& timestamp) const override + { + return + { + {"rtSwitchControllerSetup.Duration", new TimedVariant{TimestampVariant{rtSwitchControllerSetupDuration.count() }, timestamp}}, + {"rtSwitchControllerSetup.RoundTripTime", new TimedVariant{TimestampVariant{rtSwitchControllerSetupRoundTripTime.count() }, timestamp}}, + {"rtRunLVL1Controllers.Duration", new TimedVariant{TimestampVariant{rtRunLVL1ControllersDuration.count() }, timestamp}}, + {"rtRunLVL1Controllers.RoundTripTime", new TimedVariant{TimestampVariant{rtRunLVL1ControllersRoundTripTime.count() }, timestamp}}, + {"rtHandleInvalidTargets.Duration", new TimedVariant{TimestampVariant{rtHandleInvalidTargetsDuration.count() }, timestamp}}, + {"rtHandleInvalidTargets.RoundTripTime", new TimedVariant{TimestampVariant{rtHandleInvalidTargetsRoundTripTime.count() }, timestamp}}, + {"rtRunLVL0Controllers.Duration", new TimedVariant{TimestampVariant{rtRunLVL0ControllersDuration.count() }, timestamp}}, + {"rtRunLVL0Controllers.RoundTripTime", new TimedVariant{TimestampVariant{rtRunLVL0ControllersRoundTripTime.count() }, timestamp}}, + {"rtBusSendReceive.Duration", new TimedVariant{TimestampVariant{rtBusSendReceiveDuration.count() }, timestamp}}, + {"rtBusSendReceive.RoundTripTime", new TimedVariant{TimestampVariant{rtBusSendReceiveRoundTripTime.count() }, timestamp}}, + {"rtReadSensorDeviceValues.Duration", new TimedVariant{TimestampVariant{rtReadSensorDeviceValuesDuration.count() }, timestamp}}, + {"rtReadSensorDeviceValues.RoundTripTime", new TimedVariant{TimestampVariant{rtReadSensorDeviceValuesRoundTripTime.count() }, timestamp}}, + {"rtUpdateSensorAndControlBuffer.Duration", new TimedVariant{TimestampVariant{rtUpdateSensorAndControlBufferDuration.count() }, timestamp}}, + {"rtUpdateSensorAndControlBuffer.RoundTripTime", new TimedVariant{TimestampVariant{rtUpdateSensorAndControlBufferRoundTripTime.count()}, timestamp}}, + {"rtResetAllTargets.Duration", new TimedVariant{TimestampVariant{rtResetAllTargetsDuration.count() }, timestamp}}, + {"rtResetAllTargets.RoundTripTime", new TimedVariant{TimestampVariant{rtResetAllTargetsRoundTripTime.count() }, timestamp}}, + {"rtLoop.Duration", new TimedVariant{TimestampVariant{rtLoopDuration.count() }, timestamp}}, + {"rtLoop.RoundTripTime", new TimedVariant{TimestampVariant{rtLoopRoundTripTime.count() }, timestamp}}, + }; + } + }; +} +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/SyntaxCheck.cpp b/source/RobotAPI/components/units/RobotUnit/SyntaxCheck.cpp similarity index 52% rename from source/RobotAPI/libraries/RTRobotUnit/SyntaxCheck.cpp rename to source/RobotAPI/components/units/RobotUnit/SyntaxCheck.cpp index 9c3e574114e6d5f934aca89da845286386f074ca..ae4e49be703b61fd5f306a5bcb95d8e584e9b396 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/SyntaxCheck.cpp +++ b/source/RobotAPI/components/units/RobotUnit/SyntaxCheck.cpp @@ -20,25 +20,39 @@ * GNU General Public License */ + + + #include "Constants.h" #include "ControlModes.h" #include "BasicControllers.h" +#include "RobotUnit.h" +#include "structs.h" +#include "util.h" + +#include"Units/ForceTorqueSubUnit.h" +#include"Units/InertialMeasurementSubUnit.h" +#include"Units/KinematicSubUnit.h" +#include"Units/PlatformSubUnit.h" +#include"Units/RobotUnitSubUnit.h" + +#include "Devices/ControlDevice.h" +#include "Devices/DeviceBase.h" +#include "Devices/SensorDevice.h" #include "LVL0Controllers/LVL0Controller.h" #include "LVL1Controllers/LVL1Controller.h" -#include "LVL1Controllers/LVL1PassThroughController.h" - -#include "DataUnits/ForceTorqueDataUnit.h" -#include "DataUnits/HapticDataUnit.h" -#include "DataUnits/IMUDataUnit.h" -#include "DataUnits/KinematicDataUnit.h" -#include "DataUnits/PlatformDataUnit.h" +#include "LVL1Controllers/LVL1KinematicUnitPassThroughController.h" +#include "LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.h" -#include "Targets/ActuatorPositionTarget.h" -#include "Targets/TargetBase.h" -#include "Targets/ActuatorTorqueTarget.h" -#include "Targets/ActuatorVelocityTarget.h" -#include "Targets/HolonomicPlatformVelocityTarget.h" +#include "ControlTargets/ControlTargetBase.h" +#include "ControlTargets/ControlTarget1DoFActuator.h" +#include "ControlTargets/ControlTargetHolonomicPlatformVelocity.h" +#include "SensorValues/SensorValueBase.h" +#include "SensorValues/SensorValue1DoFActuator.h" +#include "SensorValues/SensorValueHolonomicPlatform.h" +#include "SensorValues/SensorValueIMU.h" +#include "SensorValues/SensorValueForceTorque.h" //this file is only for syntax checks diff --git a/source/RobotAPI/components/units/RobotUnit/Units/ForceTorqueSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/ForceTorqueSubUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6b0faa5943dd167de89d13289d11f0b5a6ed7fbe --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/ForceTorqueSubUnit.cpp @@ -0,0 +1,68 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::ForceTorqueSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "ForceTorqueSubUnit.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <RobotAPI/libraries/core/FramedPose.h> + +void armarx::ForceTorqueSubUnit::update(const armarx::SensorAndControl& sc, const LVL0AndLVL1Controllers&) +{ + if(!getProxy()) + { + //this unit is not initialized yet + ARMARX_IMPORTANT << deactivateSpam(1) << "not initialized yet"; + return; + } + if(!listenerPrx) + { + ARMARX_IMPORTANT << deactivateSpam(1) << "listener is not set"; + return; + } + for (DeviceData& dev : devs) + { + const SensorValueBase* sensorValBase = sc.sensors.at(dev.sensorIndex).get(); + ARMARX_CHECK_EXPRESSION(sensorValBase->isA<SensorValueForceTorque>()); + const SensorValueForceTorque& sensorVal = *sensorValBase->asA<SensorValueForceTorque>(); + ARMARX_CHECK_EXPRESSION(listenerPrx); + listenerPrx->reportSensorValues( + dev.deviceName, + new armarx::FramedDirection(sensorVal.force, dev.frame, agentName), + new armarx::FramedDirection(sensorVal.torque, dev.frame, agentName) + ); + } +} + +void armarx::ForceTorqueSubUnit::setOffset(const armarx::FramedDirectionBasePtr &, const armarx::FramedDirectionBasePtr &, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::ForceTorqueSubUnit::setToNull(const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::ForceTorqueSubUnit::onInitForceTorqueUnit() +{ + agentName = getProperty<std::string>("AgentName").getValue(); +} diff --git a/source/RobotAPI/components/units/RobotUnit/Units/ForceTorqueSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/ForceTorqueSubUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..3506935399b6a96c384e3eb328a1329d876d3dec --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/ForceTorqueSubUnit.h @@ -0,0 +1,63 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::ForceTorqueSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_ForceTorqueSubUnit_H +#define _ARMARX_LIB_RobotAPI_ForceTorqueSubUnit_H + +#include "RobotUnitSubUnit.h" + +#include <RobotAPI/components/units/ForceTorqueUnit.h> + +#include "../SensorValues/SensorValueForceTorque.h" + +namespace armarx +{ + TYPEDEF_PTRS_HANDLE(ForceTorqueSubUnit); + class ForceTorqueSubUnit: + virtual public RobotUnitSubUnit, + virtual public ForceTorqueUnit + { + public: + virtual void update(const SensorAndControl& sc, const LVL0AndLVL1Controllers& c) override; + + // ForceTorqueUnitInterface interface + virtual void setOffset(const FramedDirectionBasePtr&, const FramedDirectionBasePtr&, const Ice::Current& = GlobalIceCurrent) override; + virtual void setToNull(const Ice::Current& = GlobalIceCurrent) override; + + // ForceTorqueUnit interface + virtual void onInitForceTorqueUnit() override; + virtual void onStartForceTorqueUnit() override {} + virtual void onExitForceTorqueUnit() override {} + + struct DeviceData + { + std::string deviceName; + std::size_t sensorIndex; + std::string frame; + }; + + std::vector<DeviceData> devs; + private: + std::string agentName; + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Units/InertialMeasurementSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/InertialMeasurementSubUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8cf07e4ee667ce6864c71e05eba4da4645a8dfdc --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/InertialMeasurementSubUnit.cpp @@ -0,0 +1,51 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::InertialMeasurementSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "InertialMeasurementSubUnit.h" +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +void armarx::InertialMeasurementSubUnit::update(const armarx::SensorAndControl& sc, const LVL0AndLVL1Controllers&) +{ + if(!getProxy()) + { + //this unit is not initialized yet + return; + } + if(!IMUTopicPrx) + { + ARMARX_IMPORTANT << deactivateSpam(1) << "listener is not set"; + return; + } + TimestampVariantPtr t = new TimestampVariant(sc.sensorValuesTimestamp); + for (auto nam2idx : devs) + { + const auto devidx = nam2idx.second; + const auto& dev = nam2idx.first; + const SensorValueBase* sv = sc.sensors.at(devidx).get(); + ARMARX_CHECK_EXPRESSION(sv->isA<SensorValueIMU>()); + // const SensorValueIMU* s = sv->asA<SensorValueIMU>(); + IMUData data; + // data.acceleration = s->linearAcceleration; + // data.gyroscopeRotation = s->angularVelocity; + IMUTopicPrx->reportSensorValues(dev, "name", data, t); + ///TODO fix unclear imu stuff + } +} diff --git a/source/RobotAPI/components/units/RobotUnit/Units/InertialMeasurementSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/InertialMeasurementSubUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..3c4a5a6808715dc1b11007f3c337ea15db1ff95c --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/InertialMeasurementSubUnit.h @@ -0,0 +1,53 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::InertialMeasurementSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_InertialMeasurementSubUnit_H +#define _ARMARX_LIB_RobotAPI_InertialMeasurementSubUnit_H + +#include "RobotUnitSubUnit.h" + +#include <ArmarXCore/observers/variant/TimestampVariant.h> + +#include <RobotAPI/components/units/InertialMeasurementUnit.h> + +#include "../SensorValues/SensorValueIMU.h" + +namespace armarx +{ + TYPEDEF_PTRS_HANDLE(InertialMeasurementSubUnit); + class InertialMeasurementSubUnit: + virtual public RobotUnitSubUnit, + virtual public InertialMeasurementUnit + { + public: + virtual void update(const SensorAndControl& sc, const LVL0AndLVL1Controllers& c); + + // InertialMeasurementUnit interface + protected: + virtual void onInitIMU() override {} + virtual void onStartIMU() override {} + virtual void onExitIMU() override {} + public: + std::map<std::string, std::size_t> devs; + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c595351fe68783c91840329ad49bb22f01fe25af --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp @@ -0,0 +1,233 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::KinematicSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "KinematicSubUnit.h" + +void armarx::KinematicSubUnit::setupData(std::string relRobFile, VirtualRobot::RobotPtr rob, std::map<std::string, ActuatorData> newDevs, RobotUnit* newRobotUnit) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + ARMARX_CHECK_EXPRESSION(getState() == eManagedIceObjectCreated); + + ARMARX_CHECK_EXPRESSION(!robot); + ARMARX_CHECK_EXPRESSION(rob); + robot = rob; + + ARMARX_CHECK_EXPRESSION(!robotUnit); + ARMARX_CHECK_EXPRESSION(newRobotUnit); + robotUnit = newRobotUnit; + + ARMARX_CHECK_EXPRESSION(relativeRobotFile.empty()); + ARMARX_CHECK_EXPRESSION(!relRobFile.empty()); + relativeRobotFile = relRobFile; + + devs = std::move(newDevs); +} + +void armarx::KinematicSubUnit::update(const armarx::SensorAndControl& sc, const armarx::LVL0AndLVL1Controllers& c) +{ + if(!getProxy()) + { + //this unit is not initialized yet + ARMARX_IMPORTANT << deactivateSpam(1) << "not initialized yet"; + return; + } + if(!listenerPrx) + { + ARMARX_IMPORTANT << deactivateSpam(1) << "listener is not set"; + return; + } + std::lock_guard<std::mutex> guard {dataMutex}; + bool ctrlModesAValueChanged = false; + bool angAValueChanged = false; + bool velAValueChanged = false; + bool accAValueChanged = false; + bool torAValueChanged = false; + bool motorCurrentAValueChanged = false; + bool motorTemperaturesAValueChanged = false; + + long timestamp = sc.sensorValuesTimestamp.toMicroSeconds(); + std::set<LVL1Controller*> lvl1s {c.lvl1Controllers.begin(), c.lvl1Controllers.end()}; + std::vector<std::string> actuatorsWithoutSensor; + actuatorsWithoutSensor.reserve(devs.size()); + for (const auto& name2actuatorData : devs) + { + const ActuatorData& actuatorData = name2actuatorData.second; + const auto& name = actuatorData.name; + //mode + { + ControlMode c = eUnknown; + if (actuatorData.ctrlPos && lvl1s.count(actuatorData.ctrlPos.get())) + { + c = ePositionControl; + } + if (actuatorData.ctrlVel && lvl1s.count(actuatorData.ctrlVel.get())) + { + ARMARX_CHECK_EXPRESSION(eUnknown == c); + c = eVelocityControl; + } + if (actuatorData.ctrlTor && lvl1s.count(actuatorData.ctrlTor.get())) + { + ARMARX_CHECK_EXPRESSION(eUnknown == c); + c = eTorqueControl; + } + ctrlModesAValueChanged = ctrlModesAValueChanged || !ctrlModes.count(name) || (ctrlModes.at(name) != c); + ctrlModes[name] = c; + } + if (actuatorData.sensorDeviceIndex < sc.sensors.size()) + { + const SensorValueBase* s = sc.sensors.at(actuatorData.sensorDeviceIndex).get(); + UpdateNameValueMap<SensorValue1DoFActuatorPosition , &SensorValue1DoFActuatorPosition::position >(ang, name, s, angAValueChanged); + UpdateNameValueMap<SensorValue1DoFActuatorVelocity , &SensorValue1DoFActuatorVelocity::velocity >(vel, name, s, velAValueChanged); + UpdateNameValueMap<SensorValue1DoFActuatorAcceleration , &SensorValue1DoFActuatorAcceleration::acceleration >(acc, name, s, accAValueChanged); + UpdateNameValueMap<SensorValue1DoFActuatorTorque , &SensorValue1DoFActuatorTorque::torque >(tor, name, s, torAValueChanged); + UpdateNameValueMap<SensorValue1DoFActuatorCurrent , &SensorValue1DoFActuatorCurrent::motorCurrent >(motorCurrents, name, s, motorCurrentAValueChanged); + UpdateNameValueMap<SensorValue1DoFActuatorMotorTemperature, &SensorValue1DoFActuatorMotorTemperature::motorTemperature>(motorTemperatures, name, s, motorTemperaturesAValueChanged); + } + else + { + actuatorsWithoutSensor.emplace_back(name); + } + } + if (!actuatorsWithoutSensor.empty()) + { + ARMARX_WARNING << deactivateSpam(5) << "these actuators have no sensor!:\n" << actuatorsWithoutSensor; + } + ARMARX_DEBUG << deactivateSpam(1) << "reporting updated data:\n" + << ctrlModes.size() << " \tcontrol modes (updated = " << ctrlModesAValueChanged << ")\n" + << ang.size() << " \tjoint angles (updated = " << angAValueChanged << ")\n" + << vel.size() << " \tjoint velocities (updated = " << velAValueChanged << ")\n" + << acc.size() << " \tjoint accelerations (updated = " << accAValueChanged << ")\n" + << tor.size() << " \tjoint torques (updated = " << torAValueChanged << ")\n" + << motorCurrents.size() << " \tmotor currents (updated = " << motorCurrentAValueChanged << ")\n" + << motorTemperatures.size() << " \tmotor temperatures (updated = " << motorTemperaturesAValueChanged << ")\n"; + auto prx = listenerPrx->ice_batchOneway(); + prx->reportControlModeChanged(ctrlModes, timestamp, ctrlModesAValueChanged); + prx->reportJointAngles(ang, timestamp, angAValueChanged); + prx->reportJointVelocities(vel, timestamp, velAValueChanged); + prx->reportJointAccelerations(acc, timestamp, accAValueChanged); + prx->reportJointTorques(tor, timestamp, torAValueChanged); + prx->reportJointCurrents(motorCurrents, timestamp, motorCurrentAValueChanged); + prx->reportJointMotorTemperatures(motorTemperatures, timestamp, motorTemperaturesAValueChanged); + prx->ice_flushBatchRequests(); +} + +void armarx::KinematicSubUnit::requestJoints(const Ice::StringSeq &, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::KinematicSubUnit::releaseJoints(const Ice::StringSeq &, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::KinematicSubUnit::setJointAngles(const armarx::NameValueMap& angles, const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + for (const auto& n2v : angles) + { + if (devs.count(n2v.first)) + { + devs.at(n2v.first).ctrlPos->set(n2v.second); + } + } +} + +void armarx::KinematicSubUnit::setJointVelocities(const armarx::NameValueMap& velocities, const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + ARMARX_IMPORTANT << VAROUT(velocities); + for (const auto& n2v : velocities) + { + if (devs.count(n2v.first)) + { + devs.at(n2v.first).ctrlVel->set(n2v.second); + } + } +} + +void armarx::KinematicSubUnit::setJointTorques(const armarx::NameValueMap& torques, const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + for (const auto& n2v : torques) + { + if (devs.count(n2v.first)) + { + devs.at(n2v.first).ctrlTor->set(n2v.second); + } + } +} + +void armarx::KinematicSubUnit::switchControlMode(const armarx::NameControlModeMap& ncm, const Ice::Current&) +{ + std::vector<std::string> toActivate; + { + std::lock_guard<std::mutex> guard {dataMutex}; + toActivate.reserve(ncm.size()); + for (const auto& n2c : ncm) + { + if (devs.count(n2c.first)) + { + auto ctrl = devs.at(n2c.first).getController(n2c.second); + if (ctrl && !ctrl->isControllerActive()) + { + toActivate.emplace_back(ctrl->getInstanceName()); + } + } + } + ARMARX_VERBOSE << "switching control modes activates these LVL1Controllers:\n" << toActivate << std::flush; + ARMARX_CHECK_EXPRESSION(robotUnit); + } + robotUnit->activateControllers(toActivate); +} + +void armarx::KinematicSubUnit::setJointAccelerations(const armarx::NameValueMap &, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::KinematicSubUnit::setJointDecelerations(const armarx::NameValueMap &, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +armarx::NameControlModeMap armarx::KinematicSubUnit::getControlModes(const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + return ctrlModes; +} + +const armarx::LVL1ControllerPtr armarx::KinematicSubUnit::ActuatorData::getController(armarx::ControlMode c) const +{ + switch (c) + { + case ePositionVelocityControl: + return ctrlPos; + case ePositionControl: + return ctrlPos; + case eVelocityControl: + return ctrlVel; + case eTorqueControl: + return ctrlTor; + default: + return nullptr; + } +} diff --git a/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..7bbf7d699ca8362a577483a16177af5953bdeecf --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h @@ -0,0 +1,109 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::KinematicSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_KinematicSubUnit_H +#define _ARMARX_LIB_RobotAPI_KinematicSubUnit_H + +#include <mutex> + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <RobotAPI/components/units/KinematicUnit.h> + +#include "RobotUnitSubUnit.h" +#include "../util.h" +#include "../SensorValues/SensorValue1DoFActuator.h" +#include "../LVL1Controllers/LVL1KinematicUnitPassThroughController.h" + +namespace armarx +{ + class RobotUnit; + + TYPEDEF_PTRS_HANDLE(KinematicSubUnit); + class KinematicSubUnit: + virtual public RobotUnitSubUnit, + virtual public KinematicUnit + { + public: + struct ActuatorData + { + std::string name; + std::size_t sensorDeviceIndex; + LVL1KinematicUnitPassThroughControllerPtr ctrlPos; + LVL1KinematicUnitPassThroughControllerPtr ctrlVel; + LVL1KinematicUnitPassThroughControllerPtr ctrlTor; + + const LVL1ControllerPtr getController(ControlMode c) const; + }; + + void setupData(std::string relRobFile, VirtualRobot::RobotPtr rob, std::map<std::string, ActuatorData> newDevs, RobotUnit* newRobotUnit); + virtual void update(const SensorAndControl& sc, const LVL0AndLVL1Controllers& c) override; + + // KinematicUnitInterface interface + virtual void requestJoints(const Ice::StringSeq&, const Ice::Current&) override; + virtual void releaseJoints(const Ice::StringSeq&, const Ice::Current&) override; + + virtual void setJointAngles(const NameValueMap& angles, const Ice::Current&) override; + virtual void setJointVelocities(const NameValueMap& velocities, const Ice::Current&) override; + virtual void setJointTorques(const NameValueMap& torques, const Ice::Current&) override; + virtual void switchControlMode(const NameControlModeMap& ncm, const Ice::Current&) override; + + virtual void setJointAccelerations(const NameValueMap&, const Ice::Current&) override; + virtual void setJointDecelerations(const NameValueMap&, const Ice::Current&) override; + + virtual NameControlModeMap getControlModes(const Ice::Current&) override; + + // KinematicUnit interface + virtual void onInitKinematicUnit() override {} + virtual void onStartKinematicUnit() override {} + virtual void onExitKinematicUnit() override {} + private: + std::map<std::string, ActuatorData> devs; + // never use this when holding the mutex! (could cause deadlocks) + RobotUnit* robotUnit; + mutable std::mutex dataMutex; + NameControlModeMap ctrlModes; + NameValueMap ang; + NameValueMap vel; + NameValueMap acc; + NameValueMap tor; + NameValueMap motorCurrents; + NameValueMap motorTemperatures; + + template<class SensorValueT, float SensorValueT::* Member> + static void UpdateNameValueMap(NameValueMap& nvm, const std::string& name, const SensorValueBase* sv, bool& changeState) + { + const SensorValueT* s = sv->asA<SensorValueT>(); + if (!s) + { + return; + }; + const float v = s->*Member; + if (!nvm.count(name) || (nvm.at(name) != v)) + { + changeState = true; + } + nvm[name] = v; + } + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94a3d1f267b65454802899cad820135d068abc92 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.cpp @@ -0,0 +1,116 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::PlatformSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "PlatformSubUnit.h" + +void armarx::PlatformSubUnit::update(const armarx::SensorAndControl& sc, const LVL0AndLVL1Controllers&) +{ + if(!getProxy()) + { + //this unit is not initialized yet + ARMARX_IMPORTANT << deactivateSpam(1) << "not initialized yet"; + return; + } + if(!listenerPrx) + { + ARMARX_IMPORTANT << deactivateSpam(1) << "listener is not set"; + return; + } + const SensorValueBase* sensorValue = sc.sensors.at(platformSensorIndex).get(); + ARMARX_CHECK_EXPRESSION(sensorValue); + std::lock_guard<std::mutex> guard {dataMutex}; + //pos + { + ARMARX_CHECK_EXPRESSION(sensorValue->isA<SensorValueHolonomicPlatformRelativePosition>()); + const SensorValueHolonomicPlatformRelativePosition* s = sensorValue->asA<SensorValueHolonomicPlatformRelativePosition>(); + relX = s->relativePositionX; + relY = s->relativePositionY; + relR = s->relativePositionRotation; + + const Eigen::Matrix4f glob = getCorrectedPose(); + const float globR = VirtualRobot::MathTools::eigen4f2rpy(glob)(2); + listenerPrx->reportPlatformPose(glob(0, 3), glob(1, 3), globR); + } + //vel + { + ARMARX_CHECK_EXPRESSION(sensorValue->isA<SensorValueHolonomicPlatformVelocity>()); + const SensorValueHolonomicPlatformVelocity* s = sensorValue->asA<SensorValueHolonomicPlatformVelocity>(); + listenerPrx->reportPlatformVelocity(s->velocityX, s->velocityY , s->velocityRotation); + } +} + +void armarx::PlatformSubUnit::move(Ice::Float vx, Ice::Float vy, Ice::Float vr, const Ice::Current&) +{ + //holding the mutex here could deadlock + if (!pt->isControllerActive()) + { + pt->activateController(); + } + std::lock_guard<std::mutex> guard {dataMutex}; + pt->setVelocites( + boost::algorithm::clamp(vx, -maxVLin, maxVLin), + boost::algorithm::clamp(vy, -maxVLin, maxVLin), + boost::algorithm::clamp(vr, -maxVAng, maxVAng) + ); +} + +void armarx::PlatformSubUnit::moveTo(Ice::Float rx, Ice::Float ry, Ice::Float rr, Ice::Float lac, Ice::Float rac, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::PlatformSubUnit::moveRelative(Ice::Float rx, Ice::Float ry, Ice::Float rr, Ice::Float lac, Ice::Float rac, const Ice::Current &) +{ + ARMARX_WARNING << "NYI"; +} + +void armarx::PlatformSubUnit::setMaxVelocities(Ice::Float mxVLin, Ice::Float mxVAng, const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + maxVLin = std::abs(mxVLin); + maxVAng = std::abs(mxVAng); +} + +void armarx::PlatformSubUnit::setGlobalPose(armarx::PoseBasePtr globalPose, const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + PosePtr p = PosePtr::dynamicCast(globalPose); + relPositionCorrection = p->toEigen() * getRelativePose().inverse(); +} + +armarx::PoseBasePtr armarx::PlatformSubUnit::getGlobalPose(const Ice::Current&) +{ + std::lock_guard<std::mutex> guard {dataMutex}; + return new Pose {getCorrectedPose()}; +} + +void armarx::PlatformSubUnit::stopPlatform(const Ice::Current& c) +{ + move(0, 0, 0); +} + +Eigen::Matrix4f armarx::PlatformSubUnit::getRelativePose() const +{ + Eigen::Matrix4f rp = VirtualRobot::MathTools::rpy2eigen4f(0, 0, relR); + rp(0, 3) = relX; + rp(1, 3) = relY; + return rp; +} diff --git a/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..cd15f2c4ee40bab2fcef27aacb7680fee97a6e09 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h @@ -0,0 +1,87 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::PlatformSubUnit + * @author Raphael ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_PlatformSubUnit_H +#define _ARMARX_LIB_RobotAPI_PlatformSubUnit_H + +#include <mutex> + +#include <boost/algorithm/clamp.hpp> + +#include <Eigen/Core> + +#include <VirtualRobot/MathTools.h> + +#include <RobotAPI/components/units/PlatformUnit.h> +#include <RobotAPI/libraries/core/Pose.h> + +#include "RobotUnitSubUnit.h" +#include "../LVL1Controllers/LVL1HolonomicPlatformUnitVelocityPassThroughController.h" +#include "../SensorValues/SensorValueHolonomicPlatform.h" + +namespace armarx +{ + TYPEDEF_PTRS_HANDLE(PlatformSubUnit); + class PlatformSubUnit: + virtual public RobotUnitSubUnit, + virtual public PlatformUnit + { + public: + virtual void update(const SensorAndControl& sc, const LVL0AndLVL1Controllers& c) override; + + // PlatformUnitInterface interface + virtual void move(Ice::Float vx, Ice::Float vy, Ice::Float vr, const Ice::Current& = GlobalIceCurrent) override; + virtual void moveTo(Ice::Float rx, Ice::Float ry, Ice::Float rr, Ice::Float lac, Ice::Float rac, const Ice::Current& = GlobalIceCurrent) override; + virtual void moveRelative(Ice::Float rx, Ice::Float ry, Ice::Float rr, Ice::Float lac, Ice::Float rac, const Ice::Current& = GlobalIceCurrent) override; + + virtual void setMaxVelocities(Ice::Float mxVLin, Ice::Float mxVAng, const Ice::Current& = GlobalIceCurrent) override; + + virtual void setGlobalPose(PoseBasePtr globalPose, const Ice::Current& = GlobalIceCurrent) /*override*/; + virtual PoseBasePtr getGlobalPose(const Ice::Current& = GlobalIceCurrent) /*override*/; + + // PlatformUnit interface + virtual void onInitPlatformUnit() override {} + virtual void onStartPlatformUnit() override {} + virtual void onExitPlatformUnit() override {} + virtual void stopPlatform(const Ice::Current& c = GlobalIceCurrent) override; + + LVL1HolonomicPlatformUnitVelocityPassThroughControllerPtr pt; + std::size_t platformSensorIndex; + protected: + Eigen::Matrix4f getRelativePose() const; + Eigen::Matrix4f getCorrectedPose() const + { + return relPositionCorrection * getRelativePose(); + } + + mutable std::mutex dataMutex; + + Ice::Float maxVLin = std::numeric_limits<Ice::Float>::infinity(); + Ice::Float maxVAng = std::numeric_limits<Ice::Float>::infinity(); + + float relX = 0; + float relY = 0; + float relR = 0; + Eigen::Matrix4f relPositionCorrection = Eigen::Matrix4f::Identity(); + }; +} +#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/ForceTorqueDataUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/RobotUnitSubUnit.cpp similarity index 68% rename from source/RobotAPI/libraries/RTRobotUnit/DataUnits/ForceTorqueDataUnit.h rename to source/RobotAPI/components/units/RobotUnit/Units/RobotUnitSubUnit.cpp index a297a4879603508ad6a1098a76b8bcffb3d0c4bd..481cfac2884ff6689fe51112ab9232630d2316a2 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/ForceTorqueDataUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/Units/RobotUnitSubUnit.cpp @@ -13,27 +13,10 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI::ArmarXObjects::ForceTorqueDataUnit + * @package RobotAPI::ArmarXObjects::RobotUnitSubUnit * @author Raphael ( ufdrv at student dot kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ - -#ifndef _ARMARX_LIB_RobotAPI_ForceTorqueDataUnit_H -#define _ARMARX_LIB_RobotAPI_ForceTorqueDataUnit_H - -namespace armarx -{ - class ForceTorqueDataUnitInterface - { - public: - }; - - class ForceTorqueDataUnitPtrProvider: virtual public ForceTorqueDataUnitInterface - { - public: - }; -} - -#endif +#include "RobotUnitSubUnit.h" diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.cpp b/source/RobotAPI/components/units/RobotUnit/Units/RobotUnitSubUnit.h similarity index 62% rename from source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.cpp rename to source/RobotAPI/components/units/RobotUnit/Units/RobotUnitSubUnit.h index 0b8b25547253c80beff59dd74c323fe2b7a7ecb9..9e915547b41faa4984048b5fb67f9e54ec7a59d6 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.cpp +++ b/source/RobotAPI/components/units/RobotUnit/Units/RobotUnitSubUnit.h @@ -13,35 +13,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI::ArmarXObjects::LVL1Controller + * @package RobotAPI::ArmarXObjects::RobotUnitSubUnit * @author Raphael ( ufdrv at student dot kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#include "LVL1Controller.h" +#ifndef _ARMARX_LIB_RobotAPI_RobotUnitSubUnit_H +#define _ARMARX_LIB_RobotAPI_RobotUnitSubUnit_H +#include <ArmarXCore/core/ManagedIceObject.h> -using namespace armarx; +#include "../util.h" +#include "../structs.h" - - -void LVL1Controller::rtActivateController() -{ - if (!isActive) - { - rtPreActivateController(); - isActive = true; - } -} - -void LVL1Controller::rtDeactivateController() +namespace armarx { - if (isActive) + TYPEDEF_PTRS_HANDLE(RobotUnitSubUnit); + class RobotUnitSubUnit: public virtual ManagedIceObject { - isActive = false; - rtPostDeactivateController(); - } + public: + virtual void update(const SensorAndControl& sc, const LVL0AndLVL1Controllers& c) = 0; + }; } +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/structs.h b/source/RobotAPI/components/units/RobotUnit/structs.h new file mode 100644 index 0000000000000000000000000000000000000000..a589aadb4886f6a430dd8ab144bf1618b5bc7cda --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/structs.h @@ -0,0 +1,63 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::RobotUnit + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_UNIT_RobotAPI_RobotUnit_structs_H +#define _ARMARX_UNIT_RobotAPI_RobotUnit_structs_H + +#include <memory> +#include <vector> +#include "SensorValues/SensorValueBase.h" +#include "ControlTargets/ControlTargetBase.h" + +namespace armarx +{ + class LVL0Controller; + class LVL1Controller; + + /// @brief Structure used by the RobotUnit to swap out sensor values and control targets + struct SensorAndControl + { + //since this is not automatically propagated from a vector of noncopyable elements + SensorAndControl(const SensorAndControl&) = delete; + SensorAndControl& operator=(const SensorAndControl&) = delete; + //since deleting ctors stops autogeneration for everything + SensorAndControl() = default; + SensorAndControl(SensorAndControl&&) = default; + SensorAndControl& operator=(SensorAndControl &&) = default; + + IceUtil::Time sensorValuesTimestamp; + IceUtil::Time timeSinceLastIteration; + std::vector<std::unique_ptr<SensorValueBase>> sensors; + std::vector<std::vector<std::unique_ptr<ControlTargetBase>>> control; + }; + /// @brief Structure used by the RobotUnit to swap lists of lvl0 and lvl1 controllers + struct LVL0AndLVL1Controllers + { + LVL0AndLVL1Controllers(std::size_t n = 0) + : lvl0Controllers(n), + lvl1Controllers(n) + {} + std::vector<LVL0Controller*> lvl0Controllers; + std::vector<LVL1Controller*> lvl1Controllers; + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp b/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e822a463808e5523f7f8afd771a49880954dc29 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp @@ -0,0 +1,669 @@ +/* +* This file is part of ArmarX. +* +* ArmarX is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* ArmarX is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package ArmarX::RobotAPI::Test +* @author Raphael ( ufdrv at student dot kit dot edu ) +* @date 2017 +* @copyright http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#define BOOST_TEST_MODULE RobotAPI::BasicControllers::Test +#define ARMARX_BOOST_TEST +#define CREATE_LOGFILES +#include <random> +#include <iostream> + +#include <boost/algorithm/clamp.hpp> + +#include <ArmarXCore/core/util/algorithm.h> +#include <RobotAPI/Test.h> +#include "../BasicControllers.h" +using namespace armarx; +//params for random tests +const std::size_t tries = 10; +const std::size_t ticks = 20000; // each tick is 0.75 to 1.25 ms +//helpers for logging +#ifdef CREATE_LOGFILES +#define LOG_CONTROLLER_DATA_WRITE_TO(name) change_logging_file(name) +#define LOG_CONTROLLER_DATA(...) f << __VA_ARGS__ << "\n" + +#include <boost/filesystem.hpp> +#include <fstream> + +static const boost::filesystem::path fpath +{"controller_logfiles/" +}; +static std::ofstream f; +static bool isSetup = false; + +void change_logging_file(const std::string& name) +{ + if (f.is_open()) + { + f.close(); + } + if (!isSetup) + { + boost::filesystem::create_directories(fpath); + //create the python evaluation file + boost::filesystem::path tmppath(fpath / "eval.py"); + f.open(tmppath.string()); + f << R"raw_str_delim( + def consume_file(fname, save=True, show=True): +#get data + with open(fname, 'r') as f: + data = [ x.split(' ') for x in f.read().split('\n') if x != ''] +#plot + from mpl_toolkits.axes_grid1 import host_subplot + import mpl_toolkits.axisartist as AA + import matplotlib.pyplot as plt + + pos = host_subplot(111, axes_class=AA.Axes) + plt.subplots_adjust(right=0.75) + vel = pos.twinx() + acc = pos.twinx() + + + pos.axhline(0, color='black') + vel.axhline(0, color='black') + acc.axhline(0, color='black') + + offset = 60 + acc.axis['right'] = acc.get_grid_helper().new_fixed_axis(loc='right', + axes=acc, + offset=(offset, 0)) + + c={pos:'red', vel:'blue', acc:'green'} + b={pos:0 , vel:0 , acc:0 } + + pos.set_xlabel('Time [s]') + pos.set_ylabel('Position') + vel.set_ylabel('Velocity') + acc.set_ylabel('Acceleration') + + pos.axis['left'].label.set_color(c[pos]) + vel.axis['right'].label.set_color(c[vel]) + acc.axis['right'].label.set_color(c[acc]) + + + def get(name,factor=1): + if name in data[0]: + return [factor*float(x[data[0].index(name)]) for x in data[1:]] + + times=get('time') + + def add(plt,name,factor=1, clr=None, style='-'): + d=get(name,factor) + if d is None or [0]*len(d) == d: + return + if clr is None: + clr=c[plt] + plt.plot(times, d, style,color=clr) + b[plt] = max([b[plt]]+[abs(x) for x in d]) + plt.set_ylim(-b[plt]*1.1, b[plt]*1.1) + + add(pos,'curpos',clr='red') + add(pos,'targpos',clr='red', style='-.') + add(pos,'posHi',clr='darkred', style='--') + add(pos,'posLo',clr='darkred', style='--') + add(pos,'posHiHard',clr='darkred', style='--') + add(pos,'posLoHard',clr='darkred', style='--') + add(pos,'brakingDist',clr='orange', style=':') + add(pos,'posAfterBraking',clr='magenta', style=':') + + add(vel,'curvel',clr='teal') + add(vel,'targvel',clr='teal', style='-.') + add(vel,'maxv',clr='blue', style='--') + add(vel,'maxv',factor=-1,clr='blue', style='--') + + add(acc,'curacc',clr='green') + add(acc,'acc',clr='darkgreen', style='--') + add(acc,'dec',clr='darkgreen', style='--') + + plt.draw() + if save: plt.savefig(fname+'.png') + if show: plt.show() + if not show: plt.close() + + + import sys + import os + def handle_path(p, show=True): + if '.' in p: + return + if os.path.isfile(p): + consume_file(p,show=show) + if os.path.isdir(p): + show=False + for subdir, dirs, files in os.walk(p): + for f in files: + handle_path(subdir+f,show=show) + + if len(sys.argv) >= 2: + handle_path(sys.argv[1]) + else: + handle_path('./') + )raw_str_delim"; + isSetup = true; + f.close(); + } + boost::filesystem::path tmppath(fpath / name); + f.open(tmppath.string()); + std::cout << "now writing to: " << boost::filesystem::absolute(tmppath).string() << "\n"; +} + +#else +#define LOG_CONTROLLER_DATA_WRITE_TO(name) do{}while(0) +#define LOG_CONTROLLER_DATA(...) do{}while(0) +#endif + + +unsigned int getSeed() +{ + static const auto seed = std::random_device {}(); + std::cout << "seed = " << seed << std::endl; + return seed; +} + +static std::mt19937 gen {getSeed()}; + +struct Simulation +{ + float time = 0; + float dt = 0; + float maxDt = 0.001; + + float curpos = 0; + float oldpos = 0; + float targpos = 0; + float posHiHard = M_PI; + float posLoHard = -M_PI; + float posHi = M_PI; + float posLo = -M_PI; + + float curvel = 0; + float oldvel = 0; + float targvel = 0; + float maxvel = 10; + + float curacc = 0; + float oldacc = 0; + float acc = 0; + float dec = 0; + + float brakingDist = 0; + float posAfterBraking = 0; + + + std::uniform_real_distribution<float> vDist; + std::uniform_real_distribution<float> aDist; + std::uniform_real_distribution<float> pDist; + std::uniform_real_distribution<float> tDist; + + void reset() + { + time = 0; + + curpos = 0; + oldpos = 0; + targpos = 0; + + curvel = 0; + oldvel = 0; + targvel = 0; + + curacc = 0; + oldacc = 0; + acc = 0; + dec = 0; + + brakingDist = 0; + posAfterBraking = 0; + + vDist = std::uniform_real_distribution<float> { -maxvel, maxvel}; + aDist = std::uniform_real_distribution<float> {maxvel / 4, maxvel * 4 }; + tDist = std::uniform_real_distribution<float> {maxDt * 0.75f, maxDt * 1.25f}; + pDist = std::uniform_real_distribution<float> {posLoHard, posHiHard}; + } + + //updating + template<class FNC> + void tick(FNC& callee) + { + dt = tDist(gen); + callee(); + log(); + } + + void updateVel(float newvel) + { + BOOST_CHECK(std::isfinite(newvel)); + //save old + oldvel = curvel; + oldpos = curpos; + oldacc = curacc; + + //update + curvel = newvel; + curacc = std::abs(curvel - oldvel) / dt; + curpos += curvel * dt; + time += dt; + brakingDist = brakingDistance(curvel, dec); + posAfterBraking = curpos + brakingDist; + } + + //checks + void checkVel() + { + //check v + BOOST_CHECK_LE(curvel, maxvel * 1.01); + BOOST_CHECK_LE(-maxvel * 1.01, curvel); + } + + void checkPos() + { + BOOST_CHECK_LE(curpos, posHiHard); + BOOST_CHECK_LE(posLoHard, curpos); + } + + void checkAcc() + { + if (sign(curvel) != sign(oldvel)) + { + // std::cout<< "Time["<<time<<"] velocity changed sign (from/to):" << sign(oldvel) << " / " << sign(curvel)<<"\n"; + return; + } + if (std::abs(oldvel) > std::abs(curvel)) + { + //we decellerated + if (!(curacc < dec * 1.01)) + { + std::cout << "Time[" << time << "] violated deceleration bound! vold " << oldvel << " / vnew " << curvel << " / dv " << std::abs(oldvel - curvel) << " / dt " << dt << " / dec " << dec + << " / (targetv " << targvel << ")\n"; + } + BOOST_CHECK_LE(curacc, dec * 1.01); + } + else + { + //we accellerated + if (!(curacc < acc * 1.01)) + { + std::cout << "Time[" << time << "] violated deceleration bound! vold " << oldvel << " / vnew " << curvel << " / dv " << std::abs(oldvel - curvel) << " / dt " << dt << " / acc " << acc + << " / (targetv " << targvel << ")\n"; + } + BOOST_CHECK_LE(curacc, acc * 1.01); + } + } + + //logging + void writeHeader(const std::string& name) + { + LOG_CONTROLLER_DATA_WRITE_TO(name); + LOG_CONTROLLER_DATA("time curpos targpos posHiHard posLoHard posHi posLo curvel targvel maxvel curacc acc dec brakingDist posAfterBraking"); + reset(); + } + void log() + { + //output a neg val for dec and a pos val for acc + float outputacc; + if (sign(curvel) != sign(oldvel)) + { + // cant determine the current acceleration correctly (since we have an acceleration and deceleration phase) + outputacc = 0; + } + else + { + outputacc = curacc; + if (std::abs(oldvel) > std::abs(curvel)) + { + //we decelerated -> negative sign + outputacc *= -1; + } + } + LOG_CONTROLLER_DATA(time << " " << + curpos << " " << targpos << " " << posHiHard << " " << posLoHard << " " << posHi << " " << posLo << " " << + curvel << " " << targvel << " " << maxvel << " " << + outputacc << " " << acc << " " << -dec << " " << + brakingDist << " " << posAfterBraking); + } +}; + + +BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationBoundsTest) +{ + std::cout << "starting velocityControlWithAccelerationBoundsTest \n"; + Simulation s; + s.posHi = 0; + s.posLo = 0; + s.posHiHard = 0; + s.posLoHard = 0; + + float directSetVLimit = 0.005; + + + auto testTick = [&] + { + VelocityControllerWithAccelerationBounds ctrl; + ctrl.dt = s.dt; + ctrl.maxDt = s.maxDt; + ctrl.currentV = s.curvel; + ctrl.targetV = s.targvel; + ctrl.maxV = s.maxvel; + ctrl.acceleration = s.acc; + ctrl.deceleration = s.dec; + ctrl.directSetVLimit = directSetVLimit; + BOOST_CHECK(ctrl.validParameters()); + // s.updateVel(velocityControlWithAccelerationBounds(s.dt, s.maxDt, s.curvel, s.targvel, s.maxvel, s.acc, s.dec, directSetVLimit)); + s.updateVel(ctrl.run()); + s.checkVel(); + s.checkAcc(); + }; + + std::cout << "random tests\n"; + for (std::size_t try_ = 0; try_ < tries; ++ try_) + { + s.writeHeader("velCtrl+acc_random_" + std::to_string(try_)); + s.curvel = s.vDist(gen); + s.targvel = s.vDist(gen); + s.acc = s.aDist(gen); + s.dec = s.aDist(gen); + directSetVLimit = (std::min(s.acc, s.dec) * s.maxDt * 0.75f / 2.f) * 0.99f; // vlimit = accmin*dtmin/2 + + s.log(); + for (std::size_t tick = 0; tick < ticks; ++tick) + { + s.tick(testTick); + } + BOOST_CHECK_EQUAL(s.curvel, s.targvel); + } + std::cout << "bounds tests\n"; + std::cout << "TODO\n"; + ///TODO + + std::cout << "done velocityControlWithAccelerationBoundsTest \n"; +} + +BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationAndPositionBoundsTest) +{ + std::cout << "starting velocityControlWithAccelerationAndPositionBoundsTest \n"; + Simulation s; + + // const float positionSoftLimitViolationVelocity = 0.1; + float directSetVLimit = 0.005; + s.posLo = s.posLoHard * 0.99; + s.posHi = s.posHiHard * 0.99; + + auto testTick = [&] + { + VelocityControllerWithAccelerationAndPositionBounds ctrl; + ctrl.dt = s.dt; + ctrl.maxDt = s.maxDt; + ctrl.currentV = s.curvel; + ctrl.targetV = s.targvel; + ctrl.maxV = s.maxvel; + ctrl.acceleration = s.acc; + ctrl.deceleration = s.dec; + ctrl.directSetVLimit = directSetVLimit; + ctrl.currentPosition = s.curpos; + ctrl.positionLimitLoSoft = s.posLo; + ctrl.positionLimitHiSoft = s.posHi; + ctrl.positionLimitLoHard = s.posLoHard; + ctrl.positionLimitHiHard = s.posHiHard; + BOOST_CHECK(ctrl.validParameters()); + s.updateVel(ctrl.run()); + //s.updateVel(velocityControlWithAccelerationAndPositionBounds( + //s.dt, s.maxDt, + //s.curvel, s.targvel, s.maxvel, + //s.acc, s.dec, + //directSetVLimit, + //s.curpos, + //s.posLo, s.posHi, + //// positionSoftLimitViolationVelocity, + //s.posLoHard, s.posHiHard + //)); + s.checkPos(); + s.checkVel(); + }; + + std::cout << "random tests\n"; + for (std::size_t try_ = 0; try_ < tries; ++ try_) + { + s.writeHeader("velCtrl+acc+pos_random_" + std::to_string(try_)); + s.curvel = s.vDist(gen); + s.curpos = s.pDist(gen); + s.targvel = s.vDist(gen); + s.acc = s.aDist(gen); + s.dec = s.aDist(gen); + directSetVLimit = (std::min(s.acc, s.dec) * s.maxDt * 0.75f / 2.f) * 0.99f; // vlimit = accmin*dtmin/2 + s.log(); + for (std::size_t tick = 0; tick < ticks; ++tick) + { + s.tick(testTick); + } + } + std::cout << "bounds tests\n"; + std::cout << "TODO\n"; + ///TODO + + std::cout << "done velocityControlWithAccelerationAndPositionBoundsTest \n"; +} + +BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsTest) +{ + std::cout << "starting positionThroughVelocityControlWithAccelerationBounds \n"; + Simulation s; + s.posHi = 0; + s.posLo = 0; + + // float p = 0.05; + // const float pControlPosErrorLimit = 0.02; + // const float pControlVelLimit = 0.02; + float p = 0.1; + const float pControlPosErrorLimit = 0.01; + const float pControlVelLimit = 0.01; + + auto testTick = [&] + { + PositionThroughVelocityControllerWithAccelerationBounds ctrl; + ctrl.dt = s.dt; + ctrl.maxDt = s.maxDt; + ctrl.currentV = s.curvel; + ctrl.maxV = s.maxvel; + ctrl.acceleration = s.acc; + ctrl.deceleration = s.dec; + ctrl.currentPosition = s.curpos; + ctrl.targetPosition = s.targpos; + ctrl.pControlPosErrorLimit = pControlPosErrorLimit; + ctrl.pControlVelLimit = pControlVelLimit; + ctrl.p = p; + BOOST_CHECK(ctrl.validParameters()); + s.updateVel(ctrl.run()); + //s.updateVel(positionThroughVelocityControlWithAccelerationBounds( + //s.dt, s.maxDt, + //s.curvel, s.maxvel, + //s.acc, s.dec, + //s.curpos, s.targpos, + //pControlPosErrorLimit, pControlVelLimit, p + //)); + s.checkVel(); + s.checkAcc(); + }; + + std::cout << "random tests\n"; + for (std::size_t try_ = 0; try_ < tries; ++ try_) + { + s.writeHeader("posViaVelCtrl+acc_random_" + std::to_string(try_)); + s.curvel = s.vDist(gen); + s.curpos = s.pDist(gen); + s.targpos = s.pDist(gen); + s.acc = s.aDist(gen); + s.dec = s.aDist(gen); + // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 + s.log(); + for (std::size_t tick = 0; tick < ticks; ++tick) + { + s.tick(testTick); + } + BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); // allow error of 0.5729577951308232 deg + } + std::cout << "bounds tests\n"; + std::cout << "TODO\n"; + ///TODO + + std::cout << "done positionThroughVelocityControlWithAccelerationBounds \n"; +} + +BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationAndPositionBoundsTest) +{ + std::cout << "starting positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; + Simulation s; + s.posHi = 0; + s.posLo = 0; + // float p = 0.05; + // const float pControlPosErrorLimit = 0.02; + // const float pControlVelLimit = 0.02; + float p = 0.1; + const float pControlPosErrorLimit = 0.01; + const float pControlVelLimit = 0.01; + + auto testTick = [&] + { + PositionThroughVelocityControllerWithAccelerationAndPositionBounds ctrl; + ctrl.dt = s.dt; + ctrl.maxDt = s.maxDt; + ctrl.currentV = s.curvel; + ctrl.maxV = s.maxvel; + ctrl.acceleration = s.acc; + ctrl.deceleration = s.dec; + ctrl.currentPosition = s.curpos; + ctrl.targetPosition = s.targpos; + ctrl.pControlPosErrorLimit = pControlPosErrorLimit; + ctrl.pControlVelLimit = pControlVelLimit; + ctrl.p = p; + ctrl.positionLimitLo = s.posLoHard; + ctrl.positionLimitHi = s.posHiHard; + BOOST_CHECK(ctrl.validParameters()); + s.updateVel(ctrl.run()); + //s.updateVel(positionThroughVelocityControlWithAccelerationAndPositionBounds( + //s.dt, s.maxDt, + //s.curvel, s.maxvel, + //s.acc, s.dec, + //s.curpos, s.targpos, + //pControlPosErrorLimit, pControlVelLimit, p, + //s.posLoHard, s.posHiHard)); + s.checkPos(); + }; + + std::cout << "random tests\n"; + for (std::size_t try_ = 0; try_ < tries; ++ try_) + { + s.writeHeader("posViaVelCtrl+acc+pos_random_" + std::to_string(try_)); + s.curvel = s.vDist(gen); + s.curpos = s.pDist(gen); + s.targpos = s.pDist(gen); + s.acc = s.aDist(gen); + s.dec = s.aDist(gen); + // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 + s.log(); + for (std::size_t tick = 0; tick < ticks; ++tick) + { + s.tick(testTick); + } + BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.05); + } + std::cout << "bounds tests\n"; + std::cout << "TODO\n"; + ///TODO + + std::cout << "done positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; +} + +BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPositionTest) +{ + std::cout << "starting positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; + Simulation s; + s.posHi = 0; + s.posLo = 0; + // float p = 0.05; + // const float pControlPosErrorLimit = 0.02; + // const float pControlVelLimit = 0.02; + float p = 0.1; + const float pControlPosErrorLimit = 0.01; + const float pControlVelLimit = 0.01; + float direction; + + auto testTick = [&] + { + PositionThroughVelocityControllerWithAccelerationAndPositionBounds ctrl; + ctrl.dt = s.dt; + ctrl.maxDt = s.maxDt; + ctrl.currentV = s.curvel; + ctrl.maxV = s.maxvel; + ctrl.acceleration = s.acc; + ctrl.deceleration = s.dec; + ctrl.currentPosition = s.curpos; + ctrl.targetPosition = s.targpos; + ctrl.pControlPosErrorLimit = pControlPosErrorLimit; + ctrl.pControlVelLimit = pControlVelLimit; + ctrl.p = p; + ctrl.positionLimitLo = s.posLoHard; + ctrl.positionLimitHi = s.posHiHard; + BOOST_CHECK(ctrl.validParameters()); + s.updateVel(ctrl.run()); + //s.updateVel(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition( + //s.dt, s.maxDt, + //s.curvel, s.maxvel, + //s.acc, s.dec, + //s.curpos, s.targpos, + //pControlPosErrorLimit, pControlVelLimit, p, + //direction, + //s.posLoHard, s.posHiHard)); + s.checkVel(); + s.checkAcc(); + s.curpos = periodicClamp(s.curpos, s.posLoHard, s.posHiHard); + }; + + auto rngTestRuns = [&](int d) + { + std::cout << "random tests (dir=" << std::to_string(d) << "\n"; + for (std::size_t try_ = 0; try_ < tries; ++ try_) + { + s.writeHeader("posViaVelCtrl+acc+periodicPos+dir" + std::to_string(d) + "_random_" + std::to_string(try_)); + direction = d; + s.curvel = s.vDist(gen); + s.curpos = periodicClamp(s.pDist(gen), s.posLoHard, s.posHiHard); + s.targpos = periodicClamp(s.pDist(gen), s.posLoHard, s.posHiHard); + s.acc = s.aDist(gen); + s.dec = s.aDist(gen); + s.log(); + for (std::size_t tick = 0; tick < ticks; ++tick) + { + s.tick(testTick); + } + BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); + BOOST_CHECK_LE(std::abs(s.curvel), 0.1); + } + + }; + rngTestRuns(0); + + std::cout << "bounds tests\n"; + std::cout << "TODO\n"; + ///TODO + + std::cout << "done positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; +} + diff --git a/source/RobotAPI/libraries/RTRobotUnit/test/CMakeLists.txt b/source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt similarity index 70% rename from source/RobotAPI/libraries/RTRobotUnit/test/CMakeLists.txt rename to source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt index c8695b9ab1c8703fc789a737ddffc79c28b290e7..bc75c469343c89d07f893d786d72de4a16d5fe18 100644 --- a/source/RobotAPI/libraries/RTRobotUnit/test/CMakeLists.txt +++ b/source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LIBS ${LIBS} RTRobotUnit ) +set(LIBS ${LIBS} RobotUnit ) armarx_add_test(BasicControllerTest BasicControllerTest.cpp "${LIBS}") diff --git a/source/RobotAPI/components/units/RobotUnit/util.h b/source/RobotAPI/components/units/RobotUnit/util.h new file mode 100644 index 0000000000000000000000000000000000000000..da93e8a7d7f4a61242a1e07314f97ae5de661833 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/util.h @@ -0,0 +1,296 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::RobotUnit + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_UNIT_RobotAPI_RobotUnit_util_H +#define _ARMARX_UNIT_RobotAPI_RobotUnit_util_H + +#include <boost/units/detail/utility.hpp> +#include <string> +namespace armarx +{ + template<class T> + std::string getTypeName(const T&, bool withoutNamespaceSpecifier = false) + { + const std::string demangled = boost::units::detail::demangle(typeid(T).name()); + if (withoutNamespaceSpecifier) + { + std::size_t substrStart = 0; + std::size_t level = 0; + for (int i = static_cast<int>(demangled.size() - 1); i >= 0; --i) + { + + if (!level && demangled.at(i) == ':') + { + substrStart = i + 1; + break; + } + level += (demangled.at(i) == '>') - (demangled.at(i) == '<'); + } + return demangled.substr(substrStart); + } + return demangled; + } +} + +#include <string> + +namespace std +{ + inline const std::string& to_string(const std::string& s) + { + return s; + } + inline std::string to_string(std::string&& s) + { + return std::move(s); + } +} + +#include <ArmarXCore/core/util/TemplateMetaProgramming.h> +#include <vector> +#include <map> + +namespace armarx +{ + /** + * @brief This class is pretty much similar to a map. + * + * This class stores keys and values in two vectors and uses a map to map the keys to the index of the corresponding value + * This enables map acces and index access. + * The index of an inserted element never changes (this is different to a map). + * Index access may be required in a high frequency case (e.g. an rt control loop) + */ + template < class KeyT, class ValT, + class KeyContT = std::vector<KeyT>, + class ValContT = std::vector<ValT>, + class IdxT = typename ValContT::size_type, + class MapT = std::map<KeyT, IdxT >> + class KeyValueVector + { + public: + void add(KeyT key, ValT value) + { + if (indices_.find(key) != indices_.end()) + { + throw std::invalid_argument {"Already added a value for this key" + DescribeKey(&key)}; + } + indices_.emplace(key, size()); + values_.emplace_back(std::move(value)); + keys_.emplace_back(std::move(key)); + } + IdxT index(const KeyT& k) const + { + return indices_.at(k); + } + ValT& at(IdxT i) + { + return values_.at(i); + } + ValT const& at(IdxT i) const + { + return values_.at(i); + } + ValT& at(const KeyT& k) + { + return at(index(k)); + } + ValT const& at(const KeyT& k) const + { + return at(index(k)); + } + ValT& at(const KeyT& k, ValT& defaultVal) + { + return has(k) ? at(index(k)) : defaultVal; + } + ValT const& at(const KeyT& k, const ValT& defaultVal) const + { + return has(k) ? at(index(k)) : defaultVal; + } + IdxT size() const + { + return values_.size(); + } + ValContT const& values() const + { + return values_; + } + MapT const& indices() const + { + return indices_; + } + KeyContT const& keys() const + { + return keys_; + } + IdxT count(const KeyT& k) const + { + return indices_.count(k); + } + bool has(const KeyT& k) const + { + return indices_.count(k); + } + void clear() + { + indices_.clear(); + keys_.clear(); + values_.clear(); + } + + private: + template<class T, class = typename std::enable_if<meta::HasToString<T>::value>::type> + static std::string DescribeKey(const T* key) + { + return ": " + std::to_string(*key); + } + static std::string DescribeKey(...) + { + return ""; + } + + MapT indices_; + KeyContT keys_; + ValContT values_; + }; +} + +#include <atomic> +namespace armarx +{ + template <typename T> + struct AtomicWrapper + { + std::atomic<T> atom; + + AtomicWrapper() : atom {} {} + AtomicWrapper(const T& v) : atom {v} {} + AtomicWrapper(const std::atomic<T>& a) : atom {a.load()} {} + AtomicWrapper(const AtomicWrapper& other) : atom {other.atom.load()} {} + AtomicWrapper(AtomicWrapper&&) = default; + AtomicWrapper& operator=(const T& v) + { + atom.store(v); + return *this; + } + AtomicWrapper& operator=(const std::atomic<T>& a) + { + atom.store(a.load()); + return *this; + } + AtomicWrapper& operator=(const AtomicWrapper& other) + { + atom.store(other.atom.load()); + return *this; + } + AtomicWrapper& operator=(AtomicWrapper &&) = default; + + operator T() const + { + return atom.load(); + } + }; + +} + +#include <mutex> +#include <thread> +#include <ArmarXCore/core/logging/Logging.h> +namespace armarx +{ + template<class MutexT> + struct messaging_unique_lock + { + using mutex_type = MutexT; + messaging_unique_lock(messaging_unique_lock&&) = default; + messaging_unique_lock(MutexT& mtx): + l {mtx, std::defer_lock}, + m {&mtx} + { + bool isFree = l.try_lock(); + + if (isFree) + { + std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", already locked " << lock_count[m] << " times) (mutex was free) (type " + << getTypeName(mtx.native_handle()) << ") :\n" + << (lock_count[m] ? std::string {}: LogSender::CreateBackTrace()) << std::flush; + } + else + { + std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", already locked " << lock_count[m] << " times) (mutex was not free) (type " + << getTypeName(mtx.native_handle()) << ") :\n" + << (lock_count[m] ? std::string {}: LogSender::CreateBackTrace()) << std::flush; + l.lock(); + std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", now locked " << lock_count[m] << " times) (mutex was not free in 10ms)...done" << std::flush; + } + ++lock_count[m]; + } + ~messaging_unique_lock() + { + if (l.owns_lock()) + { + --lock_count[m]; + std::cout << "[thread " << std::this_thread::get_id() << "] unlocking mutex(" << m << ", still locked " << lock_count[m] << " times)" << std::flush; + } + } + std::unique_lock<MutexT> l; + const MutexT* const m; + + static thread_local std::map<const MutexT*, std::size_t> lock_count; + }; + template<class MutexT> + thread_local std::map<const MutexT*, std::size_t> messaging_unique_lock<MutexT>::lock_count {}; +} + +#define TYPEDEF_PTRS_SHARED(T) \ + class T; \ + using T##Ptr = std::shared_ptr<T>; \ + using Const##T##Ptr = std::shared_ptr<const T> + +#define TYPEDEF_PTRS_HANDLE(T) \ + class T; \ + using T##Ptr = IceUtil::Handle<T> + + + +//#include <memory> +//#include <boost/shared_ptr.hpp> +//#include <IceUtil/Handle.h> +//namespace armarx +//{ +// template<class T> +// class SharedPtr +// { +// public: + +// private: +// struct SharedPtrInterface +// { +// void reset(); +// T* get(); +// const T* get(); +// }; + +// instance +// }; +//} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/util.h.orig b/source/RobotAPI/components/units/RobotUnit/util.h.orig new file mode 100644 index 0000000000000000000000000000000000000000..facd776573ea9d5f8820b2475f5fd19b06da372a --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/util.h.orig @@ -0,0 +1,301 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::RobotUnit + * @author Raphael ( ufdrv at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_UNIT_RobotAPI_RobotUnit_util_H +#define _ARMARX_UNIT_RobotAPI_RobotUnit_util_H + +#include <boost/units/detail/utility.hpp> +#include <string> +namespace armarx +{ + template<class T> + std::string getTypeName(const T&, bool withoutNamespaceSpecifier = false) + { + const std::string demangled = boost::units::detail::demangle(typeid(T).name()); + if (withoutNamespaceSpecifier) + { + std::size_t substrStart = 0; + std::size_t level = 0; + for (int i = static_cast<int>(demangled.size() - 1); i >= 0; --i) + { + + if (!level && demangled.at(i) == ':') + { + substrStart = i + 1; + break; + } + level += (demangled.at(i) == '>') - (demangled.at(i) == '<'); + } + return demangled.substr(substrStart); + } + return demangled; + } +} + +#include <string> + +namespace std +{ + inline const std::string& to_string(const std::string& s) + { + return s; + } + inline std::string to_string(std::string&& s) + { + return std::move(s); + } +} + +#include <ArmarXCore/core/util/TemplateMetaProgramming.h> +#include <vector> +#include <map> + +namespace armarx +{ + /** + * @brief This class is pretty much similar to a map. + * + * This class stores keys and values in two vectors and uses a map to map the keys to the index of the corresponding value + * This enables map acces and index access. + * The index of an inserted element never changes (this is different to a map). + * Index access may be required in a high frequency case (e.g. an rt control loop) + */ + template < class KeyT, class ValT, + class KeyContT = std::vector<KeyT>, + class ValContT = std::vector<ValT>, + class IdxT = typename ValContT::size_type, + class MapT = std::map<KeyT, IdxT >> + class KeyValueVector + { + public: + void add(KeyT key, ValT value) + { + if (indices_.find(key) != indices_.end()) + { + throw std::invalid_argument {"Already added a value for this key" + describeKey(&key)}; + } + indices_.emplace(key, size()); + values_.emplace_back(std::move(value)); + keys_.emplace_back(std::move(key)); + } + IdxT index(const KeyT& k) const + { + return indices_.at(k); + } + ValT& at(IdxT i) + { + return values_.at(i); + } + ValT const& at(IdxT i) const + { + return values_.at(i); + } + ValT& at(const KeyT& k) + { + return at(index(k)); + } + ValT const& at(const KeyT& k) const + { + return at(index(k)); + } + ValT& at(const KeyT& k, ValT& defaultVal) + { + return has(k) ? at(index(k)) : defaultVal; + } + ValT const& at(const KeyT& k, const ValT& defaultVal) const + { + return has(k) ? at(index(k)) : defaultVal; + } + IdxT size() const + { + return values_.size(); + } + ValContT const& values() const + { + return values_; + } + MapT const& indices() const + { + return indices_; + } + KeyContT const& keys() const + { + return keys_; + } + IdxT count(const KeyT& k) const + { + return indices_.count(k); + } + bool has(const KeyT& k) const + { + return indices_.count(k); + } + void clear() + { + indices_.clear(); + keys_.clear(); + values_.clear(); + } + + private: + template<class T, class = typename std::enable_if<meta::HasToString<T>::value>::type> + static std::string describeKey(const T* key) + { + return ": " + std::to_string(*key); + } + static std::string describeKey(...) + { + return ""; + } + + MapT indices_; + KeyContT keys_; + ValContT values_; + }; +} + +#include <atomic> +namespace armarx +{ + template <typename T> + struct AtomicWrapper + { + std::atomic<T> atom; + + AtomicWrapper() : atom {} {} + AtomicWrapper(const T& v) : atom {v} {} + AtomicWrapper(const std::atomic<T>& a) : atom {a.load()} {} + AtomicWrapper(const AtomicWrapper& other) : atom {other.atom.load()} {} + AtomicWrapper(AtomicWrapper&&) = default; + AtomicWrapper& operator=(const T& v) + { + atom.store(v); + return *this; + } + AtomicWrapper& operator=(const std::atomic<T>& a) + { + atom.store(a.load()); + return *this; + } + AtomicWrapper& operator=(const AtomicWrapper& other) + { + atom.store(other.atom.load()); + return *this; + } + AtomicWrapper& operator=(AtomicWrapper &&) = default; + + operator T() const + { + return atom.load(); + } + }; + +} + +#include <mutex> +#include <thread> +#include <ArmarXCore/core/logging/Logging.h> +namespace armarx +{ + template<class MutexT> + struct messaging_unique_lock + { + using mutex_type = MutexT; + messaging_unique_lock(messaging_unique_lock&&) = default; + messaging_unique_lock(MutexT& mtx): + l {mtx, std::defer_lock}, + m {&mtx} + { +<<<<<<< HEAD + bool isFree = l.try_lock(); + + if (isFree) + { + std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", already locked " << lock_count[m] << " times) (mutex was free) (type " + << getTypeName(mtx.native_handle()) << ") :\n" + << (lock_count[m] ? std::string {}: LogSender::CreateBackTrace()) << std::flush; + } + else + { + std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", already locked " << lock_count[m] << " times) (mutex was not free) (type " + << getTypeName(mtx.native_handle()) << ") :\n" + << (lock_count[m] ? std::string {}: LogSender::CreateBackTrace()) << std::flush; + l.lock(); + std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", now locked " << lock_count[m] << " times) (mutex was not free in 10ms)...done" << std::flush; + } +======= +std::cout << "[thread " << std::this_thread::get_id() << "] locking mutex(" << m << ", already locked " << lock_count[m] << " times):\n" << (lock_count[m] ? std::string{}: LogSender::CreateBackTrace()) << std::flush; + l.lock(); +>>>>>>> 16039a95e1fda778a127463d88c6c4d36ce40c07 + ++lock_count[m]; + } + ~messaging_unique_lock() + { + if (l.owns_lock()) + { + --lock_count[m]; + std::cout << "[thread " << std::this_thread::get_id() << "] unlocking mutex(" << m << ", still locked " << lock_count[m] << " times)" << std::flush; + } + } + std::unique_lock<MutexT> l; + const MutexT* const m; + + static thread_local std::map<const MutexT*, std::size_t> lock_count; + }; + template<class MutexT> + thread_local std::map<const MutexT*, std::size_t> messaging_unique_lock<MutexT>::lock_count {}; +} + +#define TYPEDEF_PTRS_SHARED(T) \ + class T; \ + using T##Ptr = std::shared_ptr<T>; \ + using Const##T##Ptr = std::shared_ptr<const T> + +#define TYPEDEF_PTRS_HANDLE(T) \ + class T; \ + using T##Ptr = IceUtil::Handle<T> + + + +//#include <memory> +//#include <boost/shared_ptr.hpp> +//#include <IceUtil/Handle.h> +//namespace armarx +//{ +// template<class T> +// class SharedPtr +// { +// public: + +// private: +// struct SharedPtrInterface +// { +// void reset(); +// T* get(); +// const T* get(); +// }; + +// instance +// }; +//} + +#endif diff --git a/source/RobotAPI/interface/CMakeLists.txt b/source/RobotAPI/interface/CMakeLists.txt index 73a7048e0d97cd2235661f12c6b1623965e4f464..994893ab9bec42af1920282536410329be80ffac 100644 --- a/source/RobotAPI/interface/CMakeLists.txt +++ b/source/RobotAPI/interface/CMakeLists.txt @@ -2,7 +2,7 @@ ### CMakeLists.txt file for ArmarX Interfaces ### -set(ROBOTAPI_INTERFACE_DEPEND ArmarXCore) +set(ROBOTAPI_INTERFACE_DEPEND ArmarXCore ArmarXGui) set(SLICE_FILES observers/KinematicUnitObserverInterface.ice @@ -40,16 +40,14 @@ set(SLICE_FILES units/GamepadUnit.ice units/MetaWearIMUInterface.ice units/MetaWearIMU.ice + units/RobotUnit/LVL1Controller.ice + units/RobotUnit/LVL1TrajectoryController.ice + units/RobotUnit/RobotUnitInterface.ice components/ViewSelectionInterface.ice visualization/DebugDrawerInterface.ice - libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice - libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.ice - libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.ice - libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughControllerConfig.ice - libraries/RTRobotUnit/RobotUnitInterface.ice ) #core/RobotIK.ice diff --git a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughControllerConfig.ice b/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughControllerConfig.ice deleted file mode 100644 index e3256ae867ec27d348d0e75d03dae53cf3df6f63..0000000000000000000000000000000000000000 --- a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughControllerConfig.ice +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::LVL1KinematicUnitControllerConfigs - * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_ROBOTAPI_LVL1KinematicUnitControllerConfigs_SLICE_ -#define _ARMARX_ROBOTAPI_LVL1KinematicUnitControllerConfigs_SLICE_ - -#include <ArmarXCore/interface/core/BasicTypes.ice> -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice> - -module armarx -{ - class LVL1HolonomicPlatformVelocityPassThroughControllerConfig extends LVL1ControllerConfig - { - string platformName; - float initialVelocityX; - float initialVelocityY; - float initialVelocityRotation; - }; - - interface LVL1HolonomicPlatformVelocityPassThroughControllerInterface extends LVL1ControllerInterface - { - idempotent void setVelocites(float velocityX, float velocityY, float velocityRotation); - }; -}; -#endif diff --git a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.ice b/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.ice deleted file mode 100644 index cd0b074c863ce35d7ef68a831dd596fc661a38f3..0000000000000000000000000000000000000000 --- a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.ice +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::LVL1KinematicUnitControllerConfigs - * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_ROBOTAPI_LVL1KinematicUnitControllerConfigs_SLICE_ -#define _ARMARX_ROBOTAPI_LVL1KinematicUnitControllerConfigs_SLICE_ - -#include <ArmarXCore/interface/core/BasicTypes.ice> -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice> - -module armarx -{ - class LVL1KinematicUnitVelocityControllerConfig extends LVL1ControllerConfig - { - string jointName; - - float maxVelocity; - float acceleration; - float deceleration; - float maxDt; - float directSetLimit; - - float limitPositionLo; - float limitPositionHi; - - float initialVelocityTargetValue; - }; - - class LVL1KinematicUnitTorqueControllerConfig extends LVL1ControllerConfig - { - string jointName; - float maxTorque; - float initialTorqueTargetValue; - }; - - class LVL1KinematicUnitPositionControllerConfig extends LVL1ControllerConfig - { - string jointName; - - float maxVelocity; - float acceleration; - float deceleration; - float maxDt; - float directPControlLimit; - float p; - - float limitPositionLo; - float limitPositionHi; - - float initialPositionTargetValue; - }; -}; -#endif diff --git a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.ice b/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.ice deleted file mode 100644 index e997918d3224c208a16d66cd5c38fa81a9f2e6fb..0000000000000000000000000000000000000000 --- a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.ice +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::LVL1PassThroughController - * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_ROBOTAPI_LVL1PassThroughController_SLICE_ -#define _ARMARX_ROBOTAPI_LVL1PassThroughController_SLICE_ - -#include <ArmarXCore/interface/core/BasicTypes.ice> -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice> - -module armarx -{ - class LVL1PassThroughControllerConfig extends LVL1ControllerConfig - { - Ice::StringSeq jointNames; - }; - - interface LVL1PassThroughControllerInterface extends LVL1ControllerInterface - { - idempotent void setJoint(string joint, float value) throws InvalidArgumentException; - idempotent void setJoints(StringFloatDictionary values) throws InvalidArgumentException; - }; -}; -#endif diff --git a/source/RobotAPI/interface/libraries/RTRobotUnit/RobotUnitInterface.ice b/source/RobotAPI/interface/libraries/RTRobotUnit/RobotUnitInterface.ice deleted file mode 100644 index 2435b80f1ff44ecb4825028b07dcd79adf12073f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/interface/libraries/RTRobotUnit/RobotUnitInterface.ice +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::RobotUnit - * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_ROBOTAPI_UNITS_RobotUnit_SLICE_ -#define _ARMARX_ROBOTAPI_UNITS_RobotUnit_SLICE_ - -#include <ArmarXCore/interface/core/UserException.ice> - -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice> - -#include <RobotAPI/interface/units/KinematicUnitInterface.ice> -#include <RobotAPI/interface/units/ForceTorqueUnit.ice> -#include <RobotAPI/interface/units/InertialMeasurementUnit.ice> -#include <RobotAPI/interface/units/PlatformUnitInterface.ice> - -module armarx -{ - dictionary<string, Ice::StringSeq> JointNameToControlModesDictionary; - - dictionary<string, string> JointNameToLVL1Dictionary; - - interface RobotUnitInterface - { - //controllers - //names - ["cpp:const"] idempotent Ice::StringSeq getControllersKnown(); - ["cpp:const"] idempotent Ice::StringSeq getControllerNamesLoaded(); - ["cpp:const"] idempotent Ice::StringSeq getControllerNamesRequested(); - ["cpp:const"] idempotent Ice::StringSeq getControllerNamesActivated(); - //proxy - ["cpp:const"] idempotent StringLVL1ControllerPrxDictionary getControllersLoaded(); - ["cpp:const"] idempotent StringLVL1ControllerPrxDictionary getControllersRequested(); - ["cpp:const"] idempotent StringLVL1ControllerPrxDictionary getControllersActivated(); - //assignement - ["cpp:const"] idempotent JointNameToLVL1Dictionary getControllerJointAssignment(); - - //modes - ["cpp:const"] idempotent JointNameToControlModeDictionary getJointControlModesRequested(); - ["cpp:const"] idempotent JointNameToControlModeDictionary getJointControlModesActivated(); - ["cpp:const"] idempotent JointNameToControlModesDictionary getJointControlModesSupported(); - - //loading - void switchSetup(Ice::StringSeq controllerInstanceNames) throws InvalidArgumentException; - LVL1ControllerInterface* loadController(string className, string instanceName, LVL1ControllerConfig config) throws InvalidArgumentException; - - bool loadLibFromPath(string path); - bool loadLibFromPackage(string package, string lib); - - - //units - ["cpp:const"] idempotent KinematicUnitInterface* getKinematicUnit(); - ["cpp:const"] idempotent ForceTorqueUnitInterface* getForceTorqueUnit(); - ["cpp:const"] idempotent InertialMeasurementUnitInterface* getInertialMeasurementUnit(); - ["cpp:const"] idempotent PlatformUnitInterface* getPlatformUnitInterface(); - }; -}; - -#endif diff --git a/source/RobotAPI/interface/units/RobotUnit/LVL1Controller.ice b/source/RobotAPI/interface/units/RobotUnit/LVL1Controller.ice new file mode 100644 index 0000000000000000000000000000000000000000..a2e0f286a83b2e34cf9d9a60fea37b5991b67113 --- /dev/null +++ b/source/RobotAPI/interface/units/RobotUnit/LVL1Controller.ice @@ -0,0 +1,85 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::LVL1ControllerInterface + * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_ROBOTAPI_LVL1ControllerInterface_SLICE_ +#define _ARMARX_ROBOTAPI_LVL1ControllerInterface_SLICE_ + +#include <ArmarXCore/interface/core/BasicTypes.ice> +#include <ArmarXCore/interface/core/UserException.ice> +#include <ArmarXGui/interface/WidgetDescription.ice> + +module armarx +{ + interface LVL1ControllerInterface; + interface RobotUnitInterface; + + class LVL1ControllerConfig{}; + + struct LVL1ControllerDescription + { + string instanceName; + string className; + LVL1ControllerInterface* controller; + StringStringDictionary controlModeAssignment; + }; + sequence<LVL1ControllerDescription> LVL1ControllerDescriptionSeq; + + struct LVL1ControllerStatus + { + string instanceName; + bool active = false; + bool requested = false; + bool error = false; + long timestampUSec = 0; + }; + sequence<LVL1ControllerStatus> LVL1ControllerStatusSeq; + + struct LVL1ControllerDescriptionWithStatus + { + LVL1ControllerStatus status; + LVL1ControllerDescription description; + }; + sequence<LVL1ControllerDescriptionWithStatus> LVL1ControllerDescriptionWithStatusSeq; + + interface LVL1ControllerInterface + { + ["cpp:const"] idempotent string getClassName(); + ["cpp:const"] idempotent string getInstanceName(); + ["cpp:const"] idempotent StringStringDictionary getControlDeviceUsedControlModeMap(); + ["cpp:const"] idempotent bool isControllerActive(); + ["cpp:const"] idempotent bool isControllerRequested(); + ["cpp:const"] idempotent bool hasControllerError(); + ["cpp:const"] idempotent LVL1ControllerStatus getControllerStatus(); + ["cpp:const"] idempotent LVL1ControllerDescription getControllerDescription(); + ["cpp:const"] idempotent LVL1ControllerDescriptionWithStatus getControllerDescriptionWithStatus(); + ["cpp:const"] idempotent RobotUnitInterface* getRobotUnit(); + + ["cpp:const"] void activateController(); + ["cpp:const"] void deactivateController(); + + ["cpp:const"] idempotent WidgetDescription::StringWidgetDictionary getFunctionDescriptions(); + void callDescribedFunction(string fuinctionName, StringVariantBaseMap values) throws InvalidArgumentException; + }; + + dictionary<string, LVL1ControllerInterface*> StringLVL1ControllerPrxDictionary; +}; +#endif diff --git a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice b/source/RobotAPI/interface/units/RobotUnit/LVL1TrajectoryController.ice similarity index 52% rename from source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice rename to source/RobotAPI/interface/units/RobotUnit/LVL1TrajectoryController.ice index a229edc6ead598adad1cf61d0af6f630b616a389..47b8383bc2c725b6f5ea3a3ac70ee74f768ae6d7 100644 --- a/source/RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1Controller.ice +++ b/source/RobotAPI/interface/units/RobotUnit/LVL1TrajectoryController.ice @@ -14,32 +14,35 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @package RobotAPI::LVL1ControllerInterface - * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) + * @author Mirko Waechter ( mirko dot waechter at kit dot edu ) * @date 2017 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#ifndef _ARMARX_ROBOTAPI_LVL1ControllerInterface_SLICE_ -#define _ARMARX_ROBOTAPI_LVL1ControllerInterface_SLICE_ +#ifndef _ARMARX_ROBOTAPI_LVL1TrajectoryControllerInterface_SLICE_ +#define _ARMARX_ROBOTAPI_LVL1TrajectoryControllerInterface_SLICE_ -#include <RobotAPI/interface/units/KinematicUnitInterface.ice> +#include <RobotAPI/interface/units/RobotUnit/LVL1Controller.ice> +#include <RobotAPI/interface/core/Trajectory.ice> module armarx { - dictionary<string, string> JointNameToControlModeDictionary; - class LVL1ControllerConfig + class LVL1TrajectoryControllerConfig extends LVL1ControllerConfig { + float maxAcceleration = 3; + float maxVelocity = 1.4; + float maxDeviation = 5; + float preSamplingStepMs = 10; + Ice::StringSeq jointNames; + float PID_p = 1; + float PID_i = 0; + float PID_d = 0; }; - interface LVL1ControllerInterface + interface LVL1TrajectoryControllerInterface extends LVL1ControllerInterface { - ["cpp:const"] idempotent string getClassName(); - ["cpp:const"] idempotent string getInstanceName(); - ["cpp:const"] idempotent JointNameToControlModeDictionary getJointControlModeMap(); - ["cpp:const"] idempotent bool isControllerActive(); + void setTrajectory(TrajectoryBase traj); }; - - dictionary<string, LVL1ControllerInterface*> StringLVL1ControllerPrxDictionary; }; #endif diff --git a/source/RobotAPI/interface/units/RobotUnit/RobotUnitInterface.ice b/source/RobotAPI/interface/units/RobotUnit/RobotUnitInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..01db232c7530b245c841c575f0b1f2183e16e96e --- /dev/null +++ b/source/RobotAPI/interface/units/RobotUnit/RobotUnitInterface.ice @@ -0,0 +1,160 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::RobotUnit + * @author Raphael Grimm ( raphael dot grimm at student dot kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_ROBOTAPI_UNITS_RobotUnit_SLICE_ +#define _ARMARX_ROBOTAPI_UNITS_RobotUnit_SLICE_ + +#include <ArmarXCore/interface/core/UserException.ice> +#include <ArmarXCore/interface/core/BasicTypes.ice> + +#include <ArmarXGui/interface/WidgetDescription.ice> + +#include <RobotAPI/interface/units/RobotUnit/LVL1Controller.ice> + +#include <RobotAPI/interface/units/KinematicUnitInterface.ice> +#include <RobotAPI/interface/units/ForceTorqueUnit.ice> +#include <RobotAPI/interface/units/InertialMeasurementUnit.ice> +#include <RobotAPI/interface/units/PlatformUnitInterface.ice> + +module armarx +{ + dictionary<string, Ice::StringSeq> ControlDeviceNameToControlModesDictionary; + + dictionary<string, string> ControlDeviceNameToLVL1ControllerNameDictionary; + + struct ControlDeviceDescription + { + string deviceName; + StringStringDictionary contolModeToTargetType; + Ice::StringSeq tags; + }; + sequence<ControlDeviceDescription> ControlDeviceDescriptionSeq; + struct ControlDeviceStatus + { + string deviceName; + string activeControlMode; + string requestedControlMode; + StringToStringVariantBaseMapMap controlTargetValues; + long timestampUSec = 0; + }; + sequence<ControlDeviceStatus> ControlDeviceStatusSeq; + + + struct SensorDeviceDescription + { + string deviceName; + string sensorValueType; + Ice::StringSeq tags; + }; + sequence<SensorDeviceDescription> SensorDeviceDescriptionSeq; + struct SensorDeviceStatus + { + string deviceName; + StringVariantBaseMap sensorValue; + long timestampUSec = 0; + }; + sequence<SensorDeviceStatus> SensorDeviceStatusSeq; + + + struct LVL1ControllerClassDescription + { + string className; + WidgetDescription::Widget configDescription; + }; + sequence<LVL1ControllerClassDescription> LVL1ControllerClassDescriptionSeq; + + interface RobotUnitInterface + { + //controllers + //names + ["cpp:const"] idempotent Ice::StringSeq getLVL1ControllerNames(); + ["cpp:const"] idempotent Ice::StringSeq getRequestedLVL1ControllerNames(); + ["cpp:const"] idempotent Ice::StringSeq getActivatedLVL1ControllerNames(); + //proxy/information + ["cpp:const"] idempotent LVL1ControllerInterface* getLVL1Controller(string name); + ["cpp:const"] idempotent StringLVL1ControllerPrxDictionary getAllLVL1Controllers(); + + ["cpp:const"] idempotent LVL1ControllerStatus getLVL1ControllerStatus(string name) throws InvalidArgumentException; + ["cpp:const"] idempotent LVL1ControllerStatusSeq getLVL1ControllerStatuses(); + + ["cpp:const"] idempotent LVL1ControllerDescription getLVL1ControllerDescription(string name) throws InvalidArgumentException; + ["cpp:const"] idempotent LVL1ControllerDescriptionSeq getLVL1ControllerDescriptions(); + + ["cpp:const"] idempotent LVL1ControllerDescriptionWithStatus getLVL1ControllerDescriptionWithStatus(string name) throws InvalidArgumentException; + ["cpp:const"] idempotent LVL1ControllerDescriptionWithStatusSeq getLVL1ControllerDescriptionsWithStatuses(); + + //devices + ["cpp:const"] idempotent Ice::StringSeq getControlDeviceNames() throws LogicError; + ["cpp:const"] idempotent ControlDeviceDescription getControlDeviceDescription(string name) throws InvalidArgumentException, LogicError; + ["cpp:const"] idempotent ControlDeviceDescriptionSeq getControlDeviceDescriptions() throws LogicError; + ["cpp:const"] idempotent ControlDeviceStatus getControlDeviceStatus(string name) throws InvalidArgumentException, LogicError; + ["cpp:const"] idempotent ControlDeviceStatusSeq getControlDeviceStatuses() throws LogicError; + + ["cpp:const"] idempotent Ice::StringSeq getSensorDeviceNames() throws LogicError; + ["cpp:const"] idempotent SensorDeviceDescription getSensorDeviceDescription(string name) throws InvalidArgumentException, LogicError; + ["cpp:const"] idempotent SensorDeviceDescriptionSeq getSensorDeviceDescriptions() throws LogicError; + ["cpp:const"] idempotent SensorDeviceStatus getSensorDeviceStatus(string name) throws InvalidArgumentException, LogicError; + ["cpp:const"] idempotent SensorDeviceStatusSeq getSensorDeviceStatuses() throws LogicError; + //classes + ["cpp:const"] idempotent Ice::StringSeq getLVL1ControllerClassNames(); + ["cpp:const"] idempotent LVL1ControllerClassDescription getLVL1ControllerClassDescription(string name) throws InvalidArgumentException; + ["cpp:const"] idempotent LVL1ControllerClassDescriptionSeq getLVL1ControllerClassDescriptions(); + + //switching + void switchControllerSetup(Ice::StringSeq newSetup) throws InvalidArgumentException, LogicError; + + void activateController(string controllerInstanceName) throws InvalidArgumentException, LogicError; + void activateControllers(Ice::StringSeq controllerInstanceNames) throws InvalidArgumentException, LogicError; + void deactivateController(string controllerInstanceName)throws InvalidArgumentException, LogicError; + void deactivateControllers(Ice::StringSeq controllerInstanceNames)throws InvalidArgumentException, LogicError; + + //creting controllers + LVL1ControllerInterface* createController(string className, string instanceName, LVL1ControllerConfig config) throws InvalidArgumentException, LogicError; + LVL1ControllerInterface* createControllerFromVariantConfig(string className, string instanceName, StringVariantBaseMap config) throws InvalidArgumentException, LogicError; + + //loading + bool loadLibFromPath(string path); + bool loadLibFromPackage(string package, string libname); + + //units + ["cpp:const"] idempotent Object* getUnit(string staticIceId); + ["cpp:const"] idempotent Ice::ObjectProxySeq getUnits(); + ["cpp:const"] idempotent KinematicUnitInterface* getKinematicUnit(); + ["cpp:const"] idempotent ForceTorqueUnitInterface* getForceTorqueUnit(); + ["cpp:const"] idempotent InertialMeasurementUnitInterface* getInertialMeasurementUnit(); + ["cpp:const"] idempotent PlatformUnitInterface* getPlatformUnit(); + + //other + ["cpp:const"] idempotent string getListenerTopicName(); + }; + + interface RobotUnitListener + { + void lVl1ControllerStatusChanged(LVL1ControllerStatus status); + void controlDeviceStatusChanged(ControlDeviceStatus status); + void sensorDeviceStatusChanged(SensorDeviceStatus status); + void lvl1ControllerClassAdded(string className); + void lvl1ControllerAdded(string instanceName); + }; +}; + +#endif diff --git a/source/RobotAPI/libraries/CMakeLists.txt b/source/RobotAPI/libraries/CMakeLists.txt index d7fc2aeeecee68490f37a56ee67cc042f554a638..10a98da381b0b0b4da90b92fdecfad188f08a1e7 100644 --- a/source/RobotAPI/libraries/CMakeLists.txt +++ b/source/RobotAPI/libraries/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(core) add_subdirectory(widgets) -add_subdirectory(RTRobotUnit) +add_subdirectory(RobotAPIVariantWidget) diff --git a/source/RobotAPI/libraries/RTRobotUnit/BasicControllers.cpp b/source/RobotAPI/libraries/RTRobotUnit/BasicControllers.cpp deleted file mode 100644 index 9be5c8bd3aebbb90d7aa637fe407e639f365dff1..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/BasicControllers.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "BasicControllers.h" - -#include <boost/algorithm/clamp.hpp> - -#include <ArmarXCore/core/util/algorithm.h> - -///REMOVE -#include <iostream> - -namespace armarx -{ - float velocityControlWithAccelerationBounds( - float dt, float maxDt, - const float currentV, float targetV, float maxV, - float acceleration, float deceleration, - float directSetVLimit - ) - { - dt = std::min(std::abs(dt), std::abs(maxDt)); - maxV = std::abs(maxV); - acceleration = std::abs(acceleration); - deceleration = std::abs(deceleration); - targetV = boost::algorithm::clamp(targetV, -maxV, maxV); - - //we can have 3 cases: - // 1. we directly set v and ignore acc/dec (if |curr - target| <= limit) - // 2. we need to accelerate (if curr and target v have same sign and |curr| < |target|) - // 3. we need to decellerate (other cases) - - //handle case 1 - const float curverror=targetV - currentV; - if (std::abs(curverror) < directSetVLimit) - { - return targetV; - } - - //handle case 2 + 3 - const bool accelerate = sign(targetV) == sign(currentV) && // v in same direction - std::abs(targetV) > std::abs(currentV); // currently to slow - - const float usedacc = accelerate? acceleration:-deceleration; - const float maxDeltaV = std::abs(usedacc * dt); - if(maxDeltaV >= std::abs(curverror)) - { - //we change our v too much with the used acceleration - //but we could reach our target with a lower acceleration ->set the target - - return targetV; - } - const float deltaVel = boost::algorithm::clamp(sign(currentV) * usedacc * dt, -maxDeltaV, maxDeltaV); - const float nextV = currentV + deltaVel; - return nextV; - } - -//////////////////////////// -//wip? - float velocityControlWithAccelerationAndPositionBounds( - float dt, float maxDt, - float currentV, float targetV, float maxV, - float acceleration, float deceleration, - float directSetVLimit, - float currentPosition, - float positionLimitLoSoft, float positionLimitHiSoft, - float positionSoftLimitViolationVelocity, - float positionLimitLoHard, float positionLimitHiHard) - { - if(currentPosition <= positionLimitLoHard || currentPosition >= positionLimitHiHard) - { - return std::nanf("1"); - } - - float softLimitViolation =0; - if (currentPosition <= positionLimitLoSoft) - { - softLimitViolation = -1; - } - if (currentPosition >= positionLimitHiSoft) - { - softLimitViolation = 1; - } - - const float upperDt = std::max(std::abs(dt), std::abs(maxDt)); - dt = std::min(std::abs(dt), std::abs(maxDt)); - maxV = std::abs(maxV); - - //we can have 4 cases: - // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them, e.g. if we violated them initially) - // 2. we directly set v and ignore acc/dec (if |curr - target| <= limit) - // 3. we need to accelerate (if curr and target v have same sign and |curr| < |target|) - // 4. we need to decellerate (other cases) - float nextv; - //handle case 1 - const float vsquared = currentV * currentV; - const float brakingDist = sign(currentV) * vsquared / 2.f / std::abs(deceleration); //the braking distance points in the direction of the velocity - - - const float posIfBrakingNow = currentPosition + brakingDist; - if (posIfBrakingNow <= positionLimitLoSoft || posIfBrakingNow >= positionLimitHiSoft) - { - //case 1. -> brake now! (we try to have v=0 at the limit) - const auto limit = posIfBrakingNow <= positionLimitLoSoft ? positionLimitLoSoft : positionLimitHiSoft; - const float wayToGo = limit - currentPosition; - - //decellerate! - // s = v²/(2a) <=> a = v²/(2s) - const float dec = std::abs(vsquared / 2.f / wayToGo); - const float vel = currentV - sign(currentV) * dec * upperDt; - nextv = boost::algorithm::clamp(vel,-maxV,maxV); - if(sign(currentV) != sign(nextv)) - { - //stop now - nextv = 0; - } - } - else - { - //handle 2-3 - nextv = velocityControlWithAccelerationBounds(dt, maxDt, currentV, targetV, maxV, acceleration, deceleration, directSetVLimit); - } - if(softLimitViolation == sign(nextv)) - { - //the area between soft and hard limits is sticky - //the controller can only move out of it (not further in) - return 0; - } - - //the next velocity will not violate the pos bounds harder than they are already - return nextv; - } - - - float positionThroughVelocityControlWithAccelerationBounds( - float dt, float maxDt, - float currentV, float maxV, - float acceleration, float deceleration, - float currentPosition, float targetPosition, - float pControlPosErrorLimit, float pControlVelLimit, float p - ) - { - dt = std::min(std::abs(dt), std::abs(maxDt)); - maxV = std::abs(maxV); - acceleration = std::abs(acceleration); - deceleration = std::abs(deceleration); - pControlPosErrorLimit = std::abs(pControlPosErrorLimit); - pControlVelLimit = std::abs(pControlVelLimit); - const float signV = sign(currentV); - //we can have 3 cases: - // 1. we use a p controller and ignore acc/dec (if |currentPosition - targetPosition| <= pControlPosErrorLimit AND |currentV| < pControlVelLimit) - // 2. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) - // the brakingDistance have the same sign and brakingDistance < e - // and currentVel <= maxV) - // 3. we need to decellerate (other cases) - - //handle case 1 - const float positionError = targetPosition - currentPosition; - if (std::abs(positionError) < pControlPosErrorLimit && std::abs(currentV) < pControlVelLimit) - { - return positionError * p; - } - - //handle case 2-3 - const float brakingDistance = signV * currentV * currentV / 2.f / deceleration; //the braking distance points in the direction of the velocity - const float posIfBrakingNow = currentPosition + brakingDistance; - const float posErrorIfBrakingNow = targetPosition - posIfBrakingNow; - - const bool decelerate = - std::abs(currentV) > maxV || // we need to slow down (to stay in [-maxV,maxV] - std::abs(posIfBrakingNow-targetPosition) <= pControlPosErrorLimit || // we want to hit the target - sign(posErrorIfBrakingNow) != signV; // we are moving away from the target - const float usedacc= decelerate ? -deceleration : acceleration; - const float maxDeltaV = std::abs(usedacc * dt); - const float deltaVel = boost::algorithm::clamp(signV * usedacc * dt, -maxDeltaV, maxDeltaV); - return boost::algorithm::clamp(currentV + deltaVel, -maxV, maxV); - } - - float positionThroughVelocityControlWithAccelerationAndPositionBounds( - float dt, float maxDt, - float currentV, float maxV, - float acceleration, float deceleration, - float currentPosition, float targetPosition, - float pControlPosErrorLimit, float pControlVelLimit, float p, - float positionLimitLo, float positionLimitHi - - ) - { - dt = std::min(std::abs(dt), std::abs(maxDt)); - maxV = std::abs(maxV); - acceleration = std::abs(acceleration); - deceleration = std::abs(deceleration); - pControlPosErrorLimit = std::abs(pControlPosErrorLimit); - const float signV = sign(currentV); - //we can have 4 cases: - // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them) - // 2. we use a p controller and ignore acc/dec (if |currentPosition - targetPosition| <= pControlPosErrorLimit AND |currentPosition - targetPosition|*p < maxV) - // 4. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) - // and the brakingDistance have the same sign and brakingDistance < e - // and currentVel <= maxV) - // 5. we need to decellerate (other cases) - - //handle case 1 - const float vsquared = currentV * currentV; - const float brakingDistance = signV * vsquared / 2.f / deceleration; //the braking distance points in the direction of the velocity - const float posIfBrakingNow = currentPosition + brakingDistance; - if (posIfBrakingNow <= positionLimitLo || posIfBrakingNow >= positionLimitHi) - { - //case 1. -> brake now! (we try to have v=0 at the limit) - const auto limit = brakingDistance > 0 ? positionLimitHi : positionLimitLo; - const float wayToGo = std::abs(limit - currentPosition); - // s = v²/(2a) <=> a = v²/(2s) - const float dec = std::abs(vsquared / 2.f / wayToGo); - const float vel = currentV - signV * dec * dt; - return vel; - } - - //handle case 2-3 - return positionThroughVelocityControlWithAccelerationBounds( - dt, maxDt, - currentV, maxV, - acceleration, deceleration, - currentPosition, boost::algorithm::clamp(targetPosition, positionLimitLo, positionLimitHi), - pControlPosErrorLimit, pControlVelLimit, p - ); - } - - //////////////////////////// - //wip - - - float positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition( - float dt, float maxDt, - float currentV, float maxV, - float acceleration, float deceleration, - float currentPosition, float targetPosition, - float pControlPosErrorLimit, float pControlVelLimit, float p, - float &direction, - float positionPeriodLo, float positionPeriodHi) - { - currentPosition = periodicClamp(currentPosition, positionPeriodLo, positionPeriodHi); - targetPosition = periodicClamp(targetPosition , positionPeriodLo, positionPeriodHi); - - const float brakingDist = brakingDistance(currentV, deceleration); - const float posIfBrakingNow = currentPosition + brakingDist; - const float posIfBrakingNowError = targetPosition - posIfBrakingNow; - const float posError = targetPosition - currentPosition; - if( - std::abs(posIfBrakingNowError) <= pControlPosErrorLimit || - std::abs(posError) <= pControlPosErrorLimit - ) - { - //this allows slight overshooting (in the limits of the p controller) - return positionThroughVelocityControlWithAccelerationBounds( - dt, maxDt, - currentV, maxV, - acceleration, deceleration, - currentPosition, targetPosition, - pControlPosErrorLimit, pControlVelLimit, p - ); - } - - //we transform the problem with periodic bounds into - //a problem with position bounds - const float positionPeriodLength = std::abs(positionPeriodHi - positionPeriodLo); - - //we shift the positions such that 0 == target - currentPosition = periodicClamp(currentPosition - targetPosition, 0.f, positionPeriodLength); - //how many times will we go ovet the target if we simply bake now? - const float overshoot = std::trunc((currentPosition + brakingDist)/ positionPeriodLength); - - if(true||direction == 0) - { - //determine the direction to go (1 == pos vel, -1 == neg vel) - direction = (periodicClamp(currentPosition + brakingDist, 0.f , positionPeriodLength) >= positionPeriodLength/2) ? 1: -1; - } - //shift the target away from 0 - targetPosition = (overshoot-std::min(0.f,-direction)) * positionPeriodLength;// - direction * pControlPosErrorLimit; - - //move - return positionThroughVelocityControlWithAccelerationBounds( - dt, maxDt, - currentV, maxV, - acceleration, deceleration, - currentPosition, targetPosition, - pControlPosErrorLimit, pControlVelLimit, p - ); - } - - -} diff --git a/source/RobotAPI/libraries/RTRobotUnit/BasicControllers.h b/source/RobotAPI/libraries/RTRobotUnit/BasicControllers.h deleted file mode 100644 index 9fb5ae18389a76c6841e410e6bee1856f55c11f6..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/BasicControllers.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_BasicControllers_H -#define _ARMARX_LIB_RobotAPI_BasicControllers_H - -#include <cmath> -#include <ArmarXCore/core/util/algorithm.h> - -namespace armarx -{ - /** - * @param v0 The initial velocity. - * @param deceleration The deceleration. - * @return The braking distance given the parameters. - */ - inline float brakingDistance(float v0, float deceleration) - { - return sign(v0)*std::pow(v0, 2.f) / 2.f / std::abs(deceleration); - } - - template<class T> - T periodicClamp(T value, T periodLo, T periodHi) - { - float dist = periodHi - periodLo; - return std::fmod(value-periodLo + dist, dist) + periodLo; - } - - float velocityControlWithAccelerationBounds( - float dt, float maxDt, - const float currentV, float targetV, float maxV, - float acceleration, float deceleration, - float directSetVLimit); - /** - * @brief Performs velocity control while staying in positional bounds, obeying acceleration / deceleration and staying below a velocity bound. - * - * we can have 4 cases: - * 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them. e.f. if we already violate them or the robot can't brake fast enough) - * 2. we directly set v and ignore acc/dec (if |currentV - targetV| <= directSetVLimit) - * 3. we need to accelerate (if currentV and targetV have same sign and |currentV| < |currentV|) - * 4. we need to decellerate (other cases) - * @param dt The time in seconds until the next call is made. (use the time since the last call as an approximate) - * @param maxDt Limits dt in case the given approximation has a sudden high value. - * @param currentV - * @param targetV - * @param maxV - * @param acceleration - * @param deceleration - * @param directSetVLimit In case |currentV - targetV| <= directSetVLimit this function will return targetV (if the position bounds are not violated) - * @param currentPosition - * @param positionLimitLo - * @param positionLimitHi - * @return The next velocity. - */ - float velocityControlWithAccelerationAndPositionBounds(float dt, float maxDt, - float currentV, float targetV, float maxV, - float acceleration, float deceleration, - float directSetVLimit, - float currentPosition, - float positionLimitLoSoft, float positionLimitHiSoft, - float positionSoftLimitViolationVelocity, - float positionLimitLoHard, float positionLimitHiHard); - - float positionThroughVelocityControlWithAccelerationBounds(float dt, float maxDt, - float currentV, float maxV, - float acceleration, float deceleration, - float currentPosition, float targetPosition, - float pControlPosErrorLimit, float pControlVelLimit, float p - ); - - float positionThroughVelocityControlWithAccelerationAndPositionBounds( - float dt, float maxDt, - float currentV, float maxV, - float acceleration, float deceleration, - float currentPosition, float targetPosition, - float pControlPosErrorLimit, float pControlVelLimit, float p, - float positionLimitLo, float positionLimitHi); - - // float positionThroughVelocityControlWithAccelerationBounds( - // float dt, float maxDt, - // float currentV, float maxV, - // float acceleration, float deceleration, - // float currentPosition, float targetPosition, - // float pControlPosErrorLimit, float p); - - - - float positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition(float dt, float maxDt, - float currentV, float maxV, - float acceleration, float deceleration, - float currentPosition, float targetPosition, - float pControlPosErrorLimit, float pControlVelLimit, float p, - float& direction, - float positionPeriodLo, float positionPeriodHi); -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/CMakeLists.txt b/source/RobotAPI/libraries/RTRobotUnit/CMakeLists.txt deleted file mode 100644 index eeda8773d2a3124e0af4b9c0519fe2d3e7f8b97b..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -set(LIB_NAME RTRobotUnit) - -armarx_component_set_name("${LIB_NAME}") -armarx_set_target("Library: ${LIB_NAME}") - -set(LIBS - ArmarXCoreInterfaces - ArmarXCore - RobotAPIUnits - RobotAPIInterfaces -) - -set(LIB_FILES - SyntaxCheck.cpp - - LVL0Controllers/LVL0Controller.cpp - - LVL1Controllers/LVL1Controller.cpp - LVL1Controllers/LVL1PassThroughController.cpp - LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.cpp - LVL1Controllers/LVL1KinematicUnitPositionController.cpp - LVL1Controllers/LVL1KinematicUnitTorqueController.cpp - LVL1Controllers/LVL1KinematicUnitVelocityController.cpp - - RobotUnit.cpp - BasicControllers.cpp -) -set(LIB_HEADERS - Constants.h - ControlModes.h - - Targets/TargetBase.h - Targets/ActuatorPositionTarget.h - Targets/ActuatorVelocityTarget.h - Targets/ActuatorTorqueTarget.h - Targets/PlatformWheelVelocityTarget.h - Targets/HolonomicPlatformVelocityTarget.h - - DataUnits/ForceTorqueDataUnit.h - DataUnits/HapticDataUnit.h - DataUnits/IMUDataUnit.h - DataUnits/KinematicDataUnit.h - DataUnits/PlatformDataUnit.h - - LVL0Controllers/LVL0Controller.h - - LVL1Controllers/LVL1Controller.h - LVL1Controllers/LVL1PassThroughController.h - LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.h - LVL1Controllers/LVL1KinematicUnitPositionController.h - LVL1Controllers/LVL1KinematicUnitTorqueController.h - LVL1Controllers/LVL1KinematicUnitVelocityController.h - - RobotUnit.h - BasicControllers.h -) - -#add_subdirectory(test) - -armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") - diff --git a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/KinematicDataUnit.h b/source/RobotAPI/libraries/RTRobotUnit/DataUnits/KinematicDataUnit.h deleted file mode 100644 index fb5c6851f10434f6d54a6c7e4730df5f4b928d76..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/KinematicDataUnit.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::KinematicDataUnit - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_KinematicDataUnit_H -#define _ARMARX_LIB_RobotAPI_KinematicDataUnit_H - -#include <vector> -#include <string> -#include <unordered_map> -#include <algorithm> - -#include <ArmarXCore/core/util/algorithm.h> - -namespace armarx -{ - class KinematicDataUnitInterface - { - public: - virtual const std::vector<std::string>& getJointNames() const = 0; - - virtual std::vector<const float*> getJointAngles(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const float*> getJointVelocities(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const float*> getJointTorques(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const float*> getJointCurrents(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const float*> getJointMotorTemperatures(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const float*> getJointGearTemperatures(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int32_t*> - getJointRawPositionValues(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int32_t*> - getJointRawVelocityValues(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int16_t*> getJointRawCurrentValues(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int32_t*> getJointRawTorqueValues(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int32_t*> - getJointRawGearTemperatures(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int32_t*> - getJointRawAbsPositionValues(const std::vector<std::string>& joints) const = 0; - - virtual std::vector<const int16_t*> - getJointRawMotorTemperatureValues(const std::vector<std::string>& joints) const = 0; - }; - - class KinematicDataUnitPtrProvider : virtual public KinematicDataUnitInterface - { - public: - KinematicDataUnitPtrProvider(const std::vector<std::string>& jointNames) : - jointAngles(jointNames.size(), nullptr), - jointVelocites(jointNames.size(), nullptr), - jointTorques(jointNames.size(), nullptr), - jointCurrents(jointNames.size(), nullptr), - jointMotorTemperatures(jointNames.size(), nullptr), - jointGearTemperatures(jointNames.size(), nullptr), - jointAbsPositions(jointNames.size(), nullptr), - jointRawPositionValues(jointNames.size(), nullptr), - jointRawVelocityValues(jointNames.size(), nullptr), - jointRawCurrentValues(jointNames.size(), nullptr), - jointRawTorqueValues(jointNames.size(), nullptr), - jointRawGearTemperatures(jointNames.size(), nullptr), - jointRawAbsPositionValues(jointNames.size(), nullptr), - jointRawMotorTemperatureValues(jointNames.size(), nullptr), - jointNames {jointNames}, - jointIndices {toIndexMap(jointNames)} {} - - KinematicDataUnitPtrProvider() = default; - - KinematicDataUnitPtrProvider(const KinematicDataUnitPtrProvider&) = default; - - void setJointNames(std::vector<std::string>& names) - { - jointNames = names; - jointIndices = toIndexMap(names); - - jointAngles = std::vector<float*>(names.size(), nullptr); - jointVelocites = std::vector<float*>(jointNames.size(), nullptr); - jointTorques = std::vector<float*>(jointNames.size(), nullptr); - jointCurrents = std::vector<float*>(jointNames.size(), nullptr); - jointMotorTemperatures = std::vector<float*>(jointNames.size(), nullptr); - jointGearTemperatures = std::vector<float*>(jointNames.size(), nullptr); - jointAbsPositions = std::vector<float*>(jointNames.size(), nullptr); - } - - const std::vector<std::string>& getJointNames() const - { - return jointNames; - } - - std::size_t getJointIndex(const std::string& joint) const - { - return jointIndices.at(joint); - } - - virtual std::vector<const float*> getJointAngles(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointAngles); - } - - virtual std::vector<const float*> getJointVelocities(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointVelocites); - } - - virtual std::vector<const float*> getJointTorques(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointTorques); - } - - virtual std::vector<const float*> getJointCurrents(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointCurrents); - } - - virtual std::vector<const float*> getJointMotorTemperatures(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointMotorTemperatures); - } - - virtual std::vector<const float*> getJointGearTemperatures(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointGearTemperatures); - } - - virtual std::vector<const float*> getJointAbsPositions(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointAbsPositions); - } - - virtual std::vector<const int32_t*> getJointRawPositionValues(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawPositionValues); - } - - virtual std::vector<const int32_t*> getJointRawVelocityValues(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawVelocityValues); - } - - virtual std::vector<const int16_t*> getJointRawCurrentValues(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawCurrentValues); - } - - virtual std::vector<const int32_t*> getJointRawTorqueValues(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawTorqueValues); - } - - virtual std::vector<const int32_t*> getJointRawGearTemperatures(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawGearTemperatures); - } - - virtual std::vector<const int32_t*> - getJointRawAbsPositionValues(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawAbsPositionValues); - } - - virtual std::vector<const int16_t*> - getJointRawMotorTemperatureValues(const std::vector<std::string>& joints) const - { - return getPointers(joints, jointRawMotorTemperatureValues); - } - - - protected: - std::vector<float*> jointAngles; - std::vector<float*> jointVelocites; - std::vector<float*> jointTorques; - std::vector<float*> jointCurrents; - std::vector<float*> jointMotorTemperatures; - std::vector<float*> jointGearTemperatures; - std::vector<float*> jointAbsPositions; - - std::vector<int32_t*> jointRawPositionValues; - std::vector<int32_t*> jointRawVelocityValues; - std::vector<int16_t*> jointRawCurrentValues; - std::vector<int32_t*> jointRawTorqueValues; - std::vector<int32_t*> jointRawGearTemperatures; - std::vector<int32_t*> jointRawAbsPositionValues; - std::vector<int16_t*> jointRawMotorTemperatureValues; - private: - template<class T> - std::vector<const T*> - getPointers(const std::vector<std::string>& joints, const std::vector<T*>& targets) const - { - std::vector<const T*> result; - result.reserve(joints.size()); - std::transform( - joints.begin(), joints.end(), std::back_inserter(result), - [targets, this](const std::string & joint) - { - return targets.at(getJointIndex(joint)); - } - ); - return result; - } - - std::vector<std::string> jointNames; - std::unordered_map<std::string, std::size_t> jointIndices; - }; -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/PlatformDataUnit.h b/source/RobotAPI/libraries/RTRobotUnit/DataUnits/PlatformDataUnit.h deleted file mode 100644 index 0f965ade3d392ea3ee1d6ab7b886f67a93fd142e..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/DataUnits/PlatformDataUnit.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::PlatformDataUnit - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_PlatformDataUnit_H -#define _ARMARX_LIB_RobotAPI_PlatformDataUnit_H - -namespace armarx -{ - class PlatformDataUnitInterface - { - public: - virtual const float* getFrontRightWheelVelocity() const = 0; - virtual const float* getFrontLeftWheelVelocity() const = 0; - virtual const float* getRearRightWheelVelocity() const = 0; - virtual const float* getRearLeftWheelVelocity() const = 0; - }; - - class PlatformDataUnitPtrProvider : public virtual PlatformDataUnitInterface - { - public: - const float* getFrontRightWheelVelocity() const override - { - return frontRightVelocity; - } - - const float* getFrontLeftWheelVelocity() const override - { - return frontLeftVelocity; - } - - const float* getRearRightWheelVelocity() const override - { - return rearRightVelocity; - } - - const float* getRearLeftWheelVelocity() const override - { - return rearLeftVelocity; - } - - protected: - void setFrontRightVelocityPtr(float* velocity) - { - frontRightVelocity = velocity; - } - - void setFrontLeftVelocityPtr(float* velocity) - { - frontRightVelocity = velocity; - } - - void setRearRightVelocityPtr(float* velocity) - { - frontRightVelocity = velocity; - } - - void setRearLeftVelocityPtr(float* velocity) - { - frontRightVelocity = velocity; - } - - float* frontRightVelocity; - float* frontLeftVelocity; - float* rearRightVelocity; - float* rearLeftVelocity; - }; -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL0Controllers/LVL0Controller.h b/source/RobotAPI/libraries/RTRobotUnit/LVL0Controllers/LVL0Controller.h deleted file mode 100644 index 3fbdcea6bf3e7e7f94d2e55bceec99c991124daf..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL0Controllers/LVL0Controller.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL0Controller - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_LVL0Controller_H -#define _ARMARX_LIB_RobotAPI_LVL0Controller_H - -#include "../Targets/TargetBase.h" - -#include <memory> - -namespace armarx -{ - class LVL0Controller; - typedef std::shared_ptr<LVL0Controller> LVL0ControllerPtr; - - class LVL0Controller - { - public: - virtual void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) = 0; - virtual TargetBase* getTarget() = 0; - - virtual const TargetBase* getTarget() const - { - return const_cast<const TargetBase*>(const_cast<LVL0Controller*>(this)->getTarget()); - } - - virtual void rtResetTarget() - { - getTarget()->reset(); - } - - virtual bool rtIsTargetVaild() const - { - return getTarget()->isValid(); - } - - virtual std::string getControlMode() const - { - return getTarget()->getControlMode(); - } - }; -} -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.h b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.h deleted file mode 100644 index 893aa9639955ab52a0b523a69c1c8bfe2ac75a4e..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughController.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1HolonomicPlatformVelocityPassThroughController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_LVL1HolonomicPlatformVelocityPassThroughController_H -#define _ARMARX_LIB_RobotAPI_LVL1HolonomicPlatformVelocityPassThroughController_H - -#include "LVL1Controller.h" -#include "../Targets/HolonomicPlatformVelocityTarget.h" -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1HolonomicPlatformVelocityPassThroughControllerConfig.h> - -namespace armarx -{ - struct LVL1HolonomicPlatformVelocityPassThroughControllerControlData - { - float velocityX = 0; - float velocityY = 0; - float velocityRotation = 0; - }; - - class LVL1HolonomicPlatformVelocityPassThroughController: - virtual public LVL1ControllerWithTripleBuffer<LVL1HolonomicPlatformVelocityPassThroughControllerControlData>, - virtual public LVL1HolonomicPlatformVelocityPassThroughControllerInterface - { - public: - LVL1HolonomicPlatformVelocityPassThroughController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config); - - virtual void rtRun(const IceUtil::Time&, const IceUtil::Time&) override; - - //for the platform unit - virtual void setVelocites(float velocityX, float velocityY, float velocityRotation, const Ice::Current& = GlobalIceCurrent) override; - - //ice interface - virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override - { - return "LVL1HolonomicPlatformVelocityPassThroughController"; - } - protected: - virtual void onInitComponent() override {} - virtual void onConnectComponent() override {} - - HolonomicPlatformVelocityTarget* target; - LVL1HolonomicPlatformVelocityPassThroughControllerControlData initialSettings; - }; -} -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitPositionController.cpp b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitPositionController.cpp deleted file mode 100644 index 0a4b6f97ce4f1db650ea43463ed496faba183c60..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitPositionController.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitPositionController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "LVL1KinematicUnitPositionController.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "../DataUnits/KinematicDataUnit.h" -#include "../BasicControllers.h" -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.h> -using namespace armarx; - -LVL1KinematicUnitPositionController::LVL1KinematicUnitPositionController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config) -{ - LVL1KinematicUnitPositionControllerConfigPtr cfg = LVL1KinematicUnitPositionControllerConfigPtr::dynamicCast(config); - ARMARX_CHECK_EXPRESSION_W_HINT(cfg, - "The provided config has the wrong type! The type is " << config->ice_id() - << " instead of " << LVL1KinematicUnitPositionControllerConfig::ice_staticId()); - //make sure the used units are supported - auto kinunit = prov->getRTKinematicDataUnit(); - ARMARX_CHECK_EXPRESSION_W_HINT(kinunit, "The DataProvider " << prov->getName() << " has no kinematic data unit"); - //get pointers to sensor values from units - std::vector<const float*>&& jointPos = kinunit->getJointAngles({cfg->jointName}); - ARMARX_CHECK_EXPRESSION(jointPos.size() == 1); - ARMARX_CHECK_EXPRESSION(jointPos.at(0)); - currentPosition = jointPos.at(0); - std::vector<const float*> jointVel = kinunit->getJointVelocities({cfg->jointName}); - ARMARX_CHECK_EXPRESSION(jointVel.size() == 1); - ARMARX_CHECK_EXPRESSION(jointVel.at(0)); - currentVelocity = jointVel.at(0); - velocityTarget = dynamic_cast<ActuatorVelocityTarget*>(prov->getJointTarget(cfg->jointName, ControlModes::VelocityMode)); - ARMARX_CHECK_EXPRESSION_W_HINT(velocityTarget, "The joint " << cfg->jointName << " has no controll mode " << ControlModes::VelocityMode); - //read rest of config - initialSettings.maxVelocity = std::abs(cfg->maxVelocity); - initialSettings.acceleration = std::abs(cfg->acceleration); - initialSettings.deceleration = std::abs(cfg->deceleration); - initialSettings.directPControlLimit = std::abs(cfg->directPControlLimit); - initialSettings.p = std::abs(cfg->p); - initialSettings.maxDt = std::abs(cfg->maxDt); - initialSettings.limitPositionLo = cfg->limitPositionLo; - initialSettings.limitPositionHi = cfg->limitPositionHi; - ARMARX_CHECK_EXPRESSION(initialSettings.limitPositionLo <= initialSettings.limitPositionHi); - initialSettings.positionTargetValue = cfg->initialPositionTargetValue; - reinitTripleBuffer(initialSettings); -} - -void LVL1KinematicUnitPositionController::rtRun(const IceUtil::Time&, const IceUtil::Time& timeSinceLastIteration) -{ - // auto& ct = rtGetControlStruct(); - // velocityTarget->velocity = positionThroughVelocityControlWithAccelerationAndPositionBounds( - // /*dt*/ timeSinceLastIteration.toSecondsDouble(), /*maxDt*/ ct.maxDt, - // *currentVelocity, ct.maxVelocity, - // ct.acceleration, ct.deceleration, - // *currentPosition, ct.positionTargetValue, - // ct.directPControlLimit, ct.p, ct.limitPositionLo, ct.limitPositionHi - // ); -} - -void LVL1KinematicUnitPositionController::setTargetPosition(float pos, float maxVelocity, float acceleration, float deceleration) -{ - LockGuardType l {controlDataMutex}; - getWriterControlStruct().positionTargetValue = pos; - getWriterControlStruct().maxVelocity = std::abs(maxVelocity); - getWriterControlStruct().acceleration = std::abs(acceleration); - getWriterControlStruct().deceleration = std::abs(deceleration); - writeControlStruct(); -} - -void LVL1KinematicUnitPositionController::setTargetPosition(float pos) -{ - LockGuardType l {controlDataMutex}; - getWriterControlStruct().positionTargetValue = pos; - writeControlStruct(); -} - -void LVL1KinematicUnitPositionController::setControlSettings(LVL1KinematicUnitPositionControllerControlSettings settings) -{ - settings.maxVelocity = std::abs(settings.maxVelocity); - settings.acceleration = std::abs(settings.acceleration); - settings.deceleration = std::abs(settings.deceleration); - settings.directPControlLimit = std::abs(settings.directPControlLimit); - settings.p = std::abs(settings.p); - settings.maxDt = std::abs(settings.maxDt); - ARMARX_CHECK_EXPRESSION(settings.limitPositionLo <= settings.limitPositionHi); - setControlStruct(settings); -} - -LVL1ControllerRegistration<LVL1KinematicUnitPositionController> registrationLVL1KinematicUnitPositionController("LVL1KinematicUnitPositionController"); diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitPositionController.h b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitPositionController.h deleted file mode 100644 index 8c3875ce726c32f274fd51cba5dfefc5fd6f2c46..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitPositionController.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitPositionController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_LVL1KinematicUnitPositionController_H -#define _ARMARX_LIB_RobotAPI_LVL1KinematicUnitPositionController_H - -#include "LVL1Controller.h" -#include "../RobotUnit.h" -#include "../Targets/ActuatorVelocityTarget.h" - -namespace armarx -{ - struct LVL1KinematicUnitPositionControllerControlSettings - { - //'constant' data (in some cases these variables maybe need to be changed) - float directPControlLimit; - float p; - float maxDt; - - float limitPositionLo; - float limitPositionHi; - //variable data - float maxVelocity; - float acceleration; - float deceleration; - float positionTargetValue; - }; - - class LVL1KinematicUnitPositionController: - virtual public LVL1ControllerWithTripleBuffer<LVL1KinematicUnitPositionControllerControlSettings> - { - public: - LVL1KinematicUnitPositionController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config); - virtual void rtRun(const IceUtil::Time&, const IceUtil::Time& timeSinceLastIteration) override; - - //ice interface - virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override - { - return "LVL1KinematicUnitPositionController"; - } - - //interface for kunit - const LVL1KinematicUnitPositionControllerControlSettings& getInitialSettings() - { - return initialSettings; - } - void setTargetPosition(float pos, float maxVelocity, float acceleration, float deceleration); - void setTargetPosition(float pos); - void setControlSettings(LVL1KinematicUnitPositionControllerControlSettings settings); - protected: - virtual void onInitComponent() override {} - virtual void onConnectComponent() override {} - - const float* currentVelocity; - const float* currentPosition; - ActuatorVelocityTarget* velocityTarget; - LVL1KinematicUnitPositionControllerControlSettings initialSettings; - }; -} -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitTorqueController.cpp b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitTorqueController.cpp deleted file mode 100644 index 5c53b220bd9f9b27d1d78068a2fc3b1b98025dc2..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitTorqueController.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitTorqueController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "LVL1KinematicUnitTorqueController.h" -using namespace armarx; - -LVL1KinematicUnitTorqueController::LVL1KinematicUnitTorqueController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config) -{ - LVL1KinematicUnitTorqueControllerConfigPtr cfg = LVL1KinematicUnitTorqueControllerConfigPtr::dynamicCast(config); - ARMARX_CHECK_EXPRESSION_W_HINT(cfg, - "The provided config has the wrong type! The type is " << config->ice_id() - << " instead of " << LVL1KinematicUnitTorqueControllerConfig::ice_staticId()); - - torqueTarget = dynamic_cast<ActuatorTorqueTarget*>(prov->getJointTarget(cfg->jointName, ControlModes::TorqueMode)); - ARMARX_CHECK_EXPRESSION_W_HINT(torqueTarget, "The joint " << cfg->jointName << " has no controll mode " << ControlModes::TorqueMode); - - //read rest of config - initialMaxTorque = std::abs(cfg->maxTorque); - maxTorque = initialMaxTorque; - torqueTargetValue = cfg->initialTorqueTargetValue; -} - - - - diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitTorqueController.h b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitTorqueController.h deleted file mode 100644 index 4398baf00e4a97afe9d7b72158f0efbd459b7ba4..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitTorqueController.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitTorqueController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_LVL1KinematicUnitTorqueController_H -#define _ARMARX_LIB_RobotAPI_LVL1KinematicUnitTorqueController_H - -#include <atomic> - -#include <boost/algorithm/clamp.hpp> - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.h> - -#include "LVL1Controller.h" -#include "../RobotUnit.h" - -#include "../Targets/ActuatorTorqueTarget.h" - -namespace armarx -{ - class LVL1KinematicUnitTorqueController: - virtual public LVL1Controller - { - public: - inline LVL1KinematicUnitTorqueController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config); - - virtual void rtRun(const IceUtil::Time&, const IceUtil::Time&) override - { - torqueTarget->torque = boost::algorithm::clamp(torqueTargetValue, - maxTorque, maxTorque); - } - - //ice interface - inline virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override - { - return "LVL1KinematicUnitTorqueController"; - } - - //kunit - void setMaxTorque(float mt) - { - maxTorque = std::abs(mt); - } - void setTorque(float t) - { - torqueTargetValue = t; - } - - float getInitialMaxTorque() const - { - return initialMaxTorque; - } - - protected: - virtual void onInitComponent() override {} - virtual void onConnectComponent() override {} - - std::atomic<float> maxTorque; - std::atomic<float> torqueTargetValue; - float initialMaxTorque; - - ActuatorTorqueTarget* torqueTarget; - }; -} -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitVelocityController.cpp b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitVelocityController.cpp deleted file mode 100644 index fb72b71613445593abf71a885e4f146fa98451f2..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitVelocityController.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitVelocityController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "LVL1KinematicUnitVelocityController.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "../DataUnits/KinematicDataUnit.h" -#include "../BasicControllers.h" -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitControllerConfigs.h> -using namespace armarx; - -LVL1KinematicUnitVelocityController::LVL1KinematicUnitVelocityController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config) -{ - LVL1KinematicUnitVelocityControllerConfigPtr cfg = LVL1KinematicUnitVelocityControllerConfigPtr::dynamicCast(config); - ARMARX_CHECK_EXPRESSION_W_HINT(cfg, - "The provided config has the wrong type! The type is " << config->ice_id() - << " instead of " << LVL1KinematicUnitVelocityControllerConfig::ice_staticId()); - //make sure the used units are supported - auto kinunit = prov->getRTKinematicDataUnit(); - ARMARX_CHECK_EXPRESSION_W_HINT(kinunit, "The DataProvider " << prov->getName() << " has no kinematic data unit"); - //get pointers to sensor values from units - std::vector<const float*> jointPos = kinunit->getJointAngles({cfg->jointName}); - ARMARX_CHECK_EXPRESSION(jointPos.size() == 1); - ARMARX_CHECK_EXPRESSION(jointPos.at(0)); - currentPosition = jointPos.at(0); - std::vector<const float*> jointVel = kinunit->getJointVelocities({cfg->jointName}); - ARMARX_CHECK_EXPRESSION(jointVel.size() == 1); - ARMARX_CHECK_EXPRESSION(jointVel.at(0)); - currentVelocity = jointVel.at(0); - velocityTarget = dynamic_cast<ActuatorVelocityTarget*>(prov->getJointTarget(cfg->jointName, ControlModes::VelocityMode)); - ARMARX_CHECK_EXPRESSION_W_HINT(velocityTarget, "The joint " << cfg->jointName << " has no controll mode " << ControlModes::VelocityMode); - //read rest of config - initialSettings.maxVelocity = std::abs(cfg->maxVelocity); - initialSettings.acceleration = std::abs(cfg->acceleration); - initialSettings.deceleration = std::abs(cfg->deceleration); - initialSettings.directSetLimit = std::abs(cfg->directSetLimit); - initialSettings.maxDt = std::abs(cfg->maxDt); - initialSettings.limitPositionLo = cfg->limitPositionLo; - initialSettings.limitPositionHi = cfg->limitPositionHi; - ARMARX_CHECK_EXPRESSION(initialSettings.limitPositionLo <= initialSettings.limitPositionHi); - initialSettings.velocityTargetValue = cfg->initialVelocityTargetValue; - reinitTripleBuffer(initialSettings); -} - -void LVL1KinematicUnitVelocityController::rtRun(const IceUtil::Time&, const IceUtil::Time& timeSinceLastIteration) -{ - // auto& ct = rtGetControlStruct(); - // velocityTarget->velocity = velocityControlWithAccelerationAndPositionBounds( - // /*dt*/ timeSinceLastIteration.toSecondsDouble(), /*maxDt*/ ct.maxDt, - // *currentVelocity, ct.velocityTargetValue, ct.maxVelocity, - // ct.acceleration, ct.deceleration, - // ct.directSetLimit, - // *currentPosition, ct.limitPositionLo, ct.limitPositionHi - // ); -} - -void LVL1KinematicUnitVelocityController::setTargetVelocity(float v) -{ - LockGuardType l {controlDataMutex}; - getWriterControlStruct().velocityTargetValue = v; - writeControlStruct(); -} - -void LVL1KinematicUnitVelocityController::setControlSettings(LVL1KinematicUnitVelocityControllerControlSettings settings) -{ - settings.maxVelocity = std::abs(settings.maxVelocity); - settings.acceleration = std::abs(settings.acceleration); - settings.deceleration = std::abs(settings.deceleration); - settings.directSetLimit = std::abs(settings.directSetLimit); - settings.maxDt = std::abs(settings.maxDt); - ARMARX_CHECK_EXPRESSION(settings.limitPositionLo <= settings.limitPositionHi); - setControlStruct(settings); -} - -LVL1ControllerRegistration<LVL1KinematicUnitVelocityController> registrationLVL1KinematicUnitVelocityController("LVL1KinematicUnitVelocityController"); diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitVelocityController.h b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitVelocityController.h deleted file mode 100644 index 7f59f30eaa374dcfffb76217674e9de0ff764024..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1KinematicUnitVelocityController.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1KinematicUnitVelocityController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_LVL1KinematicUnitVelocityController_H -#define _ARMARX_LIB_RobotAPI_LVL1KinematicUnitVelocityController_H - -#include "LVL1Controller.h" -#include "../RobotUnit.h" -#include "../Targets/ActuatorVelocityTarget.h" - -namespace armarx -{ - struct LVL1KinematicUnitVelocityControllerControlSettings - { - //'constant' data (in some cases these variables maybe need to be changed) - float maxVelocity; - float acceleration; - float deceleration; - - float directSetLimit; - float maxDt; - - float limitPositionLo; - float limitPositionHi; - //variable data - float velocityTargetValue; - }; - - class LVL1KinematicUnitVelocityController: - virtual public LVL1ControllerWithTripleBuffer<LVL1KinematicUnitVelocityControllerControlSettings> - { - public: - LVL1KinematicUnitVelocityController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config); - virtual void rtRun(const IceUtil::Time&, const IceUtil::Time& timeSinceLastIteration) override; - - //ice interface - virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override - { - return "LVL1KinematicUnitVelocityController"; - } - - //interface for kunit - const LVL1KinematicUnitVelocityControllerControlSettings& getInitialSettings() - { - return initialSettings; - } - void setTargetVelocity(float v); - void setControlSettings(LVL1KinematicUnitVelocityControllerControlSettings settings); - protected: - virtual void onInitComponent() override {} - virtual void onConnectComponent() override {} - - const float* currentVelocity; - const float* currentPosition; - ActuatorVelocityTarget* velocityTarget; - LVL1KinematicUnitVelocityControllerControlSettings initialSettings; - }; -} -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.cpp b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.cpp deleted file mode 100644 index 83b51d8a277d163da8dbc9e680517d7e69751a1a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1PassThroughController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "LVL1PassThroughController.h" -using namespace armarx; - -LVL1ControllerRegistration<LVL1PassThroughController<ActuatorPositionTarget>> registrationControllerLVL1PositionPassThroughController("LVL1PositionPassThroughController"); -LVL1ControllerRegistration<LVL1PassThroughController<ActuatorVelocityTarget>> registrationControllerLVL1VelocityPassThroughController("LVL1VelocityPassThroughController"); -LVL1ControllerRegistration<LVL1PassThroughController<ActuatorTorqueTarget >> registrationControllerLVL1TorquePassThroughController("LVL1TorquePassThroughController"); diff --git a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.h b/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.h deleted file mode 100644 index e6d311b5bb7a1dfc55bf07288fc7d1627db75e48..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::LVL1PassThroughController - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_LVL1PassThroughController_H -#define _ARMARX_LIB_RobotAPI_LVL1PassThroughController_H - -#include <atomic> -#include <ArmarXCore/core/util/algorithm.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include "LVL1Controller.h" -#include "../RobotUnit.h" -#include <RobotAPI/interface/libraries/RTRobotUnit/LVL1Controllers/LVL1PassThroughController.h> - -#include "../Targets/ActuatorPositionTarget.h" -#include "../Targets/ActuatorTorqueTarget.h" -#include "../Targets/ActuatorVelocityTarget.h" -#include "../DataUnits/KinematicDataUnit.h" -#include "../Targets/PlatformWheelVelocityTarget.h" - -namespace armarx -{ - template <typename T> - struct AtomicWrapper - { - std::atomic<T> val; - - AtomicWrapper(): val { } {} - AtomicWrapper(const T& v): val {v } {} - AtomicWrapper(const std::atomic<T>& val): val {val.load() } {} - AtomicWrapper(const AtomicWrapper& other): val {other.val.load()} {} - - AtomicWrapper& operator=(const AtomicWrapper& other) - { - val.store(other.val.load()); - return *this; - } - }; - - template <typename TargetType> - struct LVL1PassThroughControllerTargetTypeTraits; - - template <typename TargetType> - class LVL1PassThroughController: - virtual public LVL1Controller, - virtual public LVL1PassThroughControllerInterface - { - public: - using Traits = LVL1PassThroughControllerTargetTypeTraits<TargetType>; - - inline LVL1PassThroughController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config); - - inline virtual void rtRun(const IceUtil::Time&, const IceUtil::Time&) override; - inline virtual void rtSwapBufferAndRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override; - inline virtual void rtPreActivateController() override; - - //ice interface - inline virtual std::string getClassName(const Ice::Current& = GlobalIceCurrent) const override; - inline virtual void setJoint(const std::string& name, Ice::Float value, const Ice::Current& = GlobalIceCurrent) override; - inline virtual void setJoints(const StringFloatDictionary& values, const Ice::Current& = GlobalIceCurrent) override; - protected: - std::vector<float*> lvl0Targets; - std::vector<const float*> jointStates; - std::vector<AtomicWrapper<float>> iceTargets; - std::unordered_map<std::string, std::size_t> indices; - - virtual void onInitComponent() override {} - virtual void onConnectComponent() override {} - }; - - template <> - struct LVL1PassThroughControllerTargetTypeTraits<ActuatorVelocityTarget> - { - static float* getTargetDatamember(ActuatorVelocityTarget& t) - { - return &t.velocity; - } - static std::vector<const float*> getSensorValues(const KinematicDataUnitInterface* ku, const std::vector<std::string>& v) - { - return ku->getJointVelocities(v); - } - static std::string getControlMode() - { - return ControlModes::VelocityMode; - } - }; - template <> - struct LVL1PassThroughControllerTargetTypeTraits<ActuatorTorqueTarget> - { - static float* getTargetDatamember(ActuatorTorqueTarget& t) - { - return &t.torque; - } - static std::vector<const float*> getSensorValues(const KinematicDataUnitInterface* ku, const std::vector<std::string>& v) - { - return ku->getJointTorques(v); - } - static std::string getControlMode() - { - return ControlModes::TorqueMode; - } - }; - template <> - struct LVL1PassThroughControllerTargetTypeTraits<ActuatorPositionTarget> - { - static float* getTargetDatamember(ActuatorPositionTarget& t) - { - return &t.position; - } - static std::vector<const float*> getSensorValues(const KinematicDataUnitInterface* ku, const std::vector<std::string>& v) - { - return ku->getJointAngles(v); - } - static std::string getControlMode() - { - return ControlModes::PositionMode; - } - }; - - template <> - struct LVL1PassThroughControllerTargetTypeTraits<PlatformWheelVelocityTarget> - { - static float* getTargetDatamember(PlatformWheelVelocityTarget& t) - { - return &t.velocity; - } - static const float* getFrontRightWheelVelocity(const PlatformDataUnitInterface* pu) - { - return pu->getFrontRightWheelVelocity(); - } - static const float* getFrontLeftWheelVelocity(const PlatformDataUnitInterface* pu) - { - return pu->getFrontLeftWheelVelocity(); - } - static const float* getRearRightWheelVelocity(const PlatformDataUnitInterface* pu) - { - return pu->getRearRightWheelVelocity(); - } - static const float* getRearLeftWheelVelocity(const PlatformDataUnitInterface* pu) - { - return pu->getRearLeftWheelVelocity(); - } - static std::string getControlMode() - { - return ControlModes::VelocityMode; - } - }; - - - template <typename TargetType> - std::string LVL1PassThroughController<TargetType>::getClassName(const Ice::Current&) const - { - return "LVL1PassThroughController_" + Traits::getControlMode(); - } - - template <typename TargetType> - LVL1PassThroughController<TargetType>::LVL1PassThroughController(LVL1ControllerDataProviderInterfacePtr prov, LVL1ControllerConfigPtr config) - { - LVL1PassThroughControllerConfigPtr cfg = LVL1PassThroughControllerConfigPtr::dynamicCast(config); - ARMARX_CHECK_EXPRESSION_W_HINT(cfg, - "The provided config has the wrong type! The type is " << config->ice_id() - << " instead of " << LVL1PassThroughControllerConfig::ice_staticId()); - - //make sure the used units are supported - auto kinunit = prov->getRTKinematicDataUnit(); - ARMARX_CHECK_EXPRESSION_W_HINT(kinunit, "The DataProvider " << prov->getName() << " has no kinematic data unit"); - //get pointers to sensor values from units - ARMARX_INFO << "LVL1PassThroughController for " << cfg->jointNames; - jointStates = Traits::getSensorValues(kinunit, cfg->jointNames) ; - - //not initialized, this is done in rtPreActivateController - iceTargets.resize(cfg->jointNames.size(), 0); - - //get pointers for the results of this controller - lvl0Targets.reserve(cfg->jointNames.size()); - for (const auto& j : cfg->jointNames) - { - auto target = dynamic_cast<TargetType*>(prov->getJointTarget(j, Traits::getControlMode())); - ARMARX_CHECK_EXPRESSION_W_HINT(target, "The joint " << j << " has no controll mode " << Traits::getControlMode()); - lvl0Targets.emplace_back(Traits::getTargetDatamember(*target)); - } - indices = toIndexMap(cfg->jointNames); - } - - template <typename TargetType> - void LVL1PassThroughController<TargetType>::rtRun(const IceUtil::Time&, const IceUtil::Time&) - { - for (std::size_t i = 0; i < iceTargets.size(); ++i) - { - *lvl0Targets.at(i) = iceTargets.at(i).val.load(); - } - } - - template <typename TargetType> - void LVL1PassThroughController<TargetType>::rtSwapBufferAndRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) - { - rtRun(sensorValuesTimestamp, timeSinceLastIteration); - } - - template <typename TargetType> - void LVL1PassThroughController<TargetType>::rtPreActivateController() - { - for (std::size_t i = 0; i < jointStates.size(); ++i) - { - iceTargets.at(i).val.store(*jointStates.at(i)); - //set it to zero - iceTargets.at(i).val.store(0.0); - } - } - - template <typename TargetType> - void LVL1PassThroughController<TargetType>::setJoint(const std::string& name, Ice::Float value, const Ice::Current&) - { - auto targetIt = indices.find(name); - if (targetIt == indices.end()) - { - throw InvalidArgumentException {"The joint " + name + " is not controlled by this (" + getName() + ") controller"}; - } - iceTargets.at(targetIt->second).val.store(value); - } - - template <typename TargetType> - void LVL1PassThroughController<TargetType>::setJoints(const StringFloatDictionary& values, const Ice::Current&) - { - for (const auto& value : values) - { - setJoint(value.first, value.second); - } - } - -} -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/RobotUnit.cpp b/source/RobotAPI/libraries/RTRobotUnit/RobotUnit.cpp deleted file mode 100644 index fc02567ebc7f2ef9f0b3b7882a08646420caef7b..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/RobotUnit.cpp +++ /dev/null @@ -1,600 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::RobotUnit - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "RobotUnit.h" - -#include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> - -#include <ArmarXCore/core/system/DynamicLibrary.h> - -armarx::PropertyDefinitionsPtr armarx::RobotUnit::createPropertyDefinitions() -{ - return armarx::PropertyDefinitionsPtr(new RobotUnitPropertyDefinitions(getConfigIdentifier())); -} - -void armarx::RobotUnit::addLVL0Controller(const std::string& jointName, armarx::LVL0Controller& lvl0Controller) -{ - std::string controlMode = lvl0Controller.getControlMode(); - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - if (hasLVL0Controller(jointName, controlMode)) - { - ARMARX_ERROR << "A LVL0Controller for " + jointName + "(" + controlMode + ") does already exist!"; - throw std::invalid_argument {"A LVL0Controller for " + jointName + "(" + controlMode + ") does already exist!"}; - } - lvl0Controllers[jointName][controlMode] = &lvl0Controller; - if (controlMode == ControlModes::EmergencyStopMode) - { - lvl0ControllersEmergencyStop.at(jointNameIndices.at(jointName)) = &lvl0Controller; - } -} - -const armarx::LVL0Controller* armarx::RobotUnit::getLVL0Controller(const std::string& jointName, const std::string& controlMode) const -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - return lvl0Controllers.at(jointName).at(controlMode); -} - -armarx::LVL0Controller* armarx::RobotUnit::getLVL0Controller(const std::string& jointName, const std::string& controlMode) -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - return lvl0Controllers.at(jointName).at(controlMode); -} - -Ice::StringSeq armarx::RobotUnit::getControllersKnown(const Ice::Current&) const -{ - return LVL1ControllerRegistry::getKeys(); -} - -Ice::StringSeq armarx::RobotUnit::getControllerNamesLoaded(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - Ice::StringSeq result; - result.reserve(lvl1Controllers.size()); - for (const auto& lvl1 : lvl1Controllers) - { - result.emplace_back(lvl1.first); - } - return result; -} -Ice::StringSeq armarx::RobotUnit::getControllerNamesRequested(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - Ice::StringSeq result; - result.reserve(lvl1Controllers.size()); - for (const auto& lvl1 : getRequestedLVL1Controllers()) - { - if (lvl1) - { - result.emplace_back(lvl1->getName()); - } - } - return result; -} -Ice::StringSeq armarx::RobotUnit::getControllerNamesActivated(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - Ice::StringSeq result; - result.reserve(lvl1Controllers.size()); - for (const auto& lvl1 : getActivatedLVL1Controllers()) - { - if (!lvl1) - { - continue; - } - result.emplace_back(lvl1->getName()); - } - return result; -} - -armarx::StringLVL1ControllerPrxDictionary armarx::RobotUnit::getControllersLoaded(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - StringLVL1ControllerPrxDictionary result; - for (const auto& lvl1 : lvl1Controllers) - { - result[lvl1.first] = LVL1ControllerInterfacePrx::uncheckedCast(lvl1.second->getProxy()); - } - return result; -} -armarx::StringLVL1ControllerPrxDictionary armarx::RobotUnit::getControllersRequested(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - StringLVL1ControllerPrxDictionary result; - for (const auto& lvl1 : getRequestedLVL1Controllers()) - { - if (lvl1) - { - result[lvl1->getName()] = LVL1ControllerInterfacePrx::uncheckedCast(lvl1->getProxy()); - } - } - return result; -} -armarx::StringLVL1ControllerPrxDictionary armarx::RobotUnit::getControllersActivated(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - StringLVL1ControllerPrxDictionary result; - for (const auto& lvl1 : getActivatedLVL1Controllers()) - { - result[lvl1->getName()] = LVL1ControllerInterfacePrx::uncheckedCast(lvl1->getProxy()); - } - return result; -} - -armarx::JointNameToLVL1Dictionary armarx::RobotUnit::getControllerJointAssignment(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - JointNameToLVL1Dictionary result; - for (const auto& joint : jointNames) - { - result[joint] = ""; - } - for (const auto& lvl1 : getActivatedLVL1Controllers()) - { - if (!lvl1) - { - continue; - } - for (const auto& jointMode : lvl1->jointControlModeMap) - { - result[jointMode.first] = lvl1->getName(); - } - } - return result; -} - -armarx::JointNameToControlModeDictionary armarx::RobotUnit::getJointControlModesRequested(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - JointNameToControlModeDictionary result; - const auto& requestedModes = getRequestedLVL0Controllers(); - ARMARX_CHECK_AND_THROW(jointNames.size() == requestedModes.size(), std::logic_error); - for (std::size_t i = 0; i < jointNames.size(); ++i) - { - if (requestedModes.at(i)) - { - result[jointNames.at(i)] = requestedModes.at(i)->getControlMode(); - } - else - { - result[jointNames.at(i)] = "<NO LVL0CONTROLLER SET>"; - } - } - return result; -} -armarx::JointNameToControlModeDictionary armarx::RobotUnit::getJointControlModesActivated(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - JointNameToControlModeDictionary result; - const auto& activatedModes = getActivatedLVL0Controllers(); - ARMARX_CHECK_AND_THROW(jointNames.size() == activatedModes.size(), std::logic_error); - for (std::size_t i = 0; i < jointNames.size(); ++i) - { - if (activatedModes.at(i)) - { - result[jointNames.at(i)] = activatedModes.at(i)->getControlMode(); - } - else - { - result[jointNames.at(i)] = "<NO LVL0CONTROLLER SET>"; - } - - } - return result; -} -armarx::JointNameToControlModesDictionary armarx::RobotUnit::getJointControlModesSupported(const Ice::Current&) const -{ - GuardType guard {dataMutex}; - JointNameToControlModesDictionary result; - for (const auto& joint : lvl0Controllers) - { - std::vector<std::string> modes; - modes.reserve(joint.second.size()); - for (const auto& mode : joint.second) - { - modes.emplace_back(mode.first); - } - result[joint.first] = std::move(modes); - } - return result; -} - -void armarx::RobotUnit::switchSetup(const Ice::StringSeq& controllerRequestedNames, const Ice::Current&) -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - //check if all of these controllers exist - for (const auto& lvl1 : controllerRequestedNames) - { - if (!hasLVL1Controller(lvl1)) - { - throw InvalidArgumentException {"No controller of the name '" + lvl1 + "' is loaded!"}; - } - } - //check controllers (is there a collision/which controllers need to be deactivated) - const auto currentActiveLVL1Controllers = getControllerNamesActivated(); - auto lvl1ControllerAssignement = getControllerJointAssignment(); - - ARMARX_INFO << "current active lvl1 controllers:\n" << currentActiveLVL1Controllers; - - std::set<std::string> controllersToActivate {currentActiveLVL1Controllers.begin(), currentActiveLVL1Controllers.end()}; //these controllers will be set as active controllers - - for (const auto& toActivate : controllerRequestedNames) - { - if (controllersToActivate.count(toActivate)) - { - ARMARX_INFO << "controller already active: " << toActivate; - continue; - } - controllersToActivate.emplace(toActivate); - - ARMARX_INFO << "activate '" << toActivate << "'"; - - const auto& lvl1 = lvl1Controllers.at(toActivate); - //check and update the assignement map - for (const auto& jointControlMode : lvl1->jointControlModeMap) - { - const auto& joint = jointControlMode.first; - const auto& currentAssignedLVL1 = lvl1ControllerAssignement.at(joint); - if (toActivate == currentAssignedLVL1) - { - continue; - } - if (currentAssignedLVL1.empty()) - { - lvl1ControllerAssignement[joint] = toActivate; - continue; - } - //deactivate other controller - for (auto& assignement : lvl1ControllerAssignement) - { - if (assignement.second == currentAssignedLVL1) - { - assignement.second.clear(); - } - } - controllersToActivate.erase(currentAssignedLVL1); - ARMARX_INFO << "deactivated '" << currentAssignedLVL1 << "' (caused by activation of '" << toActivate << "')"; - lvl1ControllerAssignement[joint] = toActivate; - } - } - //verify (exception for collision of requested lvl1) - for (const auto& toActivate : controllerRequestedNames) - { - if (!controllersToActivate.count(toActivate)) - { - switchSetupError = true; - ARMARX_ERROR << "The given set of controllers is in conflict and could not be activated! (conflicting is: " + toActivate + ")"; - throw InvalidArgumentException {"The given set of controllers is in conflict and could not be activated! (conflicting is: " + toActivate + ")"}; - } - } - //verify (warn for orphant joints) + populate controllersRequested.lvl0Controllers - getRequestedLVL0Controllers().resize(jointNames.size()); - for (std::size_t i = 0; i < jointNames.size(); ++i) - { - const auto& joint = jointNames.at(i); - if (lvl1ControllerAssignement[joint].empty()) - { - getRequestedLVL0Controllers().at(i) = getLVL0Controller(joint, ControlModes::EmergencyStopMode); - ARMARX_WARNING << "Joint '" << joint << "' has no lvl1 controller assigned!"; - continue; - } - const auto& lvl0Mode = lvl1Controllers.at(lvl1ControllerAssignement[joint])->jointControlModeMap.at(joint); - getRequestedLVL0Controllers().at(i) = getLVL0Controller(joint, lvl0Mode); - } - //populate controllersRequested.lvl1Controllers - getRequestedLVL1Controllers().clear(); - getRequestedLVL1Controllers().reserve(controllersToActivate.size()); - for (const auto& lvl1 : controllersToActivate) - { - getRequestedLVL1Controllers().emplace_back(lvl1Controllers.at(lvl1)); - } - - //now change the assignement - controllersRequested.commitWrite(); - ARMARX_INFO << "requested lvl1 controllers:\n" << getControllerNamesRequested(); -} - -armarx::LVL1ControllerInterfacePrx armarx::RobotUnit::loadController(const std::string& className, const std::string& instanceName, const armarx::LVL1ControllerConfigPtr& config, const Ice::Current&) -{ - if (instanceName.empty()) - { - ARMARX_ERROR << "The instance name is empty!"; - throw InvalidArgumentException {"The instance name is empty!"}; - } - //check if we would be able to create the class - if (!LVL1ControllerRegistry::has(className)) - { - std::stringstream ss; - ss << "Requested controller class '" << className << "' unknown! Known classes:" << LVL1ControllerRegistry::getKeys(); - ARMARX_ERROR << ss.str(); - throw InvalidArgumentException {ss.str()}; - } - auto factory = LVL1ControllerRegistry::get(className); - - GuardType guard {dataMutex}; - //check if the instance name is already in use - if (hasLVL1Controller(instanceName)) - { - std::stringstream ss; - ss << "There already is a controller instance with the name '" << instanceName << "'"; - ARMARX_ERROR << ss.str(); - throw InvalidArgumentException {ss.str()}; - } - //create the controller - jointsUsedByLVL1Controller.clear(); - RobotUnitInterfacePtr::dynamicCast(RobotUnitPtr {this}); - LVL1ControllerDataProviderInterfacePtr::dynamicCast(RobotUnitPtr {this}); - LVL1ControllerPtr lvl1 = factory(LVL1ControllerDataProviderInterfacePtr::dynamicCast(RobotUnitPtr {this}), config); - lvl1->jointControlModeMap = jointsUsedByLVL1Controller; - lvl1->jointIndices.clear(); - lvl1->jointIndices.reserve(jointsUsedByLVL1Controller.size()); - for (const auto& j : jointsUsedByLVL1Controller) - { - lvl1->jointIndices.emplace_back(jointNameIndices.at(j.first)); - } - - getArmarXManager()->addObject(lvl1, instanceName); - const auto prx = lvl1->getProxy(-1); - lvl1Controllers[instanceName] = lvl1; - return LVL1ControllerInterfacePrx::uncheckedCast(prx); -} - -armarx::TargetBase* armarx::RobotUnit::getJointTarget(const std::string& jointName, const std::string& controlMode) -{ - GuardType guard {dataMutex}; - if (!hasLVL0Controller(jointName, controlMode)) - { - return nullptr; - } - const auto lvl0 = getLVL0Controller(jointName, controlMode); - ARMARX_CHECK_EXPRESSION(lvl0->getControlMode() == controlMode); - jointsUsedByLVL1Controller[jointName] = controlMode; - return lvl0->getTarget(); -} - -void armarx::RobotUnit::setJointNames(const std::vector<std::string>& names) -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(!areJointNamesSet(), "JointNames were already set!"); - jointNames = names; - jointNameIndices = toIndexMap(jointNames); - lvl0ControllersEmergencyStop.resize(jointNames.size()); - controllersRequested.reinitAllBuffers(LVL0AndLVL1ControllerList(jointNames.size())); - controllersActivated.reinitAllBuffers(LVL0AndLVL1ControllerList(jointNames.size())); -} - -bool armarx::RobotUnit::hasLVL1Controller(const std::string& name) const -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - return lvl1Controllers.end() != lvl1Controllers.find(name); -} - -void armarx::RobotUnit::rtSetLVL1ControllerActive(const armarx::LVL1ControllerPtr& lvl1, bool isActive) -{ - if (isActive) - { - lvl1->rtActivateController(); - } - else - { - lvl1->rtDeactivateController(); - } -} - -bool armarx::RobotUnit::areJointNamesSet() const -{ - GuardType guard {dataMutex}; - return !jointNames.empty(); -} - -bool armarx::RobotUnit::hasLVL0Controller(const std::string& jointName, const std::string& controlMode) const -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - return lvl0Controllers.end() != lvl0Controllers.find(jointName) && - lvl0Controllers.at(jointName).end() != lvl0Controllers.at(jointName).find(controlMode) && - lvl0Controllers.at(jointName).at(controlMode); -} - - -bool armarx::RobotUnit::loadLibFromAbsolutePath(const std::string& path) -{ - GuardType guard {dataMutex}; - if (loadedLibs.count(path) > 0) - { - return true; - } - DynamicLibraryPtr lib(new DynamicLibrary()); - try - { - lib->load(path); - } - catch (...) - { - handleExceptions(); - return false; - } - - if (lib->isLibraryLoaded()) - { - loadedLibs[path] = lib; - } - else - { - ARMARX_ERROR << "Could not load lib " + path + ": " + lib->getErrorMessage(); - return false; - } - - - return true; -} - -bool armarx::RobotUnit::loadLibFromPath(const std::string& path, const Ice::Current&) -{ - std::string absPath; - if (ArmarXDataPath::getAbsolutePath(path, absPath)) - { - return loadLibFromAbsolutePath(absPath); - } - else - { - ARMARX_ERROR << "Could not find library " + path; - return false; - } -} - -bool armarx::RobotUnit::loadLibFromPackage(const std::string& package, const std::string& name, const Ice::Current&) -{ - CMakePackageFinder finder(package); - if (!finder.packageFound()) - { - ARMARX_ERROR << "Could not find package '" << package << "'"; - return false; - } - - for (auto libDirPath : armarx::Split(finder.getLibraryPaths(), ";")) - { - boost::filesystem::path fullPath = libDirPath; - fullPath /= "lib" + name + "." + DynamicLibrary::GetSharedLibraryFileExtension(); - if (!boost::filesystem::exists(fullPath)) - { - fullPath = libDirPath; - fullPath /= name; - if (!boost::filesystem::exists(fullPath)) - { - continue; - } - } - if (loadLibFromAbsolutePath(fullPath.string())) - { - return true; - } - } - ARMARX_ERROR << "Could not find library " << name << " in package " << package; - return false; -} - -bool armarx::RobotUnit::rtSwitchSetup() -{ - if (!rtControllersWereSwitched()) - { - return false; - } - - //handle lvl1 - for (std::size_t i = 0; i < rtGetRequestedLVL1Controllers().size(); ++i) - { - //deactivate old - if (rtGetActivatedLVL1Controller(i)) - { - rtGetActivatedLVL1Controller(i)->rtDeactivateController(); - } - - //activate new - if (rtGetRequestedLVL1Controller(i)) - { - rtGetRequestedLVL1Controller(i)->rtActivateController(); - } - //update activated - rtGetActivatedLVL1Controller(i) = rtGetRequestedLVL1Controller(i); - } - //handle lvl0 - for (std::size_t i = 0; i < rtGetRequestedLVL0Controllers().size(); ++i) - { - if (rtGetRequestedLVL0Controller(i) != rtGetActivatedLVL0Controller(i)) - { - rtSwitchLVL0Controller(i, rtGetActivatedLVL0Controller(i), rtGetRequestedLVL0Controller(i)); - rtGetActivatedLVL0Controller(i) = rtGetRequestedLVL0Controller(i); - } - - if (!rtGetRequestedLVL0Controller(i)) - { - //no lvl0 controller is set! - rtSwitchLVL0Controller(i, rtGetActivatedLVL0Controller(i), rtGetEmergencyStopLVL0Controller(i)); - rtGetActivatedLVL0Controller(i) = rtGetActivatedLVL0Controller(i); - } - } - - - return true; -} - -void armarx::RobotUnit::rtRunLVL1Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) -{ - for (LVL1ControllerPtr& lvl1 : rtGetActivatedLVL1Controllers()) - { - if (!lvl1) - { - continue; - } - lvl1->rtSwapBufferAndRun(sensorValuesTimestamp, timeSinceLastIteration); - } -} - -void armarx::RobotUnit::rtDeactivateAssignedLVL1Controller(std::size_t index) -{ - for (std::size_t i = 0; i < rtGetRequestedLVL1Controllers().size(); ++i) - { - if (!rtGetRequestedLVL1Controller(i)) - { - continue; - } - if (rtGetRequestedLVL1Controller(i)->rtUsesJoint(index)) - { - rtGetRequestedLVL1Controller(i)->rtDeactivateController(); - for (auto used : rtGetRequestedLVL1Controller(i)->rtGetJointIndices()) - { - rtSwitchLVL0Controller(used, rtGetActivatedLVL0Controller(i), rtGetEmergencyStopLVL0Controller(i)); - rtGetActivatedLVL0Controller(i) = rtGetEmergencyStopLVL0Controller(i); - } - rtGetActivatedLVL1Controller(i) = nullptr; - return; - } - } -} - -void armarx::RobotUnit::rtRunLVL0Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) -{ - for (LVL0Controller* lvl0 : rtGetActivatedLVL0Controllers()) - { - lvl0->rtRun(sensorValuesTimestamp, timeSinceLastIteration); - } -} - -bool armarx::RobotUnit::validateAddedLVL0Controllers() const -{ - GuardType guard {dataMutex}; - ARMARX_CHECK_EXPRESSION_W_HINT(areJointNamesSet(), "Joint names a were not set!"); - for (const auto& lvl0 : lvl0ControllersEmergencyStop) - { - if (!lvl0) - { - return false; - } - } - return true; - -} diff --git a/source/RobotAPI/libraries/RTRobotUnit/RobotUnit.h b/source/RobotAPI/libraries/RTRobotUnit/RobotUnit.h deleted file mode 100644 index 7f7fefb967b5b94fad0130204d6b44df05520df2..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/RobotUnit.h +++ /dev/null @@ -1,456 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::RobotUnit - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_UNIT_RobotAPI_RobotUnit_H -#define _ARMARX_UNIT_RobotAPI_RobotUnit_H - -#include <thread> -#include <atomic> -#include <chrono> -#include <mutex> -#include <unordered_map> - -#include <ArmarXCore/core/Component.h> -#include <ArmarXCore/core/ArmarXManager.h> -#include <ArmarXCore/core/time/TimeUtil.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <ArmarXCore/core/util/algorithm.h> -#include <ArmarXCore/core/util/TripleBuffer.h> - -#include <RobotAPI/interface/libraries/RTRobotUnit/RobotUnitInterface.h> -#include "LVL0Controllers/LVL0Controller.h" -#include "LVL1Controllers/LVL1Controller.h" - -namespace armarx -{ - class DynamicLibrary; - typedef boost::shared_ptr<DynamicLibrary> DynamicLibraryPtr; - /** - * @class RobotUnitPropertyDefinitions - * @brief - */ - class RobotUnitPropertyDefinitions: - public armarx::ComponentPropertyDefinitions - { - public: - RobotUnitPropertyDefinitions(std::string prefix): - armarx::ComponentPropertyDefinitions(prefix) - { - //defineRequiredProperty<std::string>("PropertyName", "Description"); - //defineOptionalProperty<std::string>("PropertyName", "DefaultValue", "Description"); - } - }; - - /** - * @defgroup Component-RobotUnit RobotUnit - * @ingroup RobotAPI-Components - * A description of the component RobotUnit. - * - * @class RobotUnit - * @ingroup Component-RobotUnit - * @brief Brief description of class RobotUnit. - * - * \section Datastructures you should have - * \li A thread for RT - * \li A thread for publishing + errorreporting - * \li A Tripplebuffer to publish the current robot state + current targets - * \li A WriteBufferedTripleBuffer containing an error and logging structure (to communicate such information to the publisher thread) - * - * \section RobotUnit-tasks-to-implement Tasks to implement when deriving from this class - * When implementing this class you have to handle the following tasks: - * \li Implement the real time thread. - * \li Implement the publisher thread. - * \li Implement the communication between both threads. - * \li Load all LVL0 controllers and add them to the unit. - * - * \subsection RobotUnit-tasks-to-implement-rt Implementing the RT thread - * The thread has to be RT. - * So you must not do anything of the following: - * \li Call blocking actions (e.g. mutex::lock) - * \li allocate ram using new (this calls lock on a mutex) - * \li print output (this may allocate ram or block) - * \li change the size of a datasructure (strings, vectors, etc) (this may allocate ram) - * - * The structure for a thread is: - * \code{.cpp} - * void rtThread() - * { - * try - * { - * initYourCommunication();//implement this - * passYourLVL0ControllersToTheRobotUnit();//implement this - * if(!validateAddedLVL0Controllers()) - * { - * //report errors - * throw std::logic_error{"lvl0 controller setup invalid"}; - * } - * - * //at this point the RT thread becomes RT (calls before this may not be rt) - * IceUtil::Time currentStateTimestamp = TimeUtil::GetTime(); - * IceUtil::Time lastStateTimestamp = TimeUtil::GetTime(); - * getTheCurrentRobotState();//implement this - * while(isNotStopped())//implement this - * { - * const IceUtil::Time startIteration = TimeUtil::GetTime(); - * //switching controllers - * if(rtSwitchSetup()) - * { - * if(switchSetupError) - * { - * //report this error and exit - * throw std::logic_error{"switching the controller setup failed"}; - * } - * //validate the setup if you need to - * //if you had to change something, call rtCommitActivatedControllers(); - * } - * //run the lvl1 controllers - * rtRunLVL1Controllers(currentStateTimestamp, currentStateTimestamp - lastStateTimestamp); - * //validate targets - * for(std::size_t i = 0; i<rtGetActivatedLVL0Controllers().size(); ++i) - * { - * if(!rtGetActivatedLVL0Controller(i)->isTargetVaild()) - * { - * //handle this error! - * //you probably should log this error to some error struct - * rtDeactivateAssignedLVL1Controller(i); - * rtCommitActivatedControllers(); - * } - * } - * //run lvl0 - * rtRunLVL0Controllers(); - * - * //time keeping + communicating - * lastStateTimestamp = currentStateTimestamp; - * currentStateTimestamp = TimeUtil::GetTime(); - * getTheCurrentRobotStateAndSendTheCurrentControllCommands();//implement this - * communicateWithYourPublisherThread();//implement this - * //maybe meassure how long a loop took and log this information to your error struct - * TimeUtil::Sleep(IceUtil::Time::microSeconds(1000)-(currentStateTimestamp-startIteration)); - * } - * } - * catch(...) - * { - * emergencyStop();//implement this - * dumpDebuggingData();//implement this - * doSomeErrorHandling();//implement this - * shutEverythingGracefullyDown();//implement this - * } - * } - * \endcode - * - * \subsection RobotUnit-tasks-to-implement-pub Implementing the publisher thread - * - * \subsection RobotUnit-tasks-to-implement-com Implementing the communication between both threads - * The communication between both threads has to be RT. - * So you have to use atomics or TrippleBuffers (or any other non-blocking method) - * Data you should communicate is: - * \li Current state (joint modes/values) - * \li Current goals - * \li all error messages. - * - * \subsection RobotUnit-tasks-to-implement-lvl0 Adding LVL0 controllers - * Add LVL0Controllers by calling - * \code{.cpp} - * addLVL0Controller(const std::string& jointName, LVL0Controller& lvl0Controller); - * \endcode - * If you add two controllers withe the same control mode for one joint, an std::invalid_argument exception is thrown. - * - * Each joint needs a LVL0Controller for the mode ControlModes::EmergencyStopMode. - * This controller should activate the motor brakes. - */ - class RobotUnit : - virtual public Component, - virtual public RobotUnitInterface, - virtual public LVL1ControllerDataProviderInterface - { - public: - using MutexType = std::recursive_mutex; - using GuardType = std::lock_guard<MutexType>; - - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - //ice LVL1ControllerDataProviderInterface - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - virtual std::string getName() const override - { - return Component::getName(); - } - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - //ice interface - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - //controllers - virtual Ice::StringSeq getControllersKnown(const Ice::Current& = GlobalIceCurrent) const override; - virtual Ice::StringSeq getControllerNamesLoaded(const Ice::Current& = GlobalIceCurrent) const override; - virtual Ice::StringSeq getControllerNamesRequested(const Ice::Current& = GlobalIceCurrent) const override; - virtual Ice::StringSeq getControllerNamesActivated(const Ice::Current& = GlobalIceCurrent) const override; - - virtual StringLVL1ControllerPrxDictionary getControllersLoaded(const Ice::Current& = GlobalIceCurrent) const override; - virtual StringLVL1ControllerPrxDictionary getControllersRequested(const Ice::Current& = GlobalIceCurrent) const override; - virtual StringLVL1ControllerPrxDictionary getControllersActivated(const Ice::Current& = GlobalIceCurrent) const override; - virtual JointNameToLVL1Dictionary getControllerJointAssignment(const Ice::Current& = GlobalIceCurrent) const override; - //modes - virtual JointNameToControlModeDictionary getJointControlModesRequested(const Ice::Current& = GlobalIceCurrent) const override; - virtual JointNameToControlModeDictionary getJointControlModesActivated(const Ice::Current& = GlobalIceCurrent) const override; - virtual JointNameToControlModesDictionary getJointControlModesSupported(const Ice::Current& = GlobalIceCurrent) const override; - //loading and switching - virtual void switchSetup(const Ice::StringSeq& controllerRequestedNames, const Ice::Current& = GlobalIceCurrent) override; - virtual LVL1ControllerInterfacePrx loadController(const std::string& className, const std::string& instanceName, const LVL1ControllerConfigPtr& config, const Ice::Current& = GlobalIceCurrent) override; - virtual bool loadLibFromPath(const std::string& path, const Ice::Current& = GlobalIceCurrent) override; - virtual bool loadLibFromPackage(const std::string& package, const std::string& lib, const Ice::Current& = GlobalIceCurrent) override; - - //units - virtual KinematicUnitInterfacePrx getKinematicUnit(const Ice::Current& = GlobalIceCurrent) const = 0; - virtual ForceTorqueUnitInterfacePrx getForceTorqueUnit(const Ice::Current& = GlobalIceCurrent) const = 0; - virtual InertialMeasurementUnitInterfacePrx getInertialMeasurementUnit(const Ice::Current& = GlobalIceCurrent) const = 0; - virtual PlatformUnitInterfacePrx getPlatformUnitInterface(const Ice::Current& = GlobalIceCurrent) const = 0; - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - // used by lvl1 controllers - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - //units (nonrt but results are used in rt) - virtual const ForceTorqueDataUnitInterface* getRTForceTorqueDataUnit() const = 0; - virtual const HapticDataUnitInterface* getRTHapticDataUnit() const = 0; - virtual const IMUDataUnitInterface* getRTIMUDataUnit() const = 0; - virtual const KinematicDataUnitInterface* getRTKinematicDataUnit() const = 0; - virtual const PlatformDataUnitInterface* getRTPlatformDataUnit() const = 0; - //targets (nonrt but results are used in rt) - virtual TargetBase* getJointTarget(const std::string& jointName, const std::string& controlMode) override; - - protected: - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - // NonRT - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - // setup the data structures - void setJointNames(const std::vector<std::string>& names); - - // Component - /// @see PropertyUser::createPropertyDefinitions() - virtual armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; - //get buffers - /// @return The requested lvl0 controllers (none is null) - std::vector<LVL0Controller*>& getRequestedLVL0Controllers() - { - return controllersRequested.getWriteBuffer().lvl0Controllers; - } - /// @return The requested lvl1 controllers (some may be null) - std::vector<LVL1ControllerPtr >& getRequestedLVL1Controllers() - { - return controllersRequested.getWriteBuffer().lvl1Controllers; - } - /// @return The requested lvl0 controllers (none is null) - const std::vector<LVL0Controller*>& getRequestedLVL0Controllers() const - { - return controllersRequested.getWriteBuffer().lvl0Controllers; - } - /// @return The requested lvl1 controllers (some may be null) - const std::vector<LVL1ControllerPtr >& getRequestedLVL1Controllers() const - { - return controllersRequested.getWriteBuffer().lvl1Controllers; - } - /// @return The activated lvl0 controllers (none is null) - const std::vector<LVL0Controller*>& getActivatedLVL0Controllers() const - { - return controllersActivated.getUpToDateReadBuffer().lvl0Controllers; - } - /// @return The activated lvl1 controllers (some may be null) - const std::vector<LVL1ControllerPtr >& getActivatedLVL1Controllers() const - { - return controllersActivated.getUpToDateReadBuffer().lvl1Controllers; - } - - //checks - /// @return Return whether the set of added LVL0Controllers is ok (e.g. if each joint has one EmergencyStop controller) - virtual bool validateAddedLVL0Controllers() const; - - /** - * @brief Add a LVL0Controller for a specified joint. - * @param jointName The joint controlled by the controller - * @param lvl0Controller The controller to add. - * @throw If you add two controllers withe the same control mode for one joint, an std::invalid_argument exception is thrown. - */ - void addLVL0Controller(const std::string& jointName, LVL0Controller& lvl0Controller); - - /// @return The LVL0Controller for given joint and control mode. - const LVL0Controller* getLVL0Controller(const std::string& jointName, const std::string& controlMode) const; - - /// @return The LVL0Controller for given joint and control mode. - LVL0Controller* getLVL0Controller(const std::string& jointName, const std::string& controlMode); - - LVL0Controller* rtGetEmergencyStopLVL0Controller(std::size_t index) - { - return lvl0ControllersEmergencyStop.at(index); - } - - /// @return Whether a LVL0Controller for given joint and control mode exists. - bool hasLVL0Controller(const std::string& jointName, const std::string& controlMode) const; - - /// @return Whether a LVL1Controller for this name is loaded. - bool hasLVL1Controller(const std::string& name) const; - - static void rtSetLVL1ControllerActive(const LVL1ControllerPtr& lvl1, bool isActive = 1); - - //other - private: - bool areJointNamesSet() const; - bool loadLibFromAbsolutePath(const std::string& path); - - protected: - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - // RT - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - // get buffers (rt) - bool rtControllersWereSwitched() const - { - return controllersRequested.updateReadBuffer(); - } - /// @return The requested lvl0 controllers (none is null) - const std::vector<LVL0Controller*>& rtGetRequestedLVL0Controllers() const - { - return controllersRequested.getReadBuffer().lvl0Controllers; - } - /// @return The requested lvl1 controllers (some may be null) - const std::vector<LVL1ControllerPtr >& rtGetRequestedLVL1Controllers() const - { - return controllersRequested.getReadBuffer().lvl1Controllers; - } - /// @return The activated lvl0 controllers (none is null) - std::vector<LVL0Controller*>& rtGetActivatedLVL0Controllers() - { - return controllersActivated.getWriteBuffer().lvl0Controllers; - } - /// @return The activated lvl1 controllers (some may be null) - std::vector<LVL1ControllerPtr >& rtGetActivatedLVL1Controllers() - { - return controllersActivated.getWriteBuffer().lvl1Controllers; - } - /// @return The activated lvl0 controllers (none is null) - const std::vector<LVL0Controller*>& rtGetActivatedLVL0Controllers() const - { - return controllersActivated.getWriteBuffer().lvl0Controllers; - } - /// @return The activated lvl1 controllers (some may be null) - const std::vector<LVL1ControllerPtr >& rtGetActivatedLVL1Controllers() const - { - return controllersActivated.getWriteBuffer().lvl1Controllers; - } - //get controllers (rt) - LVL0Controller* const& rtGetActivatedLVL0Controller(std::size_t index) const - { - return rtGetActivatedLVL0Controllers().at(index); - } - const LVL1ControllerPtr& rtGetActivatedLVL1Controller(std::size_t index) const - { - return rtGetActivatedLVL1Controllers().at(index); - } - LVL0Controller*& rtGetActivatedLVL0Controller(std::size_t index) - { - return rtGetActivatedLVL0Controllers().at(index); - } - LVL1ControllerPtr& rtGetActivatedLVL1Controller(std::size_t index) - { - return rtGetActivatedLVL1Controllers().at(index); - } - - - LVL0Controller* rtGetRequestedLVL0Controller(std::size_t index) const - { - return rtGetRequestedLVL0Controllers().at(index); - } - const LVL1ControllerPtr& rtGetRequestedLVL1Controller(std::size_t index) const - { - return getRequestedLVL1Controllers().at(index); - } - - void rtCommitActivatedControllers() - { - controllersActivated.commitWrite(); - } - - //switching (rt) - bool rtSwitchSetup(); - void rtRunLVL1Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); - void rtDeactivateAssignedLVL1Controller(std::size_t index); - void rtRunLVL0Controllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); - - /** - * @brief Hook for switching the lvl0 controller (this changes the controll mode) - * @param index The index of the lvl0 controller - * @param oldLVL0 The old lvl0 controller (will be deactivated) - * @param newLVL0 The new lvl0 controller (will be activated) - */ - virtual void rtSwitchLVL0Controller(std::size_t index, LVL0Controller* oldLVL0, LVL0Controller* newLVL0) = 0; - - - protected: - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - // data - // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // - mutable MutexType dataMutex; - //data accessible in RT and nonRT - std::vector<std::string> jointNames; - std::unordered_map<std::string, std::size_t> jointNameIndices; - /// @brief True if some error occured in switch LVL1Controllers - std::atomic_bool switchSetupError {false}; - - private: - //controllers - struct LVL0AndLVL1ControllerList - { - LVL0AndLVL1ControllerList(std::size_t size): - lvl0Controllers {size, nullptr}, - lvl1Controllers {size, nullptr} - {} - - LVL0AndLVL1ControllerList() = default; - LVL0AndLVL1ControllerList(const LVL0AndLVL1ControllerList&) = default; - - std::vector<LVL0Controller*> lvl0Controllers; - std::vector<LVL1ControllerPtr > lvl1Controllers; - }; - /** - * @brief Holds pointer to all lvl0 controllers. (index: [jointname][controlmode]) - * Is initialized by derived classes. - * May not be accessed when the controll loop is running - */ - std::map<std::string, std::map<std::string, LVL0Controller*>> lvl0Controllers; - /** - * @brief Holds all currently loaded LVL1 controllers (index: [instancename]) - * May not be accessed in rt. - */ - std::map<std::string, LVL1ControllerPtr> lvl1Controllers; - /// @brief LVL0Controllers for Emergency stop - std::vector<LVL0Controller*> lvl0ControllersEmergencyStop; - - //used to communicate with rt - /// @brief Controllers the RT thread is supposed to activate (direction nonRT -> RT) (some lvl1 controllers may be null) - WriteBufferedTripleBuffer<LVL0AndLVL1ControllerList> controllersRequested; - /// @brief Controllers the RT thread is currently running (direction RT -> nonRT) (some lvl1 controllers may be null) - WriteBufferedTripleBuffer<LVL0AndLVL1ControllerList> controllersActivated; - - //other - /// @brief Stores joints accessed via getJointTarget - JointNameToControlModeDictionary jointsUsedByLVL1Controller; - std::map<std::string, DynamicLibraryPtr> loadedLibs; - }; - - using RobotUnitPtr = IceInternal::Handle<RobotUnit>; -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorPositionTarget.h b/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorPositionTarget.h deleted file mode 100644 index 643a12e539d3fb3ee7e64d3784f6ab1c042aa4b0..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorPositionTarget.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::ActuatorPositionTarget - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_JointPositionTarget_H -#define _ARMARX_LIB_RobotAPI_JointPositionTarget_H - -#include "TargetBase.h" - -namespace armarx -{ - /** - * @defgroup Library-RTRobotUnit RTRobotUnit - * @ingroup RobotAPI - * A description of the library RTRobotUnit. - * - * @class ActuatorPositionTarget - * @ingroup Library-RTRobotUnit - * @brief Brief description of class ActuatorPositionTarget. - * - * Detailed description of class ActuatorPositionTarget. - */ - class ActuatorPositionTarget: public TargetBase - { - public: - float position = ControllerConstants::ValueNotSetNaN; - virtual std::string getControlMode() const override - { - return ControlModes::PositionMode; - } - virtual void reset() override - { - position = ControllerConstants::ValueNotSetNaN; - } - virtual bool isValid() const override - { - return std::isfinite(position); - } - }; - -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorTorqueTarget.h b/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorTorqueTarget.h deleted file mode 100644 index d4e29a926c249c54a864b43be6f131f8eb70dd2d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorTorqueTarget.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::ActuatorTorqueTarget - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_JointTorqueTarget_H -#define _ARMARX_LIB_RobotAPI_JointTorqueTarget_H - -#include "TargetBase.h" - -namespace armarx -{ - /** - * @defgroup Library-RTRobotUnit RTRobotUnit - * @ingroup RobotAPI - * A description of the library RTRobotUnit. - * - * @class ActuatorTorqueTarget - * @ingroup Library-RTRobotUnit - * @brief Brief description of class ActuatorTorqueTarget. - * - * Detailed description of class ActuatorTorqueTarget. - */ - class ActuatorTorqueTarget: public TargetBase - { - public: - float torque = ControllerConstants::ValueNotSetNaN; - virtual std::string getControlMode() const override - { - return ControlModes::TorqueMode; - } - virtual void reset() override - { - torque = ControllerConstants::ValueNotSetNaN; - } - virtual bool isValid() const override - { - return std::isfinite(torque); - } - }; - -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorVelocityTarget.h b/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorVelocityTarget.h deleted file mode 100644 index edfc60972f11de19009564e51bd189139f9e2877..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/Targets/ActuatorVelocityTarget.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::ActuatorVelocityTarget - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_ActuatorVelocityTarget_H -#define _ARMARX_LIB_RobotAPI_ActuatorVelocityTarget_H - -#include "TargetBase.h" - -namespace armarx -{ - /** - * @defgroup Library-RTRobotUnit RTRobotUnit - * @ingroup RobotAPI - * A description of the library RTRobotUnit. - * - * @class ActuatorVelocityTarget - * @ingroup Library-RTRobotUnit - * @brief Brief description of class ActuatorVelocityTarget. - * - * Detailed description of class ActuatorVelocityTarget. - */ - class ActuatorVelocityTarget: public TargetBase - { - public: - float velocity = ControllerConstants::ValueNotSetNaN; - virtual std::string getControlMode() const override - { - return ControlModes::VelocityMode; - } - virtual void reset() override - { - velocity = ControllerConstants::ValueNotSetNaN; - } - virtual bool isValid() const override - { - return std::isfinite(velocity); - } - }; -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/Targets/PlatformWheelVelocityTarget.h b/source/RobotAPI/libraries/RTRobotUnit/Targets/PlatformWheelVelocityTarget.h deleted file mode 100644 index c2bafb0adb4cb68407667fbf5db85c2eb9eb7ea6..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/Targets/PlatformWheelVelocityTarget.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of ArmarX. - * - * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), - * Karlsruhe Institute of Technology (KIT), all rights reserved. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @author Markus Swarowsky (markus dot swarowsky at student dot kit dot edu) - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - - -#ifndef _ARMARX_LIB_RobotAPI_PLATFORMWHEELTARGET_H -#define _ARMARX_LIB_RobotAPI_PLATFORMWHEELTARGET_H - - -#include "TargetBase.h" - - -namespace armarx -{ - - class PlatformWheelVelocityTarget : public TargetBase - { - public: - float velocity = ControllerConstants::ValueNotSetNaN; - virtual std::string getControlMode() const override - { - return ControlModes::VelocityMode; - } - virtual void reset() override - { - velocity = ControllerConstants::ValueNotSetNaN; - } - virtual bool isValid() const override - { - return std::isfinite(velocity); - } - - private: - }; - -} - -#endif //_ARMARX_LIB_RobotAPI_PLATFORMWHEELTARGET_H diff --git a/source/RobotAPI/libraries/RTRobotUnit/Targets/TargetBase.h b/source/RobotAPI/libraries/RTRobotUnit/Targets/TargetBase.h deleted file mode 100644 index 1d515e670d3a0e8428f9c301c232d497c83338c3..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/Targets/TargetBase.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::JointTargetBase - * @author Raphael ( ufdrv at student dot kit dot edu ) - * @date 2017 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_LIB_RobotAPI_JointTargetBase_H -#define _ARMARX_LIB_RobotAPI_JointTargetBase_H - -#include <RobotAPI/interface/units/KinematicUnitInterface.h> -#include "../Constants.h" -#include "../ControlModes.h" - -namespace armarx -{ - /** - * @defgroup Library-RTRobotUnit RTRobotUnit - * @ingroup RobotAPI - * A description of the library RTRobotUnit. - * - * @class TargetBase - * @ingroup Library-RTRobotUnit - * @brief Brief description of class JointTargetBase. - * - * Detailed description of class TargetBase. - */ - class TargetBase - { - public: - virtual std::string getControlMode() const = 0; - virtual void reset() = 0; - virtual bool isValid() const = 0; - }; -} - -#endif diff --git a/source/RobotAPI/libraries/RTRobotUnit/test/BasicControllerTest.cpp b/source/RobotAPI/libraries/RTRobotUnit/test/BasicControllerTest.cpp deleted file mode 100644 index a3ff1667357a7fe758d8901aeb2e0d772ecfb25a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/RTRobotUnit/test/BasicControllerTest.cpp +++ /dev/null @@ -1,596 +0,0 @@ -/* -* This file is part of ArmarX. -* -* ArmarX is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License version 2 as -* published by the Free Software Foundation. -* -* ArmarX is distributed in the hope that it will be useful, but -* WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see <http://www.gnu.org/licenses/>. -* -* @package ArmarX::RobotAPI::Test -* @author Raphael ( ufdrv at student dot kit dot edu ) -* @date 2017 -* @copyright http://www.gnu.org/licenses/gpl-2.0.txt -* GNU General Public License -*/ - -#define BOOST_TEST_MODULE RobotAPI::BasicControllers::Test -#define ARMARX_BOOST_TEST -#define CREATE_LOGFILES -#include <random> -#include <iostream> - -#include <boost/algorithm/clamp.hpp> - -#include <ArmarXCore/core/util/algorithm.h> -#include <RobotAPI/Test.h> -#include "../BasicControllers.h" -using namespace armarx; -//params for random tests -const std::size_t tries = 10; -const std::size_t ticks = 20000; // each tick is 0.75 to 1.25 ms -//helpers for logging -#ifdef CREATE_LOGFILES - #define LOG_CONTROLLER_DATA_WRITE_TO(name) change_logging_file(name) - #define LOG_CONTROLLER_DATA(...) f << __VA_ARGS__ << "\n" - - #include <boost/filesystem.hpp> - #include <fstream> - - static const boost::filesystem::path fpath{"controller_logfiles/"}; - static std::ofstream f; - static bool isSetup = false; - - void change_logging_file(const std::string& name) - { - if(f.is_open()) - { - f.close(); - } - if(!isSetup) - { - boost::filesystem::create_directories(fpath); - //create the python evaluation file - boost::filesystem::path tmppath(fpath/"eval.py"); - f.open(tmppath.string()); - f<<R"raw_str_delim( -def consume_file(fname, save=True, show=True): - #get data - with open(fname, 'r') as f: - data = [ x.split(' ') for x in f.read().split('\n') if x != ''] - #plot - from mpl_toolkits.axes_grid1 import host_subplot - import mpl_toolkits.axisartist as AA - import matplotlib.pyplot as plt - - pos = host_subplot(111, axes_class=AA.Axes) - plt.subplots_adjust(right=0.75) - vel = pos.twinx() - acc = pos.twinx() - - - pos.axhline(0, color='black') - vel.axhline(0, color='black') - acc.axhline(0, color='black') - - offset = 60 - acc.axis['right'] = acc.get_grid_helper().new_fixed_axis(loc='right', - axes=acc, - offset=(offset, 0)) - - c={pos:'red', vel:'blue', acc:'green'} - b={pos:0 , vel:0 , acc:0 } - - pos.set_xlabel('Time [s]') - pos.set_ylabel('Position') - vel.set_ylabel('Velocity') - acc.set_ylabel('Acceleration') - - pos.axis['left'].label.set_color(c[pos]) - vel.axis['right'].label.set_color(c[vel]) - acc.axis['right'].label.set_color(c[acc]) - - - def get(name,factor=1): - if name in data[0]: - return [factor*float(x[data[0].index(name)]) for x in data[1:]] - - times=get('time') - - def add(plt,name,factor=1, clr=None, style='-'): - d=get(name,factor) - if d is None or [0]*len(d) == d: - return - if clr is None: - clr=c[plt] - plt.plot(times, d, style,color=clr) - b[plt] = max([b[plt]]+[abs(x) for x in d]) - plt.set_ylim(-b[plt]*1.1, b[plt]*1.1) - - add(pos,'curpos',clr='red') - add(pos,'targpos',clr='red', style='-.') - add(pos,'posHi',clr='darkred', style='--') - add(pos,'posLo',clr='darkred', style='--') - add(pos,'posHiHard',clr='darkred', style='--') - add(pos,'posLoHard',clr='darkred', style='--') - add(pos,'brakingDist',clr='orange', style=':') - add(pos,'posAfterBraking',clr='magenta', style=':') - - add(vel,'curvel',clr='teal') - add(vel,'targvel',clr='teal', style='-.') - add(vel,'maxv',clr='blue', style='--') - add(vel,'maxv',factor=-1,clr='blue', style='--') - - add(acc,'curacc',clr='green') - add(acc,'acc',clr='darkgreen', style='--') - add(acc,'dec',clr='darkgreen', style='--') - - plt.draw() - if save: plt.savefig(fname+'.png') - if show: plt.show() - if not show: plt.close() - - -import sys -import os -def handle_path(p, show=True): - if '.' in p: - return - if os.path.isfile(p): - consume_file(p,show=show) - if os.path.isdir(p): - show=False - for subdir, dirs, files in os.walk(p): - for f in files: - handle_path(subdir+f,show=show) - -if len(sys.argv) >= 2: - handle_path(sys.argv[1]) -else: - handle_path('./') - )raw_str_delim"; - isSetup=true; - f.close(); - } - boost::filesystem::path tmppath(fpath/name); - f.open(tmppath.string()); - std::cout<<"now writing to: " << boost::filesystem::absolute(tmppath).string()<<"\n"; - } - -#else - #define LOG_CONTROLLER_DATA_WRITE_TO(name) do{}while(0) - #define LOG_CONTROLLER_DATA(...) do{}while(0) -#endif - - -unsigned int getSeed() -{ - static const auto seed = std::random_device{}(); - std::cout << "seed = " << seed << std::endl; - return seed; -} - -static std::mt19937 gen{getSeed()}; - -struct Simulation -{ - float time=0; - float dt=0; - float maxDt = 0.001; - - float curpos=0; - float oldpos=0; - float targpos=0; - float posHiHard= M_PI; - float posLoHard=-M_PI; - float posHi= M_PI; - float posLo=-M_PI; - - float curvel=0; - float oldvel=0; - float targvel=0; - float maxvel = 10; - - float curacc=0; - float oldacc=0; - float acc=0; - float dec=0; - - float brakingDist=0; - float posAfterBraking=0; - - - std::uniform_real_distribution<float> vDist; - std::uniform_real_distribution<float> aDist; - std::uniform_real_distribution<float> pDist; - std::uniform_real_distribution<float> tDist; - - void reset() - { - time=0; - - curpos=0; - oldpos=0; - targpos=0; - - curvel=0; - oldvel=0; - targvel=0; - - curacc=0; - oldacc=0; - acc=0; - dec=0; - - brakingDist=0; - posAfterBraking=0; - - vDist = std::uniform_real_distribution<float>{-maxvel, maxvel}; - aDist = std::uniform_real_distribution<float>{maxvel/4, maxvel*4 }; - tDist = std::uniform_real_distribution<float>{maxDt*0.75f, maxDt*1.25f}; - pDist = std::uniform_real_distribution<float>{posLoHard, posHiHard}; - } - - //updating - template<class FNC> - void tick(FNC& callee) - { - dt=tDist(gen); - callee(); - log(); - } - - void updateVel(float newvel) - { - BOOST_CHECK(std::isfinite(newvel)); - //save old - oldvel = curvel; - oldpos = curpos; - oldacc = curacc; - - //update - curvel = newvel; - curacc=std::abs(curvel-oldvel)/dt; - curpos+=curvel*dt; - time += dt; - brakingDist=brakingDistance(curvel,dec); - posAfterBraking=curpos+brakingDist; - } - - //checks - void checkVel() - { - //check v - BOOST_CHECK_LE(curvel,maxvel*1.01); - BOOST_CHECK_LE(-maxvel*1.01,curvel); - } - - void checkPos() - { - BOOST_CHECK_LE(curpos,posHiHard); - BOOST_CHECK_LE(posLoHard,curpos); - } - - void checkAcc() - { - if(sign(curvel) != sign(oldvel)) - { -// std::cout<< "Time["<<time<<"] velocity changed sign (from/to):" << sign(oldvel) << " / " << sign(curvel)<<"\n"; - return; - } - if(std::abs(oldvel)>std::abs(curvel)) - { - //we decellerated - if(!(curacc < dec*1.01)) - { - std::cout << "Time["<<time<<"] violated deceleration bound! vold " << oldvel << " / vnew " << curvel << " / dv " << std::abs(oldvel-curvel) << " / dt " << dt<< " / dec " <<dec - <<" / (targetv " << targvel<<")\n"; - } - BOOST_CHECK_LE(curacc,dec*1.01); - } - else - { - //we accellerated - if(!(curacc < acc*1.01)) - { - std::cout << "Time["<<time<<"] violated deceleration bound! vold " << oldvel << " / vnew " << curvel << " / dv " << std::abs(oldvel-curvel) << " / dt " << dt<< " / acc " <<acc - <<" / (targetv " << targvel<<")\n"; - } - BOOST_CHECK_LE(curacc,acc*1.01); - } - } - - //logging - void writeHeader(const std::string& name) - { - LOG_CONTROLLER_DATA_WRITE_TO(name); - LOG_CONTROLLER_DATA("time curpos targpos posHiHard posLoHard posHi posLo curvel targvel maxvel curacc acc dec brakingDist posAfterBraking"); - reset(); - } - void log() - { - //output a neg val for dec and a pos val for acc - float outputacc; - if(sign(curvel) != sign(oldvel)) - { - // cant determine the current acceleration correctly (since we have an acceleration and deceleration phase) - outputacc = 0; - } - else - { - outputacc = curacc; - if(std::abs(oldvel)>std::abs(curvel)) - { - //we decelerated -> negative sign - outputacc *= -1; - } - } - LOG_CONTROLLER_DATA(time<<" "<< - curpos<<" "<<targpos<<" "<<posHiHard<<" "<<posLoHard<<" "<<posHi<<" "<<posLo<<" "<< - curvel<<" "<<targvel<<" "<<maxvel<<" "<< - outputacc<<" "<<acc<<" "<<-dec<<" "<< - brakingDist<<" "<<posAfterBraking); - } -}; - - -BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationBoundsTest) -{ - std::cout<< "starting velocityControlWithAccelerationBoundsTest \n"; - Simulation s; - s.posHi=0; - s.posLo=0; - s.posHiHard=0; - s.posLoHard=0; - - float directSetVLimit = 0.005; - - auto testTick= [&] - { - s.updateVel(velocityControlWithAccelerationBounds(s.dt, s.maxDt, s.curvel, s.targvel, s.maxvel, s.acc, s.dec, directSetVLimit)); - s.checkVel(); - s.checkAcc(); - }; - - std::cout<< "random tests\n"; - for(std::size_t try_ = 0; try_ < tries; ++ try_) - { - s.writeHeader("velCtrl+acc_random_"+std::to_string(try_)); - s.curvel=s.vDist(gen); - s.targvel=s.vDist(gen); - s.acc=s.aDist(gen); - s.dec=s.aDist(gen); - directSetVLimit = (std::min(s.acc,s.dec)*s.maxDt*0.75f/2.f) * 0.99f; // vlimit = accmin*dtmin/2 - - s.log(); - for(std::size_t tick = 0; tick < ticks; ++tick) - { - s.tick(testTick); - } - BOOST_CHECK_EQUAL(s.curvel, s.targvel); - } - std::cout<< "bounds tests\n"; - std::cout<< "TODO\n"; - ///TODO - - std::cout<< "done velocityControlWithAccelerationBoundsTest \n"; -} - -BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationAndPositionBoundsTest) -{ - std::cout<< "starting velocityControlWithAccelerationAndPositionBoundsTest \n"; - Simulation s; - - const float positionSoftLimitViolationVelocity = 0.1; - float directSetVLimit = 0.005; - s.posLo=s.posLoHard*0.99; - s.posHi=s.posHiHard*0.99; - - auto testTick=[&] - { - s.updateVel(velocityControlWithAccelerationAndPositionBounds( - s.dt, s.maxDt, - s.curvel, s.targvel, s.maxvel, - s.acc, s.dec, - directSetVLimit, - s.curpos, - s.posLo, s.posHi, - positionSoftLimitViolationVelocity, - s.posLoHard, s.posHiHard - )); - s.checkPos(); - s.checkVel(); - }; - - std::cout<< "random tests\n"; - for(std::size_t try_ = 0; try_ < tries; ++ try_) - { - s.writeHeader("velCtrl+acc+pos_random_"+std::to_string(try_)); - s.curvel=s.vDist(gen); - s.curpos=s.pDist(gen); - s.targvel=s.vDist(gen); - s.acc=s.aDist(gen); - s.dec=s.aDist(gen); - directSetVLimit = (std::min(s.acc,s.dec)*s.maxDt*0.75f/2.f) * 0.99f; // vlimit = accmin*dtmin/2 - s.log(); - for(std::size_t tick = 0; tick < ticks; ++tick) - { - s.tick(testTick); - } - } - std::cout<< "bounds tests\n"; - std::cout<< "TODO\n"; - ///TODO - - std::cout<< "done velocityControlWithAccelerationAndPositionBoundsTest \n"; -} - -BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsTest) -{ - std::cout<< "starting positionThroughVelocityControlWithAccelerationBounds \n"; - Simulation s; - s.posHi=0; - s.posLo=0; - -// float p = 0.05; -// const float pControlPosErrorLimit = 0.02; -// const float pControlVelLimit = 0.02; - float p = 0.1; - const float pControlPosErrorLimit = 0.01; - const float pControlVelLimit = 0.01; - - auto testTick=[&] - { - s.updateVel(positionThroughVelocityControlWithAccelerationBounds( - s.dt, s.maxDt, - s.curvel, s.maxvel, - s.acc, s.dec, - s.curpos, s.targpos, - pControlPosErrorLimit, pControlVelLimit, p - )); - s.checkVel(); - s.checkAcc(); - }; - - std::cout<< "random tests\n"; - for(std::size_t try_ = 0; try_ < tries; ++ try_) - { - s.writeHeader("posViaVelCtrl+acc_random_"+std::to_string(try_)); - s.curvel=s.vDist(gen); - s.curpos=s.pDist(gen); - s.targpos=s.pDist(gen); - s.acc=s.aDist(gen); - s.dec=s.aDist(gen); -// p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 - s.log(); - for(std::size_t tick = 0; tick < ticks; ++tick) - { - s.tick(testTick); - } - BOOST_CHECK_LE(std::abs(s.curpos-s.targpos), 0.01);// allow error of 0.5729577951308232 deg - } - std::cout<< "bounds tests\n"; - std::cout<< "TODO\n"; - ///TODO - - std::cout<< "done positionThroughVelocityControlWithAccelerationBounds \n"; -} - -BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationAndPositionBoundsTest) -{ - std::cout<< "starting positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; - Simulation s; - s.posHi=0; - s.posLo=0; - // float p = 0.05; - // const float pControlPosErrorLimit = 0.02; - // const float pControlVelLimit = 0.02; - float p = 0.1; - const float pControlPosErrorLimit = 0.01; - const float pControlVelLimit = 0.01; - - auto testTick=[&] - { - s.updateVel(positionThroughVelocityControlWithAccelerationAndPositionBounds( - s.dt, s.maxDt, - s.curvel, s.maxvel, - s.acc, s.dec, - s.curpos, s.targpos, - pControlPosErrorLimit, pControlVelLimit, p, - s.posLoHard, s.posHiHard)); - s.checkPos(); - }; - - std::cout<< "random tests\n"; - for(std::size_t try_ = 0; try_ < tries; ++ try_) - { - s.writeHeader("posViaVelCtrl+acc+pos_random_"+std::to_string(try_)); - s.curvel=s.vDist(gen); - s.curpos=s.pDist(gen); - s.targpos=s.pDist(gen); - s.acc=s.aDist(gen); - s.dec=s.aDist(gen); - // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 - s.log(); - for(std::size_t tick = 0; tick < ticks; ++tick) - { - s.tick(testTick); - } - BOOST_CHECK_LE(std::abs(s.curpos-s.targpos), 0.05); - } - std::cout<< "bounds tests\n"; - std::cout<< "TODO\n"; - ///TODO - - std::cout<< "done positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; -} - -BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPositionTest) -{ - std::cout<< "starting positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; - Simulation s; - s.posHi=0; - s.posLo=0; - // float p = 0.05; - // const float pControlPosErrorLimit = 0.02; - // const float pControlVelLimit = 0.02; - float p = 0.1; - const float pControlPosErrorLimit = 0.01; - const float pControlVelLimit = 0.01; - float direction; - - auto testTick=[&] - { - s.updateVel(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition( - s.dt, s.maxDt, - s.curvel, s.maxvel, - s.acc, s.dec, - s.curpos, s.targpos, - pControlPosErrorLimit, pControlVelLimit, p, - direction, - s.posLoHard, s.posHiHard)); - s.checkVel(); - s.checkAcc(); - s.curpos=periodicClamp(s.curpos,s.posLoHard, s.posHiHard); - }; - - auto rngTestRuns=[&](int d) - { - std::cout<< "random tests (dir="<<std::to_string(d)<<"\n"; - for(std::size_t try_ = 0; try_ < tries; ++ try_) - { - s.writeHeader("posViaVelCtrl+acc+periodicPos+dir"+std::to_string(d)+"_random_"+std::to_string(try_)); - direction = d; - s.curvel=s.vDist(gen); - s.curpos=periodicClamp(s.pDist(gen),s.posLoHard, s.posHiHard); - s.targpos=periodicClamp(s.pDist(gen),s.posLoHard, s.posHiHard); - s.acc=s.aDist(gen); - s.dec=s.aDist(gen); - s.log(); - for(std::size_t tick = 0; tick < ticks; ++tick) - { - s.tick(testTick); - } - BOOST_CHECK_LE(std::abs(s.curpos-s.targpos), 0.01); - BOOST_CHECK_LE(std::abs(s.curvel), 0.1); - } - - }; - rngTestRuns(0); - rngTestRuns(1); - rngTestRuns(-1); - - - std::cout<< "bounds tests\n"; - std::cout<< "TODO\n"; - ///TODO - - std::cout<< "done positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; -} -