diff --git a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt index 69f3f77896d198d4e02ed08ed05eac395e32d0a3..f85b0daedd4db697ff63a20713aed4fd112930be 100644 --- a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt +++ b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt @@ -28,6 +28,7 @@ set(LIB_FILES RobotUnitObserver.cpp BasicControllers.cpp DefaultWidgetDescriptions.cpp + RobotSynchronization.cpp ControlTargets/ControlTargetBase.cpp @@ -38,6 +39,8 @@ set(LIB_FILES Units/InertialMeasurementSubUnit.cpp Units/KinematicSubUnit.cpp Units/PlatformSubUnit.cpp + Units/TrajectoryControllerSubUnit.cpp + Units/TCPControllerSubUnit.cpp JointControllers/JointController.cpp @@ -71,6 +74,7 @@ set(LIB_HEADERS RobotUnitObserver.h BasicControllers.h DefaultWidgetDescriptions.h + RobotSynchronization.h ControlTargets/ControlTargetBase.h ControlTargets/ControlTarget1DoFActuator.h @@ -88,6 +92,8 @@ set(LIB_HEADERS Units/InertialMeasurementSubUnit.h Units/KinematicSubUnit.h Units/PlatformSubUnit.h + Units/TrajectoryControllerSubUnit.h + Units/TCPControllerSubUnit.h JointControllers/JointController.h diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.cpp index 22e3bfaea68a5bd46cbe9f6e6c43754a1a97ae8d..d2df87384c5dac6572a462f8ab10b87e892b08c9 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.cpp @@ -39,20 +39,38 @@ namespace armarx void NJointTCPController::rtPreActivateController() { - xVel = 0; - yVel = 0; - zVel = 0; - rollVel = 0; - pitchVel = 0; - yawVel = 0; + reinitTripleBuffer(NJointTCPControllerControlData()); } void NJointTCPController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { - Eigen::VectorXf x(6); - x << this->xVel, yVel, zVel, rollVel, pitchVel, yawVel; - Eigen::MatrixXf jacobiInv = ik->getPseudoInverseJacobianMatrix(tcp); + auto mode = rtGetControlStruct().mode; + + Eigen::VectorXf x; + if (mode == VirtualRobot::IKSolver::All) + { + x.resize(6); + x << rtGetControlStruct().xVel, rtGetControlStruct().yVel, rtGetControlStruct().zVel, rtGetControlStruct().rollVel, rtGetControlStruct().pitchVel, rtGetControlStruct().yawVel; + } + else if (mode == VirtualRobot::IKSolver::Position) + { + x.resize(3); + x << rtGetControlStruct().xVel, rtGetControlStruct().yVel, rtGetControlStruct().zVel; + } + else if (mode == VirtualRobot::IKSolver::Orientation) + { + x.resize(3); + x << rtGetControlStruct().rollVel, rtGetControlStruct().pitchVel, rtGetControlStruct().yawVel; + } + else + { + // No mode has been set + return; + } + + Eigen::MatrixXf jacobiInv = ik->getPseudoInverseJacobianMatrix(tcp, mode); Eigen::VectorXf jointTargetVelocities = jacobiInv * x; + for (size_t i = 0; i < targets.size(); ++i) { targets.at(i)->velocity = jointTargetVelocities(i); @@ -155,6 +173,26 @@ namespace armarx ik.reset(new VirtualRobot::DifferentialIK(nodeset, robotUnit->getRtRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); tcp = (config->tcpName.empty() || config->tcpName == DEFAULT_TCP_STRING) ? nodeset->getTCP() : robotUnit->getRtRobot()->getRobotNode(config->tcpName); ARMARX_CHECK_EXPRESSION_W_HINT(tcp, config->tcpName); + + nodeSetName = config->nodeSetName; + } + + void NJointTCPController::setVelocities(float xVel, float yVel, float zVel, float rollVel, float pitchVel, float yawVel, VirtualRobot::IKSolver::CartesianSelection mode) + { + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().xVel = xVel; + getWriterControlStruct().yVel = yVel; + getWriterControlStruct().zVel = zVel; + getWriterControlStruct().rollVel = rollVel; + getWriterControlStruct().pitchVel = pitchVel; + getWriterControlStruct().yawVel = yawVel; + getWriterControlStruct().mode = mode; + writeControlStruct(); + } + + std::string NJointTCPController::getNodeSetName() const + { + return nodeSetName; } void NJointTCPController::rtPostDeactivateController() @@ -177,21 +215,20 @@ namespace armarx { if (name == "ControllerTarget") { - xVel = valueMap.at("x")->getFloat(); - yVel = valueMap.at("y")->getFloat(); - zVel = valueMap.at("z")->getFloat(); - rollVel = valueMap.at("roll")->getFloat(); - pitchVel = valueMap.at("pitch")->getFloat(); - yawVel = valueMap.at("yaw")->getFloat(); + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().xVel = valueMap.at("x")->getFloat(); + getWriterControlStruct().yVel = valueMap.at("y")->getFloat(); + getWriterControlStruct().zVel = valueMap.at("z")->getFloat(); + getWriterControlStruct().rollVel = valueMap.at("roll")->getFloat(); + getWriterControlStruct().pitchVel = valueMap.at("pitch")->getFloat(); + getWriterControlStruct().yawVel = valueMap.at("yaw")->getFloat(); + writeControlStruct(); } else { ARMARX_WARNING << "Unknown function name called: " << name; } } - - - } // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.h index e5520daa7ab750e834f17b6ff37e1c1047b7ad37..46eff8bfb2003a4ed9216c86a57c23faaf7a2367 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTCPController.h @@ -26,8 +26,9 @@ #include "NJointController.h" #include <VirtualRobot/Robot.h> -#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> -#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include "../RobotUnit.h" +#include "../ControlTargets/ControlTarget1DoFActuator.h" +#include "../SensorValues/SensorValue1DoFActuator.h" #include <VirtualRobot/IK/DifferentialIK.h> namespace armarx @@ -48,8 +49,22 @@ namespace armarx const std::string tcpName; }; + TYPEDEF_PTRS_HANDLE(NJointTCPControllerControlData); + class NJointTCPControllerControlData + { + public: + float xVel = 0; + float yVel = 0; + float zVel = 0; + float rollVel = 0; + float pitchVel = 0; + float yawVel = 0; + VirtualRobot::IKSolver::CartesianSelection mode = VirtualRobot::IKSolver::All; + }; - class NJointTCPController : public NJointController + + class NJointTCPController : + public NJointControllerWithTripleBuffer<NJointTCPControllerControlData> { public: using ConfigPtrT = NJointTCPControllerConfigPtr; @@ -72,6 +87,10 @@ namespace armarx NJointControllerDescriptionProviderInterfacePtr prov, NJointTCPControllerConfigPtr config, const boost::shared_ptr<VirtualRobot::Robot>& r); + + // for TCPControlUnit + void setVelocities(float xVel, float yVel, float zVel, float rollVel, float pitchVel, float yawVel, VirtualRobot::IKSolver::CartesianSelection mode); + std::string getNodeSetName() const; static ::armarx::WidgetDescription::WidgetSeq createSliders(); protected: void rtPreActivateController() override; @@ -80,19 +99,10 @@ namespace armarx std::vector<ControlTarget1DoFActuatorVelocity*> targets; std::vector<const SensorValue1DoFActuatorPosition*> sensors; - std::atomic<float> xVel; - std::atomic<float> yVel; - std::atomic<float> zVel; - std::atomic<float> rollVel; - std::atomic<float> pitchVel; - std::atomic<float> yawVel; VirtualRobot::RobotNodePtr tcp; VirtualRobot::DifferentialIKPtr ik; - - - - + std::string nodeSetName; }; } // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp index 2efa4a636ac1370101e116142b1cf16847eddd68..34ad724d12f015b5064e4476602998aa3b07647e 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp @@ -6,24 +6,18 @@ namespace armarx { NJointControllerRegistration<NJointTrajectoryController> registrationControllerNJointTrajectoryController("NJointTrajectoryController"); - NJointTrajectoryController::NJointTrajectoryController(NJointControllerDescriptionProviderInterfacePtr prov, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) + NJointTrajectoryController::NJointTrajectoryController(NJointControllerDescriptionProviderInterfacePtr prov, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr& robot) { cfg = NJointTrajectoryControllerConfigPtr::dynamicCast(config); ARMARX_CHECK_EXPRESSION_W_HINT(cfg, "Needed type: NJointTrajectoryControllerConfigPtr"); - // trajectory = TrajectoryPtr::dynamicCast(cfg->trajectory); - for (size_t i = 0; i < cfg->jointNames.size(); i++) + for (std::string jointName : cfg->jointNames) { - 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))); + targets.insert(std::make_pair(jointName, ct->asA<ControlTarget1DoFActuatorVelocity>())); + sensors.insert(std::make_pair(jointName, sv->asA<SensorValue1DoFActuatorPosition>())); } - currentPos.resize(cfg->jointNames.size()); } std::string NJointTrajectoryController::getClassName(const Ice::Current&) const @@ -33,22 +27,35 @@ namespace armarx void NJointTrajectoryController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { - TIMING_START(TrajectoryControl); - TrajectoryController& contr = *rtGetControlStruct().trajectory; - int i = 0; - for (auto& sv : sensors) + if (rtGetControlStruct().trajectory) { - currentPos(i) = sv->position; - i++; - } - auto newVelocities = contr.update(timeSinceLastIteration.toSecondsDouble(), currentPos); - bool finished = contr.getCurrentTimestamp() >= contr.getTraj()->rbegin()->getTimestamp(); - for (size_t i = 0; i < targets.size(); ++i) - { - targets.at(i)->velocity = finished ? 0.0f : newVelocities(i); - } - TIMING_END(TrajectoryControl); + TrajectoryController& contr = *rtGetControlStruct().trajectory; + + auto dimNames = contr.getTraj()->getDimensionNames(); + for (size_t i = 0; i < dimNames.size(); i++) + { + const auto& jointName = dimNames.at(i); + currentPos(i) = (sensors.count(jointName) == 1) ? sensors[jointName]->position : 0.0f; + } + + auto newVelocities = contr.update(timeSinceLastIteration.toSecondsDouble() * direction, currentPos); + bool finished = (contr.getCurrentTimestamp() >= contr.getTraj()->rbegin()->getTimestamp() && direction == 1.0) + || (contr.getCurrentTimestamp() <= contr.getTraj()->begin()->getTimestamp() && direction == -1.0); + for (size_t i = 0; i < dimNames.size(); ++i) + { + const auto& jointName = dimNames.at(i); + if (targets.count(jointName) == 1) + { + targets[jointName]->velocity = (cfg->isPreview || finished) ? 0.0f : newVelocities(i); + } + } + + if (finished && looping) + { + direction *= -1.0; + } + } } void NJointTrajectoryController::setTrajectory(const TrajectoryBasePtr& t, const Ice::Current&) @@ -59,63 +66,112 @@ namespace armarx 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) + if (cfg->considerConstraints) { - Eigen::VectorXd waypoint(trajectoryDimensions); - for (size_t i = 0; i < trajectoryDimensions; ++i) + 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) { - waypoint(i) = element.getPosition(i); + Eigen::VectorXd waypoint(trajectoryDimensions); + for (size_t i = 0; i < trajectoryDimensions; ++i) + { + waypoint(i) = element.getPosition(i); + } + pathPoints.emplace_back(waypoint); } - pathPoints.emplace_back(waypoint); - } - - VirtualRobot::Path p(pathPoints, cfg->maxDeviation); - VirtualRobot::TimeOptimalTrajectory timeOptimalTraj(p, maxVelocities, maxAccelerations); + 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); + TrajectoryPtr newTraj = new Trajectory(); - 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; + 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(t)[d]); - derivs.push_back(timeOptimalTraj.getVelocity(t)[d]); - newTraj->addDerivationsToDimension(d, t, derivs); + derivs.push_back(timeOptimalTraj.getPosition(duration)[d]); + derivs.push_back(timeOptimalTraj.getVelocity(duration)[d]); + newTraj->addDerivationsToDimension(d, duration, 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); + ARMARX_INFO << "Trajectory duration: " << newTraj->getTimeLength(); + ARMARX_INFO << VAROUT(newTraj->output()); + + trajectory = newTraj; } - newTraj->setDimensionNames(trajectory->getDimensionNames()); - TIMING_END(TrajectoryOptimization); - ARMARX_INFO << "Trajectory duration: " << newTraj->getTimeLength(); + + trajEndTime = *trajectory->getTimestamps().rbegin() - *trajectory->getTimestamps().begin(); + currentPos.resize(trajectory->getDimensionNames().size()); + LockGuardType guard {controlDataMutex}; - getWriterControlStruct().trajectory.reset(new TrajectoryController(newTraj, cfg->PID_p, cfg->PID_i, cfg->PID_d)); + getWriterControlStruct().trajectory.reset(new TrajectoryController(trajectory, cfg->PID_p, cfg->PID_i, cfg->PID_d, false)); writeControlStruct(); + rtUpdateControlStruct(); + } + + double NJointTrajectoryController::getCurrentTrajTime() const + { + LockGuardType guard {controlDataMutex}; + TrajectoryControllerPtr contr = rtGetControlStruct().trajectory; + if (contr) + { + return contr->getCurrentTimestamp(); + } + else + { + return 0.0; + } + } + + void NJointTrajectoryController::setLooping(bool looping) + { + this->looping = looping; + } + + double NJointTrajectoryController::getTrajEndTime() const + { + return trajEndTime; + } + + TrajectoryPtr NJointTrajectoryController::getTrajectoryCopy() const + { + LockGuardType guard {controlDataMutex}; + TrajectoryControllerPtr contr = rtGetControlStruct().trajectory; + if (contr) + { + return TrajectoryPtr::dynamicCast(contr->getTraj()->clone()); + } + else + { + return new Trajectory(); + } } void NJointTrajectoryController::rtPreActivateController() @@ -123,4 +179,12 @@ namespace armarx startTime = IceUtil::Time(); } + void NJointTrajectoryController::rtPostDeactivateController() + { + for (auto& target : targets) + { + target.second->velocity = 0.0f; + } + } + } diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h index 5bde47d194952216ea67decd9881d2c0c7ae8122..20090d066cc467306292a69f77db9c695b7c5187 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h @@ -4,7 +4,6 @@ #include "NJointController.h" #include <VirtualRobot/Robot.h> #include <RobotAPI/interface/units/RobotUnit/NJointTrajectoryController.h> -#include <RobotAPI/libraries/core/PIDController.h> #include <RobotAPI/libraries/core/TrajectoryController.h> #include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> #include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> @@ -12,6 +11,8 @@ namespace armarx { + TYPEDEF_PTRS_HANDLE(NJointTrajectoryController); + class NJointTrajectoryControllerControlData { public: @@ -30,17 +31,25 @@ namespace armarx // NJointController interface void rtPreActivateController() override; + void rtPostDeactivateController() override; void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override; // NJointTrajectoryControllerInterface interface void setTrajectory(const TrajectoryBasePtr& t, const Ice::Current&) override; + + double getCurrentTrajTime() const; + void setLooping(bool looping); + double getTrajEndTime() const; + TrajectoryPtr getTrajectoryCopy() const; private: // TrajectoryPtr trajectory; IceUtil::Time startTime; - std::vector<PIDControllerPtr> PIDs; - std::vector<ControlTarget1DoFActuatorVelocity*> targets; - std::vector<const SensorValue1DoFActuatorPosition*> sensors; + std::map<std::string, ControlTarget1DoFActuatorVelocity*> targets; + std::map<std::string, const SensorValue1DoFActuatorPosition*> sensors; Eigen::VectorXf currentPos; + bool looping = false; + float direction = 1.0; + double trajEndTime; NJointTrajectoryControllerConfigPtr cfg; virtual void onInitComponent() override {} virtual void onConnectComponent() override {} diff --git a/source/RobotAPI/components/units/RobotUnit/RobotSynchronization.cpp b/source/RobotAPI/components/units/RobotUnit/RobotSynchronization.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f8857e1a34184fd3617fdd97065299aa96a6728 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/RobotSynchronization.cpp @@ -0,0 +1,78 @@ +/* + * 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::RobotSynchronization + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "RobotSynchronization.h" +#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> +#include "SensorValues/SensorValue1DoFActuator.h" + +#include "RobotUnit.h" + +using namespace armarx; + +RobotSynchronization::RobotSynchronization(RobotUnit* robotUnit, VirtualRobot::RobotPtr& robot, const std::vector<std::string>& synchronizingJoints) +{ + this->robotUnit = robotUnit; + this->robot = robot; + Ice::StringSeq names = robotUnit->getSensorDeviceNames(GlobalIceCurrent); + + for (std::string name : names) + { + // skip, if the current sensor device does not correspond to a joint. + if (!this->robot->hasRobotNode(name)) + { + continue; + } + + ConstSensorDevicePtr device = robotUnit->getSensorDevice(name); + + // skip, if the sensor value of the current sensor device can't provide a position. + if (!device->getSensorValue()->isA<SensorValue1DoFActuatorPosition>()) + { + continue; + } + + std::vector<std::string>::const_iterator it = std::find(synchronizingJoints.begin(), synchronizingJoints.end(), name); + if (it == synchronizingJoints.end()) + { + continue; + } + else + { + jointSensorDeviceMap[*it] = device; + } + } +} + +void RobotSynchronization::sync() +{ + if (robotUnit->getRobotUnitState() == RobotUnitState::InitializingUnits || robotUnit->getRobotUnitState() == RobotUnitState::Running) + { + for (auto const& entry : jointSensorDeviceMap) + { + const SensorValueBase* sv = entry.second->getSensorValue(); + + ARMARX_CHECK_EXPRESSION(sv->asA<SensorValue1DoFActuatorPosition>()); + robot->getRobotNode(entry.first)->setJointValueNoUpdate(sv->asA<SensorValue1DoFActuatorPosition>()->position); + } + robot->applyJointValues(); + } +} diff --git a/source/RobotAPI/components/units/RobotUnit/RobotSynchronization.h b/source/RobotAPI/components/units/RobotUnit/RobotSynchronization.h new file mode 100644 index 0000000000000000000000000000000000000000..5e0aa926b15d58bdfcb4ad72ab57a7a228b144b4 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/RobotSynchronization.h @@ -0,0 +1,54 @@ +/* + * 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::RobotSynchronization + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_UNIT_RobotAPI_RobotSynchronization_H +#define _ARMARX_UNIT_RobotAPI_RobotSynchronization_H + +#include <VirtualRobot/Robot.h> +#include "Devices/SensorDevice.h" + +#include <string> +#include <vector> + +namespace armarx +{ + class RobotUnit; + class SensorValue1DoFActuatorPosition; + + TYPEDEF_PTRS_SHARED(RobotSynchronization); + /** + * @brief The RobotSynchronization class + */ + class RobotSynchronization + { + public: + RobotSynchronization(RobotUnit* robotUnit, VirtualRobot::RobotPtr& robot, const std::vector<std::string>& synchronizingJoints); + ~RobotSynchronization() {} + void sync(); + + private: + std::map<std::string, ConstSensorDevicePtr> jointSensorDeviceMap; + RobotUnit* robotUnit = NULL; + VirtualRobot::RobotPtr robot; + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp index 89a5b3aa6b4488af3a998e1f9209de410df41fda..20f9a6420ce70d79cc65496968e6469bb195ea71 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnit.cpp @@ -44,6 +44,8 @@ #include "Units/KinematicSubUnit.h" #include "Units/PlatformSubUnit.h" #include "SensorValues/SensorValue1DoFActuator.h" +#include "Units/TCPControllerSubUnit.h" +#include "Units/TrajectoryControllerSubUnit.h" namespace std { @@ -77,7 +79,7 @@ namespace armarx public: RobotUnitEmergencyStopMaster(RobotUnit* robotUnit, std::string emergencyStopTopicName) : robotUnit {robotUnit}, - emergencyStopTopicName {emergencyStopTopicName} + emergencyStopTopicName {emergencyStopTopicName} {} virtual void setEmergencyStopState(EmergencyStopState state, const Ice::Current& = GlobalIceCurrent) final @@ -396,6 +398,10 @@ namespace armarx ARMARX_DEBUG << "ForceTorqueUnit initialized"; initializeInertialMeasurementUnit(); ARMARX_DEBUG << "InertialMeasurementUnit initialized"; + initializeTrajectoryControllerUnit(); + ARMARX_DEBUG << "TrajectoryControllerUnit initialized"; + initializeTcpControllerUnit(); + ARMARX_DEBUG << "TcpControllerUnit initialized"; } ARMARX_INFO << "initializing default units...done! " << (MicroNow() - beg).count() << " us"; } @@ -448,7 +454,7 @@ namespace armarx } initializeKinematicUnit_tmp_helper(ControlModes::Position1DoF, ControlTarget1DoFActuatorPosition, ad.ctrlPos) initializeKinematicUnit_tmp_helper(ControlModes::Velocity1DoF, ControlTarget1DoFActuatorVelocity, ad.ctrlVel) - initializeKinematicUnit_tmp_helper(ControlModes::Torque1DoF, ControlTarget1DoFActuatorTorque, ad.ctrlTor) + initializeKinematicUnit_tmp_helper(ControlModes::Torque1DoF , ControlTarget1DoFActuatorTorque , ad.ctrlTor) #undef initializeKinematicUnit_tmp_helper @@ -466,8 +472,7 @@ namespace armarx 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 + "."; + const std::string confPre = getConfigDomain() + "." + configName + "."; Ice::PropertiesPtr properties = getIceProperties()->clone(); //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); //fill properties @@ -475,7 +480,7 @@ namespace armarx properties->setProperty(confPre + "RobotFileName", robotFileName); properties->setProperty(confPre + "RobotFileNameProject", robotProjectName); ARMARX_DEBUG << "creating unit " << configName << " using these properties: " << properties->getPropertiesForPrefix(""); - IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, configDomain); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, getConfigDomain()); //fill devices (sensor + controller) unit->setupData( @@ -521,14 +526,13 @@ namespace armarx 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 + "."; + const std::string confPre = getConfigDomain() + "." + configName + "."; Ice::PropertiesPtr properties = getIceProperties()->clone(); //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); //fill properties properties->setProperty(confPre + "PlatformName", robotPlatformName); ARMARX_DEBUG << "creating unit " << configName << " using these properties: " << properties->getPropertiesForPrefix(""); - IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, configDomain); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties , configName, getConfigDomain()); //config NJointHolonomicPlatformUnitVelocityPassThroughControllerConfigPtr config = new NJointHolonomicPlatformUnitVelocityPassThroughControllerConfig; config->initialVelocityX = 0; @@ -591,20 +595,48 @@ namespace armarx } //add it const std::string configName = getProperty<std::string>("ForceTorqueUnitName"); - const std::string& configDomain = "ArmarX"; - const std::string confPre = configDomain + "." + configName + "."; + const std::string confPre = getConfigDomain() + "." + 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_DEBUG << "creating unit " << configName << " using these properties: " << properties->getPropertiesForPrefix(""); - IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, configDomain); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, getConfigDomain()); unit->devs = ftdevs; //add addUnit(std::move(unit)); } + void RobotUnit::initializeTrajectoryControllerUnit() + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + using UnitT = TrajectoryControllerSubUnit; + + //check if unit is already added + if (getUnit(UnitT::ice_staticId())) + { + return; + } + (TrajectoryControllerSubUnitPtr::dynamicCast(trajectoryControllerSubUnit))->setup(this); + addUnit(trajectoryControllerSubUnit); + } + + void RobotUnit::initializeTcpControllerUnit() + { + auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::InitializingUnits, __FUNCTION__); + using UnitT = TCPControllerSubUnit; + + //check if unit is already added + if (getUnit(UnitT::ice_staticId())) + { + return; + } + (TCPControllerSubUnitPtr::dynamicCast(tcpControllerSubUnit))->setup(this, robot->clone()); + addUnit(tcpControllerSubUnit); + } void armarx::RobotUnit::initializeInertialMeasurementUnit() { using UnitT = InertialMeasurementSubUnit; @@ -634,13 +666,12 @@ namespace armarx } //add it const std::string configName = getProperty<std::string>("InertialMeasurementUnitName"); - const std::string& configDomain = "ArmarX"; - const std::string confPre = configDomain + "." + configName + "."; + const std::string confPre = getConfigDomain() + "." + 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); + IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, getConfigDomain()); unit->devs = imudevs; //add addUnit(std::move(unit)); @@ -681,6 +712,7 @@ namespace armarx std::size_t rtLoggingTimestep = getProperty<std::size_t>("RTLoggingPeriodMs"); rtLogging.enabled = rtLoggingTimestep; + rtLogging.loggingTaskTimestep = rtLoggingTimestep; ARMARX_CHECK_GREATER(getControlThreadTargetPeriod().toMicroSeconds(), 0); if (isRtLoggingEnabled()) { @@ -750,13 +782,7 @@ namespace armarx } } } - //start logging thread - { - rtLogging.task = new RTLoggingTaskT([&] {rtLogging.doLogging();}, rtLoggingTimestep, false, getName() + "_RTLoggingTask"); - ARMARX_INFO << "starting rt logging with timestep " << rtLoggingTimestep; - rtLogging.task->setDelayWarningTolerance(rtLoggingTimestep * 10); - rtLogging.task->start(); - } + //start logging thread is done in rtinit //maybe add the default log { const auto loggingpath = getProperty<std::string>("RTLoggingDefaultLog").getValue(); @@ -777,6 +803,14 @@ namespace armarx void armarx::RobotUnit::initRTThread() { + startSelfCollisionAvoidance(); + if (isRtLoggingEnabled()) + { + rtLogging.task = new RTLoggingTaskT([&] {rtLogging.doLogging();}, rtLogging.loggingTaskTimestep, false, getName() + "_RTLoggingTask"); + ARMARX_INFO << "starting rt logging with timestep " << rtLogging.loggingTaskTimestep; + rtLogging.task->setDelayWarningTolerance(rtLogging.loggingTaskTimestep * 10); + rtLogging.task->start(); + } auto guard = getGuard(); throwIfStateIsNot(RobotUnitState::WaitingForRTThreadInitialization, __FUNCTION__); rtThreadId = std::this_thread::get_id(); @@ -1424,6 +1458,7 @@ namespace armarx return; } auto guard = getGuard(); + throwIfStateIsNot(RobotUnitState::Running, __FUNCTION__); //get batch proxies @@ -1484,8 +1519,8 @@ namespace armarx }, timestamp }; - //publish sensor updates + timingMap["publishTimings_SensorUpdates"] = new TimedVariant { ARMARX_STOPWATCH(TimestampVariant) @@ -1639,6 +1674,7 @@ namespace armarx robotUnitObs->updateChannel(robotUnitObs->controlDevicesChannel); robotUnitObs->offerOrUpdateDataFieldsFlatCopy(robotUnitObs->sensorDevicesChannel, sensorDevMap); robotUnitObs->updateChannel(robotUnitObs->sensorDevicesChannel); + } debugObserverBatchPrx->ice_flushBatchRequests(); }; @@ -2357,6 +2393,18 @@ namespace armarx { robotUnitObs = Component::create<RobotUnitObserver>(getIceProperties(), "", getConfigDomain()); addPropertyUser(PropertyUserPtr::dynamicCast(robotUnitObs)); + + // create TCPControlSubUnit + const std::string configNameTCPControlUnit = getProperty<std::string>("TCPControlUnitName"); + const std::string& configDomain = "ArmarX"; + Ice::PropertiesPtr properties = getIceProperties()->clone(); + tcpControllerSubUnit = Component::create<TCPControllerSubUnit>(properties, configNameTCPControlUnit, configDomain); + + // create TrajectoryControllerSubUnit + const std::string configNameTrajectoryControllerUnit = getProperty<std::string>("TrajectoryControllerUnitName"); + const std::string confPre = configDomain + "." + configNameTrajectoryControllerUnit + "."; + properties->setProperty(confPre + "KinematicUnitName", getProperty<std::string>("KinematicUnitName").getValue()); + trajectoryControllerSubUnit = Component::create<TrajectoryControllerSubUnit>(properties, configNameTrajectoryControllerUnit, configDomain); } @@ -2507,12 +2555,12 @@ namespace armarx std::lock_guard<std::mutex> guard {rtLogging.mutex}; FileSystemPathBuilder pb {formatString}; pb.createParentDirectories(); - std::ofstream outCSV{pb.getPath() + ".csv"}; + std::ofstream outCSV {pb.getPath() + ".csv"}; if (!outCSV) { throw LogicError {"writeRecentIterationsToFile could not open filestream for '" + pb.getPath() + ".csv'"}; } - std::ofstream outMsg{pb.getPath() + ".messages"}; + std::ofstream outMsg {pb.getPath() + ".messages"}; if (!outMsg) { throw LogicError {"writeRecentIterationsToFile could not open filestream for '" + pb.getPath() + ".messages'"}; @@ -2678,6 +2726,9 @@ namespace armarx selfCollisionAvoidanceData.collisionModels = colModels; + // Setup robot synchronization + this->selfCollisionAvoidanceRobotSynchronization.reset(new RobotSynchronization(this, selfCollisionAvoidanceData.robot, selfCollisionAvoidanceData.robot->getRobotNodeNames())); + // start the task int colChecksPerSecond = getProperty<int>("SelfCollisionChecksPerSecond").getValue(); int intervalMs = (1.0 / (float) colChecksPerSecond) * 1000; @@ -2721,35 +2772,10 @@ namespace armarx } auto startTime = std::chrono::high_resolution_clock::now(); - sensorAndControlBufferChanged(); // update the sensor read buffer - const SensorAndControl& sc = getSensorAndControlBuffer(); - // set joint values - { - auto guard = getGuard(); - for (std::size_t i = 0; i < sc.sensors.size(); i++) - { - const auto& sensorValueBase = sc.sensors.at(i); - // skip, if the current sensor device does not correspond to a joint. - std::string deviceName = sensorDevices.at(i)->getDeviceName(); - if (!selfCollisionAvoidanceData.robot->hasRobotNode(deviceName)) - { - continue; - } - // skip, if the sensor value of the current sensor device can't provide a position. - if (!sensorValueBase->isA<SensorValue1DoFActuatorPosition>()) - { - continue; - } - const SensorValue1DoFActuatorPosition* sv = sensorValueBase->asA<SensorValue1DoFActuatorPosition>(); - if (sv) - { - float position = sv->position; - // set the joint value - selfCollisionAvoidanceData.robot->getRobotNode(deviceName)->setJointValueNoUpdate(position); - } - } - } - selfCollisionAvoidanceData.robot->applyJointValues(); + + // update joint values + selfCollisionAvoidanceRobotSynchronization->sync(); + // check distances between all collision models std::vector<VirtualRobot::SceneObjectSetPtr> conflictingColModels; std::string distancesString("All collision states:\n"); @@ -2960,7 +2986,6 @@ namespace armarx debugDrawerPrx = getTopic<DebugDrawerInterfacePrx>(getProperty<std::string>("DebugDrawerUpdatesTopicName").getValue()); debugObserverPrx = getTopic<DebugObserverInterfacePrx>(getProperty<std::string>("DebugObserverTopicName").getValue()); onConnectRobotUnit(); - startSelfCollisionAvoidance(); ARMARX_DEBUG << "RobotUnit::onConnectComponent()...done!"; } @@ -2968,6 +2993,9 @@ namespace armarx { ARMARX_DEBUG << "RobotUnit::onDisconnectComponent()"; stopSelfCollisionAvoidance(); + + // Disconnecting TCPController and TrajectoryController + onDisconnectRobotUnit(); ARMARX_DEBUG << "RobotUnit::onDisconnectComponent()...done!"; } @@ -3185,122 +3213,123 @@ namespace armarx o << std::to_string(s); return o; } +} - void RobotUnit::RtLogging::doLogging() +void armarx::RobotUnit::RtLogging::doLogging() +{ + ARMARX_CHECK_EXPRESSION(enabled); + std::lock_guard<std::mutex> guard {mutex}; + const auto now = IceUtil::Time::now(); + //remove entries { - ARMARX_CHECK_EXPRESSION(enabled); - std::lock_guard<std::mutex> guard {mutex}; - const auto now = IceUtil::Time::now(); - //remove entries + std::vector<std::string> toRemove; + toRemove.reserve(entries.size()); + for (auto& pair : entries) { - std::vector<std::string> toRemove; - toRemove.reserve(entries.size()); - for (auto& pair : entries) - { - if (pair.second->stopLogging) - { - //can't remove the current elemet - //(this would invalidate the current iterator) - toRemove.emplace_back(pair.first); - } - } - for (const auto& rem : toRemove) + if (pair.second->stopLogging) { - entries.erase(rem); + //can't remove the current elemet + //(this would invalidate the current iterator) + toRemove.emplace_back(pair.first); } } - //remove backlog entries + for (const auto& rem : toRemove) { - while (!backlog.empty() && backlog.front().writeTimestamp + keepBacklogFor < now) - { - backlog.pop_front(); - } + entries.erase(rem); } - //log all + } + //remove backlog entries + { + while (!backlog.empty() && backlog.front().writeTimestamp + keepBacklogFor < now) { - ru->controlThreadOutputBuffer.foreachNewLoggingEntry([&](const ControlThreadOutputBuffer::Entry & data) + backlog.pop_front(); + } + } + //log all + { + ru->controlThreadOutputBuffer.foreachNewLoggingEntry([&](const ControlThreadOutputBuffer::Entry & data) + { + //base (marker;iteration;timestamp) { - //base (marker;iteration;timestamp) + for (auto& pair : entries) { - for (auto& pair : entries) - { - Entry& e = *pair.second; - e.stream << "\n" - << e.marker << ";" - << data.iteration << ";" - << data.sensorValuesTimestamp.toMicroSeconds(); - e.marker.clear(); - } + Entry& e = *pair.second; + e.stream << "\n" + << e.marker << ";" + << data.iteration << ";" + << data.sensorValuesTimestamp.toMicroSeconds(); + e.marker.clear(); } - //sens + } + //sens + { + for (std::size_t idxDev = 0; idxDev < data.sensors.size(); ++ idxDev) { - for (std::size_t idxDev = 0; idxDev < data.sensors.size(); ++ idxDev) + const SensorValueBase* val = data.sensors.at(idxDev); + for (std::size_t idxField = 0; idxField < val->getNumberOfDataFields(); ++ idxField) { - const SensorValueBase* val = data.sensors.at(idxDev); - for (std::size_t idxField = 0; idxField < val->getNumberOfDataFields(); ++ idxField) + const auto str = val->getDataFieldAsString(idxField); + for (auto& pair : entries) { - const auto str = val->getDataFieldAsString(idxField); - for (auto& pair : entries) + Entry& e = *pair.second; + if (e.loggedSensorDeviceValues.at(idxDev).at(idxField)) { - Entry& e = *pair.second; - if (e.loggedSensorDeviceValues.at(idxDev).at(idxField)) - { - pair.second->stream << ";" << str; - } + pair.second->stream << ";" << str; } } } } - //ctrl + } + //ctrl + { + for (std::size_t idxDev = 0; idxDev < data.control.size(); ++ idxDev) { - for (std::size_t idxDev = 0; idxDev < data.control.size(); ++ idxDev) + const auto& vals = data.control.at(idxDev); + for (std::size_t idxVal = 0; idxVal < vals.size(); ++idxVal) { - const auto& vals = data.control.at(idxDev); - for (std::size_t idxVal = 0; idxVal < vals.size(); ++idxVal) + const ControlTargetBase* val = vals.at(idxVal); + for (std::size_t idxField = 0; idxField < val->getNumberOfDataFields(); ++ idxField) { - const ControlTargetBase* val = vals.at(idxVal); - for (std::size_t idxField = 0; idxField < val->getNumberOfDataFields(); ++ idxField) + const auto str = val->getDataFieldAsString(idxField); + for (auto& pair : entries) { - const auto str = val->getDataFieldAsString(idxField); - for (auto& pair : entries) + Entry& e = *pair.second; + if (e.loggedControlDeviceValues.at(idxDev).at(idxVal).at(idxField)) { - Entry& e = *pair.second; - if (e.loggedControlDeviceValues.at(idxDev).at(idxVal).at(idxField)) - { - pair.second->stream << ";" << str; - } + pair.second->stream << ";" << str; } } } } } - //store data to backlog + } + //store data to backlog + { + if (data.writeTimestamp + keepBacklogFor >= now) { - if (data.writeTimestamp + keepBacklogFor >= now) - { - backlog.emplace_back(data, true); //true for minimal copy - } + backlog.emplace_back(data, true); //true for minimal copy } - //print + reset messages + } + //print + reset messages + { + for (const detail::RtMessageLogEntryBase* ptr : data.messages.getEntries()) { - for (const detail::RtMessageLogEntryBase* ptr : data.messages.getEntries()) + if (!ptr) { - if (!ptr) - { - break; - } - ptr->print(rtThreadId); + break; } + ptr->print(rtThreadId); } - }); - } - ARMARX_DEBUG_S << ::deactivateSpam() << "the last " << backlog.size() << " iterations are stored"; - //flush all - { - for (auto& pair : entries) - { - pair.second->stream << std::flush; } + }); + } + ARMARX_DEBUG_S << ::deactivateSpam() << "the last " << backlog.size() << " iterations are stored"; + //flush all + { + for (auto& pair : entries) + { + pair.second->stream << std::flush; } } } + diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnit.h b/source/RobotAPI/components/units/RobotUnit/RobotUnit.h index 0aa65711e6b3d5dd7b3470ba942b270eed7dbf87..d322d8857960ff0808aa10ea19fa2f4cefee9cdd 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnit.h @@ -56,6 +56,7 @@ #include "util.h" #include "util/JointAndNJointControllers.h" #include "RobotUnitObserver.h" +#include "RobotSynchronization.h" namespace armarx { @@ -117,6 +118,7 @@ namespace armarx defineOptionalProperty<std::string>( "EmergencyStopMasterName", "EmergencyStopMaster", "The name used to register as an EmergencyStopMaster"); + defineOptionalProperty<std::string>( "EmergencyStopTopic", "EmergencyStop", "The name of the topic over which changes of the emergencyStopState are sent."); @@ -221,8 +223,14 @@ namespace armarx "Comma seperated list of pairs of collision models that should be checked against each other by collision avoidance \n" "(e.g. {rnsColModel1,rnsColModel2};{rnsColModel3,rnsColModel4}; .... " "says that rnsColModel1 will be only checked against rnsColModel2 and rnsColModel3 will only be checked against rnsColModel4). \n" - "Following predefined descriptors can be used: 'FLOOR' which represents a virtual collision model of the floor.", - PropertyDefinitionBase::eConstant); + "Following predefined descriptors can be used: 'FLOOR' which represents a virtual collision model of the floor.", PropertyDefinitionBase::eConstant); + + defineOptionalProperty<std::string>( + "TCPControlUnitName", "TCPControlUnit", + "Name of the TCPControlUnit."); + defineOptionalProperty<std::string>( + "TrajectoryControllerUnitName", "TrajectoryPlayer", + "Name of the TrajectoryControllerUnit. The respective component outside of the RobotUnit is TrajectoryPlayer"); } }; @@ -474,6 +482,9 @@ namespace armarx DebugObserverInterfacePrx debugObserverPrx; RobotUnitObserverPtr robotUnitObs; + ManagedIceObjectPtr tcpControllerSubUnit; + ManagedIceObjectPtr trajectoryControllerSubUnit; + //rt logging private: struct RtLogging @@ -502,7 +513,9 @@ namespace armarx std::deque<detail::ControlThreadOutputBufferEntry> backlog; IceUtil::Time keepBacklogFor; - Ice::Int rtThreadId; + std::size_t loggingTaskTimestep {0}; + + Ice::Int rtThreadId {0}; void doLogging(); }; RtLogging rtLogging; @@ -566,6 +579,8 @@ namespace armarx void stopSelfCollisionAvoidance(); void updateSelfCollisionAvoidance(); + RobotSynchronizationPtr selfCollisionAvoidanceRobotSynchronization; + // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // util // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -670,6 +685,8 @@ namespace armarx virtual void initializePlatformUnit(); virtual void initializeForceTorqueUnit(); virtual void initializeInertialMeasurementUnit(); + virtual void initializeTrajectoryControllerUnit(); + virtual void initializeTcpControllerUnit(); void addUnit(ManagedIceObjectPtr unit); void finishUnitInitialization(); // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/source/RobotAPI/components/units/RobotUnit/Units/TCPControllerSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/TCPControllerSubUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6dca346a5c2bd056a5d402f7edb55926fd455650 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/TCPControllerSubUnit.cpp @@ -0,0 +1,159 @@ +/* + * 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::TCPControllerSubUnit + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "TCPControllerSubUnit.h" + +#include <RobotAPI/libraries/core/FramedPose.h> + +using namespace armarx; + +void TCPControllerSubUnit::update(const SensorAndControl& sc, const JointAndNJointControllers& c) +{ + +} + +void TCPControllerSubUnit::setup(RobotUnit* rUnit, VirtualRobot::RobotPtr robot) +{ + ARMARX_CHECK_EXPRESSION(robot); + ARMARX_CHECK_EXPRESSION(!this->coordinateTransformationRobot); + coordinateTransformationRobot = robot; + + ARMARX_CHECK_EXPRESSION(!robotUnit); + ARMARX_CHECK_EXPRESSION(rUnit); + robotUnit = rUnit; + + robotSync.reset(new RobotSynchronization(robotUnit, coordinateTransformationRobot, coordinateTransformationRobot->getRobotNodeNames())); +} + +void TCPControllerSubUnit::setCycleTime(Ice::Int, const Ice::Current& c) +{ + ARMARX_WARNING << "Setting cycle time in RT-Controller does not have an effect"; +} + +void TCPControllerSubUnit::setTCPVelocity(const std::string& nodeSetName, const std::string& tcpName, const FramedDirectionBasePtr& translationVelocity, const FramedDirectionBasePtr& orientationVelocityRPY, const Ice::Current& c) +{ + ARMARX_CHECK_EXPRESSION_W_HINT(coordinateTransformationRobot->hasRobotNodeSet(nodeSetName), "The robot does not have the node set: " + nodeSetName); + std::string tcp; + if (tcpName.empty()) + { + tcp = coordinateTransformationRobot->getRobotNodeSet(nodeSetName)->getTCP()->getName(); + } + else + { + ARMARX_CHECK_EXPRESSION_W_HINT(coordinateTransformationRobot->hasRobotNode(tcpName), "The robot does not have the node: " + tcpName); + tcp = tcpName; + } + + auto activeNJointControllers = robotUnit->getNJointControllersNotNull(robotUnit->getNJointControllerNames()); + NJointTCPControllerPtr tcpController; + bool nodeSetAlreadyControlled = false; + for (NJointControllerPtr controller : activeNJointControllers) + { + tcpController = NJointTCPControllerPtr::dynamicCast(controller); + if (!tcpController) + { + continue; + } + if (tcpController->getNodeSetName() == nodeSetName) + { + nodeSetAlreadyControlled = true; + break; + } + } + + if (!nodeSetAlreadyControlled) + { + NJointTCPControllerConfigPtr config = new NJointTCPControllerConfig(nodeSetName, tcp); + NJointTCPControllerPtr ctrl = NJointTCPControllerPtr::dynamicCast( + robotUnit->createNJointController( + "NJointTCPController", this->getName() + "_" + tcp + "_" + nodeSetName, + config, true, true + ) + ); + tcpController = ctrl; + } + + robotSync->sync(); + + float xVel = 0.f; + float yVel = 0.f; + float zVel = 0.f; + float rollVel = 0.f; + float pitchVel = 0.f; + float yawVel = 0.f; + VirtualRobot::IKSolver::CartesianSelection mode = VirtualRobot::IKSolver::All; + + FramedDirectionPtr globalTransVel = FramedDirectionPtr::dynamicCast(translationVelocity); + if (globalTransVel) + { + globalTransVel->changeFrame(coordinateTransformationRobot, coordinateTransformationRobot->getRootNode()->getName()); + xVel = globalTransVel->x; + yVel = globalTransVel->y; + zVel = globalTransVel->z; + } + + FramedDirectionPtr globalOriVel = FramedDirectionPtr::dynamicCast(orientationVelocityRPY); + if (globalOriVel) + { + globalOriVel->changeFrame(coordinateTransformationRobot, coordinateTransformationRobot->getRootNode()->getName()); + rollVel = globalOriVel->x; + pitchVel = globalOriVel->y; + yawVel = globalOriVel->z; + + } + + bool noMode = false; + if (globalTransVel && globalOriVel) + { + mode = VirtualRobot::IKSolver::All; + } + else if (globalTransVel && !globalOriVel) + { + mode = VirtualRobot::IKSolver::Position; + } + else if (!globalTransVel && globalOriVel) + { + mode = VirtualRobot::IKSolver::Orientation; + } + else + { + noMode = true; + } + + ARMARX_DEBUG << "CartesianSelection-Mode: " << mode; + + // Only activate controller if at least either translationVelocity or orientaionVelocity is set + if (!noMode) + { + ARMARX_CHECK_EXPRESSION(tcpController); + tcpController->setVelocities(xVel, yVel, zVel, rollVel, pitchVel, yawVel, mode); + if (!tcpController->isControllerActive()) + { + robotUnit->activateNJointController(tcpController->getInstanceName()); + } + } +} + +bool TCPControllerSubUnit::isRequested(const Ice::Current&) +{ + return true; +} diff --git a/source/RobotAPI/components/units/RobotUnit/Units/TCPControllerSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/TCPControllerSubUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..b4965451447d376eaf0d1742f038809492257ba5 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/TCPControllerSubUnit.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::TCPControllerSubUnit + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_TCPControllerSubUnit_H +#define _ARMARX_LIB_RobotAPI_TCPControllerSubUnit_H + +#include <RobotAPI/interface/units/TCPControlUnit.h> +#include "../RobotSynchronization.h" + +#include "RobotUnitSubUnit.h" +#include "../NJointControllers/NJointTCPController.h" +#include "../util.h" + +namespace armarx +{ + class RobotUnit; + + TYPEDEF_PTRS_HANDLE(TCPControllerSubUnit); + class TCPControllerSubUnit : + virtual public RobotUnitSubUnit, + virtual public TCPControlUnitInterface, + virtual public Component + { + public: + void setup(RobotUnit* rUnit, VirtualRobot::RobotPtr robot); + + // TCPControlUnitInterface interface + void setCycleTime(Ice::Int milliseconds, const Ice::Current& c = GlobalIceCurrent) override; + void setTCPVelocity(const std::string& nodeSetName, const std::string& tcpName, const::armarx::FramedDirectionBasePtr& translationVelocity, const::armarx::FramedDirectionBasePtr& orientationVelocityRPY, const Ice::Current& c = GlobalIceCurrent) override; + bool isRequested(const Ice::Current& = GlobalIceCurrent) override; + + // RobotUnitSubUnit interface + void update(const SensorAndControl& sc, const JointAndNJointControllers& c) override; + private: + // mutable std::mutex dataMutex; + RobotUnit* robotUnit = NULL; + VirtualRobot::RobotPtr coordinateTransformationRobot; + RobotSynchronizationPtr robotSync; + + // ManagedIceObject interface + protected: + void onInitComponent() {} + void onConnectComponent() {} + std::string getDefaultName() const + { + return "TCPControlUnit"; + } + + // UnitResourceManagementInterface interface + public: + void request(const Ice::Current&) + { + } + void release(const Ice::Current&) + { + } + + // UnitExecutionManagementInterface interface + public: + void init(const Ice::Current&) {} + void start(const Ice::Current&) {} + void stop(const Ice::Current&) {} + UnitExecutionState getExecutionState(const Ice::Current&) + { + return UnitExecutionState::eUndefinedUnitExecutionState; + } + + // KinematicUnitListener interface + public: + void reportControlModeChanged(const NameControlModeMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointAngles(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointVelocities(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointTorques(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointAccelerations(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointCurrents(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointMotorTemperatures(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointStatuses(const NameStatusMap&, Ice::Long, bool, const Ice::Current&) {} + }; +} +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/Units/TrajectoryControllerSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/TrajectoryControllerSubUnit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7ea102ce2202e4f8c274af36574bafd7c2c5bf9f --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/TrajectoryControllerSubUnit.cpp @@ -0,0 +1,450 @@ +/* + * 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::TrajectoryControllerSubUnit + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "TrajectoryControllerSubUnit.h" + +using namespace armarx; + +void TrajectoryControllerSubUnit::onInitComponent() +{ + kinematicUnitName = getProperty<std::string>("KinematicUnitName").getValue(); + usingProxy(kinematicUnitName); + robotPoseUnitEnabled = getProperty<bool>("EnableRobotPoseUnit").getValue(); + robotPoseUnitName = getProperty<std::string>("RobotPoseUnitName").getValue(); + + kp = getProperty<float>("Kp").getValue(); + ki = getProperty<float>("Ki").getValue(); + kd = getProperty<float>("Kd").getValue(); + maxVelocity = getProperty<float>("absMaximumVelocity").getValue() / 180 * M_PI; // config expects value in rad/sec + + offeringTopic("DebugDrawerUpdates"); +} + +void TrajectoryControllerSubUnit::onConnectComponent() +{ + kinematicUnit = getProxy<KinematicUnitInterfacePrx>(kinematicUnitName); + debugDrawer = getTopic<armarx::DebugDrawerInterfacePrx>("DebugDrawerUpdates"); +} + +void TrajectoryControllerSubUnit::onDisconnectComponent() +{ + debugDrawer->clearLayer("Preview"); +} + +bool TrajectoryControllerSubUnit::startTrajectoryPlayer(const Ice::Current& c) +{ + ARMARX_DEBUG << "Starting TrajectoryPlayer ..."; + + ARMARX_CHECK_EXPRESSION_W_HINT(jointTraj, "No trajectory loaded!"); + assureTrajectoryController(); + + jointTrajController->setTrajectory(this->jointTraj, c); + jointTraj = jointTrajController->getTrajectoryCopy(); + jointTrajController->activateController(); + + if (isPreview) + { + std::string fileName = kinematicUnit->getRobotFilename(); + ARMARX_INFO << "robot file name : " << fileName; + debugDrawer->setRobotVisu("Preview", "previewRobot", fileName, fileName.substr(0, fileName.find("/")), armarx::CollisionModel); + debugDrawer->updateRobotColor("Preview", "previewRobot", DrawColor {0, 1, 0, 0.5}); + + previewTask = new PeriodicTask<TrajectoryControllerSubUnit>(this, &TrajectoryControllerSubUnit::previewRun, 20, false, "TrajectoryControllerSubUnitPreviewTask", false); + previewTask->start(); + } + + return true; +} + +bool TrajectoryControllerSubUnit::pauseTrajectoryPlayer(const Ice::Current& c) +{ + if (controllerExists()) + { + if (jointTrajController->isControllerActive()) + { + ARMARX_DEBUG << "Pausing TrajectoryPlayer ..."; + jointTrajController->deactivateController(); + } + else + { + ARMARX_DEBUG << "Resuming TrajectoryPlayer ..."; + jointTrajController->activateController(); + } + } + return true; +} + +bool TrajectoryControllerSubUnit::stopTrajectoryPlayer(const Ice::Current& c) +{ + ARMARX_DEBUG << "Stopping TrajectoryPlayer ..."; + + if (controllerExists()) + { + if (isPreview) + { + previewTask->stop(); + debugDrawer->clearLayer("Preview"); + } + + jointTrajController->deactivateController(); + while (jointTrajController->isControllerActive()) {} + jointTrajController->deleteController(); + } + return true; +} + +bool TrajectoryControllerSubUnit::resetTrajectoryPlayer(bool moveToFrameZeroPose, const Ice::Current& c) +{ + ARMARX_DEBUG << "Resetting TrajectoryPlayer ..."; + + if (controllerExists() && jointTrajController->isControllerActive()) + { + jointTrajController->deactivateController(); + while (jointTrajController->isControllerActive()) {} + } + + if (!jointTraj) + { + ARMARX_INFO << "No trajectory loaded! Cannot reset to FrameZeroPose!"; + return false; + } + + assureTrajectoryController(); + + jointTrajController->setTrajectory(this->jointTraj, c); + if (moveToFrameZeroPose) + { + std::vector<Ice::DoubleSeq > states = jointTraj->getAllStates(0.0f, 1); + NameValueMap frameZeroPositions; + NameControlModeMap controlModes; + + auto dimNames = jointTraj->getDimensionNames(); + for (size_t i = 0; i < dimNames.size(); i++) + { + const auto& jointName = dimNames.at(i); + if (std::find(usedJoints.begin(), usedJoints.end(), jointName) != usedJoints.end()) + { + frameZeroPositions[jointName] = states[i][0]; + controlModes[jointName] = ePositionControl; + } + } + + kinematicUnit->switchControlMode(controlModes); + kinematicUnit->setJointAngles(frameZeroPositions); + } + return true; +} + +void TrajectoryControllerSubUnit::loadJointTraj(const TrajectoryBasePtr& jointTraj, const Ice::Current& c) +{ + ARMARX_DEBUG << "Loading Trajectory ..."; + + ScopedLock lock(dataMutex); + + this->jointTraj = TrajectoryPtr::dynamicCast(jointTraj); + + if (!this->jointTraj) + { + ARMARX_ERROR << "Error when loading TrajectoryPlayer: cannot load jointTraj !!!"; + return; + } + + this->jointTraj = this->jointTraj->normalize(0, *this->jointTraj->getTimestamps().rbegin() - *this->jointTraj->getTimestamps().begin()); + usedJoints = this->jointTraj->getDimensionNames(); + ARMARX_INFO << VAROUT(usedJoints); + + if (usedJoints.size() != this->jointTraj->dim()) + { + ARMARX_ERROR << "Not all trajectory dimensions are named !!! (would cause problems when using kinematicUnit)"; + return; + } + + jointTrajController = createTrajectoryController(usedJoints, true); + jointTrajController->setTrajectory(this->jointTraj, c); + + endTime = jointTrajController->getTrajEndTime(); + trajEndTime = endTime; +} + +void TrajectoryControllerSubUnit::loadBasePoseTraj(const TrajectoryBasePtr& basePoseTraj, const Ice::Current& c) +{ + this->basePoseTraj = TrajectoryPtr::dynamicCast(basePoseTraj); +} + +void TrajectoryControllerSubUnit::setLoopPlayback(bool loop, const Ice::Current& c) +{ + ScopedLock lock(dataMutex); + + if (!controllerExists()) + { + ARMARX_WARNING << "No trajectory has been loaded and therefore no controller exists."; + return; + } + jointTrajController->setLooping(loop); +} + +double TrajectoryControllerSubUnit::getEndTime(const Ice::Current& c) +{ + return endTime; +} + +double TrajectoryControllerSubUnit::getTrajEndTime(const Ice::Current& c) +{ + return trajEndTime; +} + +double TrajectoryControllerSubUnit::getCurrentTime(const Ice::Current& c) +{ + ScopedLock lock(dataMutex); + + if (!controllerExists()) + { + ARMARX_WARNING << "No trajectory has been loaded and therefore no controller exists."; + return 0.0; + } + return jointTrajController->getCurrentTrajTime(); +} + +void TrajectoryControllerSubUnit::setEndTime(Ice::Double end, const Ice::Current& c) +{ + ARMARX_DEBUG << "Setting end-time ..."; + + ScopedLock lock(dataMutex); + + if (!jointTraj) + { + ARMARX_WARNING << "No trajectory has been loaded!"; + return; + } + assureTrajectoryController(); + + if (jointTrajController->isControllerActive()) + { + ARMARX_WARNING << "TrajectoryController already running. Please reset it before setting a new end-time."; + return; + } + + if (endTime == end) + { + return; + } + + bool b = considerConstraintsForTrajectoryOptimization; + considerConstraintsForTrajectoryOptimization = false; + + endTime = end; + jointTraj = jointTraj->normalize(0, endTime); + if (basePoseTraj) + { + basePoseTraj = basePoseTraj->normalize(0, endTime); + } + + jointTrajController->setTrajectory(jointTraj, c); + + considerConstraintsForTrajectoryOptimization = b; +} + +void TrajectoryControllerSubUnit::setIsPreview(bool isPreview, const Ice::Current& c) +{ + if (controllerExists() && jointTrajController->isControllerActive()) + { + ARMARX_WARNING << "TrajectoryController already running. Please reset it before setting wheter the controller is only for preview."; + return; + } + this->isPreview = isPreview; + + if (jointTraj) + { + jointTrajController = createTrajectoryController(usedJoints, true); + jointTrajController->setTrajectory(jointTraj, c); + } +} + +bool TrajectoryControllerSubUnit::setJointsInUse(const std::string& jointName, bool isInUse, const Ice::Current& c) +{ + ARMARX_DEBUG << "Setting joints in use ..."; + + ScopedLock lock(dataMutex); + + if (controllerExists()) + { + ARMARX_WARNING << "TrajectoryController already running. Please reset it before setting joints in use."; + } + else + { + if (isInUse && (std::find(usedJoints.begin(), usedJoints.end(), jointName) == usedJoints.end())) + { + usedJoints.push_back(jointName); + } + else if (!isInUse) + { + auto it = std::find(usedJoints.begin(), usedJoints.end(), jointName); + if (it != usedJoints.end()) + { + std::swap(*it, usedJoints.back()); + usedJoints.pop_back(); + } + } + + if (jointTraj) + { + jointTrajController = createTrajectoryController(usedJoints, true); + jointTrajController->setTrajectory(jointTraj, c); + } + } + + return isInUse; +} + +void TrajectoryControllerSubUnit::enableRobotPoseUnit(bool isRobotPose, const Ice::Current& c) +{ + ARMARX_WARNING << "NYI : TrajectoryControllerSubUnit::enableRobotPoseUnit()"; +} + +void TrajectoryControllerSubUnit::update(const SensorAndControl& sc, const JointAndNJointControllers& c) +{ + +} + +void TrajectoryControllerSubUnit::considerConstraints(bool consider, const Ice::Current& c) +{ + if (controllerExists() && jointTrajController->isControllerActive()) + { + ARMARX_WARNING << "TrajectoryController already running. Please reset it before setting whether constraints should be considered."; + return; + } + considerConstraintsForTrajectoryOptimization = consider; + + if (jointTraj) + { + jointTrajController = createTrajectoryController(usedJoints, false); + jointTrajController->setTrajectory(jointTraj, c); + } +} + +NJointTrajectoryControllerPtr TrajectoryControllerSubUnit::createTrajectoryController(std::vector<std::string>& jointNames, bool deleteIfAlreadyExist) +{ + ScopedRecursiveLock lock(controllerMutex); + + if (controllerExists() && deleteIfAlreadyExist) + { + jointTrajController->deactivateController(); + while (jointTrajController->isControllerActive()) + { + usleep(500); + } + jointTrajController->deleteController(); + while (getArmarXManager()->getIceManager()->isObjectReachable(controllerName)) + { + usleep(500); + } + } + + NJointTrajectoryControllerPtr trajController = jointTrajController; + if (!controllerExists() || deleteIfAlreadyExist) + { + NJointTrajectoryControllerConfigPtr config = new NJointTrajectoryControllerConfig(); + config->PID_p = kp; + config->PID_i = ki; + config->PID_d = kd; + config->maxVelocity = maxVelocity; + config->jointNames = jointNames; + config->considerConstraints = considerConstraintsForTrajectoryOptimization; + config->isPreview = isPreview; + + trajController = NJointTrajectoryControllerPtr::dynamicCast( + robotUnit->createNJointController( + "NJointTrajectoryController", controllerName, + config, true, true + ) + ); + + while (!getArmarXManager()->getIceManager()->isObjectReachable(controllerName)) + { + usleep(500); + } + } + return trajController; +} + +void TrajectoryControllerSubUnit::assureTrajectoryController() +{ + ScopedRecursiveLock lock(controllerMutex); + + if (!controllerExists()) + { + jointTrajController = createTrajectoryController(usedJoints, false); + } + ARMARX_CHECK_EXPRESSION(jointTrajController); +} + +bool TrajectoryControllerSubUnit::controllerExists() +{ + ScopedRecursiveLock lock(controllerMutex); + + auto allNJointControllers = robotUnit->getNJointControllersNotNull(robotUnit->getNJointControllerNames()); + NJointTrajectoryControllerPtr trajController; + for (NJointControllerPtr controller : allNJointControllers) + { + trajController = NJointTrajectoryControllerPtr::dynamicCast(controller); + if (!trajController) + { + continue; + } + if (trajController->getName() == controllerName) + { + jointTrajController = trajController; + return true; + } + } + return false; +} + +void TrajectoryControllerSubUnit::previewRun() +{ + if (controllerExists()) + { + if (jointTrajController->isControllerActive()) + { + std::vector<Ice::DoubleSeq > states = jointTraj->getAllStates(jointTrajController->getCurrentTrajTime(), 1); + NameValueMap targetPositionValues; + + auto dimNames = jointTraj->getDimensionNames(); + for (size_t i = 0; i < dimNames.size(); i++) + { + const auto& jointName = dimNames.at(i); + if (std::find(usedJoints.begin(), usedJoints.end(), jointName) != usedJoints.end()) + { + targetPositionValues[jointName] = states[i][0]; + } + } + debugDrawer->updateRobotConfig("Preview", "previewRobot", targetPositionValues); + } + } +} + +void TrajectoryControllerSubUnit::setup(RobotUnit* rUnit) +{ + ARMARX_CHECK_EXPRESSION(!robotUnit); + ARMARX_CHECK_EXPRESSION(rUnit); + robotUnit = rUnit; +} diff --git a/source/RobotAPI/components/units/RobotUnit/Units/TrajectoryControllerSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/TrajectoryControllerSubUnit.h new file mode 100644 index 0000000000000000000000000000000000000000..571f13f1a7c51d500f3542701a37bf9e0a37a5a1 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/Units/TrajectoryControllerSubUnit.h @@ -0,0 +1,169 @@ +/* + * 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::TrajectoryControllerSubUnit + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_RobotAPI_TrajectoryControllerSubUnit_H +#define _ARMARX_LIB_RobotAPI_TrajectoryControllerSubUnit_H + +#include "RobotUnitSubUnit.h" + +#include <RobotAPI/interface/components/TrajectoryPlayerInterface.h> +#include "../NJointControllers/NJointTrajectoryController.h" +#include "../RobotUnit.h" + +#include "KinematicSubUnit.h" + + +namespace armarx +{ + class TrajectoryControllerSubUnitPropertyDefinitions: + public armarx::ComponentPropertyDefinitions + { + public: + TrajectoryControllerSubUnitPropertyDefinitions(std::string prefix): + armarx::ComponentPropertyDefinitions(prefix) + { + defineRequiredProperty<std::string>("KinematicUnitName", "Name of the KinematicUnit. Only needed for returning to zeroFramePose while resetting."); + defineOptionalProperty<float>("Kp", 1.f, "Proportional gain value of PID Controller"); + defineOptionalProperty<float>("Ki", 0.0f, "Integral gain value of PID Controller"); + defineOptionalProperty<float>("Kd", 0.0f, "Differential gain value of PID Controller"); + defineOptionalProperty<float>("absMaximumVelocity", 80.0f, "The PID will never set a velocity above this threshold (degree/s)"); + defineOptionalProperty<std::string>("CustomRootNode", "", "If this value is set, the root pose of the motion is set for this node instead of the root joint."); + + defineOptionalProperty<bool>("EnableRobotPoseUnit", false, "Specify whether RobotPoseUnit should be used to move the robot's position. Only useful in simulation.") + .setCaseInsensitive(true) + .map("true", true) + .map("yes", true) + .map("1", true) + .map("false", false) + .map("no", false) + .map("0", false); + defineOptionalProperty<std::string>("RobotPoseUnitName", "RobotPoseUnit", "Name of the RobotPoseUnit to which the robot position should be sent"); + } + }; + + + TYPEDEF_PTRS_HANDLE(TrajectoryControllerSubUnit); + class TrajectoryControllerSubUnit : + virtual public RobotUnitSubUnit, + virtual public TrajectoryPlayerInterface, + virtual public Component + { + // TrajectoryControllerSubUnitInterface interface + public: + bool startTrajectoryPlayer(const Ice::Current& = GlobalIceCurrent); + bool pauseTrajectoryPlayer(const Ice::Current& = GlobalIceCurrent); + bool stopTrajectoryPlayer(const Ice::Current& = GlobalIceCurrent); + bool resetTrajectoryPlayer(bool moveToFrameZeroPose, const Ice::Current& = GlobalIceCurrent); + + void loadJointTraj(const TrajectoryBasePtr& jointTraj, const Ice::Current& = GlobalIceCurrent); + void loadBasePoseTraj(const TrajectoryBasePtr& basePoseTraj, const Ice::Current& = GlobalIceCurrent); + + void setLoopPlayback(bool loop, const Ice::Current& = GlobalIceCurrent); + Ice::Double getEndTime(const Ice::Current& = GlobalIceCurrent); + Ice::Double getTrajEndTime(const Ice::Current& = GlobalIceCurrent); + Ice::Double getCurrentTime(const Ice::Current& = GlobalIceCurrent); + void setEndTime(Ice::Double, const Ice::Current& = GlobalIceCurrent); + + // Within the RobotUnit the NJointTrajectoryController is always in VelocityControl + void setIsVelocityControl(bool, const Ice::Current& = GlobalIceCurrent) {} + + void setIsPreview(bool, const Ice::Current& = GlobalIceCurrent); + bool setJointsInUse(const std::string&, bool, const Ice::Current& = GlobalIceCurrent); + void enableRobotPoseUnit(bool, const Ice::Current& = GlobalIceCurrent); + + void considerConstraints(bool, const Ice::Current& = GlobalIceCurrent); + + // RobotUnitSubUnit interface + void update(const SensorAndControl& sc, const JointAndNJointControllers& c); + + void setup(RobotUnit* rUnit); + + // KinematicUnitListener interface + void reportControlModeChanged(const NameControlModeMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointAngles(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointVelocities(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointTorques(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointAccelerations(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointCurrents(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointMotorTemperatures(const NameValueMap&, Ice::Long, bool, const Ice::Current&) {} + void reportJointStatuses(const NameStatusMap&, Ice::Long, bool, const Ice::Current&) {} + // ManagedIceObject interface + protected: + void onInitComponent(); + void onConnectComponent(); + void onDisconnectComponent(); + std::string getDefaultName() const + { + return "TrajectoryPlayer"; + } + /** + * @see PropertyUser::createPropertyDefinitions() + */ + virtual armarx::PropertyDefinitionsPtr createPropertyDefinitions() + { + return armarx::PropertyDefinitionsPtr + { + new TrajectoryControllerSubUnitPropertyDefinitions{getConfigIdentifier()} + }; + } + + private: + NJointTrajectoryControllerPtr createTrajectoryController(std::vector<std::string>& jointNames, bool deleteIfAlreadyExist); + void assureTrajectoryController(); + bool controllerExists(); + void previewRun(); + + RobotUnit* robotUnit = NULL; + NJointTrajectoryControllerPtr jointTrajController; + std::string controllerName = this->getName() + "_" + "JointTrajectory"; + + TrajectoryPtr jointTraj; + + // so far no usage -> need to implement usage of robotPoseUnit (only for simulation) -> does it really belong here? + TrajectoryPtr basePoseTraj; + bool robotPoseUnitEnabled; + std::string robotPoseUnitName; + + double endTime; + double trajEndTime; + std::vector<std::string> usedJoints; + + std::string kinematicUnitName; + KinematicUnitInterfacePrx kinematicUnit; + std::string customRootNode; + + float kp; + float ki; + float kd; + float maxVelocity; + bool considerConstraintsForTrajectoryOptimization = false; + + bool isPreview = false; + DebugDrawerInterfacePrx debugDrawer; + PeriodicTask<TrajectoryControllerSubUnit>::pointer_type previewTask; + + Mutex dataMutex; + RecursiveMutex controllerMutex; + }; +} + +#endif diff --git a/source/RobotAPI/components/units/RobotUnit/util/introspection/DataFieldsInfo.cpp b/source/RobotAPI/components/units/RobotUnit/util/introspection/DataFieldsInfo.cpp index 1581e16558d94ddde0a1f4b9e0a94831bdbe1afc..ceccf18f5508ec86e9e8983fe948cb8e19309c2c 100644 --- a/source/RobotAPI/components/units/RobotUnit/util/introspection/DataFieldsInfo.cpp +++ b/source/RobotAPI/components/units/RobotUnit/util/introspection/DataFieldsInfo.cpp @@ -64,54 +64,54 @@ namespace armarx { namespace introspection { - #define make_DataFieldsInfo_for_eigen_vector(Type,Num) \ - std::string DataFieldsInfo<Eigen::Vector##Num##Type, void>::GetFieldAsString(std::size_t i, const Eigen::Vector##Num##Type& field) \ - { \ - ARMARX_CHECK_LESS(i, Num); \ - return std::to_string(field(i)); \ - } \ - std::map<std::string, VariantBasePtr> DataFieldsInfo<Eigen::Vector##Num##Type, void>::ToVariants( \ +#define make_DataFieldsInfo_for_eigen_vector(Type,Num) \ + std::string DataFieldsInfo<Eigen::Vector##Num##Type, void>::GetFieldAsString(std::size_t i, const Eigen::Vector##Num##Type& field) \ + { \ + ARMARX_CHECK_LESS(i, Num); \ + return std::to_string(field(i)); \ + } \ + std::map<std::string, VariantBasePtr> DataFieldsInfo<Eigen::Vector##Num##Type, void>::ToVariants( \ const Eigen::Vector##Num##Type& value, \ const std::string& name, \ const IceUtil::Time& timestamp, \ const std::string& frame, \ const std::string& agent) \ - { \ - ARMARX_CHECK_EXPRESSION_W_HINT(frame.empty() && agent.empty(), "There is no framed version of TimestampVariant"); \ - std::map<std::string, VariantBasePtr> result; \ - for(int i = 0; i < Num; ++i) \ - { \ - result.emplace(name + "." + std::to_string(i), VariantBasePtr{new TimedVariant(value(i), timestamp)}); \ - } \ - return result; \ - } \ - std::vector<std::string> DataFieldsInfo<Eigen::Vector##Num##Type, void>::GetFieldNames() \ - { \ - std::vector<std::string> result; \ - for(int i = 0; i < Num; ++i) \ - { \ - result.emplace_back(std::to_string(i)); \ - } \ - return result; \ - } + { \ + ARMARX_CHECK_EXPRESSION_W_HINT(frame.empty() && agent.empty(), "There is no framed version of TimestampVariant"); \ + std::map<std::string, VariantBasePtr> result; \ + for(int i = 0; i < Num; ++i) \ + { \ + result.emplace(name + "." + std::to_string(i), VariantBasePtr{new TimedVariant(value(i), timestamp)}); \ + } \ + return result; \ + } \ + std::vector<std::string> DataFieldsInfo<Eigen::Vector##Num##Type, void>::GetFieldNames() \ + { \ + std::vector<std::string> result; \ + for(int i = 0; i < Num; ++i) \ + { \ + result.emplace_back(std::to_string(i)); \ + } \ + return result; \ + } - make_DataFieldsInfo_for_eigen_vector(f,2) - make_DataFieldsInfo_for_eigen_vector(f,4) - make_DataFieldsInfo_for_eigen_vector(f,5) - make_DataFieldsInfo_for_eigen_vector(f,6) + make_DataFieldsInfo_for_eigen_vector(f, 2) + make_DataFieldsInfo_for_eigen_vector(f, 4) + make_DataFieldsInfo_for_eigen_vector(f, 5) + make_DataFieldsInfo_for_eigen_vector(f, 6) - make_DataFieldsInfo_for_eigen_vector(d,2) - make_DataFieldsInfo_for_eigen_vector(d,3) - make_DataFieldsInfo_for_eigen_vector(d,4) - make_DataFieldsInfo_for_eigen_vector(d,5) - make_DataFieldsInfo_for_eigen_vector(d,6) + make_DataFieldsInfo_for_eigen_vector(d, 2) + make_DataFieldsInfo_for_eigen_vector(d, 3) + make_DataFieldsInfo_for_eigen_vector(d, 4) + make_DataFieldsInfo_for_eigen_vector(d, 5) + make_DataFieldsInfo_for_eigen_vector(d, 6) - make_DataFieldsInfo_for_eigen_vector(i,2) - make_DataFieldsInfo_for_eigen_vector(i,3) - make_DataFieldsInfo_for_eigen_vector(i,4) - make_DataFieldsInfo_for_eigen_vector(i,5) - make_DataFieldsInfo_for_eigen_vector(i,6) - #undef make_DataFieldsInfo_for_eigen_vector + make_DataFieldsInfo_for_eigen_vector(i, 2) + make_DataFieldsInfo_for_eigen_vector(i, 3) + make_DataFieldsInfo_for_eigen_vector(i, 4) + make_DataFieldsInfo_for_eigen_vector(i, 5) + make_DataFieldsInfo_for_eigen_vector(i, 6) +#undef make_DataFieldsInfo_for_eigen_vector } } @@ -130,9 +130,9 @@ namespace armarx case 1: return std::to_string(field.operation); case 2: - return field.enabled ? "true" : "false"; + return std::to_string(field.enabled); case 3: - return field.emergencyStop ? "true" : "false"; + return std::to_string(field.emergencyStop); } throw std::logic_error { diff --git a/source/RobotAPI/interface/CMakeLists.txt b/source/RobotAPI/interface/CMakeLists.txt index 33d31e2cdc661bdc42a278892386e0865128de40..d6a550f504c16a6605a9e3d980b4c80e9726f956 100644 --- a/source/RobotAPI/interface/CMakeLists.txt +++ b/source/RobotAPI/interface/CMakeLists.txt @@ -45,6 +45,7 @@ set(SLICE_FILES units/RobotUnit/RobotUnitInterface.ice components/ViewSelectionInterface.ice + components/TrajectoryPlayerInterface.ice visualization/DebugDrawerInterface.ice diff --git a/source/RobotAPI/interface/components/TrajectoryPlayerInterface.ice b/source/RobotAPI/interface/components/TrajectoryPlayerInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..57f0dcfd505c981c073dcf421bcb817b8dda8fc7 --- /dev/null +++ b/source/RobotAPI/interface/components/TrajectoryPlayerInterface.ice @@ -0,0 +1,57 @@ +/* + * 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::TrajectoryControllerSubUnit + * @author Stefan Reither ( stef dot reither at web dot de ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include <RobotAPI/interface/core/Trajectory.ice> +#include <RobotAPI/interface/observers/KinematicUnitObserverInterface.ice> + +#ifndef _ARMARX_ROBOTAPI_SUBUNITS_TRAJECTORYCONTROLLER_SLICE_ +#define _ARMARX_ROBOTAPI_SUBUNITS_TRAJECTORYCONTROLLER_SLICE_ + +module armarx +{ + interface TrajectoryPlayerInterface extends KinematicUnitListener + { + bool startTrajectoryPlayer(); + bool pauseTrajectoryPlayer(); + bool stopTrajectoryPlayer(); + bool resetTrajectoryPlayer(bool moveToFrameZeroPose); + + void loadJointTraj(TrajectoryBase jointTraj); + void loadBasePoseTraj(TrajectoryBase basePoseTraj); + + void setLoopPlayback(bool loop); + void setIsVelocityControl(bool isVelocity); + void setIsPreview(bool isPreview); + bool setJointsInUse(string jointName, bool inUse); + void enableRobotPoseUnit(bool isRobotPose); + + double getCurrentTime(); + double getEndTime(); + double getTrajEndTime(); + + void setEndTime(double endTime); + + void considerConstraints(bool consider); + }; +}; + +#endif diff --git a/source/RobotAPI/interface/units/RobotUnit/NJointTrajectoryController.ice b/source/RobotAPI/interface/units/RobotUnit/NJointTrajectoryController.ice index a384c49eb909a5fb32065048f6cee063798602ad..e4e299be91408fe7cc5efd0d5dea535f92065cb9 100644 --- a/source/RobotAPI/interface/units/RobotUnit/NJointTrajectoryController.ice +++ b/source/RobotAPI/interface/units/RobotUnit/NJointTrajectoryController.ice @@ -30,14 +30,16 @@ module armarx { class NJointTrajectoryControllerConfig extends NJointControllerConfig { - float maxAcceleration = 3; - float maxVelocity = 1.4; - float maxDeviation = 0.01; - float preSamplingStepMs = 10; + float maxAcceleration = 1.5; + float maxVelocity = 0.5; + float maxDeviation = 10; + float preSamplingStepMs = 50; Ice::StringSeq jointNames; float PID_p = 1; float PID_i = 0; float PID_d = 0; + bool considerConstraints = true; + bool isPreview = false; }; interface NJointTrajectoryControllerInterface extends NJointControllerInterface diff --git a/source/RobotAPI/libraries/core/PIDController.cpp b/source/RobotAPI/libraries/core/PIDController.cpp index 12745ffe064e27a3d9d4c91b96882bcd313772ac..e39f631ab5ce5849ee88924a077dc294e07c2779 100644 --- a/source/RobotAPI/libraries/core/PIDController.cpp +++ b/source/RobotAPI/libraries/core/PIDController.cpp @@ -148,7 +148,7 @@ double PIDController::getControlValue() const } -MultiDimPIDController::MultiDimPIDController(float Kp, float Ki, float Kd, double maxControlValue, double maxDerivation) : +MultiDimPIDController::MultiDimPIDController(float Kp, float Ki, float Kd, double maxControlValue, double maxDerivation, bool threadSafe) : Kp(Kp), Ki(Ki), Kd(Kd), @@ -156,14 +156,15 @@ MultiDimPIDController::MultiDimPIDController(float Kp, float Ki, float Kd, doubl derivative(0), previousError(0), maxControlValue(maxControlValue), - maxDerivation(maxDerivation) + maxDerivation(maxDerivation), + threadSafe(threadSafe) { reset(); } void MultiDimPIDController::update(const double deltaSec, const Eigen::VectorXf& measuredValue, const Eigen::VectorXf& targetValue) { - ScopedRecursiveLock lock(mutex); + ScopedRecursiveLockPtr lock = getLock(); processValue = measuredValue; target = targetValue; @@ -239,7 +240,7 @@ void MultiDimPIDController::update(const double deltaSec, const Eigen::VectorXf& void MultiDimPIDController::update(const Eigen::VectorXf& measuredValue, const Eigen::VectorXf& targetValue) { - ScopedRecursiveLock lock(mutex); + ScopedRecursiveLockPtr lock = getLock(); IceUtil::Time now = TimeUtil::GetTime(); if (firstRun) @@ -259,7 +260,7 @@ const Eigen::VectorXf& MultiDimPIDController::getControlValue() const void MultiDimPIDController::reset() { - ScopedRecursiveLock lock(mutex); + ScopedRecursiveLockPtr lock = getLock(); firstRun = true; previousError = 0; integral = 0; @@ -269,8 +270,20 @@ void MultiDimPIDController::reset() // target.setZero(); } +ScopedRecursiveLockPtr MultiDimPIDController::getLock() const +{ + if (threadSafe) + { + return ScopedRecursiveLockPtr(new ScopedRecursiveLock(mutex)); + } + else + { + return ScopedRecursiveLockPtr(); + } +} + void MultiDimPIDController::setMaxControlValue(double value) { - ScopedRecursiveLock lock(mutex); + ScopedRecursiveLockPtr lock = getLock(); maxControlValue = value; } diff --git a/source/RobotAPI/libraries/core/PIDController.h b/source/RobotAPI/libraries/core/PIDController.h index e3676c7590e6ded3e67233c352fa415ad6a89130..240baec49b370e3ca92cac98ff1b77d8c703a77b 100644 --- a/source/RobotAPI/libraries/core/PIDController.h +++ b/source/RobotAPI/libraries/core/PIDController.h @@ -71,7 +71,12 @@ namespace armarx public Logging { public: - MultiDimPIDController(float Kp, float Ki, float Kd, double maxControlValue = std::numeric_limits<double>::max(), double maxDerivation = std::numeric_limits<double>::max()); + MultiDimPIDController(float Kp, + float Ki, + float Kd, + double maxControlValue = std::numeric_limits<double>::max(), + double maxDerivation = std::numeric_limits<double>::max(), + bool threadSafe = true); void update(const double deltaSec, const Eigen::VectorXf& measuredValue, const Eigen::VectorXf& targetValue); void update(const Eigen::VectorXf& measuredValue, const Eigen::VectorXf& targetValue); const Eigen::VectorXf& getControlValue() const; @@ -91,6 +96,9 @@ namespace armarx double maxDerivation; bool firstRun; mutable RecursiveMutex mutex; + bool threadSafe = true; + private: + ScopedRecursiveLockPtr getLock() const; }; typedef boost::shared_ptr<MultiDimPIDController> MultiDimPIDControllerPtr; } diff --git a/source/RobotAPI/libraries/core/TrajectoryController.cpp b/source/RobotAPI/libraries/core/TrajectoryController.cpp index b0ef102793e14ee49100dd080ac7420a89b7a210..d852325e0007d243f033cdb3c524a315b197adf7 100644 --- a/source/RobotAPI/libraries/core/TrajectoryController.cpp +++ b/source/RobotAPI/libraries/core/TrajectoryController.cpp @@ -26,11 +26,10 @@ namespace armarx { - TrajectoryController::TrajectoryController(const TrajectoryPtr& traj, float kp, float ki, float kd) : + TrajectoryController::TrajectoryController(const TrajectoryPtr& traj, float kp, float ki, float kd, bool threadSafe) : traj(traj) { - pid.reset(new MultiDimPIDController(kp, ki, kd)); - + pid.reset(new MultiDimPIDController(kp, ki, kd, std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), threadSafe)); ARMARX_CHECK_EXPRESSION(traj); currentTimestamp = traj->begin()->getTimestamp(); positions.resize(traj->dim(), 1); @@ -44,17 +43,23 @@ namespace armarx ARMARX_CHECK_EQUAL(static_cast<std::size_t>(currentPosition.rows()), traj->dim()); int dim = traj->dim(); currentTimestamp = currentTimestamp + deltaT; + + if (currentTimestamp < 0.0) + { + currentTimestamp = 0.0; + } + for (int i = 0; i < dim; ++i) { positions(i) = traj->getState(currentTimestamp, i, 0); - veloctities(i) = traj->getState(currentTimestamp, i, 1); + veloctities(i) = (std::signbit(deltaT) ? -1.0 : 1.0) * traj->getState(currentTimestamp, i, 1); } - pid->update(deltaT, currentPosition, positions); + pid->update(std::abs(deltaT), currentPosition, positions); veloctities += pid->getControlValue(); return veloctities; } - MultiDimPIDControllerPtr TrajectoryController::getPid() const + const MultiDimPIDControllerPtr& TrajectoryController::getPid() const { return pid; } @@ -69,7 +74,7 @@ namespace armarx return currentTimestamp; } - TrajectoryPtr TrajectoryController::getTraj() const + const TrajectoryPtr& TrajectoryController::getTraj() const { return traj; } diff --git a/source/RobotAPI/libraries/core/TrajectoryController.h b/source/RobotAPI/libraries/core/TrajectoryController.h index f8a80e6db1b7e6ec0edbbf2960240e81ba5ffa4a..f311cc25e38eab0008abc02c0d2484ac898a2ee2 100644 --- a/source/RobotAPI/libraries/core/TrajectoryController.h +++ b/source/RobotAPI/libraries/core/TrajectoryController.h @@ -32,14 +32,14 @@ namespace armarx class TrajectoryController { public: - TrajectoryController(const TrajectoryPtr& traj, float kp, float ki = 0.0f, float kd = 0.0f); + TrajectoryController(const TrajectoryPtr& traj, float kp, float ki = 0.0f, float kd = 0.0f, bool threadSafe = true); Eigen::VectorXf update(double deltaT, const Eigen::VectorXf currentPosition); - MultiDimPIDControllerPtr getPid() const; + const MultiDimPIDControllerPtr& getPid() const; void setPid(const MultiDimPIDControllerPtr& value); double getCurrentTimestamp() const; - TrajectoryPtr getTraj() const; + const TrajectoryPtr& getTraj() const; protected: TrajectoryPtr traj;