diff --git a/source/armarx/control/components/control_skill_provider/Component.cpp b/source/armarx/control/components/control_skill_provider/Component.cpp index d944e193fb02bd283f7e6cee4024eac305acd801..3a557d17bcb5e1c18be7bc23f4e3e6c01e649fb7 100644 --- a/source/armarx/control/components/control_skill_provider/Component.cpp +++ b/source/armarx/control/components/control_skill_provider/Component.cpp @@ -4,6 +4,7 @@ #include <armarx/control/skills/skills/MoveJointsToPosition.h> #include <armarx/control/skills/skills/MoveJointsWithVelocity.h> +#include <armarx/control/skills/skills/ShapeHand.h> namespace armarx::control::components::control_skill_provider { @@ -15,6 +16,8 @@ namespace armarx::control::components::control_skill_provider new ::armarx::ComponentPropertyDefinitions(getConfigIdentifier()); def->component(remote.kinematicUnit); + def->component(remote.leftHandUnit, "LeftHandUnit", "LeftHandUnit"); + def->component(remote.rightHandUnit, "RightHandUnit", "RightHandUnit"); def->required(properties.robotName, "RobotName", "Name of the robot."); @@ -43,6 +46,17 @@ namespace armarx::control::components::control_skill_provider addSkillFactory<armarx::control::skills::skills::MoveJointsWithVelocity>( skillRemote); } + { + armarx::control::skills::skills::ShapeHand::Remote skillRemote{ + remote.leftHandUnit, remote.rightHandUnit}; + addSkillFactory<armarx::control::skills::skills::ShapeHand>(skillRemote); + } + { + addSkillFactory<armarx::control::skills::skills::OpenHand>(); + } + { + addSkillFactory<armarx::control::skills::skills::CloseHand>(); + } } } diff --git a/source/armarx/control/components/control_skill_provider/Component.h b/source/armarx/control/components/control_skill_provider/Component.h index ad99599195fdb44b2401f0bd98e801de881a020c..61c32aefa06df513594bc3d3b243c6c03ca2e4a1 100644 --- a/source/armarx/control/components/control_skill_provider/Component.h +++ b/source/armarx/control/components/control_skill_provider/Component.h @@ -2,6 +2,7 @@ #include <ArmarXCore/core/Component.h> +#include <RobotAPI/interface/units/HandUnitInterface.h> #include <RobotAPI/interface/units/KinematicUnitInterface.h> #include <RobotAPI/libraries/armem/client/plugins/PluginUser.h> #include <RobotAPI/libraries/armem/client/plugins/ReaderWriterPlugin.h> @@ -48,6 +49,8 @@ namespace armarx::control::components::control_skill_provider struct Remote { ::armarx::KinematicUnitInterfacePrx kinematicUnit; + ::armarx::HandUnitInterfacePrx leftHandUnit; + ::armarx::HandUnitInterfacePrx rightHandUnit; } remote; struct Properties diff --git a/source/armarx/control/skills/CMakeLists.txt b/source/armarx/control/skills/CMakeLists.txt index 30a2865035f108052a2e68efae6d459c3d4cba70..73ac05a62262d857cf4e2bada333deb7ca485b69 100644 --- a/source/armarx/control/skills/CMakeLists.txt +++ b/source/armarx/control/skills/CMakeLists.txt @@ -2,6 +2,7 @@ armarx_add_aron_library(skills_aron ARON_FILES skills/aron/MoveJointsToPositionParams.xml skills/aron/MoveJointsWithVelocityParams.xml + skills/aron/ShapeHandParams.xml ) armarx_add_library(skills @@ -9,11 +10,13 @@ armarx_add_library(skills skills.cpp skills/MoveJointsToPosition.cpp skills/MoveJointsWithVelocity.cpp + skills/ShapeHand.cpp HEADERS skills.h skills/MoveJointsToPosition.h skills/MoveJointsWithVelocity.h + skills/ShapeHand.h DEPENDENCIES_PUBLIC @@ -24,6 +27,7 @@ armarx_add_library(skills # RobotAPI RobotAPISkills armem_robot_state + RobotAPIUnits RobotAPISimpleTrajectory # armarx_control diff --git a/source/armarx/control/skills/skills/ShapeHand.cpp b/source/armarx/control/skills/skills/ShapeHand.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8396458e42a20a973918eff28e77a7707c5df7d9 --- /dev/null +++ b/source/armarx/control/skills/skills/ShapeHand.cpp @@ -0,0 +1,228 @@ +#include "ShapeHand.h" + +#include <ArmarXCore/observers/variant/SingleTypeVariantList.h> + +namespace armarx::control::skills::skills +{ + ShapeHand::ShapeHand(const Remote& r) : + ::armarx::skills::SpecializedSkill<ShapeHandParams>(GetSkillDescription()), remote(r) + { + } + + ::armarx::skills::Skill::MainResult + ShapeHand::main() + { + // verify exactly one of the parameters 'shapeName' and 'jointAngles' is set + if ((not getParameters().shapeName) and (not getParameters().jointAngles)) + { + ARMARX_ERROR << "Exactly one of the parameters 'shapeName' and 'jointAngles' must be " + "set. However, neither has been set."; + return MakeFailedResult(); + } + else if (getParameters().shapeName and getParameters().jointAngles) + { + ARMARX_ERROR << "Exactly one of the parameters 'shapeName' and 'jointAngles' must be " + "set. However, both have been set."; + return MakeFailedResult(); + } + + // set shape/joinAngles + + auto& handUnit = getHandUnit(getParameters().hand); + + if (getParameters().shapeName) + { + const auto& shape = getParameters().shapeName.value(); + + // check that the shape is defined + const auto defShapes = + ::armarx::SingleTypeVariantListPtr::dynamicCast(handUnit->getShapeNames()) + ->toStdVector<std::string>(); + + if (not std::any_of(defShapes.begin(), + defShapes.end(), + [&shape](const auto& defShape) { return shape == defShape; })) + { + ARMARX_ERROR << "Shape with name '" << shape + << "' not defined. Defined shapes: " << defShapes; + return MakeFailedResult(); + } + + // set shape + ARMARX_INFO << "Setting shape of hand '" << handUnit->getHandName() << "' to '" << shape + << "'."; + + handUnit->setShape(shape); + + if (getParameters().waitUntilFinished) + { + return waitUntilFinished(handUnit->getShapeJointValues(shape)); + } + } + + else // getParameters().jointAngles + { + const auto jointAngles = getParameters().jointAngles.value(); + + ARMARX_INFO << "Setting joint angles of hand '" << handUnit->getHandName() + << "' to: " << jointAngles; + + handUnit->setJointAngles(jointAngles); + + if (getParameters().waitUntilFinished) + { + return waitUntilFinished(jointAngles); + } + } + return MakeSucceededResult(); + } + + armarx::skills::Skill::MainResult + ShapeHand::waitUntilFinished(const NameValueMap& targetAngles) + { + auto& handUnit = getHandUnit(getParameters().hand); + const auto movementTimeout = getParameters().waitUntilFinished->movementTimeout; + + ARMARX_INFO << "Waiting until target position is reached" + << (movementTimeout ? " or movement times out" : ""); + + struct + { + NameValueMap angles; + ::armarx::DateTime time; + } pastJointAngles{handUnit->getCurrentJointValues(), DateTime::Now()}; + + const Clock clock; + const auto sleepTime = Duration::MilliSeconds(20); + + bool reached = false; + bool movementTimedOut = false; + + while (not shouldSkillTerminate() and not reached and not movementTimedOut) + { + const auto currentAngles = handUnit->getCurrentJointValues(); + + // check if target reached + reached = true; + for (const auto& [jointName, targetAngle] : targetAngles) + { + if (std::abs(targetAngle - currentAngles.at(jointName)) > getAccuracy(jointName)) + { + reached = false; + ARMARX_VERBOSE + << deactivateSpam(1) << "Delta for joint '" << jointName + << "' is: " << std::abs(targetAngle - currentAngles.at(jointName)) << " > " + << getAccuracy(jointName) << "(accuracy)"; + break; + } + } + + // check if movement timed out + if (movementTimeout and (DateTime::Now() - pastJointAngles.time).toSecondsDouble() >= + movementTimeout.value()) + { + movementTimedOut = true; + for (const auto& [jointName, targetAngle] : targetAngles) + { + if (std::abs(currentAngles.at(jointName) - + pastJointAngles.angles.at(jointName)) > getAccuracy(jointName)) + { + movementTimedOut = false; + ARMARX_VERBOSE + << deactivateSpam(1) << "Joint '" << jointName << "' still moving: " + << std::abs(currentAngles.at(jointName) - + pastJointAngles.angles.at(jointName)) + << " > " << getAccuracy(jointName) << "(accuracy)" + << "in the last " << movementTimeout.value() << "s (movement timeout)"; + break; + } + } + pastJointAngles = {currentAngles, DateTime::Now()}; + } + + clock.waitFor(sleepTime); + } + + if (reached) + { + ARMARX_INFO << "Target position reached!"; + } + else if (movementTimedOut) + { + ARMARX_INFO << "Movement timed out! Joints stopped moving. Not waiting anymore until " + "target postion is reached"; + } + else if (shouldSkillTerminate()) + { + ARMARX_IMPORTANT + << "Skill aborted. Not waiting anymore until target position is reached"; + return MakeAbortedResult(); + } + + return MakeSucceededResult(); + } + + HandUnitInterfacePrx& + ShapeHand::getHandUnit(const Hand hand) + { + if (hand == ::armarx::control::skills::skills::Hand::Left) + { + return remote.leftHandUnit; + } + else + { + return remote.rightHandUnit; + } + } + + float + ShapeHand::getAccuracy(const std::string& jointName) + { + const auto accuracy = getParameters().waitUntilFinished->accuracy; + const auto& accuracyOverride = getParameters().waitUntilFinished->accuracyOverride; + + return accuracyOverride.count(jointName) > 0 ? accuracyOverride.at(jointName) : accuracy; + } + + OpenHand::OpenHand() : + armarx::skills::SpecializedSkill<OpenCloseHandParams>(GetSkillDescription()) + { + } + + ::armarx::skills::Skill::MainResult + OpenHand::main() + { + // call ShapeHand-skill + ::armarx::skills::SkillID id; + id.skillName = "ShapeHand"; + id.providerId = getSkillId().providerId; + + ShapeHandParams params; + params.hand = getParameters().hand; + params.shapeName = "Open"; + params.waitUntilFinished = getParameters().waitUntilFinished; + + return {(*callSubskill(::armarx::skills::SkillProxy(manager, id), params.toAron())).status}; + } + + CloseHand::CloseHand() : + armarx::skills::SpecializedSkill<OpenCloseHandParams>(GetSkillDescription()) + { + } + + ::armarx::skills::Skill::MainResult + CloseHand::main() + { + // call ShapeHand-skill + ::armarx::skills::SkillID id; + id.skillName = "ShapeHand"; + id.providerId = getSkillId().providerId; + + ShapeHandParams params; + params.hand = getParameters().hand; + params.shapeName = "Close"; + params.waitUntilFinished = getParameters().waitUntilFinished; + + return {(*callSubskill(::armarx::skills::SkillProxy(manager, id), params.toAron())).status}; + } +} // namespace armarx::control::skills::skills diff --git a/source/armarx/control/skills/skills/ShapeHand.h b/source/armarx/control/skills/skills/ShapeHand.h new file mode 100644 index 0000000000000000000000000000000000000000..9e6b0daaf62a52e47fe010f85cd00fbb57270aed --- /dev/null +++ b/source/armarx/control/skills/skills/ShapeHand.h @@ -0,0 +1,81 @@ +#pragma once + +#include <RobotAPI/interface/units/HandUnitInterface.h> +#include <RobotAPI/libraries/skills/provider/SpecializedSkill.h> + +#include <armarx/control/skills/skills/aron/ShapeHandParams.aron.generated.h> + +namespace armarx::control::skills::skills +{ + + class ShapeHand : public ::armarx::skills::SpecializedSkill<ShapeHandParams> + { + + public: + static ::armarx::skills::SkillDescription + GetSkillDescription() + { + return ::armarx::skills::SkillDescription{ + .skillId = {.skillName = "ShapeHand"}, + .description = + "Shapes the hand according to a predefined shape or passed joint angles", + .timeout = ::armarx::Duration::Minutes(1), + .parametersType = ParamType::ToAronType()}; + } + + struct Remote + { + ::armarx::HandUnitInterfacePrx& leftHandUnit; + ::armarx::HandUnitInterfacePrx& rightHandUnit; + }; + + ShapeHand(const Remote&); + + private: + ::armarx::skills::Skill::MainResult main() override; + Skill::MainResult waitUntilFinished(const NameValueMap& targetAngles); + + private: + Remote remote; + ::armarx::HandUnitInterfacePrx& getHandUnit(const Hand); + + float getAccuracy(const std::string& jointName); + }; + + class OpenHand : public ::armarx::skills::SpecializedSkill<OpenCloseHandParams> + { + public: + static ::armarx::skills::SkillDescription + GetSkillDescription() + { + return ::armarx::skills::SkillDescription{.skillId = {.skillName = "OpenHand"}, + .description = "Opens the hand", + .timeout = ::armarx::Duration::Minutes(1), + .parametersType = ParamType::ToAronType()}; + } + + OpenHand(); + + private: + ::armarx::skills::Skill::MainResult main() override; + }; + + class CloseHand : public ::armarx::skills::SpecializedSkill<OpenCloseHandParams> + { + public: + static ::armarx::skills::SkillDescription + GetSkillDescription() + { + return ::armarx::skills::SkillDescription{.skillId = {.skillName = "CloseHand"}, + .description = "Closes the hand", + .timeout = ::armarx::Duration::Minutes(1), + .parametersType = ParamType::ToAronType()}; + } + + CloseHand(); + + private: + ::armarx::skills::Skill::MainResult main() override; + }; + +} // namespace armarx::control::skills::skills diff --git a/source/armarx/control/skills/skills/aron/ShapeHandParams.xml b/source/armarx/control/skills/skills/aron/ShapeHandParams.xml new file mode 100644 index 0000000000000000000000000000000000000000..063cf8dfe6899a108b9beb4ffa13a100dd2a9b5e --- /dev/null +++ b/source/armarx/control/skills/skills/aron/ShapeHandParams.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<AronTypeDefinition> + <GenerateTypes> + + <IntEnum name="::armarx::control::skills::skills::Hand"> + <EnumValue key="Left" value="0"/> + <EnumValue key="Right" value="1"/> + </IntEnum> + + <!-- Parameters to define when the skill should terminate (if waiting) --> + <Object name="::armarx::control::skills::skills::WaitUntilFinishedParams"> + + <!-- Each joint must be within this accuracy around the target angle for the skill + to terminate --> + <ObjectChild key="accuracy"> + <float /> + </ObjectChild> + + <!-- joint name -> accuracy --> + <ObjectChild key="accuracyOverride"> + <Dict> + <float /> + </Dict> + </ObjectChild> + + <!-- If set the skill also terminates when no joint moves more than 'accuracy' in + 'movementTimeout' seconds (e.g. when joint is blocked) --> + <ObjectChild key="movementTimeout"> + <float optional="true" /> + </ObjectChild> + + </Object> + + + <!-- 'ShapeHand'-skill parameters --> + <Object name="::armarx::control::skills::skills::ShapeHandParams"> + + <ObjectChild key="hand"> + <::armarx::control::skills::skills::Hand /> + </ObjectChild> + + <!-- Must provide exactly one of the parameters 'shapeName' and 'jointAngles' --> + + <!-- name of predefined hand shape --> + <ObjectChild key="shapeName"> + <string optional="true" /> + </ObjectChild> + + <!-- joint name -> target value --> + <ObjectChild key="jointAngles"> + <Dict optional="true"> + <float /> + </Dict> + </ObjectChild> + + <!-- If set, the skill blocks until target angles are reached / angles don't change + anymore. Otherwise, the skill terminates instantly. When in simulation, + 'simulateMovement' must be set in order for this to work. --> + <ObjectChild key="waitUntilFinished"> + <::armarx::control::skills::skills::WaitUntilFinishedParams optional="true" /> + </ObjectChild> + + </Object> + + + <!-- 'OpenHand'/'CloseHand'-skills parameters --> + <Object name="::armarx::control::skills::skills::OpenCloseHandParams"> + + <ObjectChild key="hand"> + <::armarx::control::skills::skills::Hand /> + </ObjectChild> + + <!-- If set, the skill blocks until target angles are reached / angles don't change + anymore. Otherwise, the skill terminates instantly. When in simulation, + 'simulateMovement' must be set in order for this to work. --> + <ObjectChild key="waitUntilFinished"> + <::armarx::control::skills::skills::WaitUntilFinishedParams optional="true" /> + </ObjectChild> + + </Object> + + </GenerateTypes> +</AronTypeDefinition>