diff --git a/data/RobotAPI/VariantInfo-RobotAPI.xml b/data/RobotAPI/VariantInfo-RobotAPI.xml index bf445568148fca1e0a6ca7fc214c088467a1edd1..c90d353054348fc5de64387f5f1ee238d382b05c 100644 --- a/data/RobotAPI/VariantInfo-RobotAPI.xml +++ b/data/RobotAPI/VariantInfo-RobotAPI.xml @@ -13,6 +13,7 @@ <Variant baseType="::armarx::LinkedDirectionBase" dataType="::armarx::LinkedDirection" humanName="LinkedDirection" include="RobotAPI/libraries/core/LinkedPose.h" /> <Variant baseType="::armarx::OrientedPointBase" dataType="::armarx::OrientedPoint" humanName="OrientedPoint" include="RobotAPI/libraries/core/OrientedPoint.h" /> <Variant baseType="::armarx::FramedOrientedPointBase" dataType="::armarx::FramedOrientedPoint" humanName="FramedOrientedPoint" include="RobotAPI/libraries/core/FramedOrientedPoint.h" /> + <Variant baseType="::armarx::CartesianPositionControllerConfigBase" dataType="::armarx::CartesianPositionControllerConfig" humanName="CartesianPositionControllerConfig" include="RobotAPI/libraries/core/CartesianPositionController.h" /> <Variant baseType="::armarx::TrajectoryBase" dataType="::armarx::Trajectory" humanName="Trajectory" include="RobotAPI/libraries/core/Trajectory.h"/> </Lib> <Lib name="RobotAPIInterfaces"> @@ -189,7 +190,11 @@ <onConnect>remoteRobot.reset(new RemoteRobot(robotStateComponent->getSynchronizedRobot()));</onConnect> <stateMethod header="const VirtualRobot::RobotPtr getRobot() const">return %getContext%->getRobot();</stateMethod> - <method header="const VirtualRobot::RobotPtr getLocalRobot() const">return robotPoolStructure->getRobot();</method> + <method header="const VirtualRobot::RobotPtr getLocalRobot(bool performInitialRobotSync = true) const">VirtualRobot::RobotPtr robot = robotPoolStructure->getRobot(); + if(performInitialRobotSync) { + ::armarx::RemoteRobot::synchronizeLocalClone(robot, getRobotStateComponent()); + } + return robot;</method> <method header="const VirtualRobot::RobotPtr getLocalCollisionRobot() const">return robotPoolCollision->getRobot();</method> <method header="const RobotNameHelperPtr getRobotNameHelper() const">return robotNameHelper;</method> <member>VirtualRobot::RobotPtr localRobot;</member> @@ -203,7 +208,7 @@ <onConnect>localCollisionRobot = RemoteRobot::createLocalCloneFromFile(robotStateComponent, VirtualRobot::RobotIO::eCollisionModel);</onConnect> <onConnect>robotPoolCollision.reset(new RobotPool(localCollisionRobot,2));</onConnect> <onConnect>robotNameHelper = RobotNameHelper::Create(robotStateComponent->getRobotInfo(), getSelectedStatechartProfile());</onConnect> - <stateMethod header="const VirtualRobot::RobotPtr getLocalRobot() const">return %getContext%->getLocalRobot();</stateMethod> + <stateMethod header="const VirtualRobot::RobotPtr getLocalRobot(bool performInitialRobotSync = true) const">return %getContext%->getLocalRobot(performInitialRobotSync);</stateMethod> <stateMethod header="const VirtualRobot::RobotPtr getLocalCollisionRobot() const">return %getContext%->getLocalCollisionRobot();</stateMethod> <stateMethod header="const RobotNameHelperPtr getRobotNameHelper() const">return %getContext%->getRobotNameHelper();</stateMethod> @@ -228,12 +233,23 @@ </Topic> <Topic include="RobotAPI/interface/speech/SpeechInterface.h" - humanName="Text to Speech" + humanName="Text to Speech Topic" typeName="TextListenerInterfacePrx" memberName="textToSpeech" getterName="getTextToSpeech" propertyName="TextToSpeechTopicName" propertyIsOptional="true" propertyDefaultValue="TextToSpeech" /> + + + <Proxy include="RobotAPI/interface/observers/GraspCandidateObserverInterface.h" + humanName="Grasp Candidate Observer" + typeName="grasping::GraspCandidateObserverInterfacePrx" + memberName="graspCandidateObserver" + getterName="getGraspCandidateObserver" + propertyName="GraspCandidateObserverName" + propertyIsOptional="true" + propertyDefaultValue="GraspCandidateObserver" /> + </Lib> </VariantInfo> diff --git a/source/RobotAPI/applications/CMakeLists.txt b/source/RobotAPI/applications/CMakeLists.txt index 33562999950ec8e211693c9eac7867b4d01657b9..fcb12a119335518906e559c6e46ad070f13e9d5c 100644 --- a/source/RobotAPI/applications/CMakeLists.txt +++ b/source/RobotAPI/applications/CMakeLists.txt @@ -40,8 +40,10 @@ add_subdirectory(RobotNameService) add_subdirectory(SpeechObserver) add_subdirectory(DummyTextToSpeech) add_subdirectory(KITProstheticHandUnit) -add_subdirectory(ProsthesisObserver) add_subdirectory(CyberGloveObserver) add_subdirectory(RobotHealth) add_subdirectory(RobotHealthDummy) +add_subdirectory(GraspCandidateObserver) + +add_subdirectory(FrameTracking) \ No newline at end of file diff --git a/source/RobotAPI/applications/FrameTracking/CMakeLists.txt b/source/RobotAPI/applications/FrameTracking/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..69311ee8d5fb8e412d615b156eb93e9a1de31dd2 --- /dev/null +++ b/source/RobotAPI/applications/FrameTracking/CMakeLists.txt @@ -0,0 +1,11 @@ +armarx_component_set_name("FrameTrackingApp") +set(COMPONENT_LIBS FrameTracking) +armarx_add_component_executable(main.cpp) + +#find_package(MyLib QUIET) +#armarx_build_if(MyLib_FOUND "MyLib not available") +# all target_include_directories must be guarded by if(Xyz_FOUND) +# for multiple libraries write: if(X_FOUND AND Y_FOUND).... +#if(MyLib_FOUND) +# target_include_directories(FrameTracking PUBLIC ${MyLib_INCLUDE_DIRS}) +#endif() diff --git a/source/RobotAPI/applications/ProsthesisObserver/main.cpp b/source/RobotAPI/applications/FrameTracking/main.cpp similarity index 73% rename from source/RobotAPI/applications/ProsthesisObserver/main.cpp rename to source/RobotAPI/applications/FrameTracking/main.cpp index 1d3dfb4ca27ec76c18cfdbd9ac376e5a8b1109e3..d8a7c016d8a9ea8ba94638e1feaec9f55eb16e6f 100644 --- a/source/RobotAPI/applications/ProsthesisObserver/main.cpp +++ b/source/RobotAPI/applications/FrameTracking/main.cpp @@ -13,14 +13,14 @@ * 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::application::ProsthesisObserver - * @author JuliaStarke ( julia dot starke at kit dot edu ) - * @date 2018 + * @package RobotAPI::application::FrameTracking + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#include <RobotAPI/components/ProsthesisObserver/ProsthesisObserver.h> +#include <RobotAPI/components/FrameTracking/FrameTracking.h> #include <ArmarXCore/core/application/Application.h> #include <ArmarXCore/core/Component.h> @@ -28,5 +28,5 @@ int main(int argc, char* argv[]) { - return armarx::runSimpleComponentApp < armarx::ProsthesisObserver > (argc, argv, "ProsthesisObserver"); + return armarx::runSimpleComponentApp < armarx::FrameTracking > (argc, argv, "FrameTracking"); } diff --git a/source/RobotAPI/applications/GraspCandidateObserver/CMakeLists.txt b/source/RobotAPI/applications/GraspCandidateObserver/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..9e4ac83c0d1ccfbd7251fc44ebdc25a4d7c13a62 --- /dev/null +++ b/source/RobotAPI/applications/GraspCandidateObserver/CMakeLists.txt @@ -0,0 +1,25 @@ +armarx_component_set_name("GraspCandidateObserverApp") + + +find_package(Eigen3 QUIET) +armarx_build_if(Eigen3_FOUND "Eigen3 not available") + +if (Eigen3_FOUND) + include_directories(SYSTEM ${Eigen3_INCLUDE_DIR}) +endif() + +find_package(Simox ${ArmarX_Simox_VERSION} QUIET) +armarx_build_if(Simox_FOUND "Simox-VirtualRobot not available") +if(Simox_FOUND) + include_directories(${Simox_INCLUDE_DIRS}) +endif() + +set(COMPONENT_LIBS + RobotAPIUnits + ArmarXCoreInterfaces + ArmarXCore +) + +set(EXE_SOURCE main.cpp) + +armarx_add_component_executable("${EXE_SOURCE}") diff --git a/source/RobotAPI/applications/GraspCandidateObserver/main.cpp b/source/RobotAPI/applications/GraspCandidateObserver/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9de7e0e219db9e8d4a7fb289c9d09902fc8fabe6 --- /dev/null +++ b/source/RobotAPI/applications/GraspCandidateObserver/main.cpp @@ -0,0 +1,32 @@ +/* + * 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::application::GraspCandidateObserver + * @author Simon Ottenhaus ( simon dot ottenhaus at kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include <RobotAPI/components/units/GraspCandidateObserver.h> + +#include <ArmarXCore/core/application/Application.h> +#include <ArmarXCore/core/Component.h> +#include <ArmarXCore/core/logging/Logging.h> + +int main(int argc, char* argv[]) +{ + return armarx::runSimpleComponentApp < armarx::GraspCandidateObserver > (argc, argv, "GraspCandidateObserver"); +} diff --git a/source/RobotAPI/applications/ProsthesisObserver/CMakeLists.txt b/source/RobotAPI/applications/ProsthesisObserver/CMakeLists.txt deleted file mode 100644 index ea075e315bd3aab53d3abe4b5e951228a46bd71b..0000000000000000000000000000000000000000 --- a/source/RobotAPI/applications/ProsthesisObserver/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -armarx_component_set_name("ProsthesisObserverApp") - -#find_package(MyLib QUIET) -#armarx_build_if(MyLib_FOUND "MyLib not available") -# -# all include_directories must be guarded by if(Xyz_FOUND) -# for multiple libraries write: if(X_FOUND AND Y_FOUND).... -#if(MyLib_FOUND) -# include_directories(${MyLib_INCLUDE_DIRS}) -#endif() - -set(COMPONENT_LIBS - ArmarXCoreInterfaces - ArmarXCore - ProsthesisObserver -) - -set(EXE_SOURCE main.cpp) - -armarx_add_component_executable("${EXE_SOURCE}") diff --git a/source/RobotAPI/components/CMakeLists.txt b/source/RobotAPI/components/CMakeLists.txt index a49e87af6ab414b169ba4a29a83a373443be1266..a158a9de601e92895908de099ef5c1f6534571d9 100644 --- a/source/RobotAPI/components/CMakeLists.txt +++ b/source/RobotAPI/components/CMakeLists.txt @@ -7,7 +7,8 @@ add_subdirectory(GamepadControlUnit) add_subdirectory(RobotNameService) add_subdirectory(DummyTextToSpeech) add_subdirectory(KITProstheticHandUnit) -add_subdirectory(ProsthesisObserver) add_subdirectory(CyberGloveObserver) add_subdirectory(RobotHealth) + +add_subdirectory(FrameTracking) \ No newline at end of file diff --git a/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.cpp b/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.cpp index 4784feb5614234a69becf0be6c2f87d2ae8e76a2..eacd69f2afe16f464dc4486665e7c4e6674a7147 100644 --- a/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.cpp +++ b/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.cpp @@ -98,7 +98,8 @@ namespace armarx DebugDrawerComponent::DebugDrawerComponent(): mutex(new RecursiveMutex()), dataUpdateMutex(new RecursiveMutex()), - topicMutex(new RecursiveMutex()) + topicMutex(new RecursiveMutex()), + defaultLayerVisibility(true) { cycleTimeMS = 1000.0f / 30.0f; timerSensor = NULL; @@ -2327,6 +2328,11 @@ namespace armarx removePointCloudVisu(layerName, i.first); } + for (const auto& i : layer.addedColoredPointCloudVisualizations) + { + removeColoredPointCloudVisu(layerName, i.first); + } + for (const auto& i : layer.addedPolygonVisualizations) { removePolygonVisu(layerName, i.first); @@ -2410,10 +2416,13 @@ namespace armarx SoSeparator* mainNode = new SoSeparator {}; mainNode->ref(); - layerMainNode->addChild(mainNode); layers[layerName] = Layer(); layers.at(layerName).mainNode = mainNode; - layers.at(layerName).visible = true; + layers.at(layerName).visible = getDefaultLayerVisibility(layerName); + if (layers.at(layerName).visible) + { + layerMainNode->addChild(mainNode); + } return layers.at(layerName); } @@ -2722,7 +2731,7 @@ namespace armarx pclSep->addChild(new SoPointSet); - ARMARX_INFO << d.name << "PointCloud Update " << pcl.size(); + ARMARX_DEBUG << d.name << "PointCloud Update " << pcl.size(); layer.addedColoredPointCloudVisualizations[d.name] = pclSep; layer.mainNode->addChild(pclSep); diff --git a/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.h b/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.h index 6ed54f0450c85f234a23895e91771df5167d136e..2e27aed9ea0292e0161d8429301ccebb81eb6908 100644 --- a/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.h +++ b/source/RobotAPI/components/DebugDrawer/DebugDrawerComponent.h @@ -143,81 +143,81 @@ namespace armarx } /* Inherited from DebugDrawerInterface. */ - void exportScene(const std::string& filename, const ::Ice::Current& = ::Ice::Current()) override; - void exportLayer(const std::string& filename, const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; - - void setPoseVisu(const std::string& layerName, const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Current& = ::Ice::Current()) override; - void setScaledPoseVisu(const std::string& layerName, const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Float scale, const ::Ice::Current& = ::Ice::Current()) override; - void setPoseDebugLayerVisu(const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Current& = ::Ice::Current()) override; - void setScaledPoseDebugLayerVisu(const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Float scale, const ::Ice::Current& = ::Ice::Current()) override; - void removePoseVisu(const std::string& layerName, const std::string& poseName, const ::Ice::Current& = ::Ice::Current()) override; - void removePoseDebugLayerVisu(const std::string& poseName, const ::Ice::Current& = ::Ice::Current()) override; - - void setLineVisu(const std::string& layerName, const std::string& lineName, const ::armarx::Vector3BasePtr& globalPosition1, const ::armarx::Vector3BasePtr& globalPosition2, float lineWidth, const ::armarx::DrawColor& color, const ::Ice::Current& = ::Ice::Current()) override; - void setLineDebugLayerVisu(const std::string& lineName, const ::armarx::Vector3BasePtr& globalPosition1, const ::armarx::Vector3BasePtr& globalPosition2, float lineWidth, const ::armarx::DrawColor& color, const ::Ice::Current& = ::Ice::Current()) override; - void removeLineVisu(const std::string& layerName, const std::string& lineName, const ::Ice::Current& = ::Ice::Current()) override; - void removeLineDebugLayerVisu(const std::string& lineName, const ::Ice::Current& = ::Ice::Current()) override; - - void setLineSetVisu(const std::string& layerName, const std::string& lineSetName, const DebugDrawerLineSet& lineSet, const ::Ice::Current& = ::Ice::Current()) override; - void setLineSetDebugLayerVisu(const std::string& lineSetName, const DebugDrawerLineSet& lineSet, const ::Ice::Current& = ::Ice::Current()) override; - void removeLineSetVisu(const std::string& layerName, const std::string& lineSetName, const ::Ice::Current& = ::Ice::Current()) override; - void removeLineSetDebugLayerVisu(const std::string& lineSetName, const ::Ice::Current& = ::Ice::Current()) override; - - void setBoxVisu(const std::string& layerName, const std::string& boxName, const ::armarx::PoseBasePtr& globalPose, const ::armarx::Vector3BasePtr& dimensions, const DrawColor& color, const ::Ice::Current& = ::Ice::Current()) override; - void setBoxDebugLayerVisu(const std::string& boxName, const ::armarx::PoseBasePtr& globalPose, const ::armarx::Vector3BasePtr& dimensions, const DrawColor& color, const ::Ice::Current& = ::Ice::Current()) override; - void removeBoxVisu(const std::string& layerName, const std::string& boxName, const ::Ice::Current& = ::Ice::Current()) override; - void removeBoxDebugLayerVisu(const std::string& boxName, const ::Ice::Current& = ::Ice::Current()) override; - - void setTextVisu(const std::string& layerName, const std::string& textName, const std::string& text, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, int size, const ::Ice::Current& = ::Ice::Current()) override; - void setTextDebugLayerVisu(const std::string& textName, const std::string& text, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, int size, const ::Ice::Current& = ::Ice::Current()) override; - void removeTextVisu(const std::string& layerName, const std::string& textName, const ::Ice::Current& = ::Ice::Current()) override; - void removeTextDebugLayerVisu(const std::string& textName, const ::Ice::Current& = ::Ice::Current()) override; - - void setSphereVisu(const std::string& layerName, const std::string& sphereName, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, float radius, const ::Ice::Current& = ::Ice::Current()) override; - void setSphereDebugLayerVisu(const std::string& sphereName, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, float radius, const ::Ice::Current& = ::Ice::Current()) override; - void removeSphereVisu(const std::string& layerName, const std::string& sphereName, const ::Ice::Current& = ::Ice::Current()) override; - void removeSphereDebugLayerVisu(const std::string& sphereName, const ::Ice::Current& = ::Ice::Current()) override; - - void setCylinderVisu(const std::string& layerName, const std::string& cylinderName, const ::armarx::Vector3BasePtr& globalPosition, const ::armarx::Vector3BasePtr& direction, float length, float radius, const DrawColor& color, const ::Ice::Current& = ::Ice::Current()) override; - void setCylinderDebugLayerVisu(const std::string& cylinderName, const ::armarx::Vector3BasePtr& globalPosition, const ::armarx::Vector3BasePtr& direction, float length, float radius, const DrawColor& color, const ::Ice::Current& = ::Ice::Current()) override; - void removeCylinderVisu(const std::string& layerName, const std::string& cylinderName, const ::Ice::Current& = ::Ice::Current()) override; - void removeCylinderDebugLayerVisu(const std::string& cylinderName, const ::Ice::Current& = ::Ice::Current()) override; - - void setPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const DebugDrawerPointCloud& pointCloud, const ::Ice::Current& = ::Ice::Current()) override; - void setPointCloudDebugLayerVisu(const std::string& pointCloudName, const DebugDrawerPointCloud& pointCloud, const ::Ice::Current& = ::Ice::Current()) override; - void removePointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const ::Ice::Current& = ::Ice::Current()) override; - void removePointCloudDebugLayerVisu(const std::string& pointCloudName, const ::Ice::Current& = ::Ice::Current()) override; - - virtual void setColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const DebugDrawerColoredPointCloud& pointCloud, const ::Ice::Current& = ::Ice::Current()); - void setColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const DebugDrawerColoredPointCloud& pointCloud, const ::Ice::Current& = ::Ice::Current()) override; - void removeColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const ::Ice::Current& = ::Ice::Current()) override; - void removeColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const ::Ice::Current& = ::Ice::Current()) override; - - void set24BitColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const DebugDrawer24BitColoredPointCloud& pointCloud, const ::Ice::Current& = ::Ice::Current()) override; - void set24BitColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const DebugDrawer24BitColoredPointCloud& pointCloud, const ::Ice::Current& = ::Ice::Current()) override; - void remove24BitColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const ::Ice::Current& = ::Ice::Current()) override; - void remove24BitColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const ::Ice::Current& = ::Ice::Current()) override; - - - void setPolygonVisu(const std::string& layerName, const std::string& polygonName, const std::vector< ::armarx::Vector3BasePtr >& polygonPoints, const DrawColor& colorInner, const DrawColor& colorBorder, float lineWidth, const ::Ice::Current& = ::Ice::Current()) override; - void setPolygonDebugLayerVisu(const std::string& polygonName, const std::vector< ::armarx::Vector3BasePtr >& polygonPoints, const DrawColor& colorInner, const DrawColor& colorBorder, float lineWidth, const ::Ice::Current& = ::Ice::Current()) override; - void removePolygonVisu(const std::string& layerName, const std::string& polygonName, const ::Ice::Current& = ::Ice::Current()) override; - void removePolygonDebugLayerVisu(const std::string& polygonName, const ::Ice::Current& = ::Ice::Current()) override; - - void setTriMeshVisu(const std::string& layerName, const std::string& triMeshName, const DebugDrawerTriMesh& triMesh, const ::Ice::Current& = ::Ice::Current()) override; - void setTriMeshDebugLayerVisu(const std::string& triMeshName, const DebugDrawerTriMesh& triMesh, const ::Ice::Current& = ::Ice::Current()) override; - void removeTriMeshVisu(const std::string& layerName, const std::string& triMeshName, const ::Ice::Current& = ::Ice::Current()) override; - void removeTriMeshDebugLayerVisu(const std::string& triMeshName, const ::Ice::Current& = ::Ice::Current()) override; - - void setArrowVisu(const std::string& layerName, const std::string& arrowName, const ::armarx::Vector3BasePtr& position, const ::armarx::Vector3BasePtr& direction, const DrawColor& color, float length, float width, const ::Ice::Current& = ::Ice::Current()) override; - void setArrowDebugLayerVisu(const std::string& arrowName, const ::armarx::Vector3BasePtr& position, const ::armarx::Vector3BasePtr& direction, const DrawColor& color, float length, float width, const ::Ice::Current& = ::Ice::Current()) override; - void removeArrowVisu(const std::string& layerName, const std::string& arrowName, const ::Ice::Current& = ::Ice::Current()) override; - void removeArrowDebugLayerVisu(const std::string& arrowName, const ::Ice::Current& = ::Ice::Current()) override; - - void setCircleArrowVisu(const std::string& layerName, const std::string& circleName, const Vector3BasePtr& globalPosition, const Vector3BasePtr& directionVec, Ice::Float radius, Ice::Float circleCompletion, Ice::Float width, const DrawColor& color, const Ice::Current& = ::Ice::Current()) override; - void setCircleDebugLayerVisu(const std::string& circleName, const Vector3BasePtr& globalPosition, const Vector3BasePtr& directionVec, Ice::Float radius, Ice::Float circleCompletion, Ice::Float width, const DrawColor& color, const Ice::Current& = ::Ice::Current()) override; - void removeCircleVisu(const std::string& layerName, const std::string& circleName, const Ice::Current& = ::Ice::Current()) override; - void removeCircleDebugLayerVisu(const std::string& circleName, const Ice::Current& = ::Ice::Current()) override; + void exportScene(const std::string& filename, const ::Ice::Current& = Ice::emptyCurrent) override; + void exportLayer(const std::string& filename, const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setPoseVisu(const std::string& layerName, const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Current& = Ice::emptyCurrent) override; + void setScaledPoseVisu(const std::string& layerName, const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Float scale, const ::Ice::Current& = Ice::emptyCurrent) override; + void setPoseDebugLayerVisu(const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Current& = Ice::emptyCurrent) override; + void setScaledPoseDebugLayerVisu(const std::string& poseName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Float scale, const ::Ice::Current& = Ice::emptyCurrent) override; + void removePoseVisu(const std::string& layerName, const std::string& poseName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removePoseDebugLayerVisu(const std::string& poseName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setLineVisu(const std::string& layerName, const std::string& lineName, const ::armarx::Vector3BasePtr& globalPosition1, const ::armarx::Vector3BasePtr& globalPosition2, float lineWidth, const ::armarx::DrawColor& color, const ::Ice::Current& = Ice::emptyCurrent) override; + void setLineDebugLayerVisu(const std::string& lineName, const ::armarx::Vector3BasePtr& globalPosition1, const ::armarx::Vector3BasePtr& globalPosition2, float lineWidth, const ::armarx::DrawColor& color, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeLineVisu(const std::string& layerName, const std::string& lineName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeLineDebugLayerVisu(const std::string& lineName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setLineSetVisu(const std::string& layerName, const std::string& lineSetName, const DebugDrawerLineSet& lineSet, const ::Ice::Current& = Ice::emptyCurrent) override; + void setLineSetDebugLayerVisu(const std::string& lineSetName, const DebugDrawerLineSet& lineSet, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeLineSetVisu(const std::string& layerName, const std::string& lineSetName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeLineSetDebugLayerVisu(const std::string& lineSetName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setBoxVisu(const std::string& layerName, const std::string& boxName, const ::armarx::PoseBasePtr& globalPose, const ::armarx::Vector3BasePtr& dimensions, const DrawColor& color, const ::Ice::Current& = Ice::emptyCurrent) override; + void setBoxDebugLayerVisu(const std::string& boxName, const ::armarx::PoseBasePtr& globalPose, const ::armarx::Vector3BasePtr& dimensions, const DrawColor& color, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeBoxVisu(const std::string& layerName, const std::string& boxName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeBoxDebugLayerVisu(const std::string& boxName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setTextVisu(const std::string& layerName, const std::string& textName, const std::string& text, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, int size, const ::Ice::Current& = Ice::emptyCurrent) override; + void setTextDebugLayerVisu(const std::string& textName, const std::string& text, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, int size, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeTextVisu(const std::string& layerName, const std::string& textName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeTextDebugLayerVisu(const std::string& textName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setSphereVisu(const std::string& layerName, const std::string& sphereName, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, float radius, const ::Ice::Current& = Ice::emptyCurrent) override; + void setSphereDebugLayerVisu(const std::string& sphereName, const ::armarx::Vector3BasePtr& globalPosition, const DrawColor& color, float radius, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeSphereVisu(const std::string& layerName, const std::string& sphereName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeSphereDebugLayerVisu(const std::string& sphereName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setCylinderVisu(const std::string& layerName, const std::string& cylinderName, const ::armarx::Vector3BasePtr& globalPosition, const ::armarx::Vector3BasePtr& direction, float length, float radius, const DrawColor& color, const ::Ice::Current& = Ice::emptyCurrent) override; + void setCylinderDebugLayerVisu(const std::string& cylinderName, const ::armarx::Vector3BasePtr& globalPosition, const ::armarx::Vector3BasePtr& direction, float length, float radius, const DrawColor& color, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeCylinderVisu(const std::string& layerName, const std::string& cylinderName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeCylinderDebugLayerVisu(const std::string& cylinderName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const DebugDrawerPointCloud& pointCloud, const ::Ice::Current& = Ice::emptyCurrent) override; + void setPointCloudDebugLayerVisu(const std::string& pointCloudName, const DebugDrawerPointCloud& pointCloud, const ::Ice::Current& = Ice::emptyCurrent) override; + void removePointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removePointCloudDebugLayerVisu(const std::string& pointCloudName, const ::Ice::Current& = Ice::emptyCurrent) override; + + virtual void setColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const DebugDrawerColoredPointCloud& pointCloud, const ::Ice::Current& = Ice::emptyCurrent); + void setColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const DebugDrawerColoredPointCloud& pointCloud, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void set24BitColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const DebugDrawer24BitColoredPointCloud& pointCloud, const ::Ice::Current& = Ice::emptyCurrent) override; + void set24BitColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const DebugDrawer24BitColoredPointCloud& pointCloud, const ::Ice::Current& = Ice::emptyCurrent) override; + void remove24BitColoredPointCloudVisu(const std::string& layerName, const std::string& pointCloudName, const ::Ice::Current& = Ice::emptyCurrent) override; + void remove24BitColoredPointCloudDebugLayerVisu(const std::string& pointCloudName, const ::Ice::Current& = Ice::emptyCurrent) override; + + + void setPolygonVisu(const std::string& layerName, const std::string& polygonName, const std::vector< ::armarx::Vector3BasePtr >& polygonPoints, const DrawColor& colorInner, const DrawColor& colorBorder, float lineWidth, const ::Ice::Current& = Ice::emptyCurrent) override; + void setPolygonDebugLayerVisu(const std::string& polygonName, const std::vector< ::armarx::Vector3BasePtr >& polygonPoints, const DrawColor& colorInner, const DrawColor& colorBorder, float lineWidth, const ::Ice::Current& = Ice::emptyCurrent) override; + void removePolygonVisu(const std::string& layerName, const std::string& polygonName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removePolygonDebugLayerVisu(const std::string& polygonName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setTriMeshVisu(const std::string& layerName, const std::string& triMeshName, const DebugDrawerTriMesh& triMesh, const ::Ice::Current& = Ice::emptyCurrent) override; + void setTriMeshDebugLayerVisu(const std::string& triMeshName, const DebugDrawerTriMesh& triMesh, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeTriMeshVisu(const std::string& layerName, const std::string& triMeshName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeTriMeshDebugLayerVisu(const std::string& triMeshName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setArrowVisu(const std::string& layerName, const std::string& arrowName, const ::armarx::Vector3BasePtr& position, const ::armarx::Vector3BasePtr& direction, const DrawColor& color, float length, float width, const ::Ice::Current& = Ice::emptyCurrent) override; + void setArrowDebugLayerVisu(const std::string& arrowName, const ::armarx::Vector3BasePtr& position, const ::armarx::Vector3BasePtr& direction, const DrawColor& color, float length, float width, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeArrowVisu(const std::string& layerName, const std::string& arrowName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeArrowDebugLayerVisu(const std::string& arrowName, const ::Ice::Current& = Ice::emptyCurrent) override; + + void setCircleArrowVisu(const std::string& layerName, const std::string& circleName, const Vector3BasePtr& globalPosition, const Vector3BasePtr& directionVec, Ice::Float radius, Ice::Float circleCompletion, Ice::Float width, const DrawColor& color, const Ice::Current& = Ice::emptyCurrent) override; + void setCircleDebugLayerVisu(const std::string& circleName, const Vector3BasePtr& globalPosition, const Vector3BasePtr& directionVec, Ice::Float radius, Ice::Float circleCompletion, Ice::Float width, const DrawColor& color, const Ice::Current& = Ice::emptyCurrent) override; + void removeCircleVisu(const std::string& layerName, const std::string& circleName, const Ice::Current& = Ice::emptyCurrent) override; + void removeCircleDebugLayerVisu(const std::string& circleName, const Ice::Current& = Ice::emptyCurrent) override; /*! @@ -228,50 +228,50 @@ namespace armarx * \param DrawStyle Either FullModel ior CollisionModel. * \param armarxProject Additional armarx project that should be used to search the robot. Must be locally available and accessible through the armarx cmake search procedure. */ - void setRobotVisu(const std::string& layerName, const std::string& robotName, const std::string& robotFile, const std::string& armarxProject, DrawStyle drawStyle, const ::Ice::Current& = ::Ice::Current()) override; - void updateRobotPose(const std::string& layerName, const std::string& robotName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Current& = ::Ice::Current()) override; - void updateRobotConfig(const std::string& layerName, const std::string& robotName, const std::map< std::string, float>& configuration, const ::Ice::Current& = ::Ice::Current()) override; + void setRobotVisu(const std::string& layerName, const std::string& robotName, const std::string& robotFile, const std::string& armarxProject, DrawStyle drawStyle, const ::Ice::Current& = Ice::emptyCurrent) override; + void updateRobotPose(const std::string& layerName, const std::string& robotName, const ::armarx::PoseBasePtr& globalPose, const ::Ice::Current& = Ice::emptyCurrent) override; + void updateRobotConfig(const std::string& layerName, const std::string& robotName, const std::map< std::string, float>& configuration, const ::Ice::Current& = Ice::emptyCurrent) override; /*! * \brief updateRobotColor Colorizes the robot visualization * \param layerName The layer * \param robotName The robot identifyer * \param c The draw color, if all is set to 0, the colorization is disabled (i.e. the original vizaulization shows up) */ - void updateRobotColor(const std::string& layerName, const std::string& robotName, const armarx::DrawColor& c, const ::Ice::Current& = ::Ice::Current()) override; - void updateRobotNodeColor(const std::string& layerName, const std::string& robotName, const std::string& robotNodeName, const armarx::DrawColor& c, const ::Ice::Current& = ::Ice::Current()) override; - void removeRobotVisu(const std::string& layerName, const std::string& robotName, const ::Ice::Current& = ::Ice::Current()) override; + void updateRobotColor(const std::string& layerName, const std::string& robotName, const armarx::DrawColor& c, const ::Ice::Current& = Ice::emptyCurrent) override; + void updateRobotNodeColor(const std::string& layerName, const std::string& robotName, const std::string& robotNodeName, const armarx::DrawColor& c, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeRobotVisu(const std::string& layerName, const std::string& robotName, const ::Ice::Current& = Ice::emptyCurrent) override; - void clearAll(const ::Ice::Current& = ::Ice::Current()) override; - void clearLayer(const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; - void clearDebugLayer(const ::Ice::Current& = ::Ice::Current()) override; + void clearAll(const ::Ice::Current& = Ice::emptyCurrent) override; + void clearLayer(const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; + void clearDebugLayer(const ::Ice::Current& = Ice::emptyCurrent) override; - bool hasLayer(const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; - void removeLayer(const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; + bool hasLayer(const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; + void removeLayer(const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; - void enableLayerVisu(const std::string& layerName, bool visible, const ::Ice::Current& = ::Ice::Current()) override; - void enableDebugLayerVisu(bool visible, const ::Ice::Current& = ::Ice::Current()) override; + void enableLayerVisu(const std::string& layerName, bool visible, const ::Ice::Current& = Ice::emptyCurrent) override; + void enableDebugLayerVisu(bool visible, const ::Ice::Current& = Ice::emptyCurrent) override; - ::Ice::StringSeq layerNames(const ::Ice::Current& = ::Ice::Current()) override; - ::armarx::LayerInformationSequence layerInformation(const ::Ice::Current& = ::Ice::Current()) override; + ::Ice::StringSeq layerNames(const ::Ice::Current& = Ice::emptyCurrent) override; + ::armarx::LayerInformationSequence layerInformation(const ::Ice::Current& = Ice::emptyCurrent) override; - void disableAllLayers(const ::Ice::Current& = ::Ice::Current()) override; - void enableAllLayers(const ::Ice::Current& = ::Ice::Current()) override; + void disableAllLayers(const ::Ice::Current& = Ice::emptyCurrent) override; + void enableAllLayers(const ::Ice::Current& = Ice::emptyCurrent) override; // Methods for selection management - void enableSelections(const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; - void disableSelections(const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; - void clearSelections(const std::string& layerName, const ::Ice::Current& = ::Ice::Current()) override; - void select(const std::string& layerName, const std::string& elementName, const ::Ice::Current& = ::Ice::Current()) override; - void deselect(const std::string& layerName, const std::string& elementName, const ::Ice::Current& = ::Ice::Current()) override; + void enableSelections(const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; + void disableSelections(const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; + void clearSelections(const std::string& layerName, const ::Ice::Current& = Ice::emptyCurrent) override; + void select(const std::string& layerName, const std::string& elementName, const ::Ice::Current& = Ice::emptyCurrent) override; + void deselect(const std::string& layerName, const std::string& elementName, const ::Ice::Current& = Ice::emptyCurrent) override; - DebugDrawerSelectionList getSelections(const ::Ice::Current& = ::Ice::Current()) override; + DebugDrawerSelectionList getSelections(const ::Ice::Current& = Ice::emptyCurrent) override; void selectionCallback(); void deselectionCallback(); void installSelectionCallbacks(); void removeSelectionCallbacks(); - void reportSelectionChanged(const DebugDrawerSelectionList& selectedElements, const ::Ice::Current& = ::Ice::Current()) override; + void reportSelectionChanged(const DebugDrawerSelectionList& selectedElements, const ::Ice::Current& = Ice::emptyCurrent) override; /*! * \brief getScopedLock If using the coin visualization it must be ensured that all rendering calls are protected with this mutex @@ -288,6 +288,36 @@ namespace armarx void setMutex(boost::shared_ptr<boost::recursive_mutex> m); ~DebugDrawerComponent() override; + + void setDefaultLayerVisibility(bool defaultLayerVisibility) + { + this->defaultLayerVisibility = defaultLayerVisibility; + } + void setDefaultLayerVisibility(const std::string& layerName, bool defaultLayerVisibility) + { + defaultLayerVisibilityPerLayer[layerName] = defaultLayerVisibility; + } + void removeDefaultLayerVisibility(const std::string& layerName) + { + defaultLayerVisibilityPerLayer.erase(layerName); + } + bool getDefaultLayerVisibility() const + { + return defaultLayerVisibility; + } + bool getDefaultLayerVisibility(const std::string& layerName) const + { + auto elem = defaultLayerVisibilityPerLayer.find(layerName); + if (elem != defaultLayerVisibilityPerLayer.end()) + { + return elem->second; + } + return defaultLayerVisibility; + } + std::map<std::string, bool> getAllDefaultLayerVisibilities() const + { + return defaultLayerVisibilityPerLayer; + } protected: static void updateVisualizationCB(void* data, SoSensor* sensor); @@ -581,6 +611,9 @@ namespace armarx std::map<std::string, VirtualRobot::RobotPtr> loadedRobots; + bool defaultLayerVisibility; + std::map<std::string, bool> defaultLayerVisibilityPerLayer; + }; using DebugDrawerComponentPtr = IceInternal::Handle<DebugDrawerComponent>; diff --git a/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.cpp b/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.cpp index 833d4dbdfca4c8ad28964bf3e6de1b33a3108b29..d4d68c30e9a198e5a50ba7238774f2cfaf3cb9b5 100644 --- a/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.cpp +++ b/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.cpp @@ -30,72 +30,214 @@ using namespace math; using namespace armarx; +#define CHECK_AND_ADD(name,type) if (!enableVisu) {return;} addNewElement(name,type); +//#define CHECK_AND_ADD(name,type) if (!enableVisu) {return;} + DebugDrawerHelper::DebugDrawerHelper(const DebugDrawerInterfacePrx& debugDrawerPrx, const std::string& layerName, const VirtualRobot::RobotPtr& robot) - : debugDrawerPrx(debugDrawerPrx), robot(robot), rn(robot->getRootNode()) + : debugDrawerPrx(debugDrawerPrx), layerName(layerName), robot(robot), rn(robot ? robot->getRootNode() : VirtualRobot::RobotNodePtr()) { } void DebugDrawerHelper::drawPose(const std::string& name, const Eigen::Matrix4f& pose) { + CHECK_AND_ADD(name, DrawElementType::Pose) debugDrawerPrx->setPoseVisu(layerName, name, makeGlobal(pose)); } void DebugDrawerHelper::drawPose(const std::string& name, const Eigen::Matrix4f& pose, float scale) { + CHECK_AND_ADD(name, DrawElementType::Pose) debugDrawerPrx->setScaledPoseVisu(layerName, name, makeGlobal(pose), scale); } void DebugDrawerHelper::drawBox(const std::string& name, const Eigen::Vector3f& position, float size, const DrawColor& color) { + CHECK_AND_ADD(name, DrawElementType::Box) drawBox(name, Helpers::CreatePose(position, Eigen::Matrix3f::Identity()), Eigen::Vector3f(size, size, size), color); } void DebugDrawerHelper::drawBox(const std::string& name, const Eigen::Matrix4f& pose, float size, const DrawColor& color) { + CHECK_AND_ADD(name, DrawElementType::Box) drawBox(name, pose, Eigen::Vector3f(size, size, size), color); } void DebugDrawerHelper::drawBox(const std::string& name, const Eigen::Matrix4f& pose, const Eigen::Vector3f& size, const DrawColor& color) { + CHECK_AND_ADD(name, DrawElementType::Box) debugDrawerPrx->setBoxVisu(layerName, name, makeGlobal(pose), new Vector3(size), color); } void DebugDrawerHelper::drawLine(const std::string& name, const Eigen::Vector3f& p1, const Eigen::Vector3f& p2, float width, const DrawColor& color) { + CHECK_AND_ADD(name, DrawElementType::Line) debugDrawerPrx->setLineVisu(layerName, name, makeGlobal(p1), makeGlobal(p2), width, color); } void DebugDrawerHelper::drawLine(const std::string& name, const Eigen::Vector3f& p1, const Eigen::Vector3f& p2) { + CHECK_AND_ADD(name, DrawElementType::Line) drawLine(name, p1, p2, defaults.lineWidth, defaults.lineColor); } void DebugDrawerHelper::drawText(const std::string& name, const Eigen::Vector3f& p1, const std::string& text, const DrawColor& color, int size) { + CHECK_AND_ADD(name, DrawElementType::Text) debugDrawerPrx->setTextVisu(layerName, name, text, makeGlobal(p1), color, size); } void DebugDrawerHelper::drawArrow(const std::string& name, const Eigen::Vector3f& pos, const Eigen::Vector3f& direction, const DrawColor& color, float length, float width) { + CHECK_AND_ADD(name, DrawElementType::Arrow) debugDrawerPrx->setArrowVisu(layerName, name, makeGlobal(pos), makeGlobalDirection(direction), color, length, width); } +void DebugDrawerHelper::drawPointCloud(const std::string& name, const std::vector<Eigen::Vector3f>& points, float pointSize, const DrawColor& color) +{ + CHECK_AND_ADD(name, DrawElementType::ColoredPointCloud) + DebugDrawerColoredPointCloud pc; + pc.pointSize = pointSize; + for (const Eigen::Vector3f& p : points) + { + Eigen::Vector3f pg = rn ? rn->getGlobalPosition(p) : p; + DebugDrawerColoredPointCloudElement e; + e.x = pg(0); + e.y = pg(1); + e.z = pg(2); + e.color = color; //DrawColor24Bit {(Ice::Byte)(color.r * 255), (Ice::Byte)(color.g * 255), (Ice::Byte)(color.b * 255)}; + pc.points.push_back(e); + } + debugDrawerPrx->setColoredPointCloudVisu(layerName, name, pc); +} + +void armarx::DebugDrawerHelper::drawRobot(const std::string& name, const std::string& robotFile, const std::string& armarxProject, const Eigen::Matrix4f& pose, const armarx::DrawColor& color) +{ + CHECK_AND_ADD(name, DrawElementType::Robot) + debugDrawerPrx->setRobotVisu(layerName, name, robotFile, armarxProject, DrawStyle::FullModel); + debugDrawerPrx->updateRobotPose(layerName, name, makeGlobal(pose)); + debugDrawerPrx->updateRobotColor(layerName, name, color); +} + +void DebugDrawerHelper::setRobotConfig(const std::string& name, const std::map<std::string, float>& config) +{ + debugDrawerPrx->updateRobotConfig(layerName, name, config); +} + +void DebugDrawerHelper::clearLayer() +{ + debugDrawerPrx->clearLayer(layerName); + oldElements.clear(); + newElements.clear(); +} + +void DebugDrawerHelper::cyclicCleanup() +{ + /* + // debug code... + std::stringstream ss; + ss << "OLD: "; + for (const DrawElement& e : oldElements) + { + ss << e.name << ","; + } + ss << " NEW: "; + for (const DrawElement& e : newElements) + { + ss << e.name << ","; + } + ARMARX_IMPORTANT << ss.str();*/ + + for (const DrawElement& old : oldElements) + { + if (newElements.find(old) == newElements.end()) + { + //ARMARX_IMPORTANT << "removing " << old.name; + removeElement(old.name, old.type); + } + } + oldElements = newElements; + newElements.clear(); +} + +void DebugDrawerHelper::setVisuEnabled(bool enableVisu) +{ + this->enableVisu = enableVisu; +} + PosePtr DebugDrawerHelper::makeGlobal(const Eigen::Matrix4f& pose) { - return new Pose(rn->getGlobalPose(pose)); + Eigen::Matrix4f gp = rn ? rn->getGlobalPose(pose) : pose; + return new Pose(gp); } Vector3Ptr DebugDrawerHelper::makeGlobal(const Eigen::Vector3f& position) { - return new Vector3(rn->getGlobalPosition(position)); + Eigen::Vector3f gp = rn ? rn->getGlobalPosition(position) : position; + return new Vector3(gp); } Vector3Ptr DebugDrawerHelper::makeGlobalDirection(const Eigen::Vector3f& direction) { - return new Vector3(math::Helpers::TransformDirection(rn->getGlobalPose(), direction)); + Eigen::Vector3f gd = rn ? math::Helpers::TransformDirection(rn->getGlobalPose(), direction) : direction; + return new Vector3(gd); +} + +Eigen::Vector3f DebugDrawerHelper::makeGlobalEigen(const Eigen::Vector3f& position) +{ + return rn ? rn->getGlobalPosition(position) : position; } void armarx::DebugDrawerHelper::drawSphere(const std::string& name, const Eigen::Vector3f& position, float size, const armarx::DrawColor& color) { debugDrawerPrx->setSphereVisu(layerName, name, makeGlobal(position), color, size); } + +void DebugDrawerHelper::removeElement(const std::string& name, DebugDrawerHelper::DrawElementType type) +{ + switch (type) + { + case DrawElementType::Pose: + debugDrawerPrx->removePoseVisu(layerName, name); + return; + case DrawElementType::Box: + debugDrawerPrx->removeBoxVisu(layerName, name); + return; + case DrawElementType::Line: + debugDrawerPrx->removeLineVisu(layerName, name); + return; + case DrawElementType::Text: + debugDrawerPrx->removeTextVisu(layerName, name); + return; + case DrawElementType::Arrow: + debugDrawerPrx->removeArrowVisu(layerName, name); + return; + case DrawElementType::ColoredPointCloud: + debugDrawerPrx->removeColoredPointCloudVisu(layerName, name); + return; + case DrawElementType::Robot: + debugDrawerPrx->removeRobotVisu(layerName, name); + return; + } +} + +void DebugDrawerHelper::addNewElement(const std::string& name, DebugDrawerHelper::DrawElementType type) +{ + newElements.insert(DrawElement {name, type}); +} + +const DrawColor DebugDrawerHelper::Colors::Red = DrawColor {1, 0, 0, 1}; +const DrawColor DebugDrawerHelper::Colors::Green = DrawColor {0, 1, 0, 1}; +const DrawColor DebugDrawerHelper::Colors::Blue = DrawColor {0, 0, 1, 1}; +const DrawColor DebugDrawerHelper::Colors::Yellow = DrawColor {1, 1, 0, 1}; +const DrawColor DebugDrawerHelper::Colors::Orange = DrawColor {1, 0.5f, 0, 1}; + +DrawColor DebugDrawerHelper::Colors::Lerp(const DrawColor& a, const DrawColor& b, float f) +{ + return DrawColor + { + math::Helpers::Lerp(a.r, b.r, f), + math::Helpers::Lerp(a.g, b.g, f), + math::Helpers::Lerp(a.b, b.b, f), + math::Helpers::Lerp(a.a, b.a, f) + }; +} diff --git a/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h b/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h index 217e548fd28d252c3408f8aa1e59f9e002353b48..334fa002eaa8cc1c8be7023757a48edd9cf5e620 100644 --- a/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h +++ b/source/RobotAPI/components/DebugDrawer/DebugDrawerHelper.h @@ -42,6 +42,30 @@ namespace armarx float lineWidth = 2; DrawColor lineColor = DrawColor {0, 1, 0, 1}; }; + enum class DrawElementType + { + Pose, Box, Line, Text, Arrow, ColoredPointCloud, Robot + }; + struct DrawElement + { + std::string name; + DrawElementType type; + bool operator<(const DrawElement& rhs) const + { + int cmp = name.compare(rhs.name); + return cmp < 0 || (cmp == 0 && type < rhs.type); + } + }; + class Colors + { + public: + static const DrawColor Red; + static const DrawColor Green; + static const DrawColor Blue; + static const DrawColor Yellow; + static const DrawColor Orange; + static DrawColor Lerp(const DrawColor& a, const DrawColor& b, float f); + }; DebugDrawerHelper(const DebugDrawerInterfacePrx& debugDrawerPrx, const std::string& layerName, const VirtualRobot::RobotPtr& robot); @@ -61,14 +85,32 @@ namespace armarx void drawSphere(const std::string& name, const Eigen::Vector3f& position, float size, const DrawColor& color); + void drawPointCloud(const std::string& name, const std::vector<Eigen::Vector3f>& points, float pointSize, const DrawColor& color); + + void drawRobot(const std::string& name, const std::string& robotFile, const std::string& armarxProject, const Eigen::Matrix4f& pose, const DrawColor& color); + void setRobotConfig(const std::string& name, const std::map<std::string, float>& config); + + void clearLayer(); + void cyclicCleanup(); + + void setVisuEnabled(bool enableVisu); + void removeElement(const std::string& name, DrawElementType type); + PosePtr makeGlobal(const Eigen::Matrix4f& pose); Vector3Ptr makeGlobal(const Eigen::Vector3f& position); Vector3Ptr makeGlobalDirection(const Eigen::Vector3f& direction); + Eigen::Vector3f makeGlobalEigen(const Eigen::Vector3f& position); Defaults defaults; private: + void addNewElement(const std::string& name, DrawElementType type); + + private: + std::set<DrawElement> newElements; + std::set<DrawElement> oldElements; + bool enableVisu = true; DebugDrawerInterfacePrx debugDrawerPrx; std::string layerName; VirtualRobot::RobotPtr robot; diff --git a/source/RobotAPI/components/ProsthesisObserver/CMakeLists.txt b/source/RobotAPI/components/FrameTracking/CMakeLists.txt similarity index 53% rename from source/RobotAPI/components/ProsthesisObserver/CMakeLists.txt rename to source/RobotAPI/components/FrameTracking/CMakeLists.txt index a9767421b3dba96a35c7b1680d67c82af4c88def..54f4372df34157e13fb91d07b87d6806ec7aaadc 100644 --- a/source/RobotAPI/components/ProsthesisObserver/CMakeLists.txt +++ b/source/RobotAPI/components/FrameTracking/CMakeLists.txt @@ -1,26 +1,25 @@ -armarx_component_set_name("ProsthesisObserver") +armarx_component_set_name("FrameTracking") -#find_package(MyLib QUIET) -#armarx_build_if(MyLib_FOUND "MyLib not available") -# -# all include_directories must be guarded by if(Xyz_FOUND) -# for multiple libraries write: if(X_FOUND AND Y_FOUND).... -#if(MyLib_FOUND) -# include_directories(${MyLib_INCLUDE_DIRS}) -#endif() - -set(COMPONENT_LIBS ArmarXCoreInterfaces ArmarXCore RobotAPICore ArmarXCoreObservers) +set(COMPONENT_LIBS ArmarXCore RobotAPIInterfaces RobotAPICore RemoteGui ArmarXCoreObservers ArmarXGuiInterfaces RobotUnit) set(SOURCES -./ProsthesisObserver.cpp +./FrameTracking.cpp #@TEMPLATE_LINE@@COMPONENT_PATH@/@COMPONENT_NAME@.cpp ) set(HEADERS -./ProsthesisObserver.h +./FrameTracking.h #@TEMPLATE_LINE@@COMPONENT_PATH@/@COMPONENT_NAME@.h ) armarx_add_component("${SOURCES}" "${HEADERS}") +#find_package(MyLib QUIET) +#armarx_build_if(MyLib_FOUND "MyLib not available") +# all target_include_directories must be guarded by if(Xyz_FOUND) +# for multiple libraries write: if(X_FOUND AND Y_FOUND).... +#if(MyLib_FOUND) +# target_include_directories(FrameTracking PUBLIC ${MyLib_INCLUDE_DIRS}) +#endif() + # add unit tests add_subdirectory(test) diff --git a/source/RobotAPI/components/FrameTracking/FrameTracking.cpp b/source/RobotAPI/components/FrameTracking/FrameTracking.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d57f358307e245e560117783481fb5993e9349f1 --- /dev/null +++ b/source/RobotAPI/components/FrameTracking/FrameTracking.cpp @@ -0,0 +1,544 @@ +/* + * 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::FrameTracking + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "FrameTracking.h" + +#include <ArmarXCore/interface/core/BasicVectorTypesHelpers.h> +#include <ArmarXCore/observers/variant/DatafieldRef.h> + +#include <ArmarXGui/libraries/RemoteGui/WidgetBuilder.h> + +#include <RobotAPI/components/units/KinematicUnitObserver.h> + +#include <time.h> + + +using namespace armarx; + + +void FrameTracking::onInitComponent() +{ + usingProxy(getProperty<std::string>("RobotStateComponentName").getValue()); + usingProxy(getProperty<std::string>("KinematicUnitName").getValue()); + usingProxy(getProperty<std::string>("KinematicUnitObserverName").getValue()); + if (!getProperty<std::string>("RemoteGuiName").getValue().empty()) + { + usingProxy(getProperty<std::string>("RemoteGuiName").getValue()); + } + + enabled = false; + frameName = getProperty<std::string>("FrameOnStartup").getValue(); + + maxYawVelocity = getProperty<float>("MaxYawVelocity").getValue(); + yawAcceleration = getProperty<float>("YawAcceleration").getValue(); + + maxPitchVelocity = getProperty<float>("MaxPitchVelocity").getValue(); + pitchAcceleration = getProperty<float>("PitchAcceleration").getValue(); +} + + +void FrameTracking::onConnectComponent() +{ + robotStateComponent = getProxy<RobotStateComponentInterfacePrx>(getProperty<std::string>("RobotStateComponentName").getValue()); + kinematicUnitInterfacePrx = getProxy<KinematicUnitInterfacePrx>(getProperty<std::string>("KinematicUnitName").getValue()); + kinematicUnitObserverInterfacePrx = getProxy<KinematicUnitObserverInterfacePrx>(getProperty<std::string>("KinematicUnitObserverName").getValue()); + + localRobot = armarx::RemoteRobot::createLocalClone(robotStateComponent); + headYawJoint = localRobot->getRobotNode(getProperty<std::string>("HeadYawJoint").getValue()); + if (!headYawJoint || !(headYawJoint->isRotationalJoint() || headYawJoint->isTranslationalJoint())) + { + ARMARX_ERROR << getProperty<std::string>("HeadYawJoint").getValue() << " is not a valid joint."; + getArmarXManager()->asyncShutdown(); + } + headPitchJoint = localRobot->getRobotNode(getProperty<std::string>("HeadPitchJoint").getValue()); + if (!headPitchJoint || !(headPitchJoint->isRotationalJoint() || headPitchJoint->isTranslationalJoint())) + { + ARMARX_ERROR << getProperty<std::string>("HeadPitchJoint").getValue() << " is not a valid joint."; + getArmarXManager()->asyncShutdown(); + } + cameraNode = localRobot->getRobotNode(getProperty<std::string>("CameraNode").getValue()); + if (!cameraNode) + { + ARMARX_ERROR << getProperty<std::string>("CameraNode").getValue() << " is not a valid node."; + getArmarXManager()->asyncShutdown(); + } + + processorTask = new PeriodicTask<FrameTracking>(this, &FrameTracking::process, 30); + _enableTracking(getProperty<bool>("EnableTrackingOnStartup").getValue()); + + if (!getProperty<std::string>("RemoteGuiName").getValue().empty()) + { + _remoteGui = getProxy<RemoteGuiInterfacePrx>(getProperty<std::string>("RemoteGuiName").getValue()); + RemoteGui::detail::VBoxLayoutBuilder rootLayoutBuilder = RemoteGui::makeVBoxLayout(); + + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel("Tracking: ")) + .addChild(RemoteGui::makeTextLabel("Enabled")) + .addChild(RemoteGui::makeCheckBox("enabled").value(enabled)) + .addChild(RemoteGui::makeTextLabel("Frame")) + .addChild( + RemoteGui::makeComboBox("tracking_frame") + .options(localRobot->getRobotNodeNames()) + .addOptions({""}) + .value(frameName) + ) + ); + + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel("Look at frame: ")) + .addChild( + RemoteGui::makeComboBox("frame_look") + .options(localRobot->getRobotNodeNames()) + .value(frameName) + ) + .addChild(RemoteGui::makeButton("button_look_at_frame").label("look at")) + ); + + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel("Look at global point: ")) + .addChild(RemoteGui::makeFloatSpinBox("global_point_x").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("global_point_y").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("global_point_z").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeButton("button_look_at_global_point").label("look at")) + ); + + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel("Look at point in robot frame: ")) + .addChild(RemoteGui::makeFloatSpinBox("robot_point_x").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("robot_point_y").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("robot_point_z").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeButton("button_look_at_robot_point").label("look at")) + ); + + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel("Scan: ")) + .addChild(RemoteGui::makeTextLabel("yaw from ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_configuration_space_yaw_from").min(headYawJoint->getJointLimitLo()).max(headYawJoint->getJointLimitHi()).steps(static_cast<int>((headYawJoint->getJointLimitHi() - headYawJoint->getJointLimitLo()) / 0.1))) + .addChild(RemoteGui::makeTextLabel("yaw to ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_configuration_space_yaw_to").min(headYawJoint->getJointLimitLo()).max(headYawJoint->getJointLimitHi()).steps(static_cast<int>((headYawJoint->getJointLimitHi() - headYawJoint->getJointLimitLo()) / 0.1))) + .addChild(RemoteGui::makeTextLabel("pitch ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_configuration_space_pitch").min(headPitchJoint->getJointLimitLo()).max(headPitchJoint->getJointLimitHi()).steps(static_cast<int>((headPitchJoint->getJointLimitHi() - headPitchJoint->getJointLimitLo()) / 0.1))) + .addChild(RemoteGui::makeTextLabel("velocity ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_configuration_space_velocity").min(0).max(6).steps(static_cast<int>(6 / 0.1)).value(0.8f)) + .addChild(RemoteGui::makeButton("button_scan_in_configuration_space").label("scan")) + ); + + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel("Scan: ")) + .addChild(RemoteGui::makeTextLabel("from ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_from_x").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_from_y").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_from_z").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeTextLabel("to ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_to_x").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_to_y").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_to_z").min(-1000000000).max(1000000000).steps(2 * 1000000000 / 10).value(0.f)) + .addChild(RemoteGui::makeTextLabel("velocity ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_velocity").min(0).max(6).steps(static_cast<int>(6 / 0.1)).value(0.8f)) + .addChild(RemoteGui::makeTextLabel("acceleration ")) + .addChild(RemoteGui::makeFloatSpinBox("scan_in_workspace_acceleration").min(0).max(8).steps(static_cast<int>(8 / 0.1)).value(4.0f)) + .addChild(RemoteGui::makeButton("button_scan_in_workspace").label("scan")) + ); + + rootLayoutBuilder.addChild(new RemoteGui::VSpacer()); + + _guiTask = new SimplePeriodicTask<>([this]() + { + bool oldEnabledGui = _guiTab.getValue<bool>("enabled").get(); + std::string oldFrameGui = _guiTab.getValue<std::string>("tracking_frame").get(); + + _guiTab.receiveUpdates(); + + if (oldEnabledGui == enabled) + { + // only apply changes of gui if not already changed by ice + _enableTracking(_guiTab.getValue<bool>("enabled").get()); + } + _guiTab.getValue<bool>("enabled").set(enabled); + + if (oldFrameGui == frameName && oldFrameGui != _guiTab.getValue<std::string>("tracking_frame").get()) + { + // only apply changes of gui if not already changed by ice + setFrame(_guiTab.getValue<std::string>("tracking_frame").get()); + } + _guiTab.getValue<std::string>("tracking_frame").set(frameName); + + _guiTab.sendUpdates(); + + if (_guiTab.getButton("button_look_at_frame").clicked()) + { + lookAtFrame(_guiTab.getValue<std::string>("frame_look").get()); + } + + if (_guiTab.getButton("button_look_at_global_point").clicked()) + { + lookAtPointInGlobalFrame(Vector3f{_guiTab.getValue<float>("global_point_x").get(), + _guiTab.getValue<float>("global_point_y").get(), + _guiTab.getValue<float>("global_point_z").get()}); + } + + if (_guiTab.getButton("button_look_at_robot_point").clicked()) + { + lookAtPointInRobotFrame(Vector3f{_guiTab.getValue<float>("robot_point_x").get(), + _guiTab.getValue<float>("robot_point_y").get(), + _guiTab.getValue<float>("robot_point_z").get()}); + } + + if (_guiTab.getButton("button_scan_in_configuration_space").clicked()) + { + scanInConfigurationSpace(_guiTab.getValue<float>("scan_in_configuration_space_yaw_from").get(), + _guiTab.getValue<float>("scan_in_configuration_space_yaw_to").get(), + {_guiTab.getValue<float>("scan_in_configuration_space_pitch").get()}, + _guiTab.getValue<float>("scan_in_configuration_space_velocity").get()); + } + + if (_guiTab.getButton("button_scan_in_workspace").clicked()) + { + scanInWorkspace( + { + { + _guiTab.getValue<float>("scan_in_workspace_from_x").get(), + _guiTab.getValue<float>("scan_in_workspace_from_y").get(), + _guiTab.getValue<float>("scan_in_workspace_from_z").get() + }, + { + _guiTab.getValue<float>("scan_in_workspace_to_x").get(), + _guiTab.getValue<float>("scan_in_workspace_to_y").get(), + _guiTab.getValue<float>("scan_in_workspace_to_z").get() + } + }, + _guiTab.getValue<float>("scan_in_workspace_velocity").get(), + _guiTab.getValue<float>("scan_in_workspace_acceleration").get()); + } + }, 10); + + RemoteGui::WidgetPtr rootLayout = rootLayoutBuilder; + + _remoteGui->createTab(getName(), rootLayout); + _guiTab = RemoteGui::TabProxy(_remoteGui, getName()); + + _guiTask->start(); + } +} + + +void FrameTracking::onDisconnectComponent() +{ + _enableTracking(false); + if (_guiTask) + { + _guiTask->stop(); + _guiTask = nullptr; + } +} + + +void FrameTracking::onExitComponent() +{ + +} + +armarx::PropertyDefinitionsPtr FrameTracking::createPropertyDefinitions() +{ + return armarx::PropertyDefinitionsPtr(new FrameTrackingPropertyDefinitions( + getConfigIdentifier())); +} + +void FrameTracking::enableTracking(bool enable, const Ice::Current&) +{ + _enableTracking(enable); +} + +void FrameTracking::setFrame(const std::string& frameName, const Ice::Current&) +{ + if (enabled) + { + ARMARX_WARNING << "Disable tracking to set new frame."; + return; + } + this->frameName = frameName; +} + +std::string FrameTracking::getFrame(const Ice::Current&) const +{ + return frameName; +} + +void FrameTracking::lookAtFrame(const std::string& frameName, const Ice::Current&) +{ + if (enabled) + { + ARMARX_WARNING << "Disable tracking to use lookAt functions."; + return; + } + if (!localRobot->hasRobotNode(frameName)) + { + ARMARX_ERROR << frameName << " does not exist."; + return; + } + syncronizeLocalClone(); + _lookAtFrame(frameName); +} + +void FrameTracking::lookAtPointInGlobalFrame(const Vector3f& point, const Ice::Current&) +{ + if (enabled) + { + ARMARX_WARNING << "Disable tracking to use lookAt functions."; + return; + } + syncronizeLocalClone(); + _lookAtPoint(localRobot->toLocalCoordinateSystemVec(ToEigen(point))); +} + +void FrameTracking::lookAtPointInRobotFrame(const Vector3f& point, const Ice::Current&) +{ + if (enabled) + { + ARMARX_WARNING << "Disable tracking to use lookAt functions."; + return; + } + syncronizeLocalClone(); + _lookAtPoint(ToEigen(point)); +} + +void FrameTracking::scanInConfigurationSpace(float yawFrom, float yawTo, const Ice::FloatSeq& pitchValues, float velocity, const Ice::Current&) +{ + if (enabled) + { + ARMARX_WARNING << "Disable tracking to use scan functions."; + return; + } + velocity = std::abs(velocity); + + syncronizeLocalClone(); + auto currentModes = kinematicUnitInterfacePrx->getControlModes(); + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), ControlMode::eVelocityControl}, {headPitchJoint->getName(), ControlMode::eVelocityControl}}); + + // to initial yaw + { + float yawVelocityToInit = (headYawJoint->getJointValue() > yawFrom) ? -velocity : velocity; + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), yawVelocityToInit}, {headPitchJoint->getName(), 0.f}}); + while (std::abs(headYawJoint->getJointValue() - yawFrom) > static_cast<float>(M_PI / 180.)) + { + syncronizeLocalClone(); + } + } + + for (const auto& p : pitchValues) + { + // to pitch value + float velocityPitch = headPitchJoint->getJointValue() < p ? velocity : -velocity; + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), 0.f}, {headPitchJoint->getName(), velocityPitch}}); + while (std::abs(headPitchJoint->getJointValue() - p) > static_cast<float>(M_PI / 180.)) + { + syncronizeLocalClone(); + } + + // to yaw value + float velocityYaw = yawFrom < yawTo ? velocity : -velocity; + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), velocityYaw}, {headPitchJoint->getName(), 0.f}}); + while (std::abs(headYawJoint->getJointValue() - yawTo) > static_cast<float>(M_PI / 180.)) + { + syncronizeLocalClone(); + } + + std::swap(yawFrom, yawTo); + } + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), 0.f}, {headPitchJoint->getName(), 0.f}}); + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), currentModes[headYawJoint->getName()]}, {headPitchJoint->getName(), currentModes[headPitchJoint->getName()]}}); +} + +void FrameTracking::scanInWorkspace(const Vector3fSeq& points, float maxVelocity, float acceleration, const Ice::Current&) +{ + if (enabled) + { + ARMARX_WARNING << "Disable tracking to use scan functions."; + return; + } + syncronizeLocalClone(); + auto currentModes = kinematicUnitInterfacePrx->getControlModes(); + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), ControlMode::eVelocityControl}, {headPitchJoint->getName(), ControlMode::eVelocityControl}}); + struct timespec req = {0, 30 * 1000000L}; + for (const auto& p : points) + { + auto pEigen = localRobot->toLocalCoordinateSystemVec(ToEigen(p)); + auto target = _calculateJointAngles(pEigen); + while (std::abs(target.currentYawPos - target.desiredYawPos) > static_cast<float>(M_PI / 180.) || + std::abs(target.currentPitchPos - target.desiredPitchPos) > static_cast<float>(M_PI / 180.)) + { + ARMARX_INFO << "yaw: " << target.currentYawPos << " - " << target.desiredYawPos << " pitch: " << target.currentPitchPos << " - " << target.desiredPitchPos; + syncronizeLocalClone(); + target = _calculateJointAngles(pEigen); + _doVelocityControl(target, maxVelocity, acceleration, maxVelocity, acceleration); + // sleep for 30 milliseconds + nanosleep(&req, nullptr); + } + } + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), 0.f}, {headPitchJoint->getName(), 0.f}}); + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), currentModes[headYawJoint->getName()]}, {headPitchJoint->getName(), currentModes[headPitchJoint->getName()]}}); +} + +void FrameTracking::process() +{ + if (!localRobot->hasRobotNode(frameName)) + { + ARMARX_ERROR << frameName << " does not exist. Task will be disabled."; + std::thread([this]() + { + _enableTracking(false); + }).detach(); + return; + } + syncronizeLocalClone(); + _doVelocityControl(_calculateJointAnglesContinously(frameName), maxYawVelocity, yawAcceleration, maxPitchVelocity, pitchAcceleration); +} + +void FrameTracking::syncronizeLocalClone() +{ + armarx::RemoteRobot::synchronizeLocalClone(localRobot, robotStateComponent); +} + +void FrameTracking::_lookAtFrame(const std::string& frameName) +{ + auto frame = localRobot->getRobotNode(frameName); + auto posInRobotFrame = localRobot->toLocalCoordinateSystemVec(frame->getGlobalPosition()); + _lookAtPoint(posInRobotFrame); +} + +void FrameTracking::_lookAtPoint(const Eigen::Vector3f& point) +{ + _doPositionControl(_calculateJointAngles(point)); +} + +FrameTracking::HeadState FrameTracking::_calculateJointAnglesContinously(const std::string& frameName) +{ + auto frame = localRobot->getRobotNode(frameName); + auto posInRobotFrame = localRobot->toLocalCoordinateSystemVec(frame->getGlobalPosition()); + // do nothing if the robot works above his head + // he should already look upwards because if this component runs continously + if (std::sqrt(posInRobotFrame.x()*posInRobotFrame.x() + posInRobotFrame.y()*posInRobotFrame.y()) < 300.f) + { + return FrameTracking::HeadState{true, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; + } + return _calculateJointAngles(posInRobotFrame); +} + +FrameTracking::HeadState FrameTracking::_calculateJointAngles(const Eigen::Vector3f& point) +{ + float yaw = -std::atan2(point.x(), point.y()); + // make shure the joint value satisfies the joint limits + yaw = std::max(headYawJoint->getJointLimitLo(), yaw); + yaw = std::min(headYawJoint->getJointLimitHi(), yaw); + // we dont want the robot to move from one limit to the other in one step + const float currentYaw = headYawJoint->getJointValue(); + if (!headYawJoint->isLimitless() && std::abs(currentYaw - yaw) > headYawJoint->getJointLimitHi() - headYawJoint->getJointLimitLo() - static_cast<float>(M_PI) / 8) + { + yaw = currentYaw; + } + + const auto pointInPitchJointFrame = headPitchJoint->toLocalCoordinateSystemVec(localRobot->toGlobalCoordinateSystemVec(point)); + const Eigen::Vector2f pj{pointInPitchJointFrame.y(), pointInPitchJointFrame.z()}; + const float headHeightRealativeToPitchJoint = headPitchJoint->toLocalCoordinateSystemVec(cameraNode->getGlobalPosition()).z(); + float pitch = headPitchJoint->getJointValue() - std::asin(pj.x() / pj.norm()) + std::asin(headHeightRealativeToPitchJoint / pj.norm()); + // make shure the joint value satisfies the joint limits + pitch = std::max(headPitchJoint->getJointLimitLo(), pitch); + pitch = std::min(headPitchJoint->getJointLimitHi(), pitch); + const float currentPitch = headPitchJoint->getJointValue(); + + ARMARX_INFO << deactivateSpam(1.f, "FrameTracking") << "Looking at " << point << " using yaw=" << yaw << " and pitch=" << pitch; + + const float currentYawVel = DatafieldRefPtr::dynamicCast(kinematicUnitObserverInterfacePrx->getDatafieldRefByName("jointvelocities", headYawJoint->getName()))->getFloat(); + const float currentPitchVel = DatafieldRefPtr::dynamicCast(kinematicUnitObserverInterfacePrx->getDatafieldRefByName("jointvelocities", headPitchJoint->getName()))->getFloat(); + + FrameTracking::HeadState headState; + headState.currentYawPos = currentYaw; + headState.desiredYawPos = yaw; + headState.currentYawVel = currentYawVel; + headState.currentPitchPos = currentPitch; + headState.desiredPitchPos = pitch; + headState.currentPitchVel = currentPitchVel; + return headState; +} + +void FrameTracking::_doVelocityControl(const FrameTracking::HeadState& headState, float maxYawVelocity, float yawAcceleration, float maxPitchVelocity, float pitchAcceleration) +{ + if (headState.stop) + { + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), 0.f}, {headPitchJoint->getName(), 0.f}}); + return; + } + + float desiredYawVelocity = positionThroughVelocityControlWithAccelerationBounds( + 30.f / 1000, 35.f / 1000, + headState.currentYawVel, maxYawVelocity, + yawAcceleration, yawAcceleration, + headState.currentYawPos, headState.desiredYawPos, + 1.f); + float desiredPitchVelocity = positionThroughVelocityControlWithAccelerationBounds( + 30.f / 1000, 35.f / 1000, + headState.currentPitchVel, maxPitchVelocity, + pitchAcceleration, pitchAcceleration, + headState.currentPitchPos, headState.desiredPitchPos, + 1.f); + + // control mode is set when enable task + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), desiredYawVelocity}, {headPitchJoint->getName(), desiredPitchVelocity}}); +} + +void FrameTracking::_doPositionControl(const FrameTracking::HeadState& headState) +{ + auto currentModes = kinematicUnitInterfacePrx->getControlModes(); + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), ControlMode::ePositionControl}, {headPitchJoint->getName(), ControlMode::ePositionControl}}); + if (headState.stop) + { + return; + } + kinematicUnitInterfacePrx->setJointAngles({{headYawJoint->getName(), headState.desiredYawPos}, {headPitchJoint->getName(), headState.desiredPitchPos}}); + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), currentModes[headYawJoint->getName()]}, {headPitchJoint->getName(), currentModes[headPitchJoint->getName()]}}); +} + +void FrameTracking::_enableTracking(bool enable) +{ + if (this->enabled == enable) + { + return; + } + this->enabled = enable; + if (enable) + { + kinematicUnitInterfacePrx->switchControlMode({{headYawJoint->getName(), ControlMode::eVelocityControl}, {headPitchJoint->getName(), ControlMode::eVelocityControl}}); + processorTask->start(); + } + else + { + kinematicUnitInterfacePrx->setJointVelocities({{headYawJoint->getName(), 0.f}, {headPitchJoint->getName(), 0.f}}); + processorTask->stop(); + } +} + diff --git a/source/RobotAPI/components/FrameTracking/FrameTracking.h b/source/RobotAPI/components/FrameTracking/FrameTracking.h new file mode 100644 index 0000000000000000000000000000000000000000..c8fc80e595ac71a6b55a96dfc435b3ce2a1e2091 --- /dev/null +++ b/source/RobotAPI/components/FrameTracking/FrameTracking.h @@ -0,0 +1,178 @@ +/* + * 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::FrameTracking + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + + +#include <ArmarXCore/core/Component.h> +#include <ArmarXCore/core/services/tasks/PeriodicTask.h> +#include <ArmarXCore/core/services/tasks/TaskUtil.h> + +#include <RobotAPI/interface/components/FrameTrackingInterface.h> +#include <RobotAPI/interface/core/RobotState.h> +#include <RobotAPI/interface/units/KinematicUnitInterface.h> +#include <RobotAPI/interface/observers/KinematicUnitObserverInterface.h> +#include <RobotAPI/libraries/core/remoterobot/RemoteRobot.h> +#include <RobotAPI/components/units/RobotUnit/BasicControllers.h> + +#include <ArmarXGui/interface/RemoteGuiInterface.h> +#include <ArmarXGui/libraries/RemoteGui/WidgetProxy.h> + +#include <Eigen/Core> + +namespace armarx +{ + /** + * @class FrameTrackingPropertyDefinitions + * @brief + */ + class FrameTrackingPropertyDefinitions: + public armarx::ComponentPropertyDefinitions + { + public: + FrameTrackingPropertyDefinitions(std::string prefix): + armarx::ComponentPropertyDefinitions(prefix) + { + defineOptionalProperty<std::string>("RobotStateComponentName", "RobotStateComponent", "Name of the robot state component that should be used"); + defineOptionalProperty<std::string>("KinematicUnitName", "KinematicUnit", "Name of the kinematic unit that should be used"); + defineOptionalProperty<std::string>("KinematicUnitObserverName", "KinematicUnitObserver", "Name of the kinematic unit observer that should be used"); + defineRequiredProperty<std::string>("HeadYawJoint", "Name of the yaw joint of the head."); + defineRequiredProperty<std::string>("HeadPitchJoint", "Name of the pitch joint of the head."); + defineRequiredProperty<std::string>("CameraNode", "Name of the camera node."); + defineOptionalProperty<bool>("EnableTrackingOnStartup", true, "Start Tracking on startup. This needs a frame to be defined."); + defineOptionalProperty<std::string>("FrameOnStartup", "", "Name of the frame that should be tracked."); + defineOptionalProperty<std::string>("RemoteGuiName", "RemoteGuiProvider", "Name of the remote gui. Remote gui is disabled if empty."); + defineOptionalProperty<float>("MaxYawVelocity", 0.8f, "The maximum velocity the yaw joint while tracking objects."); + defineOptionalProperty<float>("YawAcceleration", 4.f, "The acceleration of the yaw joint while tracking objects."); + defineOptionalProperty<float>("MaxPitchVelocity", 0.8f, "The maximum velocity the pitch joint while tracking objects."); + defineOptionalProperty<float>("PitchAcceleration", 4.f, "The acceleration of the pitch joint while tracking objects."); + } + }; + + /** + * @defgroup Component-FrameTracking FrameTracking + * @ingroup RobotAPI-Components + * A description of the component FrameTracking. + * + * @class FrameTracking + * @ingroup Component-FrameTracking + * @brief Brief description of class FrameTracking. + * + * Detailed description of class FrameTracking. + */ + class FrameTracking : + virtual public armarx::Component, + public FrameTrackingInterface + { + private: + struct HeadState + { + bool stop = false; + float currentYawPos; + float currentYawVel; + float desiredYawPos; + float currentPitchPos; + float currentPitchVel; + float desiredPitchPos; + }; + public: + /** + * @see armarx::ManagedIceObject::getDefaultName() + */ + std::string getDefaultName() const override + { + return "FrameTracking"; + } + + protected: + /** + * @see armarx::ManagedIceObject::onInitComponent() + */ + void onInitComponent() override; + + /** + * @see armarx::ManagedIceObject::onConnectComponent() + */ + void onConnectComponent() override; + + /** + * @see armarx::ManagedIceObject::onDisconnectComponent() + */ + void onDisconnectComponent() override; + + /** + * @see armarx::ManagedIceObject::onExitComponent() + */ + void onExitComponent() override; + + /** + * @see PropertyUser::createPropertyDefinitions() + */ + armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; + + // FrameTrackingInterface interface + public: + void enableTracking(bool enable, const Ice::Current& = Ice::emptyCurrent) override; + void setFrame(const std::string& frameName, const Ice::Current& = Ice::emptyCurrent) override; + std::string getFrame(const Ice::Current& = Ice::emptyCurrent) const override; + + void lookAtFrame(const std::string& frameName, const Ice::Current& = Ice::emptyCurrent) override; + void lookAtPointInGlobalFrame(const Vector3f& point, const Ice::Current& = Ice::emptyCurrent) override; + void lookAtPointInRobotFrame(const Vector3f& point, const Ice::Current& = Ice::emptyCurrent) override; + + void scanInConfigurationSpace(float yawFrom, float yawTo, const ::Ice::FloatSeq& pitchValues, float velocity, const Ice::Current& = Ice::emptyCurrent) override; + void scanInWorkspace(const ::armarx::Vector3fSeq& points, float maxVelocity, float acceleration, const Ice::Current& = Ice::emptyCurrent) override; + + protected: + void process(); + + void syncronizeLocalClone(); + void _lookAtFrame(const std::string& frame); + void _lookAtPoint(const Eigen::Vector3f& point); + HeadState _calculateJointAnglesContinously(const std::string& frame); + HeadState _calculateJointAngles(const Eigen::Vector3f& point); + void _doVelocityControl(const HeadState& headstate, float maxYawVelocity, float yawAcceleration, float maxPitchVelocity, float pitchAcceleration); + void _doPositionControl(const HeadState& headstate); + void _enableTracking(bool enable); + + RobotStateComponentInterfacePrx robotStateComponent; + KinematicUnitInterfacePrx kinematicUnitInterfacePrx;// send commands to kinematic unit + PeriodicTask<FrameTracking>::pointer_type processorTask; + KinematicUnitObserverInterfacePrx kinematicUnitObserverInterfacePrx; + + std::atomic<bool> enabled; + std::string frameName; + float maxYawVelocity; + float yawAcceleration; + float maxPitchVelocity; + float pitchAcceleration; + + VirtualRobot::RobotPtr localRobot; + VirtualRobot::RobotNodePtr headYawJoint; + VirtualRobot::RobotNodePtr headPitchJoint; + VirtualRobot::RobotNodePtr cameraNode; + + RemoteGuiInterfacePrx _remoteGui; + SimplePeriodicTask<>::pointer_type _guiTask; + RemoteGui::TabProxy _guiTab; + }; +} diff --git a/source/RobotAPI/components/FrameTracking/test/CMakeLists.txt b/source/RobotAPI/components/FrameTracking/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1d0500243d00099fe138fd0ccf1310dbd31c915d --- /dev/null +++ b/source/RobotAPI/components/FrameTracking/test/CMakeLists.txt @@ -0,0 +1,5 @@ + +# Libs required for the tests +SET(LIBS ${LIBS} ArmarXCore FrameTracking) + +armarx_add_test(FrameTrackingTest FrameTrackingTest.cpp "${LIBS}") \ No newline at end of file diff --git a/source/RobotAPI/components/ProsthesisObserver/test/ProsthesisObserverTest.cpp b/source/RobotAPI/components/FrameTracking/test/FrameTrackingTest.cpp similarity index 72% rename from source/RobotAPI/components/ProsthesisObserver/test/ProsthesisObserverTest.cpp rename to source/RobotAPI/components/FrameTracking/test/FrameTrackingTest.cpp index cd0633992dcf9ae131efbfe6c4e444bfbe95bd84..d7fbb0b3e6768337d4dd9084960f84e3f981f9ab 100644 --- a/source/RobotAPI/components/ProsthesisObserver/test/ProsthesisObserverTest.cpp +++ b/source/RobotAPI/components/FrameTracking/test/FrameTrackingTest.cpp @@ -13,25 +13,25 @@ * 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::ProsthesisObserver - * @author JuliaStarke ( julia dot starke at kit dot edu ) - * @date 2018 + * @package RobotAPI::ArmarXObjects::FrameTracking + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#define BOOST_TEST_MODULE RobotAPI::ArmarXObjects::ProsthesisObserver +#define BOOST_TEST_MODULE RobotAPI::ArmarXObjects::FrameTracking #define ARMARX_BOOST_TEST #include <RobotAPI/Test.h> -#include <RobotAPI/components/ProsthesisObserver/ProsthesisObserver.h> +#include <RobotAPI/components/FrameTracking/FrameTracking.h> #include <iostream> BOOST_AUTO_TEST_CASE(testExample) { - armarx::ProsthesisObserver instance; + armarx::FrameTracking instance; BOOST_CHECK_EQUAL(true, true); } diff --git a/source/RobotAPI/components/GamepadControlUnit/GamepadControlUnit.cpp b/source/RobotAPI/components/GamepadControlUnit/GamepadControlUnit.cpp index 7d29b0a42699884b5f5d1aba07e5c534269030cf..f4e94fa036c7e0ac2c340d717802d71bd8644817 100644 --- a/source/RobotAPI/components/GamepadControlUnit/GamepadControlUnit.cpp +++ b/source/RobotAPI/components/GamepadControlUnit/GamepadControlUnit.cpp @@ -70,7 +70,14 @@ armarx::PropertyDefinitionsPtr GamepadControlUnit::createPropertyDefinitions() void GamepadControlUnit::reportGamepadState(const std::string& device, const std::string& name, const GamepadData& data, const TimestampBasePtr& timestamp, const Ice::Current& c) { //scales are for the robot axis - platformUnitPrx->move(data.leftStickY * scaleX, data. leftStickX * scaleY, data.rightStickX * scaleRotation); + if (data.rightTrigger > 0) + { + platformUnitPrx->move(data.leftStickY * scaleX, data. leftStickX * scaleY, data.rightStickX * scaleRotation); + } + else + { + platformUnitPrx->move(0, 0, 0); + } //ARMARX_INFO << "sending targets" << data.leftStickX* scaleX << " " << data.leftStickY* scaleY << " " << data.rightStickX* scaleRotation; } diff --git a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp index 549629821483ac7a3060f9312c28e1d7180e6c3b..f7bc0e29d1593cb0c481acf61fd8c331ac62a166 100644 --- a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp +++ b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp @@ -25,6 +25,8 @@ #include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXGui/libraries/RemoteGui/WidgetBuilder.h> + #include "KITProstheticHandUnit.h" using namespace armarx; @@ -50,6 +52,91 @@ void KITProstheticHandUnit::onInitHandUnit() addShapeName("G6"); addShapeName("G7"); addShapeName("G8"); + + offeringTopic(getProperty<std::string>("DebugObserverName")); + if (!getProperty<std::string>("RemoteGuiName").getValue().empty()) + { + usingProxy(getProperty<std::string>("RemoteGuiName")); + } +} + +void KITProstheticHandUnit::onStartHandUnit() +{ + _debugObserver = getTopic<DebugObserverInterfacePrx>(getProperty<std::string>("DebugObserverName")); + if (!getProperty<std::string>("RemoteGuiName").getValue().empty()) + { + _remoteGuiPrx = getProxy<RemoteGuiInterfacePrx>(getProperty<std::string>("RemoteGuiName").getValue()); + + + RemoteGui::detail::VBoxLayoutBuilder rootLayoutBuilder = RemoteGui::makeVBoxLayout(); + + auto addFinger = [&](std::string name, float min, float max, float val, int steps) + { + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel(name)) + .addChild(RemoteGui::makeTextLabel("min " + std::to_string(min))) + .addChild(RemoteGui::makeFloatSlider(name).min(min).max(max).value(val).steps(steps)) + .addChild(RemoteGui::makeTextLabel("max " + std::to_string(max))) + ); + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel(name + " Pos ")) + .addChild(RemoteGui::makeLabel(name + "_pos").value("0")) + .addChild(new RemoteGui::HSpacer()) + ); + rootLayoutBuilder.addChild( + RemoteGui::makeHBoxLayout() + .addChild(RemoteGui::makeTextLabel(name + " PWM ")) + .addChild(RemoteGui::makeLabel(name + "_pwm").value("0")) + .addChild(new RemoteGui::HSpacer()) + ); + }; + + addFinger("Thumb", 0, 1, _lastGuiValueThumb, _driver->getMaxPosThumb()); + addFinger("Fingers", 0, 1, _lastGuiValueFingers, _driver->getMaxPosFingers()); + + rootLayoutBuilder.addChild(new RemoteGui::VSpacer()); + + _guiTask = new SimplePeriodicTask<>([&]() + { + _guiTab.receiveUpdates(); + _driver->getMaxPosThumb(); + const float t = _guiTab.getValue<float>("Thumb").get(); + const float f = _guiTab.getValue<float>("Fingers").get(); + + bool updateT = t != _lastGuiValueThumb; + bool updateF = f != _lastGuiValueFingers; + _lastGuiValueThumb = t; + _lastGuiValueFingers = f; + + if (updateT && updateF) + { + setJointAngles({{"Thumb", t}, {"Fingers", f}}); + } + else if (updateT) + { + setJointAngles({{"Thumb", t}}); + } + else if (updateF) + { + setJointAngles({{"Fingers", f}}); + } + + _guiTab.getValue<std::string>("Thumb_pos").set(std::to_string(_driver->getThumbPos())); + _guiTab.getValue<std::string>("Thumb_pwm").set(std::to_string(_driver->getThumbPWM())); + _guiTab.getValue<std::string>("Fingers_pos").set(std::to_string(_driver->getFingerPos())); + _guiTab.getValue<std::string>("Fingers_pwm").set(std::to_string(_driver->getFingerPWM())); + _guiTab.sendUpdates(); + }, 10); + + RemoteGui::WidgetPtr rootLayout = rootLayoutBuilder; + + _remoteGuiPrx->createTab("KITProstheticHandUnit", rootLayout); + _guiTab = RemoteGui::TabProxy(_remoteGuiPrx, "KITProstheticHandUnit"); + + _guiTask->start(); + } } void KITProstheticHandUnit::onExitHandUnit() @@ -65,7 +152,7 @@ void KITProstheticHandUnit::setJointAngles(const NameValueMap& targetJointAngles { if (pair.first == "Fingers") { - _driver->sendFingerPWM(200, 2999, static_cast<std::uint64_t>(pair.second * _driver->maxPosF())); + _driver->sendFingerPWM(200, 2999, static_cast<std::uint64_t>(pair.second * _driver->getMaxPosFingers())); // fix until hw driver is fixed to handle multiple commands at the same time std::this_thread::sleep_for(std::chrono::milliseconds(100)); } @@ -85,7 +172,7 @@ void KITProstheticHandUnit::setJointAngles(const NameValueMap& targetJointAngles NameValueMap KITProstheticHandUnit::getCurrentJointValues(const Ice::Current&) { NameValueMap jointValues; - jointValues["Fingers"] = _driver->getFingerPos() * 1.f / _driver->maxPosF(); + jointValues["Fingers"] = _driver->getFingerPos() * 1.f / _driver->getMaxPosFingers(); jointValues["Thumb"] = _driver->getThumbPos() * 1.f / _driver->getMaxPosThumb(); return jointValues; } diff --git a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.h b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.h index 7b9c89a13d141076e1c8aabf8a1f66b6d0d5897e..1cba8b8eb3e34e163780298b3fda9af46c4f4985 100644 --- a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.h +++ b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.h @@ -22,8 +22,12 @@ #pragma once - #include <ArmarXCore/core/Component.h> +#include <ArmarXCore/core/services/tasks/TaskUtil.h> +#include <ArmarXCore/interface/observers/ObserverInterface.h> + +#include <ArmarXGui/interface/RemoteGuiInterface.h> +#include <ArmarXGui/libraries/RemoteGui/WidgetProxy.h> #include <RobotAPI/components/units/HandUnit.h> #include <RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h> @@ -42,7 +46,8 @@ namespace armarx armarx::HandUnitPropertyDefinitions(prefix) { defineRequiredProperty<std::string>("MAC", "The KIT Prosthetic Hand's Bluetooth MAC"); - //defineOptionalProperty<std::string>("PropertyName", "DefaultValue", "Description"); + defineOptionalProperty<std::string>("RemoteGuiName", "", "Name of the remote gui"); + defineOptionalProperty<std::string>("DebugObserverName", "DebugObserver", "Name of the debug observer that should be used"); } }; @@ -70,7 +75,7 @@ namespace armarx } void onInitHandUnit() override; - void onStartHandUnit() override {} + void onStartHandUnit() override; void onExitHandUnit() override; void setJointAngles(const NameValueMap& targetJointAngles, const Ice::Current& = Ice::emptyCurrent) override; NameValueMap getCurrentJointValues(const Ice::Current&) override; @@ -90,5 +95,13 @@ namespace armarx private: std::unique_ptr<BLEProthesisInterface> _driver; std::map<std::string, std::map<std::string, float>> _shapes; + + DebugObserverInterfacePrx _debugObserver; + //gui + RemoteGuiInterfacePrx _remoteGuiPrx; + SimplePeriodicTask<>::pointer_type _guiTask; + RemoteGui::TabProxy _guiTab; + float _lastGuiValueThumb{0}; + float _lastGuiValueFingers{0}; }; } diff --git a/source/RobotAPI/components/ProsthesisObserver/ProsthesisObserver.cpp b/source/RobotAPI/components/ProsthesisObserver/ProsthesisObserver.cpp deleted file mode 100644 index 45046d5d42b0b39ffacbd8001bf1e9bb7a9690d9..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/ProsthesisObserver/ProsthesisObserver.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::ProsthesisObserver - * @author JuliaStarke ( julia dot starke at kit dot edu ) - * @date 2018 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#include "ProsthesisObserver.h" -#include <ArmarXCore/observers/variant/TimestampVariant.h> - -using namespace armarx; - - -void ProsthesisObserver::onInitObserver() -{ - usingTopic(getProperty<std::string>("ProsthesisTopicName").getValue()); - offeringTopic(getProperty<std::string>("DebugDrawerTopic").getValue()); -} - - -void ProsthesisObserver::onConnectObserver() -{ - debugDrawerPrx = getTopic<DebugDrawerInterfacePrx>(getProperty<std::string>("DebugDrawerTopic").getValue()); -} - - -//void ProsthesisObserver::onDisconnectComponent() -//{ - -//} - - -void ProsthesisObserver::onExitObserver() -{ - -} - -PropertyDefinitionsPtr ProsthesisObserver::createPropertyDefinitions() -{ - return PropertyDefinitionsPtr(new ProsthesisObserverPropertyDefinitions( - getConfigIdentifier())); -} - -void ProsthesisObserver::reportMotorValues(const ProsthesisMotorValues& motorValues, const Ice::Current&) -{ - ScopedLock lock(dataMutex); - //TimestampVariantPtr timestampPtr = TimestampVariantPtr::dynamicCast(timestamp); - - std::string name = motorValues.name; - if (!existsChannel(name)) - { - offerChannel(name, "Prosthesis motor data"); - } - - offerOrUpdateDataField(name, "name", Variant(name), "Name of the prostesis"); - //offerOrUpdateDataField(name, "device", Variant(device), "Device name"); - offerOrUpdateDataField(name, "position1", Variant(motorValues.position1), "Position motor 2"); - offerOrUpdateDataField(name, "pwm1", Variant(motorValues.pwm1), "PWM motor 2"); - offerOrUpdateDataField(name, "position2", Variant(motorValues.position2), "Position motor 3"); - offerOrUpdateDataField(name, "pwm2", Variant(motorValues.pwm2), "PWM motor 3"); - //offerOrUpdateDataField(name, "timestamp", timestampPtr, "Timestamp"); - //offerOrUpdateDataField(name, "position", Vector2(position1, position2), "Position"); - //offerOrUpdateDataField(name, "pwm", Vector2(pwm1, pwm2), "PWM"); - - updateChannel(name); -} diff --git a/source/RobotAPI/components/ProsthesisObserver/ProsthesisObserver.h b/source/RobotAPI/components/ProsthesisObserver/ProsthesisObserver.h deleted file mode 100644 index 53f5109da36071e84fbf0838e9931abf6a21cdfd..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/ProsthesisObserver/ProsthesisObserver.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This file is part of ArmarX. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI::ArmarXObjects::ProsthesisObserver - * @author JuliaStarke ( julia dot starke at kit dot edu ) - * @date 2018 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#pragma once - - -//#include <ArmarXCore/core/Component.h> -//#include <RobotAPI/interface/units/ProsthesisInterface.h> -#include <RobotAPI/interface/units/ProsthesisObserverInterface.h> -#include <ArmarXCore/observers/Observer.h> -#include <RobotAPI/interface/visualization/DebugDrawerInterface.h> - -namespace armarx -{ - /** - * @class ProsthesisObserverPropertyDefinitions - * @brief - */ - class ProsthesisObserverPropertyDefinitions: - public armarx::ObserverPropertyDefinitions - { - public: - ProsthesisObserverPropertyDefinitions(std::string prefix): - armarx::ObserverPropertyDefinitions(prefix) - { - //defineRequiredProperty<std::string>("PropertyName", "Description"); - defineOptionalProperty<std::string>("ProsthesisTopicName", "ProsthesisMotorValues", "Name of the Prosthesis Topic"); - defineOptionalProperty<std::string>("DebugDrawerTopic", "DebugDrawerUpdates", "Name of the DebugDrawerTopic"); - } - }; - - /** - * @defgroup Component-ProsthesisObserver ProsthesisObserver - * @ingroup RobotAPI-Components - * A description of the component ProsthesisObserver. - * - * @class ProsthesisObserver - * @ingroup Component-ProsthesisObserver - * @brief Brief description of class ProsthesisObserver. - * - * Detailed description of class ProsthesisObserver. - */ - class ProsthesisObserver : - virtual public Observer, - virtual public ProsthesisObserverInterface - { - public: - ProsthesisObserver() {} - - /** - * @see armarx::ManagedIceObject::getDefaultName() - */ - std::string getDefaultName() const override - { - return "ProsthesisObserver"; - } - - protected: - /** - * @see armarx::ManagedIceObject::onInitComponent() - */ - void onInitObserver() override; - - /** - * @see armarx::ManagedIceObject::onConnectComponent() - */ - void onConnectObserver() override; - - /** - * @see armarx::ManagedIceObject::onDisconnectComponent() - */ - //void onDisconnectObserver() override; - - /** - * @see armarx::ManagedIceObject::onExitComponent() - */ - void onExitObserver() override; - - /** - * @see PropertyUser::createPropertyDefinitions() - */ - armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; - - private: - - Mutex dataMutex; - DebugDrawerInterfacePrx debugDrawerPrx; - - // ProsthesisListenerInterface interface - public: - void reportMotorValues(const ProsthesisMotorValues& motorValues, const Ice::Current&) override; - }; -} diff --git a/source/RobotAPI/components/ProsthesisObserver/test/CMakeLists.txt b/source/RobotAPI/components/ProsthesisObserver/test/CMakeLists.txt deleted file mode 100644 index d89897a05dbb8e3df8051cf1d45bf8d7f3e9e8ed..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/ProsthesisObserver/test/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ - -# Libs required for the tests -SET(LIBS ${LIBS} ArmarXCore ProsthesisObserver) - -armarx_add_test(ProsthesisObserverTest ProsthesisObserverTest.cpp "${LIBS}") \ No newline at end of file diff --git a/source/RobotAPI/components/RobotState/RobotStateComponent.cpp b/source/RobotAPI/components/RobotState/RobotStateComponent.cpp index 697a5c50c01f83a36eb9de3108ae183809eedf93..de0dde42b8e8670e6c3953405ad59993eca07460 100644 --- a/source/RobotAPI/components/RobotState/RobotStateComponent.cpp +++ b/source/RobotAPI/components/RobotState/RobotStateComponent.cpp @@ -116,7 +116,7 @@ namespace armarx { ARMARX_VERBOSE << "Node: " << node->getName() << endl; } - usingTopic(robotNodeSetName + "State"); + usingTopic(getProperty<std::string>("TopicPrefix").getValue() + robotNodeSetName + "State"); try { diff --git a/source/RobotAPI/components/RobotState/RobotStateComponent.h b/source/RobotAPI/components/RobotState/RobotStateComponent.h index 817743c6d9c4ccc5e1ae79af641afc7a0c6df5dd..661b9d3375240cf2d83e51f1b57819315dd574f5 100644 --- a/source/RobotAPI/components/RobotState/RobotStateComponent.h +++ b/source/RobotAPI/components/RobotState/RobotStateComponent.h @@ -57,6 +57,8 @@ namespace armarx defineOptionalProperty<std::string>("RobotStateReportingTopic", "RobotStateUpdates", "Name of the topic on which updates of the robot state are reported."); defineOptionalProperty<int>("HistoryLength", 10000, "Number of entries in the robot state history"); defineOptionalProperty<float>("RobotModelScaling", 1.0f, "Scaling of the robot model"); + defineOptionalProperty<std::string>("TopicPrefix", "", "Prefix for the sensor value topic name."); + } }; @@ -164,14 +166,14 @@ namespace armarx ~RobotStateComponent() override; // inherited from KinematicUnitInterface - void reportControlModeChanged(const NameControlModeMap& jointModes, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointAngles(const NameValueMap& jointAngles, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointVelocities(const NameValueMap& jointVelocities, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointTorques(const NameValueMap& jointTorques, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; + void reportControlModeChanged(const NameControlModeMap& jointModes, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointAngles(const NameValueMap& jointAngles, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointVelocities(const NameValueMap& jointVelocities, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointTorques(const NameValueMap& jointTorques, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; void reportJointAccelerations(const NameValueMap& jointAccelerations, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c) override; - void reportJointCurrents(const NameValueMap& jointCurrents, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointMotorTemperatures(const NameValueMap& jointMotorTemperatures, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointStatuses(const NameStatusMap& jointStatuses, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; + void reportJointCurrents(const NameValueMap& jointCurrents, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointMotorTemperatures(const NameValueMap& jointMotorTemperatures, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointStatuses(const NameStatusMap& jointStatuses, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; private: void readRobotInfo(const std::string& robotFile); diff --git a/source/RobotAPI/components/RobotState/SharedRobotServants.h b/source/RobotAPI/components/RobotState/SharedRobotServants.h index 9d4d60cf2e36fe6caa9b091cf58ad80c0a4b6cb5..922588ddbdbf15100fa4a3e358a5abd232de3546 100644 --- a/source/RobotAPI/components/RobotState/SharedRobotServants.h +++ b/source/RobotAPI/components/RobotState/SharedRobotServants.h @@ -53,9 +53,9 @@ namespace armarx virtual public SharedObjectInterface { public: - void ref(const Ice::Current& c = Ice::Current()) override; - void unref(const Ice::Current& c = Ice::Current()) override; - void destroy(const Ice::Current& c = Ice::Current()) override; + void ref(const Ice::Current& c = Ice::emptyCurrent) override; + void unref(const Ice::Current& c = Ice::emptyCurrent) override; + void destroy(const Ice::Current& c = Ice::emptyCurrent) override; SharedObjectBase(); private: unsigned int _referenceCount; @@ -74,32 +74,32 @@ namespace armarx public SharedObjectBase { public: - SharedRobotNodeServant(VirtualRobot::RobotNodePtr node /*,const Ice::Current & current = Ice::Current()*/); + SharedRobotNodeServant(VirtualRobot::RobotNodePtr node /*,const Ice::Current & current = Ice::emptyCurrent*/); ~SharedRobotNodeServant() override; - float getJointValue(const Ice::Current& current = Ice::Current()) const override; - std::string getName(const Ice::Current& current = Ice::Current()) const override; + float getJointValue(const Ice::Current& current = Ice::emptyCurrent) const override; + std::string getName(const Ice::Current& current = Ice::emptyCurrent) const override; - PoseBasePtr getLocalTransformation(const Ice::Current& current = Ice::Current()) const override; - FramedPoseBasePtr getGlobalPose(const Ice::Current& current = Ice::Current()) const override; - FramedPoseBasePtr getPoseInRootFrame(const Ice::Current& current = Ice::Current()) const override; + PoseBasePtr getLocalTransformation(const Ice::Current& current = Ice::emptyCurrent) const override; + FramedPoseBasePtr getGlobalPose(const Ice::Current& current = Ice::emptyCurrent) const override; + FramedPoseBasePtr getPoseInRootFrame(const Ice::Current& current = Ice::emptyCurrent) const override; - JointType getType(const Ice::Current& current = Ice::Current()) const override; - Vector3BasePtr getJointTranslationDirection(const Ice::Current& current = Ice::Current()) const override; - Vector3BasePtr getJointRotationAxis(const Ice::Current& current = Ice::Current()) const override; + JointType getType(const Ice::Current& current = Ice::emptyCurrent) const override; + Vector3BasePtr getJointTranslationDirection(const Ice::Current& current = Ice::emptyCurrent) const override; + Vector3BasePtr getJointRotationAxis(const Ice::Current& current = Ice::emptyCurrent) const override; - bool hasChild(const std::string& name, bool recursive, const Ice::Current& current = Ice::Current()) const override; - std::string getParent(const Ice::Current& current = Ice::Current()) const override; - NameList getChildren(const Ice::Current& current = Ice::Current()) const override; - NameList getAllParents(const std::string& name, const Ice::Current& current = Ice::Current()) const override; + bool hasChild(const std::string& name, bool recursive, const Ice::Current& current = Ice::emptyCurrent) const override; + std::string getParent(const Ice::Current& current = Ice::emptyCurrent) const override; + NameList getChildren(const Ice::Current& current = Ice::emptyCurrent) const override; + NameList getAllParents(const std::string& name, const Ice::Current& current = Ice::emptyCurrent) const override; - float getJointValueOffest(const Ice::Current& current = Ice::Current()) const override; - float getJointLimitHigh(const Ice::Current& current = Ice::Current()) const override; - float getJointLimitLow(const Ice::Current& current = Ice::Current()) const override; + float getJointValueOffest(const Ice::Current& current = Ice::emptyCurrent) const override; + float getJointLimitHigh(const Ice::Current& current = Ice::emptyCurrent) const override; + float getJointLimitLow(const Ice::Current& current = Ice::emptyCurrent) const override; - Vector3BasePtr getCoM(const Ice::Current& current = Ice::Current()) const override; - Ice::FloatSeq getInertia(const Ice::Current& current = Ice::Current()) const override; - float getMass(const Ice::Current& current = Ice::Current()) const override; + Vector3BasePtr getCoM(const Ice::Current& current = Ice::emptyCurrent) const override; + Ice::FloatSeq getInertia(const Ice::Current& current = Ice::emptyCurrent) const override; + float getMass(const Ice::Current& current = Ice::emptyCurrent) const override; @@ -123,29 +123,29 @@ namespace armarx SharedRobotServant(VirtualRobot::RobotPtr robot, RobotStateComponentInterfacePrx robotStateComponent, RobotStateListenerInterfacePrx robotStateListenerPrx); ~SharedRobotServant() override; void setRobotStateComponent(RobotStateComponentInterfacePrx robotStateComponent); - SharedRobotNodeInterfacePrx getRobotNode(const std::string& name, const Ice::Current& current = Ice::Current()) override; - SharedRobotNodeInterfacePrx getRootNode(const Ice::Current& current = Ice::Current()) override; - bool hasRobotNode(const std::string& name, const Ice::Current& current = Ice::Current()) override; + SharedRobotNodeInterfacePrx getRobotNode(const std::string& name, const Ice::Current& current = Ice::emptyCurrent) override; + SharedRobotNodeInterfacePrx getRootNode(const Ice::Current& current = Ice::emptyCurrent) override; + bool hasRobotNode(const std::string& name, const Ice::Current& current = Ice::emptyCurrent) override; - NameList getRobotNodes(const Ice::Current& current = Ice::Current()) override; - RobotNodeSetInfoPtr getRobotNodeSet(const std::string& name, const Ice::Current& current = Ice::Current()) override; - NameList getRobotNodeSets(const Ice::Current& current = Ice::Current()) override; - bool hasRobotNodeSet(const std::string& name, const Ice::Current& current = Ice::Current()) override; + NameList getRobotNodes(const Ice::Current& current = Ice::emptyCurrent) override; + RobotNodeSetInfoPtr getRobotNodeSet(const std::string& name, const Ice::Current& current = Ice::emptyCurrent) override; + NameList getRobotNodeSets(const Ice::Current& current = Ice::emptyCurrent) override; + bool hasRobotNodeSet(const std::string& name, const Ice::Current& current = Ice::emptyCurrent) override; - std::string getName(const Ice::Current& current = Ice::Current()) override; - std::string getType(const Ice::Current& current = Ice::Current()) override; + std::string getName(const Ice::Current& current = Ice::emptyCurrent) override; + std::string getType(const Ice::Current& current = Ice::emptyCurrent) override; - PoseBasePtr getGlobalPose(const Ice::Current& current = Ice::Current()) override; - NameValueMap getConfig(const Ice::Current& current = Ice::Current()) override; - NameValueMap getConfigAndPose(PoseBasePtr& globalPose, const Ice::Current& current = Ice::Current()) override; - void setGlobalPose(const PoseBasePtr& pose, const Ice::Current& current = Ice::Current()) override; + PoseBasePtr getGlobalPose(const Ice::Current& current = Ice::emptyCurrent) override; + NameValueMap getConfig(const Ice::Current& current = Ice::emptyCurrent) override; + NameValueMap getConfigAndPose(PoseBasePtr& globalPose, const Ice::Current& current = Ice::emptyCurrent) override; + void setGlobalPose(const PoseBasePtr& pose, const Ice::Current& current = Ice::emptyCurrent) override; - float getScaling(const Ice::Current& = Ice::Current()) override; + float getScaling(const Ice::Current& = Ice::emptyCurrent) override; VirtualRobot::RobotPtr getRobot() const; void setTimestamp(const IceUtil::Time& updateTime); - TimestampBasePtr getTimestamp(const Ice::Current& = Ice::Current()) const override; + TimestampBasePtr getTimestamp(const Ice::Current& = Ice::emptyCurrent) const override; RobotStateComponentInterfacePrx getRobotStateComponent(const Ice::Current&) const override; protected: VirtualRobot::RobotPtr _robot; diff --git a/source/RobotAPI/components/ViewSelection/ViewSelection.h b/source/RobotAPI/components/ViewSelection/ViewSelection.h index 414f566f4936a2536e3b82d0c3a72912cdd9d5cf..f8e6cbdc93a26500fef455bc989b2149bd1554b4 100644 --- a/source/RobotAPI/components/ViewSelection/ViewSelection.h +++ b/source/RobotAPI/components/ViewSelection/ViewSelection.h @@ -143,13 +143,13 @@ namespace armarx */ PropertyDefinitionsPtr createPropertyDefinitions() override; - void addManualViewTarget(const FramedPositionBasePtr& target, const Ice::Current& c = Ice::Current()) override; + void addManualViewTarget(const FramedPositionBasePtr& target, const Ice::Current& c = Ice::emptyCurrent) override; - void clearManualViewTargets(const Ice::Current& c = Ice::Current()) override; + void clearManualViewTargets(const Ice::Current& c = Ice::emptyCurrent) override; - ViewTargetList getManualViewTargets(const Ice::Current& c = Ice::Current()) override; + ViewTargetList getManualViewTargets(const Ice::Current& c = Ice::emptyCurrent) override; - void activateAutomaticViewSelection(const Ice::Current& c = Ice::Current()) override + void activateAutomaticViewSelection(const Ice::Current& c = Ice::emptyCurrent) override { boost::mutex::scoped_lock lock(manualViewTargetsMutex); @@ -159,7 +159,7 @@ namespace armarx viewSelectionObserver->onActivateAutomaticViewSelection(); } - void deactivateAutomaticViewSelection(const Ice::Current& c = Ice::Current()) override + void deactivateAutomaticViewSelection(const Ice::Current& c = Ice::emptyCurrent) override { boost::mutex::scoped_lock lock(manualViewTargetsMutex); @@ -169,22 +169,22 @@ namespace armarx viewSelectionObserver->onDeactivateAutomaticViewSelection(); } - bool isEnabledAutomaticViewSelection(const Ice::Current& c = Ice::Current()) override + bool isEnabledAutomaticViewSelection(const Ice::Current& c = Ice::emptyCurrent) override { boost::mutex::scoped_lock lock(manualViewTargetsMutex); return doAutomaticViewSelection; } - void updateSaliencyMap(const SaliencyMapBasePtr& map, const Ice::Current& c = ::Ice::Current()) override; + void updateSaliencyMap(const SaliencyMapBasePtr& map, const Ice::Current& c = Ice::emptyCurrent) override; - void removeSaliencyMap(const std::string& name, const Ice::Current& c = ::Ice::Current()) override; + void removeSaliencyMap(const std::string& name, const Ice::Current& c = Ice::emptyCurrent) override; - ::Ice::StringSeq getSaliencyMapNames(const Ice::Current& c = ::Ice::Current()) override; + ::Ice::StringSeq getSaliencyMapNames(const Ice::Current& c = Ice::emptyCurrent) override; - void drawSaliencySphere(const ::Ice::StringSeq& names, const Ice::Current& c = ::Ice::Current()) override; + void drawSaliencySphere(const ::Ice::StringSeq& names, const Ice::Current& c = Ice::emptyCurrent) override; - void clearSaliencySphere(const Ice::Current& c = ::Ice::Current()) override; + void clearSaliencySphere(const Ice::Current& c = Ice::emptyCurrent) override; private: diff --git a/source/RobotAPI/components/units/CMakeLists.txt b/source/RobotAPI/components/units/CMakeLists.txt index a8fa129b5b5aa0da0aad734709048bd581094c9a..ce1deee815879ee57d182d4671d0e744ebff4d4e 100644 --- a/source/RobotAPI/components/units/CMakeLists.txt +++ b/source/RobotAPI/components/units/CMakeLists.txt @@ -24,6 +24,7 @@ set(LIB_HEADERS GamepadUnitObserver.h TCPControlUnit.h TCPControlUnitObserver.h + GraspCandidateObserver.h HandUnit.h HandUnitSimulation.h @@ -55,6 +56,7 @@ set(LIB_FILES GamepadUnitObserver.cpp TCPControlUnit.cpp TCPControlUnitObserver.cpp + GraspCandidateObserver.cpp HandUnit.cpp HandUnitSimulation.cpp diff --git a/source/RobotAPI/components/units/ForceTorqueObserver.cpp b/source/RobotAPI/components/units/ForceTorqueObserver.cpp index fe4074bbb71e9440ac1a914a014fa19f3baec52a..e896c60c2bc25183376659e1622cdb8905516d0e 100644 --- a/source/RobotAPI/components/units/ForceTorqueObserver.cpp +++ b/source/RobotAPI/components/units/ForceTorqueObserver.cpp @@ -131,7 +131,7 @@ void ForceTorqueObserver::visualizerFunction() { // if (localRobot->hasRobotNode(channel.first)) { - DatafieldRefPtr field = DatafieldRefPtr::dynamicCast(getForceDatafield(channel.first, Ice::Current())); + DatafieldRefPtr field = DatafieldRefPtr::dynamicCast(getForceDatafield(channel.first, Ice::emptyCurrent)); FramedDirectionPtr value = field->getDataField()->get<FramedDirection>(); if (frameAlreadyReported.count(value->frame) > 0 || (value->frame != GlobalFrame && !value->frame.empty() && !localRobot->hasRobotNode(value->frame))) @@ -153,7 +153,7 @@ void ForceTorqueObserver::visualizerFunction() std::max(arrowLength * forceMag, 10.f), 3); - field = DatafieldRefPtr::dynamicCast(getTorqueDatafield(channel.first, Ice::Current())); + field = DatafieldRefPtr::dynamicCast(getTorqueDatafield(channel.first, Ice::emptyCurrent)); value = field->getDataField()->get<FramedDirection>(); auto torque = value; // ARMARX_INFO << deactivateSpam(1, torque->frame) << "Reporting for " << channel.first << " in frame " << torque->frame; diff --git a/source/RobotAPI/components/units/ForceTorqueUnitSimulation.h b/source/RobotAPI/components/units/ForceTorqueUnitSimulation.h index a52d64e37776d0d499af4099d7cd35f88a2e6b69..cc3115aa4d96c40baab5a7aed418abbe813db229 100644 --- a/source/RobotAPI/components/units/ForceTorqueUnitSimulation.h +++ b/source/RobotAPI/components/units/ForceTorqueUnitSimulation.h @@ -79,12 +79,12 @@ namespace armarx /** * \warning Not implemented yet */ - void setOffset(const FramedDirectionBasePtr& forceOffsets, const FramedDirectionBasePtr& torqueOffsets, const Ice::Current& c = ::Ice::Current()) override; + void setOffset(const FramedDirectionBasePtr& forceOffsets, const FramedDirectionBasePtr& torqueOffsets, const Ice::Current& c = Ice::emptyCurrent) override; /** * \warning Not implemented yet */ - void setToNull(const Ice::Current& c = ::Ice::Current()) override; + void setToNull(const Ice::Current& c = Ice::emptyCurrent) override; /** * \see PropertyUser::createPropertyDefinitions() diff --git a/source/RobotAPI/components/units/GraspCandidateObserver.cpp b/source/RobotAPI/components/units/GraspCandidateObserver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f22b1dfde85d6a6b94a729face9ed4ccc272171d --- /dev/null +++ b/source/RobotAPI/components/units/GraspCandidateObserver.cpp @@ -0,0 +1,265 @@ +/** +* 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 as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package RobotAPI +* @author Simon Ottenhaus +* @copyright 2019 Humanoids Group, H2T, KIT +* @license http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#include "GraspCandidateObserver.h" + +//#include <ArmarXCore/core/checks/ConditionCheckEqualsPoseWithTolerance.h> +#include <ArmarXCore/observers/checks/ConditionCheckUpdated.h> +#include <ArmarXCore/observers/checks/ConditionCheckEquals.h> +#include <ArmarXCore/observers/checks/ConditionCheckInRange.h> +#include <ArmarXCore/observers/checks/ConditionCheckLarger.h> +#include <ArmarXCore/observers/checks/ConditionCheckSmaller.h> +#include <RobotAPI/libraries/core/checks/ConditionCheckEqualsPoseWithTolerance.h> +#include <RobotAPI/libraries/core/checks/ConditionCheckMagnitudeChecks.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#define TCP_POSE_CHANNEL "TCPPose" +#define TCP_TRANS_VELOCITIES_CHANNEL "TCPVelocities" +using namespace armarx; +using namespace armarx::grasping; + +GraspCandidateObserver::GraspCandidateObserver() +{ +} + +void GraspCandidateObserver::onInitObserver() +{ + usingTopic(getProperty<std::string>("GraspCandidatesTopicName").getValue()); + offeringTopic(getProperty<std::string>("ConfigTopicName").getValue()); + + +} + +void GraspCandidateObserver::onConnectObserver() +{ + configTopic = getTopic<GraspCandidateProviderInterfacePrx>(getProperty<std::string>("ConfigTopicName").getValue()); +} + +PropertyDefinitionsPtr GraspCandidateObserver::createPropertyDefinitions() +{ + return PropertyDefinitionsPtr(new GraspCandidateObserverPropertyDefinitions( + getConfigIdentifier())); +} + +bool GraspCandidateObserver::FilterMatches(const CandidateFilterConditionPtr& filter, const std::string& providerName, const GraspCandidatePtr& candidate) +{ + if (filter->providerName != "*" && filter->providerName != providerName) + { + return false; + } + if (filter->minimumSuccessProbability > candidate->graspSuccessProbability) + { + return false; + } + if (filter->approach != AnyApproach && (candidate->executionHints == 0 || filter->approach != candidate->executionHints->approach)) + { + return false; + } + if (filter->preshape != AnyAperture && (candidate->executionHints == 0 || filter->preshape != candidate->executionHints->preshape)) + { + return false; + } + if (filter->objectType != AnyObject && filter->objectType != candidate->objectType) + { + return false; + } + return true; +} + +std::string GraspCandidateObserver::ObjectTypeToString(ObjectTypeEnum type) +{ + switch (type) + { + case grasping::AnyObject: + return "AnyObject"; + case grasping::KnownObject: + return "KnownObject"; + case grasping::UnknownObject: + return "UnknownObject"; + default: + return "ERROR"; + } +} + +void GraspCandidateObserver::reportGraspCandidates(const std::string& providerName, const GraspCandidateSeq& candidates, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + this->candidates[providerName] = candidates; + if (updateCounters.count(providerName) == 0) + { + updateCounters[providerName] = 0; + } + updateCounters[providerName]++; + if (providers.count(providerName) == 0) + { + providers[providerName] = new ProviderInfo(); + } + + + if (!existsChannel(providerName)) + { + offerChannel(providerName, "Channel of " + providerName); + } + offerOrUpdateDataField(providerName, "updateCounter", Variant(updateCounters[providerName]), "Counter that increases for each update"); + offerOrUpdateDataField(providerName, "candidateCount", Variant((int)candidates.size()), "Number of provided candiates"); + +} + +void GraspCandidateObserver::reportProviderInfo(const std::string& providerName, const ProviderInfoPtr& info, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + providers[providerName] = info; + if (updateCounters.count(providerName) == 0) + { + updateCounters[providerName] = 0; + } + + + if (!existsChannel(providerName)) + { + offerChannel(providerName, "Channel of " + providerName); + } + offerOrUpdateDataField(providerName, "objectType", ObjectTypeToString(info->objectType), ""); +} + +InfoMap GraspCandidateObserver::getAvailableProvidersWithInfo(const Ice::Current&) +{ + ScopedLock lock(dataMutex); + return providers; +} + +StringSeq GraspCandidateObserver::getAvailableProviderNames(const Ice::Current&) +{ + ScopedLock lock(dataMutex); + return getAvailableProviderNames(); +} + + + +ProviderInfoPtr GraspCandidateObserver::getProviderInfo(const std::string& providerName, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + checkHasProvider(providerName); + return providers[providerName]; +} + +bool GraspCandidateObserver::hasProvider(const std::string& providerName, const Ice::Current& c) +{ + ScopedLock lock(dataMutex); + return hasProvider(providerName); +} + + + +GraspCandidateSeq GraspCandidateObserver::getAllCandidates(const Ice::Current&) +{ + ScopedLock lock(dataMutex); + GraspCandidateSeq all; + for (const std::pair<std::string, grasping::GraspCandidateSeq>& pair : candidates) + { + all.insert(all.end(), pair.second.begin(), pair.second.end()); + } + return all; +} + +GraspCandidateSeq GraspCandidateObserver::getCandidatesByProvider(const std::string& providerName, const Ice::Current& c) +{ + CandidateFilterConditionPtr filter = new CandidateFilterCondition(); + filter->providerName = providerName; + return getCandidatesByFilter(filter, c); +} + +GraspCandidateSeq GraspCandidateObserver::getCandidatesByFilter(const CandidateFilterConditionPtr& filter, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + GraspCandidateSeq matching; + for (const std::pair<std::string, grasping::GraspCandidateSeq>& pair : candidates) + { + for (const grasping::GraspCandidatePtr& candidate : pair.second) + { + if (FilterMatches(filter, pair.first, candidate)) + { + matching.push_back(candidate); + } + } + } + return matching; +} + +Ice::Int GraspCandidateObserver::getUpdateCounterByProvider(const std::string& providerName, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + checkHasProvider(providerName); + return updateCounters[providerName]; +} + +IntMap GraspCandidateObserver::getAllUpdateCounters(const Ice::Current& providerName) +{ + ScopedLock lock(dataMutex); + return updateCounters; +} + +bool GraspCandidateObserver::setProviderConfig(const std::string& providerName, const StringVariantBaseMap& config, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + if (providers.count(providerName) == 0) + { + return false; + } + configTopic->setServiceConfig(providerName, config); + return true; +} + +void GraspCandidateObserver::setSelectedCandidates(const GraspCandidateSeq& candidates, const Ice::Current&) +{ + ScopedLock lock(selectedCandidatesMutex); + selectedCandidates = candidates; +} + +GraspCandidateSeq GraspCandidateObserver::getSelectedCandidates(const Ice::Current&) +{ + ScopedLock lock(selectedCandidatesMutex); + return selectedCandidates; +} + + +bool GraspCandidateObserver::hasProvider(const std::string& providerName) +{ + return providers.count(providerName) > 0; +} +void GraspCandidateObserver::checkHasProvider(const std::string& providerName) +{ + if (!hasProvider(providerName)) + { + throw LocalException("Unknown provider name '") << providerName << "'. Available providers: " << getAvailableProviderNames(); + } +} +StringSeq GraspCandidateObserver::getAvailableProviderNames() +{ + StringSeq names; + for (const std::pair<std::string, ProviderInfoPtr>& pair : providers) + { + names.push_back(pair.first); + } + return names; +} diff --git a/source/RobotAPI/components/units/GraspCandidateObserver.h b/source/RobotAPI/components/units/GraspCandidateObserver.h new file mode 100644 index 0000000000000000000000000000000000000000..7588252723921b1ce40903a6d7ccfbef6459ad01 --- /dev/null +++ b/source/RobotAPI/components/units/GraspCandidateObserver.h @@ -0,0 +1,114 @@ +/** +* 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 as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package RobotAPI +* @author Simon Ottenhaus +* @copyright 2019 Humanoids Group, H2T, KIT +* @license http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#pragma once + +#include <ArmarXCore/observers/Observer.h> +#include <RobotAPI/interface/observers/GraspCandidateObserverInterface.h> + +namespace armarx +{ + /** + * \class GraspCandidateObserverPropertyDefinitions + * \brief + */ + class GraspCandidateObserverPropertyDefinitions: + public ObserverPropertyDefinitions + { + public: + GraspCandidateObserverPropertyDefinitions(std::string prefix): + ObserverPropertyDefinitions(prefix) + { + defineOptionalProperty<std::string>("GraspCandidatesTopicName", "GraspCandidatesTopic", "Name of the Grasp Candidate Topic"); + defineOptionalProperty<std::string>("ConfigTopicName", "GraspCandidateProviderConfigTopic", "Name of the Grasp Candidate Provider Config Topic"); + } + }; + + /** + * \class GraspCandidateObserver + * \ingroup RobotAPI-SensorActorUnits-observers + */ + class GraspCandidateObserver : + virtual public Observer, + virtual public grasping::GraspCandidateObserverInterface + { + public: + GraspCandidateObserver(); + + // framework hooks + std::string getDefaultName() const override + { + return "GraspCandidateObserver"; + } + void onInitObserver() override; + void onConnectObserver() override; + + /** + * \see PropertyUser::createPropertyDefinitions() + */ + PropertyDefinitionsPtr createPropertyDefinitions() override; + + public: + static bool FilterMatches(const grasping::CandidateFilterConditionPtr& filter, const std::string& providerName, const grasping::GraspCandidatePtr& candidate); + static std::string ObjectTypeToString(grasping::ObjectTypeEnum type); + + // GraspCandidateProviderListener interface + public: + void reportGraspCandidates(const std::string& providerName, const grasping::GraspCandidateSeq& candidates, const Ice::Current&) override; + void reportProviderInfo(const std::string& providerName, const grasping::ProviderInfoPtr& info, const Ice::Current&) override; + + // GraspCandidateObserverInterface interface + public: + grasping::InfoMap getAvailableProvidersWithInfo(const Ice::Current&) override; + grasping::StringSeq getAvailableProviderNames(const Ice::Current&) override; + grasping::ProviderInfoPtr getProviderInfo(const std::string& providerName, const Ice::Current&) override; + bool hasProvider(const std::string& providerName, const Ice::Current& c) override; + grasping::GraspCandidateSeq getAllCandidates(const Ice::Current&) override; + grasping::GraspCandidateSeq getCandidatesByProvider(const std::string& providerName, const Ice::Current& c) override; + grasping::GraspCandidateSeq getCandidatesByFilter(const grasping::CandidateFilterConditionPtr& filter, const Ice::Current&) override; + Ice::Int getUpdateCounterByProvider(const std::string& providerName, const Ice::Current&) override; + grasping::IntMap getAllUpdateCounters(const Ice::Current& providerName) override; + bool setProviderConfig(const std::string& providerName, const StringVariantBaseMap& config, const Ice::Current&) override; + + void setSelectedCandidates(const grasping::GraspCandidateSeq& candidates, const Ice::Current&) override; + grasping::GraspCandidateSeq getSelectedCandidates(const Ice::Current&) override; + + private: + bool hasProvider(const std::string& providerName); + void checkHasProvider(const std::string& providerName); + grasping::StringSeq getAvailableProviderNames(); + Mutex dataMutex; + std::map<std::string, grasping::GraspCandidateSeq> candidates; + grasping::InfoMap providers; + std::map<std::string, int> updateCounters; + + grasping::GraspCandidateProviderInterfacePrx configTopic; + + Mutex selectedCandidatesMutex; + grasping::GraspCandidateSeq selectedCandidates; + + + }; + +} + diff --git a/source/RobotAPI/components/units/HandUnit.h b/source/RobotAPI/components/units/HandUnit.h index 12d79c41b602ce701c2a473090edd8b0cb2479cb..6f7cc714ab242930d2a2a32d6022b77889a473bc 100644 --- a/source/RobotAPI/components/units/HandUnit.h +++ b/source/RobotAPI/components/units/HandUnit.h @@ -118,7 +118,7 @@ namespace armarx /** * Send command to the hand to form a specific shape position. The shapes are defined in the robot.xml file. */ - void setShape(const std::string& shapeName, const Ice::Current& c = ::Ice::Current()) override; + void setShape(const std::string& shapeName, const Ice::Current& c = Ice::emptyCurrent) override; /** * @brief setShapeWithObjectInstance Send command to the hand to form a specific shape position. @@ -128,15 +128,15 @@ namespace armarx * @param shapeName Name of the well known shape that the hand should form * @param graspedObjectInstanceName name of the object instance which is used to check for collisions while setting the shape */ - void setShapeWithObjectInstance(const std::string& shapeName, const std::string& objectInstanceName, const Ice::Current& c = ::Ice::Current()) override; + void setShapeWithObjectInstance(const std::string& shapeName, const std::string& objectInstanceName, const Ice::Current& c = Ice::emptyCurrent) override; /** * \return a list of strings for shape positions which can be used together with HandUnit::shape(). */ - SingleTypeVariantListBasePtr getShapeNames(const Ice::Current& c = ::Ice::Current()) override; + SingleTypeVariantListBasePtr getShapeNames(const Ice::Current& c = Ice::emptyCurrent) override; - NameValueMap getShapeJointValues(const std::string& shapeName, const Ice::Current& c = ::Ice::Current()) override; - NameValueMap getCurrentJointValues(const Ice::Current& c = ::Ice::Current()) override; + NameValueMap getShapeJointValues(const std::string& shapeName, const Ice::Current& c = Ice::emptyCurrent) override; + NameValueMap getCurrentJointValues(const Ice::Current& c = Ice::emptyCurrent) override; void setObjectGrasped(const std::string& objectName, const Ice::Current&) override; void setObjectReleased(const std::string& objectName, const Ice::Current&) override; diff --git a/source/RobotAPI/components/units/HandUnitSimulation.h b/source/RobotAPI/components/units/HandUnitSimulation.h index 1c02fd686c7a39512cd4ed6f732a8d879904a2ba..d5fff658f3e0c76a56669ef8e1b3fbddfd6a4d22 100644 --- a/source/RobotAPI/components/units/HandUnitSimulation.h +++ b/source/RobotAPI/components/units/HandUnitSimulation.h @@ -72,12 +72,12 @@ namespace armarx * * \warning Not implemented yet! */ - void setShape(const std::string& shapeName, const Ice::Current& c = ::Ice::Current()) override; + void setShape(const std::string& shapeName, const Ice::Current& c = Ice::emptyCurrent) override; /** * \warning Not implemented yet! */ - void setJointAngles(const NameValueMap& jointAngles, const Ice::Current& c = ::Ice::Current()) override; + void setJointAngles(const NameValueMap& jointAngles, const Ice::Current& c = Ice::emptyCurrent) override; /** * \see armarx::PropertyUser::createPropertyDefinitions() diff --git a/source/RobotAPI/components/units/HapticObserver.h b/source/RobotAPI/components/units/HapticObserver.h index e6b31b334d2b3b2bf6263d25c3e17918029810d8..5e75db5b05f74cf23cecb6ce3908f6f9ca1fd2c9 100644 --- a/source/RobotAPI/components/units/HapticObserver.h +++ b/source/RobotAPI/components/units/HapticObserver.h @@ -139,7 +139,7 @@ namespace armarx void onConnectObserver() override; void onExitObserver() override; - void reportSensorValues(const ::std::string& device, const ::std::string& name, const ::armarx::MatrixFloatBasePtr& values, const ::armarx::TimestampBasePtr& timestamp, const ::Ice::Current& = ::Ice::Current()) override; + void reportSensorValues(const ::std::string& device, const ::std::string& name, const ::armarx::MatrixFloatBasePtr& values, const ::armarx::TimestampBasePtr& timestamp, const ::Ice::Current& = Ice::emptyCurrent) override; /** * \see PropertyUser::createPropertyDefinitions() diff --git a/source/RobotAPI/components/units/HeadIKUnit.h b/source/RobotAPI/components/units/HeadIKUnit.h index bbdd75e22205b8e8f4b966cac6543b3d2047a517..6783a2b2e1a7609b19bfae27a898e893879bc8f9 100644 --- a/source/RobotAPI/components/units/HeadIKUnit.h +++ b/source/RobotAPI/components/units/HeadIKUnit.h @@ -77,18 +77,18 @@ namespace armarx } // HeadIKUnitInterface interface - void setCycleTime(Ice::Int milliseconds, const Ice::Current& c = Ice::Current()) override; - void setHeadTarget(const std::string& robotNodeSetName, const FramedPositionBasePtr& targetPosition, const Ice::Current& c = Ice::Current()) override; + void setCycleTime(Ice::Int milliseconds, const Ice::Current& c = Ice::emptyCurrent) override; + void setHeadTarget(const std::string& robotNodeSetName, const FramedPositionBasePtr& targetPosition, const Ice::Current& c = Ice::emptyCurrent) override; // UnitExecutionManagementInterface interface - void init(const Ice::Current& c = Ice::Current()) override; - void start(const Ice::Current& c = Ice::Current()) override; - void stop(const Ice::Current& c = Ice::Current()) override; - UnitExecutionState getExecutionState(const Ice::Current& c = Ice::Current()) override; + void init(const Ice::Current& c = Ice::emptyCurrent) override; + void start(const Ice::Current& c = Ice::emptyCurrent) override; + void stop(const Ice::Current& c = Ice::emptyCurrent) override; + UnitExecutionState getExecutionState(const Ice::Current& c = Ice::emptyCurrent) override; // UnitResourceManagementInterface interface - void request(const Ice::Current& c = Ice::Current()) override; - void release(const Ice::Current& c = Ice::Current()) override; + void request(const Ice::Current& c = Ice::emptyCurrent) override; + void release(const Ice::Current& c = Ice::emptyCurrent) override; // PropertyUser interface PropertyDefinitionsPtr createPropertyDefinitions() override; diff --git a/source/RobotAPI/components/units/InertialMeasurementUnitObserver.cpp b/source/RobotAPI/components/units/InertialMeasurementUnitObserver.cpp index 3f387568df7d54c2b6c93ea27852269ff39ba562..e5ae05a54c63e5fd262a7c0025df9d54a7ffdf24 100644 --- a/source/RobotAPI/components/units/InertialMeasurementUnitObserver.cpp +++ b/source/RobotAPI/components/units/InertialMeasurementUnitObserver.cpp @@ -23,6 +23,7 @@ */ #include "InertialMeasurementUnitObserver.h" +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <ArmarXCore/observers/checks/ConditionCheckEquals.h> #include <ArmarXCore/observers/checks/ConditionCheckLarger.h> #include <ArmarXCore/observers/checks/ConditionCheckSmaller.h> @@ -66,7 +67,9 @@ void InertialMeasurementUnitObserver::onExitObserver() } } -void InertialMeasurementUnitObserver::reportSensorValues(const std::string& device, const std::string& name, const IMUData& values, const TimestampBasePtr& timestamp, const Ice::Current& c) +void InertialMeasurementUnitObserver::reportSensorValues( + const std::string& device, const std::string& name, const IMUData& values, + const TimestampBasePtr& timestamp, const Ice::Current&) { ScopedLock lock(dataMutex); diff --git a/source/RobotAPI/components/units/InertialMeasurementUnitObserver.h b/source/RobotAPI/components/units/InertialMeasurementUnitObserver.h index 1ceba2cf5f084d2f2f47e2ecf4fd0ec5e5a14679..4591228d92d457967b85106554898345895b6ee2 100644 --- a/source/RobotAPI/components/units/InertialMeasurementUnitObserver.h +++ b/source/RobotAPI/components/units/InertialMeasurementUnitObserver.h @@ -73,7 +73,7 @@ namespace armarx void onConnectObserver() override; void onExitObserver() override; - void reportSensorValues(const std::string& device, const std::string& name, const IMUData& values, const TimestampBasePtr& timestamp, const Ice::Current& c = ::Ice::Current()) override; + void reportSensorValues(const std::string& device, const std::string& name, const IMUData& values, const TimestampBasePtr& timestamp, const Ice::Current& c = Ice::emptyCurrent) override; /** * @see PropertyUser::createPropertyDefinitions() diff --git a/source/RobotAPI/components/units/KinematicUnit.cpp b/source/RobotAPI/components/units/KinematicUnit.cpp index 085b96d83e2f8c67c9a522effc037d981096922f..0348c59145cf662d46e8d3b17bab1be247e8bb89 100644 --- a/source/RobotAPI/components/units/KinematicUnit.cpp +++ b/source/RobotAPI/components/units/KinematicUnit.cpp @@ -99,7 +99,7 @@ void KinematicUnit::onInitComponent() robotNodes = robotNodeSetPtr->getAllRobotNodes(); // component dependencies - listenerName = robotNodeSetName + "State"; + listenerName = getProperty<std::string>("TopicPrefix").getValue() + robotNodeSetName + "State"; offeringTopic(listenerName); this->onInitKinematicUnit(); diff --git a/source/RobotAPI/components/units/KinematicUnit.h b/source/RobotAPI/components/units/KinematicUnit.h index 40be0bf28f23c852c778ea866405f3a84ee1e5a2..67b896fc0f4640ecf5c276afa68104184bc17b14 100644 --- a/source/RobotAPI/components/units/KinematicUnit.h +++ b/source/RobotAPI/components/units/KinematicUnit.h @@ -51,6 +51,7 @@ namespace armarx defineRequiredProperty<std::string>("RobotNodeSetName", "Robot node set name as defined in robot xml file, e.g. 'LeftArm'"); defineRequiredProperty<std::string>("RobotFileName", "Robot file name, e.g. robot_model.xml"); defineOptionalProperty<std::string>("RobotFileNameProject", "", "Project in which the robot filename is located (if robot is loaded from an external project)"); + defineOptionalProperty<std::string>("TopicPrefix", "", "Prefix for the sensor value topic name."); } }; diff --git a/source/RobotAPI/components/units/KinematicUnitObserver.cpp b/source/RobotAPI/components/units/KinematicUnitObserver.cpp index bdf8dc94e2068fb373f5f5984604d262bb78d1a1..ea9b8da615bdeaccbb5cd505caf8f528ef40241a 100644 --- a/source/RobotAPI/components/units/KinematicUnitObserver.cpp +++ b/source/RobotAPI/components/units/KinematicUnitObserver.cpp @@ -56,7 +56,7 @@ void KinematicUnitObserver::onInitObserver() offerConditionCheck("larger", new ConditionCheckLarger()); offerConditionCheck("smaller", new ConditionCheckSmaller()); - usingTopic(robotNodeSetName + "State"); + usingTopic(getProperty<std::string>("TopicPrefix").getValue() + robotNodeSetName + "State"); } void KinematicUnitObserver::onConnectObserver() @@ -91,11 +91,12 @@ void KinematicUnitObserver::onConnectObserver() throw UserException("RobotNodeSet not defined"); } - VirtualRobot::RobotNodeSetPtr robotNodeSetPtr = robot->getRobotNodeSet(robotNodeSetName); + auto robotNodeSetPtr = robot->getRobotNodeSet(robotNodeSetName); std::vector< VirtualRobot::RobotNodePtr > robotNodes; robotNodes = robotNodeSetPtr->getAllRobotNodes(); - + auto robotNodeNames = robotNodeSetPtr->getNodeNames(); + this->robotNodes = std::set<std::string>(robotNodeNames.begin(), robotNodeNames.end()); // register all channels offerChannel("jointangles", "Joint values of the " + robotNodeSetName + " kinematic chain"); offerChannel("jointvelocities", "Joint velocities of the " + robotNodeSetName + " kinematic chain"); @@ -298,14 +299,20 @@ void KinematicUnitObserver::nameValueMapToDataFields(const std::string& channelN { for (const auto& it : nameValueMap) { - map[it.first] = new Variant(it.second); + if (robotNodes.count(it.first)) + { + map[it.first] = new Variant(it.second); + } } } else { for (const auto& it : nameValueMap) { - map[it.first] = new TimedVariant(new Variant(it.second), IceUtil::Time::microSeconds(timestamp)); + if (robotNodes.count(it.first)) + { + map[it.first] = new TimedVariant(new Variant(it.second), IceUtil::Time::microSeconds(timestamp)); + } } } setDataFieldsFlatCopy(channelName, map); diff --git a/source/RobotAPI/components/units/KinematicUnitObserver.h b/source/RobotAPI/components/units/KinematicUnitObserver.h index 1f306d6863f85f203d9cd24db6486494ff15fd8f..92cf7f72095ebbe825087379ad1c662cc0e0cc7f 100644 --- a/source/RobotAPI/components/units/KinematicUnitObserver.h +++ b/source/RobotAPI/components/units/KinematicUnitObserver.h @@ -48,6 +48,8 @@ namespace armarx defineRequiredProperty<std::string>("RobotNodeSetName", "Robot node set name as defined in robot xml file, e.g. 'LeftArm'"); defineRequiredProperty<std::string>("RobotFileName", "Robot file name, e.g. robot_model.xml"); defineOptionalProperty<std::string>("RobotFileNameProject", "", "Project in which the robot filename is located (if robot is loaded from an external project)"); + defineOptionalProperty<std::string>("TopicPrefix", "", "Prefix for the sensor value topic name."); + } }; @@ -75,14 +77,14 @@ namespace armarx // slice interface implementation - void reportControlModeChanged(const NameControlModeMap& jointModes, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointAngles(const NameValueMap& jointAngles, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointVelocities(const NameValueMap& jointVelocities, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointTorques(const NameValueMap& jointTorques, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; + void reportControlModeChanged(const NameControlModeMap& jointModes, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointAngles(const NameValueMap& jointAngles, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointVelocities(const NameValueMap& jointVelocities, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointTorques(const NameValueMap& jointTorques, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; void reportJointAccelerations(const NameValueMap& jointAccelerations, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c) override; - void reportJointCurrents(const NameValueMap& jointCurrents, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointMotorTemperatures(const NameValueMap& jointMotorTemperatures, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; - void reportJointStatuses(const NameStatusMap& jointStatuses, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = ::Ice::Current()) override; + void reportJointCurrents(const NameValueMap& jointCurrents, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointMotorTemperatures(const NameValueMap& jointMotorTemperatures, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; + void reportJointStatuses(const NameStatusMap& jointStatuses, Ice::Long timestamp, bool aValueChanged, const Ice::Current& c = Ice::emptyCurrent) override; std::string getDefaultName() const override { @@ -155,6 +157,7 @@ namespace armarx Mutex initializedChannelsMutex; private: std::string robotNodeSetName; + std::set<std::string> robotNodes; }; diff --git a/source/RobotAPI/components/units/KinematicUnitSimulation.h b/source/RobotAPI/components/units/KinematicUnitSimulation.h index 7a04769c70c74fcfe8f679e7d9b272b84dcf70d7..cc3dae20ac7476572a4250c384a6395c7fd7d61b 100644 --- a/source/RobotAPI/components/units/KinematicUnitSimulation.h +++ b/source/RobotAPI/components/units/KinematicUnitSimulation.h @@ -148,26 +148,26 @@ namespace armarx void simulationFunction(); // proxy implementation - void requestJoints(const Ice::StringSeq& joints, const Ice::Current& c = ::Ice::Current()) override; - void releaseJoints(const Ice::StringSeq& joints, const Ice::Current& c = ::Ice::Current()) override; - void switchControlMode(const NameControlModeMap& targetJointModes, const Ice::Current& c = ::Ice::Current()) override; - void setJointAngles(const NameValueMap& targetJointAngles, const Ice::Current& c = ::Ice::Current()) override; - void setJointVelocities(const NameValueMap& targetJointVelocities, const Ice::Current& c = ::Ice::Current()) override; - void setJointTorques(const NameValueMap& targetJointTorques, const Ice::Current& c = ::Ice::Current()) override; + void requestJoints(const Ice::StringSeq& joints, const Ice::Current& c = Ice::emptyCurrent) override; + void releaseJoints(const Ice::StringSeq& joints, const Ice::Current& c = Ice::emptyCurrent) override; + void switchControlMode(const NameControlModeMap& targetJointModes, const Ice::Current& c = Ice::emptyCurrent) override; + void setJointAngles(const NameValueMap& targetJointAngles, const Ice::Current& c = Ice::emptyCurrent) override; + void setJointVelocities(const NameValueMap& targetJointVelocities, const Ice::Current& c = Ice::emptyCurrent) override; + void setJointTorques(const NameValueMap& targetJointTorques, const Ice::Current& c = Ice::emptyCurrent) override; - NameControlModeMap getControlModes(const Ice::Current& c = ::Ice::Current()) override; + NameControlModeMap getControlModes(const Ice::Current& c = Ice::emptyCurrent) override; /** * \warning Not implemented yet! */ - void setJointAccelerations(const NameValueMap& targetJointAccelerations, const Ice::Current& c = ::Ice::Current()) override; + void setJointAccelerations(const NameValueMap& targetJointAccelerations, const Ice::Current& c = Ice::emptyCurrent) override; /** * \warning Not implemented yet! */ - void setJointDecelerations(const NameValueMap& targetJointDecelerations, const Ice::Current& c = ::Ice::Current()) override; + void setJointDecelerations(const NameValueMap& targetJointDecelerations, const Ice::Current& c = Ice::emptyCurrent) override; - void stop(const Ice::Current& c = Ice::Current()) override; + void stop(const Ice::Current& c = Ice::emptyCurrent) override; /** * \see PropertyUser::createPropertyDefinitions() diff --git a/source/RobotAPI/components/units/OptoForceUnitObserver.h b/source/RobotAPI/components/units/OptoForceUnitObserver.h index 1fcdb266986b47dc46f1dd135ffe9b964890961d..3bdc63e1a6b874cb9005260801c8c11869b009c7 100644 --- a/source/RobotAPI/components/units/OptoForceUnitObserver.h +++ b/source/RobotAPI/components/units/OptoForceUnitObserver.h @@ -72,7 +72,7 @@ namespace armarx void onConnectObserver() override; void onExitObserver() override; - void reportSensorValues(const std::string& device, const std::string& name, float fx, float fy, float fz, const TimestampBasePtr& timestamp, const Ice::Current& c = ::Ice::Current()) override; + void reportSensorValues(const std::string& device, const std::string& name, float fx, float fy, float fz, const TimestampBasePtr& timestamp, const Ice::Current& c = Ice::emptyCurrent) override; /** * @see PropertyUser::createPropertyDefinitions() diff --git a/source/RobotAPI/components/units/PlatformUnit.h b/source/RobotAPI/components/units/PlatformUnit.h index 119f4a2249b94538ed25abb4dbb173432e595d5d..5bedf16ac7a782f2c6c17d4fad32792d3700e99f 100644 --- a/source/RobotAPI/components/units/PlatformUnit.h +++ b/source/RobotAPI/components/units/PlatformUnit.h @@ -105,9 +105,9 @@ namespace armarx * The platform will move until it reaches the specified target with the specified accuracy. */ void moveTo(Ice::Float targetPlatformPositionX, Ice::Float targetPlatformPositionY, Ice::Float targetPlatformRotation, - Ice::Float positionalAccuracy, Ice::Float orientationalAccuracy, const Ice::Current& c = ::Ice::Current()) override; + Ice::Float positionalAccuracy, Ice::Float orientationalAccuracy, const Ice::Current& c = Ice::emptyCurrent) override; - void stopPlatform(const Ice::Current& c = Ice::Current()) override {} + void stopPlatform(const Ice::Current& c = Ice::emptyCurrent) override {} /** * \see armarx::PropertyUser::createPropertyDefinitions() */ diff --git a/source/RobotAPI/components/units/PlatformUnitObserver.h b/source/RobotAPI/components/units/PlatformUnitObserver.h index 47d86bbefffc0824ea357be9fdaf5a9af25e321c..412b9c57f942a48789c833773c01c5f9d6927331 100644 --- a/source/RobotAPI/components/units/PlatformUnitObserver.h +++ b/source/RobotAPI/components/units/PlatformUnitObserver.h @@ -78,9 +78,9 @@ namespace armarx void onConnectObserver() override; // slice interface implementation - void reportPlatformPose(::Ice::Float currentPlatformPositionX, ::Ice::Float currentPlatformPositionY, ::Ice::Float currentPlatformRotation, const Ice::Current& c = ::Ice::Current()) override; - void reportNewTargetPose(::Ice::Float newPlatformPositionX, ::Ice::Float newPlatformPositionY, ::Ice::Float newPlatformRotation, const Ice::Current& c = ::Ice::Current()) override; - void reportPlatformVelocity(::Ice::Float currentPlatformVelocityX, ::Ice::Float currentPlatformVelocityY, ::Ice::Float currentPlatformVelocityRotation, const Ice::Current& c = ::Ice::Current()) override; + void reportPlatformPose(::Ice::Float currentPlatformPositionX, ::Ice::Float currentPlatformPositionY, ::Ice::Float currentPlatformRotation, const Ice::Current& c = Ice::emptyCurrent) override; + void reportNewTargetPose(::Ice::Float newPlatformPositionX, ::Ice::Float newPlatformPositionY, ::Ice::Float newPlatformRotation, const Ice::Current& c = Ice::emptyCurrent) override; + void reportPlatformVelocity(::Ice::Float currentPlatformVelocityX, ::Ice::Float currentPlatformVelocityY, ::Ice::Float currentPlatformVelocityRotation, const Ice::Current& c = Ice::emptyCurrent) override; void reportPlatformOdometryPose(Ice::Float x, Ice::Float y, Ice::Float angle, const Ice::Current&) override; std::string getDefaultName() const override diff --git a/source/RobotAPI/components/units/PlatformUnitSimulation.h b/source/RobotAPI/components/units/PlatformUnitSimulation.h index a4ba66acbd58aa3c740471b79bfcbf658953f5c6..8c7743a7f940c74860597157a46beb16b4de6794 100644 --- a/source/RobotAPI/components/units/PlatformUnitSimulation.h +++ b/source/RobotAPI/components/units/PlatformUnitSimulation.h @@ -83,16 +83,16 @@ namespace armarx void simulationFunction(); // proxy implementation - void moveTo(Ice::Float targetPlatformPositionX, Ice::Float targetPlatformPositionY, Ice::Float targetPlatformRotation, Ice::Float positionalAccuracy, Ice::Float orientationalAccuracy, const Ice::Current& c = ::Ice::Current()) override; + void moveTo(Ice::Float targetPlatformPositionX, Ice::Float targetPlatformPositionY, Ice::Float targetPlatformRotation, Ice::Float positionalAccuracy, Ice::Float orientationalAccuracy, const Ice::Current& c = Ice::emptyCurrent) override; /** * \warning Not yet implemented! */ - void move(float targetPlatformVelocityX, float targetPlatformVelocityY, float targetPlatformVelocityRotation, const Ice::Current& c = Ice::Current()) override; + void move(float targetPlatformVelocityX, float targetPlatformVelocityY, float targetPlatformVelocityRotation, const Ice::Current& c = Ice::emptyCurrent) override; - void moveRelative(float targetPlatformOffsetX, float targetPlatformOffsetY, float targetPlatformOffsetRotation, float positionalAccuracy, float orientationalAccuracy, const Ice::Current& c = Ice::Current()) override; - void setMaxVelocities(float positionalVelocity, float orientaionalVelocity, const Ice::Current& c = Ice::Current()) override; - void stopPlatform(const Ice::Current& c = Ice::Current()) override; + void moveRelative(float targetPlatformOffsetX, float targetPlatformOffsetY, float targetPlatformOffsetRotation, float positionalAccuracy, float orientationalAccuracy, const Ice::Current& c = Ice::emptyCurrent) override; + void setMaxVelocities(float positionalVelocity, float orientaionalVelocity, const Ice::Current& c = Ice::emptyCurrent) override; + void stopPlatform(const Ice::Current& c = Ice::emptyCurrent) override; /** * \see PropertyUser::createPropertyDefinitions() */ diff --git a/source/RobotAPI/components/units/RobotPoseUnit.h b/source/RobotAPI/components/units/RobotPoseUnit.h index d620765925628a8820355ab2c45dc7f8ec828b7b..fb8e12eaf71b99496001d626a461750e8fba2763 100644 --- a/source/RobotAPI/components/units/RobotPoseUnit.h +++ b/source/RobotAPI/components/units/RobotPoseUnit.h @@ -107,9 +107,9 @@ namespace armarx * @param postionalAccuracy Robot stops translating if distance to target position gets lower than this threshhold. * @param orientationalAccuracy Robot stops rotating if distance from current to target orientation gets lower than this threshhold. **/ - virtual void moveTo(PoseBasePtr targetPose, Ice::Float positionalAccuracy, Ice::Float orientationalAccuracy, const Ice::Current& c = ::Ice::Current()); + virtual void moveTo(PoseBasePtr targetPose, Ice::Float positionalAccuracy, Ice::Float orientationalAccuracy, const Ice::Current& c = Ice::emptyCurrent); - void stopMovement(const Ice::Current& c = Ice::Current()) override {} + void stopMovement(const Ice::Current& c = Ice::emptyCurrent) override {} /** * \see armarx::PropertyUser::createPropertyDefinitions() */ diff --git a/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp b/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp index 950db1b8cb86ce96affe1e0fec2c9d90be72d3d2..61115c4e124cf52f73255721f53f199df8bd52f0 100644 --- a/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp +++ b/source/RobotAPI/components/units/RobotUnit/BasicControllers.cpp @@ -26,7 +26,7 @@ #include <ArmarXCore/core/util/algorithm.h> #include <ArmarXCore/core/logging/Logging.h> - +#include "util/CtrlUtil.h" namespace armarx { float velocityControlWithAccelerationBounds( @@ -353,6 +353,81 @@ namespace armarx // return targetV; // TODO vel can't be larger than 3.14?? } + bool VelocityControllerWithRampedAcceleration::validParameters() const + { + return maxV > 0 && + jerk > 0 && + targetV <= maxV && + targetV >= -maxV; + } + + VelocityControllerWithRampedAcceleration::Output VelocityControllerWithRampedAcceleration::run() const + { + const double useddt = std::min(std::abs(dt), std::abs(maxDt)); + + //we can have 3 cases: + // 1. we directly set v and ignore acc/dec (if |curr - target| <= limit) + // 2. we need to accelerate (if curr and target v have same sign and |curr| < |target|) + // 3. we need to decelerate (other cases) + //handle case 1 + const double clampedTargetV = math::MathUtils::LimitTo(targetV, maxV); + const double curverror = clampedTargetV - currentV; + if (std::abs(curverror) < directSetVLimit) + { + double nextAcc = (clampedTargetV - currentV) / useddt; + double nextJerk = (nextAcc - currentAcc) / useddt; + Output result {clampedTargetV, nextAcc, nextJerk}; + + return result; + } + + //handle case 2 + 3 + // const bool accelerate = sign(clampedTargetV) == sign(currentV) && // v in same direction + // std::abs(clampedTargetV) > std::abs(currentV); // currently to slow + const auto goalDir = math::MathUtils::Sign(clampedTargetV - currentV); + // calculate if we need to increase or decrease acc + bool increaseAcc = true; + if (goalDir == math::MathUtils::Sign(currentAcc)) + { + // double t_to_v = ctrlutil::t_to_v(clampedTargetV - currentV, currentAcc, goalDir * jerk); + // double acc_at_t = ctrlutil::a(t_to_v, std::abs(currentAcc), -jerk); + // increaseJerk = acc_at_t < 0.0; + + double t_to_zero_acc = std::abs(currentAcc) / jerk; + double v_at_t = ctrlutil::v(t_to_zero_acc, currentV, currentAcc, -goalDir * jerk); + increaseAcc = math::MathUtils::Sign(v_at_t - clampedTargetV) != goalDir ; + // ARMARX_INFO << VAROUT(t_to_zero_acc) << VAROUT(v_at_t) << VAROUT(increaseAcc); + } + // v = a*a/(2j) + const double adjustedJerk = std::abs(currentAcc * currentAcc / (2 * (clampedTargetV - currentV))); + double usedJerk = increaseAcc ? goalDir * jerk : + -goalDir * + ((std::abs(currentV) > 0.01 && !std::isnan(adjustedJerk)) ? adjustedJerk : jerk); + // double t_to_target_v = ctrlutil::t_to_v(clampedTargetV - currentV, currentAcc, usedJerk); + // double v_at_t2 = ctrlutil::v(t_to_target_v, currentV, currentAcc, usedJerk); + // ctrlutil::t_to_v(currentV, currentAcc, jerk); + // const double usedacc = currentAcc + useddt * usedJerk; + // const double maxDeltaVSimple = usedacc * useddt; + + const double maxDeltaV = std::abs(ctrlutil::v(useddt, 0, currentAcc, usedJerk)); + if (maxDeltaV >= std::abs(curverror)) + { + //we change our v too much with the used acceleration + //but we could reach our target with a lower acceleration ->set the target + double nextAcc = (clampedTargetV - currentV) / useddt; + double nextJerk = (nextAcc - currentAcc) / useddt; + // ARMARX_INFO << "overshooting! " << VAROUT(clampedTargetV) << VAROUT(nextAcc) << VAROUT(nextJerk); + Output result {clampedTargetV, nextAcc, nextJerk}; + return result; + } + // const double deltaVel = boost::algorithm::clamp(sign(currentV) * usedacc * useddt, -maxDeltaV, maxDeltaV); + const double nextV = ctrlutil::v(useddt, currentV, currentAcc, usedJerk); + const double nextAcc = ctrlutil::a(useddt, currentAcc, usedJerk); + // ARMARX_INFO << VAROUT(clampedTargetV) << VAROUT(nextV) << VAROUT(nextAcc) << VAROUT(usedJerk); + Output result {nextV, nextAcc, usedJerk}; + return result; + } + float VelocityControllerWithAccelerationBounds::estimateTime() const { const float curverror = std::abs(targetV - currentV); @@ -363,6 +438,8 @@ namespace armarx return curverror / ((targetV > currentV) ? acceleration : deceleration); } + + bool VelocityControllerWithAccelerationAndPositionBounds::validParameters() const { return VelocityControllerWithAccelerationBounds::validParameters() && @@ -445,8 +522,23 @@ namespace armarx #ifdef DEBUG_POS_CTRL buffer = boost::circular_buffer<HelpStruct>(20); #endif + pid.reset(new PIDController(1, 0, 0)); + pid->threadSafe = false; + } + + float PositionThroughVelocityControllerWithAccelerationBounds::calculateProportionalGain() const + { + /* s = v_0*v_0/(2*a) -> sqrt(s*2*a) = v_0; + + K_p * error = v_0; -> v_0/error = K_p; + */ + auto v_0 = std::sqrt(pControlPosErrorLimit * 2 * deceleration); + return v_0 / pControlPosErrorLimit; } + + + bool PositionThroughVelocityControllerWithAccelerationBounds::validParameters() const { return maxV > 0 && @@ -454,7 +546,7 @@ namespace armarx deceleration > 0 && // pControlPosErrorLimit > 0 && // pControlVelLimit > 0 && - p > 0; + pid->Kp > 0; } float PositionThroughVelocityControllerWithAccelerationBounds::run() const @@ -470,7 +562,8 @@ namespace armarx //handle case 1 const float positionError = targetPosition - currentPosition; - float newTargetVelPController = positionError * p; + pid->update((double)useddt, (double)currentPosition, (double)targetPosition); + float newTargetVelPController = pid->getControlValue(); //handle case 2-3 const float brakingDistance = signV * currentV * currentV / 2.f / deceleration; //the braking distance points in the direction of the velocity @@ -490,10 +583,15 @@ namespace armarx const float usedacc = decelerate ? -usedDeceleration : acceleration; const float deltaVel = signV * usedacc * useddt; float newTargetVelRampCtrl = boost::algorithm::clamp(currentV + deltaVel, -maxV, maxV); - currentlyPIDActive = std::abs(newTargetVelPController) < std::abs(newTargetVelRampCtrl) - || std::abs(newTargetVelPController) < pControlVelLimit - || std::abs(positionError) < pControlPosErrorLimit; - float finalTargetVel = (currentlyPIDActive && p > 0.0f) ? newTargetVelPController : newTargetVelRampCtrl; + bool PIDActive = /*std::abs(newTargetVelPController) < std::abs(newTargetVelRampCtrl) + || std::abs(newTargetVelPController) < pControlVelLimit ||*/ + std::abs(positionError) < pControlPosErrorLimit; + // if (currentlyPIDActive != PIDActive && PIDActive) + // { + // ARMARX_INFO << "Switching to PID mode: " << VAROUT(positionError) << VAROUT(newTargetVelPController) << VAROUT(newTargetVelRampCtrl); + // } + this->currentlyPIDActive = PIDActive; + float finalTargetVel = (currentlyPIDActive) ? newTargetVelPController : newTargetVelRampCtrl; if (std::abs(positionError) < accuracy) { return 0.0;// if close to target set zero velocity to avoid oscillating around target @@ -603,7 +701,6 @@ namespace armarx // and the brakingDistance have the same sign and brakingDistance < e // and currentVel <= maxV) // 5. we need to decelerate (other cases) - //handle case 1 const float vsquared = currentV * currentV; const float brakingDistance = signV * vsquared / 2.f / deceleration; //the braking distance points in the direction of the velocity @@ -776,13 +873,690 @@ namespace armarx return sub.run(); } + double PositionThroughVelocityControllerWithAccelerationRamps::getTargetPosition() const + { + return targetPosition; + } + bool PositionThroughVelocityControllerWithAccelerationBounds::getCurrentlyPIDActive() const { return currentlyPIDActive; } + void PositionThroughVelocityControllerWithAccelerationRamps::setTargetPosition(double value) + { + if (std::abs(value - targetPosition) > 0.0001) + { + state = State::Unknown; + } + targetPosition = value; + } + + PositionThroughVelocityControllerWithAccelerationRamps::PositionThroughVelocityControllerWithAccelerationRamps() + { + + } + + bool PositionThroughVelocityControllerWithAccelerationRamps::validParameters() const + { + return maxV > 0 && + acceleration > 0 && + deceleration > 0 && + jerk > 0 && + // pControlPosErrorLimit > 0 && + // pControlVelLimit > 0 && + p > 0; + } + + PositionThroughVelocityControllerWithAccelerationRamps::Output PositionThroughVelocityControllerWithAccelerationRamps::run() + { + PositionThroughVelocityControllerWithAccelerationRamps::Output result; + const double useddt = std::min(std::abs(dt), std::abs(maxDt)); + const double signV = sign(currentV); + //we can have 3 cases: + // 1. we use a p controller and ignore acc/dec (if |currentPosition - targetPosition| <= pControlPosErrorLimit AND |currentV| < pControlVelLimit) + // 2. we need to accelerate (or hold vel) (if e = (targetPosition - currentPosition) + // the brakingDistance have the same sign and brakingDistance < e + // and currentVel <= maxV) + // 3. we need to decelerate (other cases) + + //handle case 1 + const double positionError = targetPosition - currentPosition; + double newTargetVelPController = (positionError * p) * 0.5 + currentV * 0.5; + + //handle case 2-3 + auto brData = ctrlutil::braking_distance_with_wedge(currentV, currentAcc, jerk); + if (brData.dt2 < 0) + { + brData = ctrlutil::braking_distance_with_wedge(currentV, currentAcc, jerk * 10); + } + const auto goalDir = math::MathUtils::Sign(targetPosition - currentPosition); + // State currentState; + double usedAbsJerk; + State newState; + Output output; + std::tie(newState, output) = calcState(); + usedAbsJerk = output.jerk; + // double newJerk = output.jerk; + // double newAcceleration = output.acceleration; + // ARMARX_INFO << "state: " << (int)state << " new state: " << (int)newState; + state = newState; + const double brakingDistance = brData.dt2 >= 0 ? brData.s_total : signV * currentV * currentV / 2.f / deceleration; //the braking distance points in the direction of the velocity + const double posIfBrakingNow = currentPosition + brakingDistance; + const double posErrorIfBrakingNow = targetPosition - posIfBrakingNow; + const bool hardBrakingNeeded = std::abs(brakingDistance) > std::abs(positionError); + // const double usedJerk = jerk;//currentState==State::DecAccDecJerk?std::abs(ctrlutil::jerk(ctrlutil::t_to_v(currentV, -signV*currentAcc, signV*jerk), positionError, currentV, currentAcc)) + // :jerk; + const double jerkDir = (newState == State::DecAccIncJerk || newState == State::IncAccDecJerk) ? -1.0f : 1.0f; + const double usedJerk = goalDir * jerkDir * usedAbsJerk; + const double deltaAcc = usedJerk * useddt; + const double newAcceleration = (newState == State::Keep) ? currentAcc : (newState == State::DecAccDecJerk ? output.acceleration : currentAcc + deltaAcc); + result.acceleration = newAcceleration; + result.jerk = usedJerk; + const double usedDeceleration = hardBrakingNeeded ? + -std::abs(currentV * currentV / 2.f / math::MathUtils::LimitTo(positionError, 0.0001)) : + newAcceleration; + const bool decelerate = + std::abs(currentV) > maxV || // we need to slow down (to stay in [-maxV,maxV] + hardBrakingNeeded || + sign(posErrorIfBrakingNow) != signV; // we are moving away from the target + const double deltaVel = ctrlutil::v(useddt, 0, newAcceleration, usedJerk);//signV * newAcceleration * useddt; + double newTargetVelRampCtrl = ctrlutil::v(useddt, currentV, currentAcc, usedJerk);//boost::algorithm::clamp(currentV + deltaVel, -maxV, maxV); + newTargetVelRampCtrl = boost::algorithm::clamp(newTargetVelRampCtrl, -maxV, maxV); + bool usePID = std::abs(newTargetVelPController) < std::abs(newTargetVelRampCtrl); //|| + // std::abs(newTargetVelPController) < pControlVelLimit && + //std::abs(positionError) < pControlPosErrorLimit; + usePID |= state == State::PCtrl; + usePID &= usePIDAtEnd; + if (usePID) + { + state = State::PCtrl; + } + double finalTargetVel = usePID ? newTargetVelPController : newTargetVelRampCtrl; + ARMARX_INFO << VAROUT(usePID) << VAROUT(result.jerk) << VAROUT(newTargetVelPController) << VAROUT(currentV); + // ARMARX_INFO << VAROUT(currentV) << VAROUT(currentAcc) << VAROUT(usedJerk) << VAROUT(newAcceleration) << VAROUT(deltaVel) << VAROUT(finalTargetVel) << " new distance: " << (targetPosition - ctrlutil::s(dt, currentPosition, currentV, currentAcc, usedJerk)); + if (std::abs(positionError) < accuracy) + { + finalTargetVel = 0.0f; // if close to target set zero velocity to avoid oscillating around target + } + +#ifdef DEBUG_POS_CTRL + buffer.push_back({currentPosition, newTargetVelPController, newTargetVelRampCtrl, currentV, positionError, IceUtil::Time::now().toMicroSeconds()}); + + // if (PIDModeActive != usePID) + if (buffer.size() > 0 && sign(positionError) * sign(buffer[buffer.size() - 2].currentError) < 0 && eventHappeningCounter < 0) + { + eventHappeningCounter = 10; + ARMARX_IMPORTANT << "HIGH VELOCITY DETECTED"; + } + if (eventHappeningCounter == 0) + { + ARMARX_IMPORTANT << "BUFFER CONTENT"; + for (auto& elem : buffer) + { + ARMARX_INFO << VAROUT(elem.currentPosition) << VAROUT(elem.targetVelocityPID) << VAROUT(elem.targetVelocityRAMP) << VAROUT(elem.currentV) << VAROUT(elem.currentError) << VAROUT(elem.timestamp); + } + ARMARX_IMPORTANT << VAROUT(newTargetVelPController) << VAROUT(newTargetVelRampCtrl) << VAROUT(currentPosition) << VAROUT(usePID) << VAROUT(positionError); + + } + if (eventHappeningCounter >= 0) + { + eventHappeningCounter--; + } + + ARMARX_INFO << deactivateSpam(5) << VAROUT(newTargetVelPController) << VAROUT(newTargetVelRampCtrl) << VAROUT(currentPosition) << VAROUT(usePID) << VAROUT(positionError) << VAROUT(targetPosition) << VAROUT(currentV) << VAROUT(deltaVel) << VAROUT(useddt) << VAROUT(usedacc); + PIDModeActive = usePID; +#endif + result.velocity = finalTargetVel; + return result; + } + + double PositionThroughVelocityControllerWithAccelerationRamps::estimateTime() const + { + throw LocalException("NYI"); + return 0; + } + + double PositionThroughVelocityControllerWithAccelerationRamps::calculateProportionalGain() const + { + /* s = v_0*v_0/(2*a) -> sqrt(s*2*a) = v_0; + K_p * error = v_0; -> v_0/error = K_p; + */ + auto v_0 = std::sqrt(pControlPosErrorLimit * 2 * deceleration); + return v_0 / pControlPosErrorLimit; + } + std::pair<PositionThroughVelocityControllerWithAccelerationRamps::State, + PositionThroughVelocityControllerWithAccelerationRamps::Output> PositionThroughVelocityControllerWithAccelerationRamps::calcState() const + { + // auto integratedChange = [](v0) + + // double newJerk = this->jerk; + // double newAcc = currentAcc; + // double newVel = currentV; + State newState = state; + double a0 = currentAcc, newJerk = jerk, t; + Output result = {currentV, a0, newJerk}; + + const auto brData = ctrlutil::braking_distance_with_wedge(currentV, currentAcc, newJerk); + const auto goalDir = math::MathUtils::Sign(targetPosition - currentPosition); + const auto curVDir = math::MathUtils::Sign(currentV); + const auto straightBrakingDistance = ctrlutil::brakingDistance(currentV, currentAcc, newJerk); + const double brakingDistance = brData.dt2 < 0 ? straightBrakingDistance : brData.s_total; + const double distance = std::abs(targetPosition - currentPosition); + const double t_to_stop = ctrlutil::t_to_v(currentV, -curVDir * currentAcc, curVDir * newJerk); + const auto calculatedJerk = std::abs(ctrlutil::jerk(t_to_stop, distance, currentV, currentAcc)); + double tmp_t, tmp_acc, tmp_jerk; + // std::tie( tmp_t, tmp_acc, tmp_jerk) = ctrlutil::calcAccAndJerk(targetPosition-(currentPosition+brData.s1), brData.v1); + std::tie(tmp_t, tmp_acc, tmp_jerk) = ctrlutil::calcAccAndJerk(targetPosition - currentPosition, currentV); + const double posWhenBrakingWithCustomJerk = ctrlutil::s(tmp_t, currentPosition + brData.s1, brData.v1, tmp_acc, curVDir * tmp_jerk); + // const double brakingDistanceWithCustomJerk = ctrlutil::s(tmp_t, 0, currentV, tmp_acc, curVDir * tmp_jerk); + const double posWhenBrakingWithFixedJerk = ctrlutil::s(brData.dt2, currentPosition + brData.s1, brData.v1, brData.a1, curVDir * jerk); + + ARMARX_INFO << VAROUT((int)state) << VAROUT(distance) << VAROUT(brakingDistance);//VAROUT(straightBrakingDistance) << VAROUT(brData.dt1) << VAROUT(brData.dt2) << VAROUT(brData.s_total) << VAROUT(calculatedJerk); + + // if(brData.dt2 < 0) + // { + // ARMARX_INFO << "distance with fixed jerk: " << ctrlutil::brakingDistance(currentV, currentAcc, newJerk) + // << " distance with custom jerk: " << ctrlutil::brakingDistance(currentV, currentAcc, calculatedJerk) + // << " vel after: " << ctrlutil::v(t_to_stop, currentV, currentAcc, -curVDir * newJerk) << " vel after next step: " << ctrlutil::v(dt, currentV, currentAcc, -curVDir * newJerk); + //// << " acc at stop: " << ctrlutil::a(t_to_stop, currentAcc, -curVDir *newJerk); + // jerk = 0.95*calculatedJerk; + // state = State::DecAccDecJerk; + // return; + // } + if (state < State::DecAccIncJerk && + ((distance > brakingDistance && (math::MathUtils::Sign(currentAcc) == curVDir || std::abs(currentAcc) < 0.1)) // we are accelerating + || distance > brakingDistance * 2 // we are decelerating -> dont switch to accelerating to easily + || curVDir != goalDir)) // we are moving away from goal + { + if (std::abs(maxV - currentV) < 0.1 && std::abs(currentAcc) < 0.1) + { + newState = State::Keep; + newJerk = 0; + return std::make_pair(newState, result); + } + // calculate if we need to increase or decrease acc + double t_to_max_v = ctrlutil::t_to_v(goalDir * maxV - currentV, currentAcc, goalDir * newJerk); + double acc_at_t = ctrlutil::a(t_to_max_v, std::abs(currentAcc), -newJerk); + if (acc_at_t < 0.1) + { + newState = State::IncAccIncJerk; + return std::make_pair(newState, result); + } + else + { + newState = State::IncAccDecJerk; + return std::make_pair(newState, result); + } + } + else + { + // calculate if we need to increase or decrease acc + double t_to_0 = ctrlutil::t_to_v(currentV, -curVDir * currentAcc, curVDir * newJerk); + double tmpV = ctrlutil::v(t_to_0, 0, currentAcc, -curVDir * newJerk); + double vAfterBraking = ctrlutil::v(t_to_0, currentV, currentAcc, -curVDir * newJerk); + double accAfterBraking = ctrlutil::a(t_to_0, currentAcc, curVDir * newJerk); + const double posWhenBrakingNow = ctrlutil::s(t_to_0, currentPosition, currentV, currentAcc, -curVDir * newJerk); + const double simpleBrakingDistance = ctrlutil::s(t_to_0, 0, currentV, currentAcc, -curVDir * newJerk); + double acc_at_t = ctrlutil::a(t_to_0, std::abs(currentAcc), -newJerk); + + std::tie(t, a0, newJerk) = ctrlutil::calcAccAndJerk(targetPosition - currentPosition, currentV); + double at = ctrlutil::a(t, a0, newJerk); + double vt = ctrlutil::v(t, currentV, a0, newJerk); + double st = ctrlutil::s(t, targetPosition - currentPosition, currentV, a0, newJerk); + if (this->state <= State::Keep) + { + newState = State::DecAccIncJerk; + return std::make_pair(newState, result); + } + else if ((state == State::DecAccIncJerk && acc_at_t > 1) || state == State::DecAccDecJerk) + { + result.jerk = newJerk; + result.acceleration = currentAcc + (a0 - currentAcc) * 0.5 + dt * newJerk; + newState = State::DecAccDecJerk; + return std::make_pair(newState, result); + } + else + { + return std::make_pair(newState, result); + } + + } + throw LocalException(); + } + + + + void MinJerkPositionController::reset() + { + currentTime = 0; + + fixedMinJerkState.reset(); + // pid.reset(); + currentP_Phase2 = -1; + currentP_Phase3 = -1; + } + + double MinJerkPositionController::getTargetPosition() const + { + return targetPosition; + } + + // MinJerkPositionController::Output MinJerkPositionController::run() + // { + // auto min_jerk_calc_jerk = [&](double tf, double s0, double v0, double a0, double xf = 0, double t0 = 0.0) + // { + // double D = tf - t0; + // return 60.0 / (D * D * D) * (xf - s0) - 36.0 / (D * D) * v0 - 9.0 / D * a0; + // }; + + + // auto min_jerk = [&](double t, double tf, double s0, double v0, double a0, double xf = 0, double t0 = 0.0) + // { + // double D = tf - t0; + // BOOST_ASSERT(D != 0.0); + // double D2 = D * D; + // double tau = (t - t0) / D; + // double b0 = s0; + // double b1 = D * v0; + // double b2 = D2 * a0 / 2; + + // double b3 = (-3 * D2) / 2 * a0 - 6 * D * v0 + 10 * (xf - s0); + // double b4 = (3 * D2) / 2 * a0 + 8 * D * v0 - 15 * (xf - s0); + // double b5 = (-D2) / 2 * a0 - 3 * D * v0 + 6 * (xf - s0); + + // double tau2 = tau * tau; + // double tau3 = tau2 * tau; + // double tau4 = tau3 * tau; + // double tau5 = tau4 * tau; + // double st = b0 + b1 * tau + b2 * tau2 + b3 * tau3 + b4 * tau4 + b5 * tau5; + // double vt = b1 / D + 2 * b2 / D * tau + 3 * b3 / D * tau2 + 4 * b4 / D * tau3 + 5 * b5 / D * tau4; + // double at = 2 * b2 / D2 + 6 * b3 / D2 * tau + 12 * b4 / D2 * tau2 + 20 * b5 / D2 * tau3; + // return State{t, st, vt, at}; + // }; + + // double remainingTime = finishTime - currentTime; + // double signedDistance = targetPosition - currentPosition; + // if (remainingTime <= 0.0) + // { + // // if(!pid) + // // { + // // double Kd = (currentV - distance * p) /(-currentV); + // // ARMARX_INFO << VAROUT(Kd); + // // pid.reset(new PIDController(1, 0, Kd)); + // // } + // if (currentP_Phase3 < 0) + // { + // currentP_Phase3 = std::abs(currentV / signedDistance); + // ARMARX_DEBUG << "optimal P: " << currentP_Phase3; + // } + + + // currentP_Phase3 = currentP_Phase3 * p_adjust_ratio + p * (1.0 - p_adjust_ratio); + // double newVel = signedDistance * currentP_Phase3; + // double newAcc = (newVel - currentV) / dt; + // double newJerk = (newAcc - currentAcc) / dt; + // double newPos = currentPosition + newVel * dt; + // Output result {newPos, newVel, newAcc, newJerk}; + // currentTime += dt; + // // State s = min_jerk(currentTime, finishTime, fixedMinJerkState->s0, fixedMinJerkState->v0, fixedMinJerkState->a0, targetPosition, fixedMinJerkState->t0); + // ARMARX_DEBUG << VAROUT(remainingTime) << VAROUT(signedDistance) << VAROUT(signedDistance * currentP_Phase3) << VAROUT(currentP_Phase3);// << VAROUT(s.s) << VAROUT(s.v) << VAROUT(s.a); + // return result; + // } + + // if (std::abs(signedDistance) < phase2SwitchDistance && remainingTime < phase2SwitchMaxRemainingTime) + // { + // if (!fixedMinJerkState) + // { + // fixedMinJerkState.reset(new FixedMinJerkState{currentTime, currentPosition, currentV, currentAcc}); + // currentP_Phase2 = 0; + // } + // // std::vector<State> states; + // // double t = currentTime; + // // while(t < finishTime) + // // { + // // State s = min_jerk(t, finishTime, fixedMinJerkState->s0, fixedMinJerkState->v0, fixedMinJerkState->a0, targetPosition, fixedMinJerkState->t0); + // // t += dt; + // // states.push_back(s); + // // } + // currentTime += dt; + // State s = min_jerk(currentTime, finishTime, fixedMinJerkState->s0, fixedMinJerkState->v0, fixedMinJerkState->a0, targetPosition, fixedMinJerkState->t0); + // currentP_Phase2 = currentP_Phase2 * p_adjust_ratio + p * (1.0 - p_adjust_ratio); + // double newVel = (s.s - currentPosition) * currentP_Phase2 + s.v; + // ARMARX_DEBUG << VAROUT(s.s) << VAROUT(s.v) << VAROUT(newVel); + // double newAcc = (newVel - currentV) / dt; + // double newJerk = (newAcc - currentAcc) / dt; + // double newPos = currentPosition + newVel * dt; + // Output result {newPos, newVel, newAcc, newJerk}; + // return result; + // } + + + + + // double jerk = min_jerk_calc_jerk(remainingTime, currentPosition, currentV, currentAcc, targetPosition); + // double newAcc = currentAcc + jerk * dt; + // double newVelocity = ctrlutil::v(dt, currentV, currentAcc, jerk); + // double accAtEnd = ctrlutil::a(remainingTime, currentAcc, jerk); + + // std::vector<State> states; + // std::vector<State> states2; + // //#define debug + //#ifdef debug + // { + // double dt = 0.001; + // double t = 1; + // double simS = currentPosition, simV = currentV, simAcc = currentAcc; + // while (t > 0) + // { + // double jerk = min_jerk_calc_jerk(t, simS, simV, simAcc, targetPosition); + // simAcc = simAcc + jerk * dt; + // simV = ctrlutil::v(dt, simV, simAcc, 0); + // simS += dt * simV; + // t -= dt; + // states.push_back(State{t, simS, simV, simAcc, jerk}); + // } + // } + // { + // double dt = 0.001; + + // double t = 1; + // double simS = currentPosition, simV = currentV, simAcc = currentAcc; + // while (t > 0) + // { + // double jerk = min_jerk_calc_jerk(t, simS, simV, simAcc, targetPosition); + // simS += ctrlutil::s(dt, 0, simV, simAcc, jerk); + // simV = ctrlutil::v(dt, simV, simAcc, jerk); + // simAcc = simAcc + jerk * dt; + // t -= dt; + // states2.push_back(State{t, simS, simV , simAcc, jerk}); + // } + // } + //#endif + // ARMARX_DEBUG << VAROUT(currentTime) << VAROUT(remainingTime) << VAROUT(jerk) << VAROUT(accAtEnd) << VAROUT(signedDistance) << " delta acc: " << jerk* dt; + // currentTime += dt; + + + // double newPos = ctrlutil::s(dt, currentPosition, currentV, currentAcc, jerk); + // Output result { newPos, newVelocity, newAcc, jerk}; + // return result; + + // } + + bool VelocityControllerWithRampedAccelerationAndPositionBounds::validParameters() const + { + return VelocityControllerWithRampedAcceleration::validParameters() && + positionLimitHiSoft > positionLimitLoSoft && + deceleration > 0; + } + + VelocityControllerWithRampedAcceleration::Output VelocityControllerWithRampedAccelerationAndPositionBounds::run() const + { + const double useddt = std::min(std::abs(dt), std::abs(maxDt)); + + // if (currentPosition <= positionLimitLoHard || currentPosition >= positionLimitHiHard) + // { + // ARMARX_INFO << deactivateSpam(1) << "Hard limit violation. " << VAROUT(currentPosition) << VAROUT(positionLimitLoHard) << VAROUT(positionLimitHiHard); + // return std::nanf("1"); + // } + + double softLimitViolation = 0; + if (currentPosition <= positionLimitLoSoft) + { + softLimitViolation = -1; + } + if (currentPosition >= positionLimitHiSoft) + { + softLimitViolation = 1; + } + + const double upperDt = std::max(std::abs(dt), std::abs(maxDt)); + + //we can have 4 cases: + // 1. we need to decelerate now or we will violate the position limits (maybe we still will violate them, e.g. if we violated them initially) + // 2. we directly set v and ignore acc/dec (if |curr - target| <= limit) + // 3. we need to accelerate (if curr and target v have same sign and |curr| < |target|) + // 4. we need to decelerate (other cases) + double nextv; + //handle case 1 + const double vsquared = currentV * currentV; + const double brakingDist = sign(currentV) * vsquared / 2.f / std::abs(deceleration); //the braking distance points in the direction of the velocity + + const double posIfBrakingNow = currentPosition + brakingDist; + // ARMARX_INFO << deactivateSpam(0.1) << VAROUT(currentPosition) << VAROUT(positionLimitLoSoft) << VAROUT(positionLimitHiSoft) << VAROUT(posIfBrakingNow) << VAROUT(currentV); + if ((posIfBrakingNow <= positionLimitLoSoft && currentV < 0) || (posIfBrakingNow >= positionLimitHiSoft && currentV > 0)) + { + //case 1. -> brake now! (we try to have v=0 at the limit) + const auto limit = posIfBrakingNow <= positionLimitLoSoft ? positionLimitLoSoft : positionLimitHiSoft; + const double wayToGo = limit - currentPosition; + + //decelerate! + // s = v²/(2a) <=> a = v²/(2s) + const double dec = std::abs(vsquared / 2.0 / wayToGo); + const double vel = currentV - sign(currentV) * dec * upperDt; + nextv = boost::algorithm::clamp(vel, -maxV, maxV); + // ARMARX_INFO /*<< deactivateSpam(0.1) */ << "clamped new Vel: " << VAROUT(nextv) << VAROUT(currentV); + if (sign(currentV) != sign(nextv)) + { + // ARMARX_INFO << deactivateSpam(1) << "wrong sign: stopping"; //stop now + nextv = 0; + } + } + else + { + //handle 2-3 + return VelocityControllerWithRampedAcceleration::run(); + // ARMARX_INFO << deactivateSpam(1) << "Desired target Vel: " << targetV << " " << VAROUT(nextv); + + } + if (softLimitViolation == sign(nextv) && nextv != 0) + { + //the area between soft and hard limits is sticky + //the controller can only move out of it (not further in) + // ARMARX_INFO << deactivateSpam(1) << "Soft limit violation. " << softLimitViolation << VAROUT(nextv); + nextv = 0; + } + double nextAcc = (nextv - currentV) / useddt; // doesnt work with calculated acc + double nextJerk = (nextAcc - currentAcc) / useddt; + //the next velocity will not violate the pos bounds harder than they are already + return Output {nextv, 0, 0}; + } + + + + void MinJerkPositionController::setTargetPosition(double newTargetPosition) + { + if (std::abs(targetPosition - newTargetPosition) > 0.0001) + { + reset(); + double distance = newTargetPosition - currentPosition; + double rawFinishTime = 0.0; + double decelerationTime = std::abs(currentV / desiredDeceleration); + // calculate the time it should take to reach the goal (ToDo: find exact time) + if (math::MathUtils::Sign(currentV) == math::MathUtils::Sign(distance) + && std::abs(currentV) > 0.0 + && ctrlutil::s(decelerationTime, 0, std::abs(currentV), -desiredDeceleration, 0) > std::abs(distance)) + // && ctrlutil::brakingDistance(currentV, currentAcc, desiredJerk) < distance) + { + // ARMARX_INFO << "branch 1: " << VAROUT(currentV) << VAROUT(desiredDeceleration) << VAROUT(distance); + rawFinishTime = decelerationTime; + } + else + { + // ARMARX_INFO << "branch 2: " << VAROUT(currentV) << VAROUT(desiredDeceleration) << VAROUT(distance); + double accelerationTime = std::abs((math::MathUtils::Sign(distance) * maxV - currentV) / desiredDeceleration); + double decelerationTime = std::abs(maxV / desiredDeceleration); + rawFinishTime = std::max(decelerationTime + accelerationTime, std::abs(distance / (maxV * 0.75))); + } + double estimatedTime = ctrlutil::t_to_v_with_wedge_acc(std::abs(distance), std::abs(currentV), std::abs(desiredDeceleration)); + double minTime = 0.8; + if (!std::isnan(estimatedTime)) + { + finishTime = std::max(estimatedTime, minTime); + } + else + { + finishTime = std::max(rawFinishTime, minTime); + } + targetPosition = newTargetPosition; + // ARMARX_INFO << "New finish time: " << finishTime << " " << VAROUT(rawFinishTime) << VAROUT(estimatedTime); + + } + + + } + + MinJerkPositionController::Output MinJerkPositionController::run() + { + auto min_jerk_calc_jerk = [&](double tf, double s0, double v0, double a0, double xf = 0, double t0 = 0.0) + { + double D = tf - t0; + return 60.0 / (D * D * D) * (xf - s0) - 36.0 / (D * D) * v0 - 9.0 / D * a0; + }; + + + auto min_jerk = [&](double t, double tf, double s0, double v0, double a0, double xf = 0, double t0 = 0.0) + { + double D = tf - t0; + BOOST_ASSERT(D != 0.0); + double D2 = D * D; + double tau = (t - t0) / D; + double b0 = s0; + double b1 = D * v0; + double b2 = D2 * a0 / 2; + + double b3 = (-3 * D2) / 2 * a0 - 6 * D * v0 + 10 * (xf - s0); + double b4 = (3 * D2) / 2 * a0 + 8 * D * v0 - 15 * (xf - s0); + double b5 = (-D2) / 2 * a0 - 3 * D * v0 + 6 * (xf - s0); + + double tau2 = tau * tau; + double tau3 = tau2 * tau; + double tau4 = tau3 * tau; + double tau5 = tau4 * tau; + double st = b0 + b1 * tau + b2 * tau2 + b3 * tau3 + b4 * tau4 + b5 * tau5; + double vt = b1 / D + 2 * b2 / D * tau + 3 * b3 / D * tau2 + 4 * b4 / D * tau3 + 5 * b5 / D * tau4; + double at = 2 * b2 / D2 + 6 * b3 / D2 * tau + 12 * b4 / D2 * tau2 + 20 * b5 / D2 * tau3; + return State {t, st, vt, at}; + }; + + double remainingTime = finishTime - currentTime; + double signedDistance = targetPosition - currentPosition; + if (remainingTime <= 0.0) + { + // if(!pid) + // { + // double Kd = (currentV - distance * p) /(-currentV); + // ARMARX_INFO << VAROUT(Kd); + // pid.reset(new PIDController(1, 0, Kd)); + // } + if (currentP_Phase3 < 0) + { + if (signedDistance == 0.0f) + { + currentP_Phase3 = p; + } + else + { + currentP_Phase3 = std::abs(currentV / signedDistance); + currentP_Phase3 = math::MathUtils::LimitTo(currentP_Phase3, p * 10); + } + // ARMARX_INFO << deactivateSpam(0.1) << "optimal P: " << currentP_Phase3 << VAROUT(currentV) << VAROUT(signedDistance); + } + + + currentP_Phase3 = currentP_Phase3 * p_adjust_ratio + p * (1.0 - p_adjust_ratio); + double newVel = signedDistance * currentP_Phase3; + double newAcc = (newVel - currentV) / dt; + double newJerk = (newAcc - currentAcc) / dt; + double newPos = currentPosition + newVel * dt; + + Output result {newPos, newVel, newAcc, newJerk}; + currentTime += dt; + // State s = min_jerk(currentTime, finishTime, fixedMinJerkState->s0, fixedMinJerkState->v0, fixedMinJerkState->a0, targetPosition, fixedMinJerkState->t0); + // ARMARX_INFO << deactivateSpam(0.1) << VAROUT(remainingTime) << VAROUT(signedDistance) << VAROUT(signedDistance * currentP_Phase3) << VAROUT(currentP_Phase3);// << VAROUT(s.s) << VAROUT(s.v) << VAROUT(s.a); + return result; + } + + if (std::abs(signedDistance) < phase2SwitchDistance && remainingTime < phase2SwitchMaxRemainingTime) + { + if (!fixedMinJerkState) + { + fixedMinJerkState.reset(new FixedMinJerkState {currentTime, currentPosition, currentV, currentAcc}); + currentP_Phase2 = 0; + } + // std::vector<State> states; + // double t = currentTime; + // while(t < finishTime) + // { + // State s = min_jerk(t, finishTime, fixedMinJerkState->s0, fixedMinJerkState->v0, fixedMinJerkState->a0, targetPosition, fixedMinJerkState->t0); + // t += dt; + // states.push_back(s); + // } + currentTime += dt; + State s = min_jerk(currentTime, finishTime, fixedMinJerkState->s0, fixedMinJerkState->v0, fixedMinJerkState->a0, targetPosition, fixedMinJerkState->t0); + double newVelocity = (s.s - (currentPosition + s.v * dt)) * currentP_Phase2 + s.v; + double newAcc = (newVelocity - currentV) / dt; + double newJerk = (newAcc - currentAcc) / dt; + double newPos = ctrlutil::s(dt, currentPosition, currentV, currentAcc, newJerk);//currentPosition + newVelocity * dt; + // ARMARX_INFO /*<< deactivateSpam(0.1) */ << VAROUT(currentP_Phase2) << VAROUT(currentPosition) << VAROUT(signedDistance) << VAROUT(s.s) << VAROUT(s.v) << VAROUT(newVelocity) << VAROUT(newAcc) << VAROUT(newJerk) << VAROUT(s.a); + currentP_Phase2 = currentP_Phase2 * p_adjust_ratio + p * (1.0 - p_adjust_ratio); + Output result {newPos, newVelocity, newAcc, newJerk}; + return result; + } + + + + + double jerk = min_jerk_calc_jerk(remainingTime, currentPosition, currentV, currentAcc, targetPosition); + double newAcc = currentAcc + jerk * dt; + double newVelocity = ctrlutil::v(dt, currentV, currentAcc, jerk); + double accAtEnd = ctrlutil::a(remainingTime, currentAcc, jerk); // assumes that fixed jerk would be used, which is not the case here + + std::vector<State> states; + std::vector<State> states2; + //#define debug +#ifdef debug + { + double dt = 0.001; + double t = 1; + double simS = currentPosition, simV = currentV, simAcc = currentAcc; + while (t > 0) + { + double jerk = min_jerk_calc_jerk(t, simS, simV, simAcc, targetPosition); + simAcc = simAcc + jerk * dt; + simV = ctrlutil::v(dt, simV, simAcc, 0); + simS += dt * simV; + t -= dt; + states.push_back(State {t, simS, simV, simAcc, jerk}); + } + } + { + double dt = 0.001; + + double t = 1; + double simS = currentPosition, simV = currentV, simAcc = currentAcc; + while (t > 0) + { + double jerk = min_jerk_calc_jerk(t, simS, simV, simAcc, targetPosition); + simS += ctrlutil::s(dt, 0, simV, simAcc, jerk); + simV = ctrlutil::v(dt, simV, simAcc, jerk); + simAcc = simAcc + jerk * dt; + t -= dt; + states2.push_back(State {t, simS, simV, simAcc, jerk}); + } + } +#endif + // ARMARX_INFO << /*deactivateSpam(0.1) <<*/ VAROUT(currentTime) << VAROUT(remainingTime) << VAROUT(jerk) << VAROUT(accAtEnd) << VAROUT(signedDistance) << " new acc: " << newAcc << VAROUT(currentV); + currentTime += dt; + + + double newPos = ctrlutil::s(dt, currentPosition, currentV, currentAcc, jerk); + Output result {newPos, newVelocity, newAcc, jerk}; + return result; + + } } diff --git a/source/RobotAPI/components/units/RobotUnit/BasicControllers.h b/source/RobotAPI/components/units/RobotUnit/BasicControllers.h index e15f9a3cd8cacd07118ddfd33dc185b05613bdf2..b164554c2c35c4cc219aaab75b96e2c10c781632 100644 --- a/source/RobotAPI/components/units/RobotUnit/BasicControllers.h +++ b/source/RobotAPI/components/units/RobotUnit/BasicControllers.h @@ -26,11 +26,14 @@ #include <type_traits> #include <ArmarXCore/core/util/algorithm.h> #include <RobotAPI/libraries/core/math/MathUtils.h> +#include <RobotAPI/libraries/core/PIDController.h> // #define DEBUG_POS_CTRL #ifdef DEBUG_POS_CTRL #include <boost/circular_buffer.hpp> #endif + + namespace armarx { template<class T, class = typename std::enable_if<std:: is_floating_point<T>::value>::type > @@ -142,7 +145,7 @@ namespace armarx */ inline float brakingDistance(float v0, float deceleration) { - return accelerateToVelocity(v0, deceleration, 0).dx; + return accelerateToVelocity(std::abs(v0), std::abs(deceleration), 0).dx; } inline float angleDistance(float angle1, float angle2) @@ -166,6 +169,42 @@ namespace armarx float estimateTime() const; }; + struct VelocityControllerWithRampedAcceleration + { + + struct Output + { + double velocity; + double acceleration; + double jerk; + }; + float dt; + float maxDt; + float currentV; + float currentAcc; + float targetV; + float maxV; + float jerk; + float directSetVLimit; + + + bool validParameters() const; + Output run() const; + }; + + struct VelocityControllerWithRampedAccelerationAndPositionBounds: VelocityControllerWithRampedAcceleration + { + + double currentPosition; + double positionLimitLoSoft; + double positionLimitHiSoft; + // double positionLimitLoHard; + // double positionLimitHiHard; + double deceleration; + bool validParameters() const; + Output run() const; + }; + float velocityControlWithAccelerationBounds( float dt, float maxDt, const float currentV, float targetV, float maxV, @@ -195,6 +234,7 @@ namespace armarx */ struct VelocityControllerWithAccelerationAndPositionBounds: VelocityControllerWithAccelerationBounds { + float currentPosition; float positionLimitLoSoft; float positionLimitHiSoft; @@ -229,8 +269,10 @@ namespace armarx float pControlPosErrorLimit = 0.01; float pControlVelLimit = 0.002; // if target is below this threshold, PID controller will be used float accuracy = 0.001; - float p; + std::shared_ptr<PIDController> pid; + // float p; PositionThroughVelocityControllerWithAccelerationBounds(); + float calculateProportionalGain() const; bool validParameters() const; float run() const; float estimateTime() const; @@ -255,7 +297,76 @@ namespace armarx private: mutable bool currentlyPIDActive = false; + + }; + + struct PositionThroughVelocityControllerWithAccelerationRamps + { + enum class State + { + Unknown = -1, + IncAccIncJerk, + IncAccDecJerk, + Keep, + DecAccIncJerk, + DecAccDecJerk, + PCtrl + + }; + struct Output + { + double velocity; + double acceleration; + double jerk; + }; + + double dt; + double maxDt; + double currentV; + double currentAcc; + double maxV; + double acceleration; + double deceleration; + double jerk; + double currentPosition; + protected: + double targetPosition; + public: + double getTargetPosition() const; + void setTargetPosition(double value); + + double pControlPosErrorLimit = 0.01; + double pControlVelLimit = 0.002; // if target is below this threshold, PID controller will be used + double accuracy = 0.001; + double p; + bool usePIDAtEnd = true; + mutable State state = State::Unknown; + PositionThroughVelocityControllerWithAccelerationRamps(); + bool validParameters() const; + Output run(); + double estimateTime() const; + double calculateProportionalGain() const; + + + std::pair<State, Output> calcState() const; +#ifdef DEBUG_POS_CTRL + mutable bool PIDModeActive = false; + struct HelpStruct + { + double currentPosition; + double targetVelocityPID; + double targetVelocityRAMP; + double currentV; + double currentError; + long timestamp; + }; + + mutable boost::circular_buffer<HelpStruct> buffer; + mutable int eventHappeningCounter = -1; +#endif + }; + float positionThroughVelocityControlWithAccelerationBounds(float dt, float maxDt, float currentV, float maxV, float acceleration, float deceleration, @@ -290,5 +401,52 @@ namespace armarx float pControlPosErrorLimit, float p, float& direction, float positionPeriodLo, float positionPeriodHi); + + class MinJerkPositionController + { + public: + struct State + { + double t, s, v, a; + }; + struct FixedMinJerkState + { + double t0, s0, v0, a0; + }; + struct Output + { + double position; + double velocity; + double acceleration; + double jerk; + }; + + double dt; + double maxDt; + double currentPosition; + double currentV; + double currentAcc; + double maxV; + double desiredDeceleration; + double desiredJerk; + double p = 1.0; + double phase2SwitchDistance = 0.1; + double phase2SwitchMaxRemainingTime = 0.2; // seconds + double p_adjust_ratio = 0.8; + // PIDControllerPtr pid; + protected: + double currentP_Phase3 = -1; + double currentP_Phase2 = -1; + double targetPosition; + double finishTime = 0; + double currentTime = 0; + std::unique_ptr<FixedMinJerkState> fixedMinJerkState; + public: + void reset(); + double getTargetPosition() const; + void setTargetPosition(double value); + + Output run(); + }; } diff --git a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt old mode 100644 new mode 100755 index 397d386f44f8b8349526e92382a49b3fb60dad25..60ce76b94f2d47967c60ff5799dac1a864392c8c --- a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt +++ b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt @@ -13,6 +13,7 @@ set(LIBS set(LIB_FILES util.cpp util/ControlThreadOutputBuffer.cpp + util/DynamicsHelper.cpp util/introspection/DataFieldsInfo.cpp SyntaxCheck.cpp RobotUnit.cpp @@ -42,6 +43,7 @@ set(LIB_FILES NJointControllers/NJointKinematicUnitPassThroughController.cpp NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp NJointControllers/NJointHolonomicPlatformRelativePositionController.cpp + NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp NJointControllers/NJointTCPController.cpp NJointControllers/NJointCartesianVelocityController.cpp NJointControllers/NJointCartesianTorqueController.cpp @@ -60,6 +62,7 @@ set(LIB_HEADERS util/KeyValueVector.h util/AtomicWrapper.h util/Time.h + util/DynamicsHelper.h util/ControlThreadOutputBuffer.h util/JointAndNJointControllers.h util/introspection/DataFieldsInfo.h @@ -67,6 +70,7 @@ set(LIB_HEADERS util/introspection/ClassMemberInfo.h util/RtLogging.h util/RtTiming.h + util/CtrlUtil.h #robot unit modules need to be added to the list below (but not here) RobotUnitModules/RobotUnitModuleBase.h @@ -108,6 +112,7 @@ set(LIB_HEADERS NJointControllers/NJointKinematicUnitPassThroughController.h NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h NJointControllers/NJointHolonomicPlatformRelativePositionController.h + NJointControllers/NJointHolonomicPlatformGlobalPositionController.h NJointControllers/NJointTCPController.h NJointControllers/NJointCartesianVelocityController.h NJointControllers/NJointCartesianTorqueController.h diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianVelocityControllerWithRamp.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianVelocityControllerWithRamp.h index d23148c615cd88770586a77d4b6e081deba09023..7b9994929a379228956f72a0f6de7f8efbdcf474 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianVelocityControllerWithRamp.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointCartesianVelocityControllerWithRamp.h @@ -138,7 +138,7 @@ namespace armarx // NJointCartesianVelocityControllerWithRampInterface interface public: - void setMaxAccelerations(float maxPositionAcceleration, float maxOrientationAcceleration, float maxNullspaceAcceleration, const Ice::Current& = Ice::Current()) override; + void setMaxAccelerations(float maxPositionAcceleration, float maxOrientationAcceleration, float maxNullspaceAcceleration, const Ice::Current& = Ice::emptyCurrent) override; void setTargetVelocity(float vx, float vy, float vz, float vrx, float vry, float vrz, const Ice::Current&) override; void setJointLimitAvoidanceScale(float jointLimitAvoidanceScale, const Ice::Current&) override; void setKpJointLimitAvoidance(float KpJointLimitAvoidance, const Ice::Current&) override; diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.cpp index 6fd11bd486166177694ec16cee0b594202cda03f..86911d7e44b317bf7d04ea226a516853967947c4 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.cpp @@ -252,7 +252,7 @@ namespace armarx return RobotUnitModule::Devices::Instance().getControlDevice(deviceName); } - void NJointController::useSynchronizedRtRobot(bool updateCollisionModel) + const VirtualRobot::RobotPtr& NJointController::useSynchronizedRtRobot(bool updateCollisionModel) { ARMARX_CHECK_EXPRESSION_W_HINT( NJointControllerRegistryEntry::ConstructorIsRunning(), @@ -261,39 +261,27 @@ namespace armarx ARMARX_CHECK_IS_NULL_W_HINT(rtRobot, "useSynchronizedRtRobot was already called"); rtRobot = RobotUnitModule::RobotData::Instance().cloneRobot(updateCollisionModel); rtRobotNodes = rtRobot->getRobotNodes(); + return rtRobot; } - ThreadPoolPtr NJointController::getThreadPool() const + void NJointController::onInitComponent() { - ARMARX_CHECK_EXPRESSION(Application::getInstance()); - return Application::getInstance()->getThreadPool(); + onInitNJointController(); } - - const SensorValueBase* NJointController::useSensorValue(const std::string& deviceName) const + void NJointController::onConnectComponent() { - ARMARX_CHECK_EXPRESSION_W_HINT( - NJointControllerRegistryEntry::ConstructorIsRunning(), - "The function '" << BOOST_CURRENT_FUNCTION << "' must only be called during the construction of an NJointController." - ); - auto dev = peekSensorDevice(deviceName); - if (!dev) - { - ARMARX_DEBUG << "No sensor device for " << deviceName; - return nullptr; - } - return dev->getSensorValue(); + onConnectNJointController(); } - NJointController::NJointController() : - robotUnit(RobotUnitModule::ModuleBase::Instance< ::armarx::RobotUnit>()), - controlDeviceUsedBitmap(robotUnit.getNumberOfControlDevices(), 0) + void NJointController::onDisconnectComponent() { - controlDeviceUsedIndices.reserve(robotUnit.getNumberOfControlDevices()); + onDisconnectNJointController(); } - NJointController::~NJointController() + void NJointController::onExitComponent() { + onExitNJointController(); // make sure the destructor of the handles does not throw an exception and triggers a termination of the process ARMARX_DEBUG << "Deleting thread handles"; ScopedLock lock(threadHandlesMutex); @@ -337,8 +325,39 @@ namespace armarx } threadHandles.clear(); + } + + ThreadPoolPtr NJointController::getThreadPool() const + { + ARMARX_CHECK_EXPRESSION(Application::getInstance()); + return Application::getInstance()->getThreadPool(); + } + + + const SensorValueBase* NJointController::useSensorValue(const std::string& deviceName) const + { + ARMARX_CHECK_EXPRESSION_W_HINT( + NJointControllerRegistryEntry::ConstructorIsRunning(), + "The function '" << BOOST_CURRENT_FUNCTION << "' must only be called during the construction of an NJointController." + ); + auto dev = peekSensorDevice(deviceName); + if (!dev) + { + ARMARX_DEBUG << "No sensor device for " << deviceName; + return nullptr; + } + return dev->getSensorValue(); + } + NJointController::NJointController() : + robotUnit(RobotUnitModule::ModuleBase::Instance< ::armarx::RobotUnit>()), + controlDeviceUsedBitmap(robotUnit.getNumberOfControlDevices(), 0) + { + controlDeviceUsedIndices.reserve(robotUnit.getNumberOfControlDevices()); + } + NJointController::~NJointController() + { } ControlTargetBase* NJointController::useControlTarget(const std::string& deviceName, const std::string& controlMode) diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h index 40284b1c85821917abcb14768def1f369a86b699..3b2deaf46191bc48c46bdaba02d4707cc25e4452 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h @@ -643,7 +643,7 @@ namespace armarx * @param updateCollisionModel Whether the robot's collision model should be updated * @see rtGetRobot */ - void useSynchronizedRtRobot(bool updateCollisionModel = false); + const VirtualRobot::RobotPtr& useSynchronizedRtRobot(bool updateCollisionModel = false); // //////////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////// Component interface ///////////////////////////////// // @@ -652,9 +652,19 @@ namespace armarx /// @see Component::getDefaultName std::string getDefaultName() const override; /// @see Component::onInitComponent - void onInitComponent() override {} + void onInitComponent() final; /// @see Component::onConnectComponent - void onConnectComponent() override {} + void onConnectComponent() final; + /// @see Component::onDisconnectComponent + void onDisconnectComponent() final; + /// @see Component::onExitComponent + void onExitComponent() final; + + + virtual void onInitNJointController() {} + virtual void onConnectNJointController() {} + virtual void onDisconnectNJointController() {} + virtual void onExitNJointController() {} // //////////////////////////////////////////////////////////////////////////////////////// // // ////////////////////////////////// ThreadPool functionality///////////////////////////// // @@ -666,7 +676,7 @@ namespace armarx * @brief Executes a given task in a separate thread from the Application ThreadPool. * @param taskName Descriptive name of this task to identify it on errors. * @param task std::function object (or lambda) that is to be executed. - * @note This task will be joined in the destructor of the NJointController. So make sure it terminates, when the + * @note This task will be joined in onExitComponent of the NJointController. So make sure it terminates, when the * controller is deactivated or removed! * * @code{.cpp} @@ -696,9 +706,10 @@ namespace armarx ScopedLock lock(threadHandlesMutex); ARMARX_CHECK_EXPRESSION(!taskName.empty()); ARMARX_CHECK_EXPRESSION(!threadHandles.count(taskName)); + ARMARX_CHECK_EXPRESSION(getState() < eManagedIceObjectExiting); ARMARX_VERBOSE << "Adding NJointController task named '" << taskName << "' - current available thread count: " << getThreadPool()->getAvailableTaskCount(); auto handlePtr = std::make_shared<ThreadPool::Handle>(getThreadPool()->runTask(task)); - ARMARX_CHECK_EXPRESSION_W_HINT(handlePtr->isValid(), "Could not add task (" << taskName << " - " << typeid(task).name() << " ) - available threads: " << getThreadPool()->getAvailableTaskCount()); + ARMARX_CHECK_EXPRESSION_W_HINT(handlePtr->isValid(), "Could not add task (" << taskName << " - " << GetTypeString(task) << " ) - available threads: " << getThreadPool()->getAvailableTaskCount()); threadHandles[taskName] = handlePtr; } std::map<std::string, std::shared_ptr<ThreadPool::Handle>> threadHandles; diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp new file mode 100755 index 0000000000000000000000000000000000000000..222b7f42c475ca3645676bb4460a822f62328b48 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp @@ -0,0 +1,151 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Markus Grotz (markus.grotz at kit dot edu) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "NJointHolonomicPlatformGlobalPositionController.h" + +namespace armarx +{ + NJointControllerRegistration<NJointHolonomicPlatformGlobalPositionController> + registrationNJointHolonomicPlatformGlobalPositionController("NJointHolonomicPlatformGlobalPositionController"); + + + NJointHolonomicPlatformGlobalPositionController::NJointHolonomicPlatformGlobalPositionController( + const RobotUnitPtr&, + const NJointHolonomicPlatformGlobalPositionControllerConfigPtr& cfg, + const VirtualRobot::RobotPtr&) : + pid(cfg->p, cfg->i, cfg->d, cfg->maxVelocity, cfg->maxAcceleration) + + { + const SensorValueBase* sv = useSensorValue(cfg->platformName); + this->sv = sv->asA<SensorValueHolonomicPlatform>(); + target = useControlTarget(cfg->platformName, ControlModes::HolonomicPlatformVelocity)->asA<ControlTargetHolonomicPlatformVelocity>(); + ARMARX_CHECK_EXPRESSION_W_HINT(target, "The actuator '" << cfg->platformName << "' has no control mode " << ControlModes::HolonomicPlatformVelocity); + + pid.threadSafe = false; + + oriCtrl.maxV = cfg->maxRotationVelocity; + oriCtrl.acceleration = cfg->maxRotationAcceleration; + oriCtrl.deceleration = cfg->maxRotationAcceleration; + oriCtrl.maxDt = 0.1; + oriCtrl.pid->Kp = cfg->p; + oriCtrl.positionPeriodLo = -M_PI; + oriCtrl.positionPeriodHi = M_PI; + pid.preallocate(2); + + } + + void NJointHolonomicPlatformGlobalPositionController::rtRun(const IceUtil::Time& currentTime, const IceUtil::Time& timeSinceLastIteration) + { + currentPosition << sv->relativePositionX, sv->relativePositionY; + currentOrientation = sv->relativePositionRotation; + + + if (rtGetControlStruct().newTargetSet) + { + pid.reset(); + *const_cast<bool*>(&rtGetControlStruct().newTargetSet) = false; + } + + if ((rtGetControlStruct().lastUpdate + IceUtil::Time::seconds(2)) < currentTime) + { + + // ARMARX_RT_LOGF_WARNING << deactivateSpam(0.5) << "Waiting for global pos"; + + target->velocityX = 0; + target->velocityY = 0; + + target->velocityRotation = 0; + + return; + } + + float relativeOrientation = currentOrientation - rtGetControlStruct().startOrientation; + Eigen::Vector2f relativeCurrentPosition = currentPosition - rtGetControlStruct().startPosition; + + Eigen::Vector2f updatedPosition = rtGetControlStruct().globalPosition + relativeCurrentPosition; + float updatedOrientation = rtGetControlStruct().globalOrientation + relativeOrientation; + updatedOrientation = std::atan2(std::sin(updatedOrientation), std::cos(updatedOrientation)); + + pid.update(timeSinceLastIteration.toSecondsDouble(), updatedPosition, rtGetControlStruct().target); + + oriCtrl.dt = timeSinceLastIteration.toSecondsDouble(); + oriCtrl.accuracy = rtGetControlStruct().rotationAccuracy; + oriCtrl.currentPosition = updatedOrientation; + oriCtrl.targetPosition = getWriterControlStruct().targetOrientation; + oriCtrl.currentV = sv->velocityRotation; + + Eigen::Vector2f localTargetVelocity = Eigen::Rotation2Df(-updatedOrientation) * Eigen::Vector2f(pid.getControlValue()[0], pid.getControlValue()[1]); + target->velocityX = localTargetVelocity(0); + target->velocityY = localTargetVelocity(1); + target->velocityRotation = oriCtrl.run(); + + Eigen::Vector2f posError = pid.target.head(2) - pid.processValue.head(2); + if (posError.norm() < rtGetControlStruct().translationAccuracy) + { + // target->velocityX = 0; + // target->velocityY = 0; + } + + float orientationError = oriCtrl.currentPosition - oriCtrl.targetPosition; + orientationError = std::atan2(std::sin(orientationError), std::cos(orientationError)); + if (std::fabs(orientationError) < rtGetControlStruct().rotationAccuracy) + { + //target->velocityRotation = 0; + } + } + + void NJointHolonomicPlatformGlobalPositionController::rtPreActivateController() + { + } + + void NJointHolonomicPlatformGlobalPositionController::setTarget(float x, float y, float yaw, float translationAccuracy, float rotationAccuracy) + { + // todo do we really need a recursive mutex? + + std::lock_guard<std::recursive_mutex> lock(controlDataMutex); + + getWriterControlStruct().target << x, y; + getWriterControlStruct().targetOrientation = std::atan2(std::sin(yaw), std::cos(yaw)); + getWriterControlStruct().translationAccuracy = translationAccuracy; + getWriterControlStruct().rotationAccuracy = rotationAccuracy; + getWriterControlStruct().newTargetSet = true; + writeControlStruct(); + } + + + void NJointHolonomicPlatformGlobalPositionController::setGlobalPos(float x, float y, float yaw, const IceUtil::Time& time = ::IceUtil::Time::now()) + { + // ..todo:: lock :) + + getWriterControlStruct().globalPosition << x, y; + getWriterControlStruct().globalOrientation = yaw; + + getWriterControlStruct().startPosition = currentPosition; + getWriterControlStruct().startOrientation = currentOrientation; + + getWriterControlStruct().lastUpdate = time; + writeControlStruct(); + } + +} // namespace armarx + diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h new file mode 100755 index 0000000000000000000000000000000000000000..b151f38024bbd08336f3aed67d89f98215bbad36 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h @@ -0,0 +1,122 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Markus Grotz (markus.grotz at kit dot edu) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#pragma once + + +#include <atomic> + +#include <VirtualRobot/Robot.h> + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include "NJointController.h" +#include "../RobotUnit.h" +#include "../SensorValues/SensorValueHolonomicPlatform.h" + +#include "../ControlTargets/ControlTarget1DoFActuator.h" +#include <RobotAPI/libraries/core/PIDController.h> + +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTargetHolonomicPlatformVelocity.h> + +#include <RobotAPI/components/units/RobotUnit/BasicControllers.h> +namespace armarx +{ + + TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformGlobalPositionControllerConfig); + class NJointHolonomicPlatformGlobalPositionControllerConfig : virtual public NJointControllerConfig + { + public: + std::string platformName; + float p = 1.0f; + float i = 0.0f; + float d = 0.0f; + float maxVelocity = 300; + float maxAcceleration = 500; + float maxRotationVelocity = 0.5; + float maxRotationAcceleration = 0.5; + }; + + struct NJointHolonomicPlatformGlobalPositionControllerTarget + { + Eigen::Vector2f target; // x,y + float targetOrientation; + float translationAccuracy = 0.0f; + float rotationAccuracy = 0.0f; + bool newTargetSet = false; + + + Eigen::Vector2f startPosition; + float startOrientation; + + Eigen::Vector2f globalPosition; + float globalOrientation; + IceUtil::Time lastUpdate = IceUtil::Time::seconds(0); + }; + + TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformGlobalPositionController); + + /** + * @brief The NJointHolonomicPlatformGlobalPositionController class + * @ingroup Library-RobotUnit-NJointControllers + */ + class NJointHolonomicPlatformGlobalPositionController: + virtual public NJointControllerWithTripleBuffer<NJointHolonomicPlatformGlobalPositionControllerTarget> + { + public: + using ConfigPtrT = NJointHolonomicPlatformGlobalPositionControllerConfigPtr; + + inline NJointHolonomicPlatformGlobalPositionController( + const RobotUnitPtr& robotUnit, + const NJointHolonomicPlatformGlobalPositionControllerConfigPtr& cfg, + const VirtualRobot::RobotPtr&); + + inline virtual void rtRun(const IceUtil::Time&, const IceUtil::Time& timeSinceLastIteration) override; + inline virtual void rtPreActivateController() override; + + //ice interface + inline virtual std::string getClassName(const Ice::Current& = Ice::emptyCurrent) const override + { + return "NJointHolonomicPlatformGlobalPositionController"; + } + + void setTarget(float x, float y, float yaw, float translationAccuracy, float rotationAccuracy); + + void setGlobalPos(float x, float y, float yaw, const IceUtil::Time&); + + + protected: + const SensorValueHolonomicPlatform* sv; + MultiDimPIDController pid; + + PositionThroughVelocityControllerWithAccelerationBoundsAndPeriodicPosition oriCtrl; + ControlTargetHolonomicPlatformVelocity* target; + + + Eigen::Vector2f currentPosition; + float currentOrientation; + + }; +} // namespace armarx + + diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.cpp index 15e6f325590e7fdfd02f5ec4305dddf2ce982459..3243b686d7e5ca71a3e992150693ebc241321b8d 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.cpp @@ -46,7 +46,7 @@ namespace armarx oriCtrl.acceleration = cfg->maxRotationAcceleration; oriCtrl.deceleration = cfg->maxRotationAcceleration; oriCtrl.maxDt = 0.1; - oriCtrl.p = cfg->p; + oriCtrl.pid->Kp = cfg->p; oriCtrl.positionPeriodLo = -M_PI; oriCtrl.positionPeriodHi = M_PI; pid.preallocate(2); diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.h index 30805e93ea77a7d5488a2afaff986617b643379a..cf4e73188f070f801567d175b514703f728c9328 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.h @@ -104,9 +104,6 @@ namespace armarx Eigen::Vector2f startPose, currentPose; float orientationOffset; // float rad2MMFactor; - - virtual void onInitComponent() override {} - virtual void onConnectComponent() override {} }; } // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp index 9c055b45e0f65150b309957bcb4b0f230a4e101a..e42356516b7645e998cf74a40c58e88c7931630e 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp @@ -29,7 +29,8 @@ using namespace armarx; NJointHolonomicPlatformUnitVelocityPassThroughController::NJointHolonomicPlatformUnitVelocityPassThroughController( RobotUnitPtr prov, NJointHolonomicPlatformUnitVelocityPassThroughControllerConfigPtr - cfg, const VirtualRobot::RobotPtr&) + cfg, const VirtualRobot::RobotPtr&) : + maxCommandDelay(IceUtil::Time::milliSeconds(500)) { target = useControlTarget(cfg->platformName, ControlModes::HolonomicPlatformVelocity)->asA<ControlTargetHolonomicPlatformVelocity>(); ARMARX_CHECK_EXPRESSION_W_HINT(target, "The actuator " << cfg->platformName << " has no control mode " << ControlModes::HolonomicPlatformVelocity); @@ -40,11 +41,21 @@ NJointHolonomicPlatformUnitVelocityPassThroughController::NJointHolonomicPlatfor reinitTripleBuffer(initialSettings); } -void NJointHolonomicPlatformUnitVelocityPassThroughController::rtRun(const IceUtil::Time&, const IceUtil::Time&) +void NJointHolonomicPlatformUnitVelocityPassThroughController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time&) { - target->velocityX = rtGetControlStruct().velocityX; - target->velocityY = rtGetControlStruct().velocityY; - target->velocityRotation = rtGetControlStruct().velocityRotation; + auto commandAge = sensorValuesTimestamp - rtGetControlStruct().commandTimestamp; + + if (commandAge > maxCommandDelay && // command must be recent + (rtGetControlStruct().velocityX != 0.0f || rtGetControlStruct().velocityY != 0.0f || rtGetControlStruct().velocityRotation != 0.0f)) // only throw error if any command is not zero + { + throw LocalException("Platform target velocity was not set for a too long time: delay: ") << commandAge.toSecondsDouble() << " s, max allowed delay: " << maxCommandDelay.toSecondsDouble() << " s"; + } + else + { + target->velocityX = rtGetControlStruct().velocityX; + target->velocityY = rtGetControlStruct().velocityY; + target->velocityRotation = rtGetControlStruct().velocityRotation; + } } void NJointHolonomicPlatformUnitVelocityPassThroughController::setVelocites(float velocityX, float velocityY, @@ -54,8 +65,19 @@ void NJointHolonomicPlatformUnitVelocityPassThroughController::setVelocites(floa getWriterControlStruct().velocityX = velocityX; getWriterControlStruct().velocityY = velocityY; getWriterControlStruct().velocityRotation = velocityRotation; + getWriterControlStruct().commandTimestamp = IceUtil::Time::now(); writeControlStruct(); } +IceUtil::Time NJointHolonomicPlatformUnitVelocityPassThroughController::getMaxCommandDelay() const +{ + return maxCommandDelay; +} + +void NJointHolonomicPlatformUnitVelocityPassThroughController::setMaxCommandDelay(const IceUtil::Time& value) +{ + maxCommandDelay = value; +} + NJointControllerRegistration<NJointHolonomicPlatformUnitVelocityPassThroughController> registrationNJointHolonomicPlatformUnitVelocityPassThroughController("NJointHolonomicPlatformUnitVelocityPassThroughController"); diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h index 89cb892d3343854996e623b4bfe07748d587ba79..3c793d040a30b463b4419a3f168d389fa03b2f83 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h @@ -48,6 +48,7 @@ namespace armarx float velocityX = 0; float velocityY = 0; float velocityRotation = 0; + IceUtil::Time commandTimestamp; }; TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformUnitVelocityPassThroughController); @@ -76,9 +77,11 @@ namespace armarx { return "NJointHolonomicPlatformUnitVelocityPassThroughController"; } + IceUtil::Time getMaxCommandDelay() const; + void setMaxCommandDelay(const IceUtil::Time& value); + protected: - void onInitComponent() override {} - void onConnectComponent() override {} + IceUtil::Time maxCommandDelay; ControlTargetHolonomicPlatformVelocity* target; NJointHolonomicPlatformUnitVelocityPassThroughControllerControlData initialSettings; diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointKinematicUnitPassThroughController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointKinematicUnitPassThroughController.h index 2985febdcd46aa373e2dd12dfeaefee5fe988412..1d5b4d81faf87c0f5190b7124a3e0293f39b9e81 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointKinematicUnitPassThroughController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointKinematicUnitPassThroughController.h @@ -99,8 +99,5 @@ namespace armarx }; float sensorToControlOnActivateFactor {0}; float resetZeroThreshold {0}; - - void onInitComponent() override {} - void onConnectComponent() override {} }; } diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.cpp index f735aba8fc065d888b69c59afe55bcef63f83dc9..3af83fb38da192c09309dd1a9f35eaa3f9a89ecd 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.cpp @@ -169,7 +169,6 @@ void NJointTaskSpaceImpedanceController::rtRun(const IceUtil::Time& sensorValues Eigen::VectorXf nullqerror = desiredJointPosition - qpos; - // ARMARX_CHECK_EQUAL(nullqerror.rows(), targets.size()); ARMARX_CHECK_EQUAL(static_cast<std::size_t>(nullqerror.rows()), targets.size()); for (int i = 0; i < nullqerror.rows(); ++i) @@ -182,7 +181,6 @@ void NJointTaskSpaceImpedanceController::rtRun(const IceUtil::Time& sensorValues Eigen::VectorXf nullspaceTorque = knull * nullqerror - dnull * qvel; Eigen::VectorXf jointDesiredTorques = jacobi.transpose() * tcpDesiredWrench + (I - jacobi.transpose() * jtpinv) * nullspaceTorque; - // ARMARX_CHECK_EQUAL(jointDesiredTorques.rows(), targets.size()); ARMARX_CHECK_EQUAL(static_cast<std::size_t>(jointDesiredTorques.rows()), targets.size()); for (size_t i = 0; i < targets.size(); ++i) @@ -257,6 +255,37 @@ void NJointTaskSpaceImpedanceController::setTarget(const Ice::FloatSeq& target, writeControlStruct(); } +void NJointTaskSpaceImpedanceController::setImpedanceParameters(const std::string& name, const Ice::FloatSeq& vals, const Ice::Current&) +{ + ARMARX_CHECK_EQUAL(vals.size(), 3); + + Eigen::Vector3f vec; + for (size_t i = 0; i < 3; ++i) + { + vec(i) = vals.at(i); + } + + LockGuardType guard {controlDataMutex}; + if (name == "Kpos") + { + getWriterControlStruct().Kpos = vec; + } + else if (name == "Kori") + { + getWriterControlStruct().Kori = vec; + } + else if (name == "Dpos") + { + getWriterControlStruct().Dpos = vec; + } + else if (name == "Dori") + { + getWriterControlStruct().Dori = vec; + } + writeControlStruct(); + +} + void armarx::NJointTaskSpaceImpedanceController::rtPreActivateController() { diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h index 53462f8942496ac25c9e62e2e32dd2e12ffd0d6d..b93979d00f45293e4277b8cd3f7e7f7bd16a8846 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTaskSpaceImpedanceController.h @@ -39,6 +39,10 @@ namespace armarx public: Eigen::VectorXf desiredPose; Eigen::VectorXf desiredTwist; + Eigen::Vector3f Kpos; + Eigen::Vector3f Dpos; + Eigen::Vector3f Kori; + Eigen::Vector3f Dori; }; /** * @defgroup Library-NJointTaskSpaceImpedanceController NJointTaskSpaceImpedanceController @@ -71,6 +75,8 @@ namespace armarx void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; void setTarget(const Ice::FloatSeq&, const Ice::Current&) override; + void setImpedanceParameters(const std::string&, const Ice::FloatSeq&, const Ice::Current&); + private: Eigen::Vector3f desiredPosition; Eigen::Quaternionf desiredQuaternion; diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp index 612e0bc888507ff3a483fbdf33f445b255249465..b51bb59d3907675755f3a65c7278370ea6bb4027 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.cpp @@ -44,12 +44,12 @@ namespace armarx return "NJointTrajectoryController"; } - void NJointTrajectoryController::onInitComponent() + void NJointTrajectoryController::onInitNJointController() { offeringTopic("StaticPlotter"); } - void NJointTrajectoryController::onConnectComponent() + void NJointTrajectoryController::onConnectNJointController() { plotter = getTopic<StaticPlotterInterfacePrx>("StaticPlotter"); dbgObs = getTopic<DebugObserverInterfacePrx>("DebugObserver"); diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h index e184ffb8f8bc3a691e842f0b7948b3752ce3257b..560f33100464eb9e2994069059f0262925579e88 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointTrajectoryController.h @@ -32,8 +32,8 @@ namespace armarx // NJointControllerInterface interface std::string getClassName(const Ice::Current&) const override; - void onInitComponent() override; - void onConnectComponent() override; + void onInitNJointController() override; + void onConnectNJointController() override; // NJointController interface void rtPreActivateController() override; diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleBase.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleBase.h index 618620785c39e76e3f775d16957b2a7a708fc47d..8c107ae88887a6ba8a98fd298efdd6a3bd520f26 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleBase.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleBase.h @@ -32,6 +32,7 @@ #include <VirtualRobot/Robot.h> +#include <ArmarXCore/util/CPPUtility/Pointer.h> #include <ArmarXCore/core/Component.h> #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> @@ -50,15 +51,6 @@ namespace armarx TYPEDEF_PTRS_HANDLE(RobotUnit); TYPEDEF_PTRS_HANDLE(NJointController); - template<class T> - T& CheckedDeref(T* ptr) - { - if (!ptr) - { - throw std::invalid_argument {"Ptr passed to CheckedDeref is NULL"}; - } - return *ptr; - } template<class Targ, class Src> Targ& CheckedCastAndDeref(Src* src) { diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp index 5abecbc624674ca7b139339eb7aef3ab6be58215..8e7ddfa650cc87fd68b5a5a5a658eb42a249a1d7 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.cpp @@ -24,6 +24,9 @@ #include <ArmarXCore/core/util/OnScopeExit.h> #include <ArmarXCore/core/time/TimeUtil.h> +#include <RobotAPI/components/units/RobotUnit/util/DynamicsHelper.h> +#include <ArmarXCore/observers/filters/rtfilters/AverageFilter.h> +#include <ArmarXCore/observers/filters/rtfilters/ButterworthFilter.h> #include "RobotUnitModuleControlThread.h" #include "../NJointControllers/NJointController.h" @@ -368,9 +371,21 @@ namespace armarx device->rtReadSensorValues(sensorValuesTimestamp, timeSinceLastIteration); } DevicesAttorneyForControlThread::UpdateRobotWithSensorValues(this, rtRobot, rtRobotNodes); + rtPostReadSensorDeviceValues(sensorValuesTimestamp, timeSinceLastIteration); rtGetThreadTimingsSensorDevice().rtMarkRtReadSensorDeviceValuesEnd(); } + void ControlThread::rtPostReadSensorDeviceValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + if (dynamicsHelpers) + { +// auto start = IceUtil::Time::now(); + dynamicsHelpers->update(sensorValuesTimestamp, timeSinceLastIteration); +// auto end = IceUtil::Time::now(); + // ARMARX_INFO << "Dynamics duration: " << (end-start).toMicroSeconds(); + } + } + void ControlThread::rtRunJointControllers(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { rtGetThreadTimingsSensorDevice().rtMarkRtRunJointControllersStart(); @@ -607,6 +622,33 @@ namespace armarx preSwitchSetup_ActivatedNJointControllers.resize(rtGetActivatedNJointControllers().size()); postSwitchSetup_ActivatedNJointControllers.resize(rtGetActivatedNJointControllers().size()); + + // setup inverse dynamics + if (getProperty<bool>("EnableInverseDynamics").getValue()) + { + RobotUnit* robotUnit = dynamic_cast<RobotUnit*>(this); + ARMARX_CHECK_EXPRESSION(robotUnit); + std::shared_ptr<DynamicsHelper> dynamicsHelpers(new DynamicsHelper(robotUnit)); + auto bodySetName = getProperty<std::string>("InverseDynamicsRobotBodySet").getValue(); + auto rtRobotBodySet = rtGetRobot()->getRobotNodeSet(bodySetName); + ARMARX_CHECK_EXPRESSION_W_HINT(rtRobotBodySet, "could not find robot node set with name: " << bodySetName << " - Check property InverseDynamicsRobotBodySet"); + // rtfilters::RTFilterBasePtr accFilter(new rtfilters::ButterworthFilter(100, 1000, Lowpass, 1)); + // rtfilters::RTFilterBasePtr velFilter(new rtfilters::ButterworthFilter(100, 1000, Lowpass, 1)); + rtfilters::RTFilterBasePtr accFilter(new rtfilters::AverageFilter(30)); + rtfilters::RTFilterBasePtr velFilter(new rtfilters::AverageFilter(30)); + + auto setList = armarx::Split(getProperty<std::string>("InverseDynamicsRobotJointSets").getValue(), ",", true, true); + for (auto& set : setList) + { + auto rns = rtGetRobot()->getRobotNodeSet(set); + ARMARX_CHECK_EXPRESSION_W_HINT(rns, "could not find robot node set with name: " << set << " - Check property InverseDynamicsRobotJointSets"); + dynamicsHelpers->addRobotPart(rns, rtRobotBodySet, velFilter, accFilter); + ARMARX_VERBOSE << "Added nodeset " << set << " for inverse dynamics"; + } + + this->dynamicsHelpers = dynamicsHelpers; + } + } void ControlThread::_preOnInitRobotUnit() diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h index 33e8a906f57c5ae4ed4ed71c964a06da73eee780..59644ac61b37fd7e2b303fddc109e59fc2d430cd 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleControlThread.h @@ -33,6 +33,7 @@ namespace armarx { + class DynamicsHelper; class RTThreadTimingsSensorDevice; namespace RobotUnitModule { @@ -51,6 +52,18 @@ namespace armarx "An Error will be printed, If the execution time in micro seconds of a NJointController " "exceeds this parameter times the number of ControlDevices." ); + defineOptionalProperty<bool>("EnableInverseDynamics", false, + "If true, inverse dynamics are calculated for all joints given in the " + "InverseDynamicsRobotJointSets property during each loop of " + "the rt control thread."); + defineOptionalProperty<std::string>( + "InverseDynamicsRobotJointSets", "LeftArm,RightArm", + "Comma separated list of robot nodesets for which the inverse dynamics should be calculcated." + ); + defineOptionalProperty<std::string>( + "InverseDynamicsRobotBodySet", "RobotCol", + "Robot nodeset of which the masses should be used for the inverse dynamics" + ); } }; @@ -182,6 +195,12 @@ namespace armarx * @param timeSinceLastIteration Time between the last two updates of \ref SensorValueBase SensorValues */ void rtReadSensorDeviceValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + /** + * @brief Execute code after updating the sensor values. Default implementation calculates the inverse dynamics. + * @param sensorValuesTimestamp Timestamp of the current \ref SensorValueBase SensorValues + * @param timeSinceLastIteration Time between the last two updates of \ref SensorValueBase SensorValues + */ + void virtual rtPostReadSensorDeviceValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); /// @brief Deactivates all NJointControllers generating invalid targets. void rtHandleInvalidTargets(); /// @brief Calls \ref JointController::rtResetTarget for all active Joint controllers @@ -333,6 +352,9 @@ namespace armarx std::vector<NJointController*> postSwitchSetup_ActivatedNJointControllers; std::atomic<EmergencyStopStateRequest> emergencyStopStateRequest {EmergencyStopStateRequest::RequestNone}; + + std::shared_ptr<DynamicsHelper> dynamicsHelpers; + // //////////////////////////////////////////////////////////////////////////////////////// // // /////////////////////////////////////// Attorneys ////////////////////////////////////// // // //////////////////////////////////////////////////////////////////////////////////////// // diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModulePublisher.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModulePublisher.cpp index df4a0f5503a21b05f1df52686ca34c42c90ae076..c24b433f99d08cad72c4d91b8893b4ce20f5987f 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModulePublisher.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModulePublisher.cpp @@ -572,7 +572,7 @@ namespace armarx return !jointCtrl; })) { - ARMARX_WARNING << "armarx::RobotUnit::publish: Some activated JointControllers are reported to be nullptr! " + ARMARX_WARNING << deactivateSpam(5) << "armarx::RobotUnit::publish: Some activated JointControllers are reported to be nullptr! " << "(did you forget to call rtSwitchControllerSetup()?)"; } } diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.cpp index 94c7b9d8481c0a58a0e9de33cadabdc828e53a75..3be94c7a50619cfe95ff85046d544afd032e9888 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.cpp @@ -41,6 +41,11 @@ namespace armarx return robotPlatformName; } + std::string RobotData::getRobotPlatformInstanceName() const + { + return robotPlatformInstanceName; + } + const std::string& RobotData::getRobotNodetSeName() const { throwIfInControlThread(BOOST_CURRENT_FUNCTION); @@ -82,6 +87,8 @@ namespace armarx return clone; } + + void RobotData::_initVirtualRobot() { throwIfInControlThread(BOOST_CURRENT_FUNCTION); @@ -94,6 +101,7 @@ namespace armarx robotProjectName = getProperty<std::string>("RobotFileNameProject").getValue(); robotFileName = getProperty<std::string>("RobotFileName").getValue(); robotPlatformName = getProperty<std::string>("PlatformName").getValue(); + robotPlatformInstanceName = getProperty<std::string>("PlatformInstanceName").getValue(); //load robot { diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.h index 0c0457b99f6b2242e82adefa656718ddcfb42c15..ff1e0637952cff7229a8ece8af322cfb205bd23f 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleRobotData.h @@ -55,7 +55,11 @@ namespace armarx "Robot node set name as defined in robot xml file, e.g. 'LeftArm'"); defineOptionalProperty<std::string>( "PlatformName", "Platform", - "Name of the platform (will publish values on PlatformName + 'State')"); + "Name of the platform needs to correspond to a node in the virtual robot."); + defineOptionalProperty<std::string>( + "PlatformInstanceName", "Platform", + "Name of the platform instance (will publish values on PlatformInstanceName + 'State')"); + } }; @@ -114,6 +118,12 @@ namespace armarx */ std::string getRobotName() const; + /** + * @brief Returns the name of the robot platform instance. Used for the platform topic: RobotPlatformInstance + "State" + */ + std::string getRobotPlatformInstanceName() const; + + /** * @brief Returns a clone of the robot's model * @return A clone of the robot's model @@ -122,6 +132,7 @@ namespace armarx // //////////////////////////////////////////////////////////////////////////////////////// // // ///////////////////////////////////////// Data ///////////////////////////////////////// // // //////////////////////////////////////////////////////////////////////////////////////// // + private: /// @brief The name of the robot's RobotNodeSet std::string robotNodeSetName; @@ -131,6 +142,8 @@ namespace armarx std::string robotFileName; /// @brief The name of the robot's platform std::string robotPlatformName; + /// @brief The name of the robot's platform instance + std::string robotPlatformInstanceName; /// @brief The robot's model. VirtualRobot::RobotPtr robot; diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp index be11508013b7f3276c2b487476179136e9e557ba..27c760de9e0fbce031aa522e3f24b887ec17d25a 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp @@ -266,6 +266,8 @@ namespace armarx properties->setProperty(confPre + "RobotNodeSetName", _module<RobotData>().getRobotNodetSeName()); properties->setProperty(confPre + "RobotFileName", _module<RobotData>().getRobotFileName()); properties->setProperty(confPre + "RobotFileNameProject", _module<RobotData>().getRobotProjectName()); + properties->setProperty(confPre + "TopicPrefix", getProperty<std::string>("KinematicUnitNameTopicPrefix")); + ARMARX_DEBUG << "creating unit " << configName << " using these properties: " << properties->getPropertiesForPrefix(""); IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, getConfigDomain()); @@ -317,7 +319,7 @@ namespace armarx Ice::PropertiesPtr properties = getIceProperties()->clone(); //properties->setProperty(confPre + "MinimumLoggingLevel", getProperty<std::string>("MinimumLoggingLevel").getValue()); //fill properties - properties->setProperty(confPre + "PlatformName", _module<RobotData>().getRobotPlatformName()); + properties->setProperty(confPre + "PlatformName", _module<RobotData>().getRobotPlatformInstanceName()); ARMARX_DEBUG << "creating unit " << configName << " using these properties: " << properties->getPropertiesForPrefix(""); IceInternal::Handle<UnitT> unit = Component::create<UnitT>(properties, configName, getConfigDomain()); //config @@ -352,6 +354,24 @@ namespace armarx unit->platformSensorIndex = _module<Devices>().getSensorDevices().index(_module<RobotData>().getRobotPlatformName()); //add addUnit(std::move(unit)); + + + NJointHolonomicPlatformGlobalPositionControllerConfigPtr configGlobalPositionCtrlCfg = new NJointHolonomicPlatformGlobalPositionControllerConfig; + configGlobalPositionCtrlCfg->platformName = _module<RobotData>().getRobotPlatformName(); + auto ctrlGlobalPosition = NJointHolonomicPlatformGlobalPositionControllerPtr::dynamicCast( + _module<ControllerManagement>().createNJointController( + "NJointHolonomicPlatformGlobalPositionController", + getName() + "_" + configName + "_GlobalPositionContoller", + configGlobalPositionCtrlCfg, false, true + ) + ); + ARMARX_CHECK_EXPRESSION(ctrlGlobalPosition); + unit->pt = ctrl; + unit->globalPosCtrl = ctrlGlobalPosition; + + unit->platformSensorIndex = _module<Devices>().getSensorDevices().index(_module<RobotData>().getRobotPlatformName()); + //add + addUnit(std::move(unit)); } void Units::initializeForceTorqueUnit() diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h index 7978f56c700d39eda754f675f8889e013afacc2d..54d6c2dcc75ad6b33f9f1a865676da86d2d9833c 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h @@ -39,6 +39,10 @@ namespace armarx defineOptionalProperty<std::string>( "KinematicUnitName", "KinematicUnit", "The name of the created kinematic unit"); + defineOptionalProperty<std::string>( + "KinematicUnitNameTopicPrefix", "", + "Prefix for the kinematic sensor values topic"); + defineOptionalProperty<std::string>( "PlatformUnitName", "PlatformUnit", diff --git a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h index 7573abc1e5980a0762bfe5e9e098e21ff48a9c23..827b23913b7a840f512ec60ad5e55d7087de1cf2 100644 --- a/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h +++ b/source/RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h @@ -101,6 +101,22 @@ namespace armarx return svi; } }; + class SensorValue1DoFInverseDynamicsTorque : + virtual public SensorValue1DoFGravityTorque + { + public: + DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + float inverseDynamicsTorque = 0.0f; + float inertiaTorque = 0.0f; + static SensorValueInfo<SensorValue1DoFInverseDynamicsTorque> GetClassMemberInfo() + { + SensorValueInfo<SensorValue1DoFInverseDynamicsTorque> svi; + svi.addBaseClass<SensorValue1DoFGravityTorque>(); + svi.addMemberVariable(&SensorValue1DoFInverseDynamicsTorque::inverseDynamicsTorque, "inverseDynamicsTorque"); + svi.addMemberVariable(&SensorValue1DoFInverseDynamicsTorque::inertiaTorque, "inertiaTorque"); + return svi; + } + }; class SensorValue1DoFActuator : @@ -108,7 +124,7 @@ namespace armarx virtual public SensorValue1DoFActuatorVelocity, virtual public SensorValue1DoFActuatorAcceleration, virtual public SensorValue1DoFActuatorTorque, - virtual public SensorValue1DoFGravityTorque + virtual public SensorValue1DoFInverseDynamicsTorque { public: DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION @@ -119,7 +135,7 @@ namespace armarx svi.addBaseClass<SensorValue1DoFActuatorVelocity>(); svi.addBaseClass<SensorValue1DoFActuatorAcceleration>(); svi.addBaseClass<SensorValue1DoFActuatorTorque>(); - svi.addBaseClass<SensorValue1DoFGravityTorque>(); + svi.addBaseClass<SensorValue1DoFInverseDynamicsTorque>(); return svi; } }; diff --git a/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp index 36b9181fd443f9dc1028efd53d0ef2b63bb6df5c..2e0408925c30f5ee0d8b9304b9efb220bf6bd057 100644 --- a/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp +++ b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.cpp @@ -20,6 +20,8 @@ * GNU General Public License */ #include "KinematicSubUnit.h" +#include <VirtualRobot/RobotNodeSet.h> +#include <RobotAPI/libraries/RobotStatechartHelpers/KinematicUnitHelper.h> #include <RobotAPI/libraries/RobotStatechartHelpers/KinematicUnitHelper.h> @@ -43,6 +45,9 @@ void armarx::KinematicSubUnit::setupData( ARMARX_CHECK_EXPRESSION(!robot); ARMARX_CHECK_EXPRESSION(rob); robot = rob; + robot->setUpdateCollisionModel(false); + robot->setUpdateVisualization(false); + robot->setThreadsafe(false); ARMARX_CHECK_EXPRESSION(!robotUnit); ARMARX_CHECK_EXPRESSION(newRobotUnit); @@ -55,6 +60,15 @@ void armarx::KinematicSubUnit::setupData( devs = std::move(newDevs); controlDeviceHardwareControlModeGroups = controlDeviceHardwareControlModeGrps; controlDeviceHardwareControlModeGroupsMerged = controlDeviceHardwareControlModeGrpsMerged; + + auto nodes = robot->getRobotNodes(); + for (auto& node : nodes) + { + if ((node->isRotationalJoint() || node->isTranslationalJoint()) && !devs.count(node->getName())) + { + sensorLessJoints.push_back(node); + } + } } void armarx::KinematicSubUnit::update(const armarx::SensorAndControl& sc, const armarx::JointAndNJointControllers& c) @@ -128,6 +142,20 @@ void armarx::KinematicSubUnit::update(const armarx::SensorAndControl& sc, const { ARMARX_WARNING << deactivateSpam(5) << "these actuators have no sensor!:\n" << actuatorsWithoutSensor; } + + // update the joint values of linked joints + robot->setJointValues(ang); + auto nodes = robot->getRobotNodes(); + for (auto& node : nodes) + { + node->updatePose(false); + } + for (auto& node : sensorLessJoints) + { + ang[node->getName()] = node->getJointValue(); + } + + ARMARX_DEBUG << deactivateSpam(30) << "reporting updated data:\n" << ctrlModes.size() << " \tcontrol modes (updated = " << ctrlModesAValueChanged << ")\n" << ang.size() << " \tjoint angles (updated = " << angAValueChanged << ")\n" diff --git a/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h index 01183607f958e2b53b67e39966846d4fdd33fa0c..65e8263cc44f4669dd43f0c4b084840f76b2b847 100644 --- a/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/Units/KinematicSubUnit.h @@ -104,6 +104,7 @@ namespace armarx std::vector<std::set<std::string>> controlDeviceHardwareControlModeGroups; std::set<std::string> controlDeviceHardwareControlModeGroupsMerged; IceReportSkipper reportSkipper; + std::vector<VirtualRobot::RobotNodePtr> sensorLessJoints; template<class ValueT, class SensorValueT, ValueT SensorValueT::* Member> static void UpdateNameValueMap(std::map<std::string, ValueT>& nvm, const std::string& name, const SensorValueBase* sv, bool& changeState) diff --git a/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h index bac5190ed8f1a3f9a68b1cdfa304b8ea7fb00b61..428cc7c295396938bed6d9d6210296dc14ffe65d 100644 --- a/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h @@ -32,6 +32,7 @@ #include <RobotAPI/components/units/PlatformUnit.h> #include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.h> +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h> #include <RobotAPI/libraries/core/Pose.h> #include "RobotUnitSubUnit.h" @@ -66,6 +67,7 @@ namespace armarx NJointHolonomicPlatformUnitVelocityPassThroughControllerPtr pt; NJointHolonomicPlatformRelativePositionControllerPtr relativePosCtrl; + NJointHolonomicPlatformGlobalPositionControllerPtr globalPosCtrl; std::size_t platformSensorIndex; protected: Eigen::Matrix4f getRelativePose() const; diff --git a/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp b/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp index 1b856974e9a0f1d4be92c2125a62c03370054159..5f202a8f8da633a10380e0bc2f4e0f77d4ca179f 100644 --- a/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp +++ b/source/RobotAPI/components/units/RobotUnit/test/BasicControllerTest.cpp @@ -25,7 +25,8 @@ #define CREATE_LOGFILES #include <random> #include <iostream> - +#include <filesystem> +#include "../util/CtrlUtil.h" #include <boost/algorithm/clamp.hpp> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/util/algorithm.h> @@ -34,7 +35,7 @@ using namespace armarx; //params for random tests const std::size_t tries = 1; -const std::size_t ticks = 20000; // each tick is 0.75 to 1.25 ms +const std::size_t ticks = 5000; // each tick is 0.75 to 1.25 ms //helpers for logging #ifdef CREATE_LOGFILES #define LOG_CONTROLLER_DATA_WRITE_TO(name) change_logging_file(name) @@ -43,7 +44,7 @@ const std::size_t ticks = 20000; // each tick is 0.75 to 1.25 ms #include <boost/filesystem.hpp> #include <fstream> -static const boost::filesystem::path fpath +static const std::filesystem::path fpath { "controller_logfiles/" }; @@ -58,17 +59,17 @@ void change_logging_file(const std::string& name) } if (!isSetup) { - boost::filesystem::create_directories(fpath); + std::filesystem::create_directories(fpath); //create the python evaluation file - boost::filesystem::path tmppath(fpath / "eval.py"); + std::filesystem::path tmppath(fpath / "eval.py"); f.open(tmppath.string()); #include "eval_script.inc" isSetup = true; f.close(); } - boost::filesystem::path tmppath(fpath / name); + std::filesystem::path tmppath(fpath / (name + ".log")); f.open(tmppath.string()); - std::cout << "now writing to: " << boost::filesystem::absolute(tmppath).string() << "\n"; + std::cout << "now writing to: " << std::filesystem::absolute(tmppath).string() << "\n"; } #else @@ -88,36 +89,38 @@ static std::mt19937 gen {getSeed()}; struct Simulation { - float time = 0; - float dt = 0; - float maxDt = 0.001; + double time = 0; + double dt = 0; + double maxDt = 0.01; + + double curpos = 0; + double oldpos = 0; + double targpos = 0; + double posHiHard = M_PI; + double posLoHard = -M_PI; + double posHi = M_PI; + double posLo = -M_PI; - float curpos = 0; - float oldpos = 0; - float targpos = 0; - float posHiHard = M_PI; - float posLoHard = -M_PI; - float posHi = M_PI; - float posLo = -M_PI; + double curvel = 0; + double oldvel = 0; + double targvel = 0; + double maxvel = 10; - float curvel = 0; - float oldvel = 0; - float targvel = 0; - float maxvel = 10; + double curacc = 0; + double oldacc = 0; + double acc = 0; + double dec = 0; - float curacc = 0; - float oldacc = 0; - float acc = 0; - float dec = 0; + double jerk = 0; - float brakingDist = 0; - float posAfterBraking = 0; + double brakingDist = 0; + double posAfterBraking = 0; - std::uniform_real_distribution<float> vDist; - std::uniform_real_distribution<float> aDist; - std::uniform_real_distribution<float> pDist; - std::uniform_real_distribution<float> tDist; + std::uniform_real_distribution<double> vDist; + std::uniform_real_distribution<double> aDist; + std::uniform_real_distribution<double> pDist; + std::uniform_real_distribution<double> tDist; void reset() { @@ -135,26 +138,27 @@ struct Simulation oldacc = 0; acc = 0; dec = 0; + jerk = 0; brakingDist = 0; posAfterBraking = 0; - vDist = std::uniform_real_distribution<float> { -maxvel, maxvel}; - aDist = std::uniform_real_distribution<float> {maxvel / 4, maxvel * 4 }; - tDist = std::uniform_real_distribution<float> {maxDt * 0.75f, maxDt * 1.25f}; - pDist = std::uniform_real_distribution<float> {posLoHard, posHiHard}; + vDist = std::uniform_real_distribution<double> { -maxvel, maxvel}; + aDist = std::uniform_real_distribution<double> {maxvel / 4, maxvel * 4 }; + tDist = std::uniform_real_distribution<double> {maxDt * 0.75f, maxDt * 1.25f}; + pDist = std::uniform_real_distribution<double> {posLoHard, posHiHard}; } //updating template<class FNC> void tick(FNC& callee) { - dt = tDist(gen); + dt = maxDt;//tDist(gen); callee(); log(); } - void updateVel(float newvel) + void updateVel(double newvel) { BOOST_CHECK(std::isfinite(newvel)); //save old @@ -164,8 +168,27 @@ struct Simulation //update curvel = newvel; - curacc = std::abs(curvel - oldvel) / dt; - curpos += curvel * dt; + curacc = (curvel - oldvel) / dt; + jerk = (curacc - oldacc) / dt; + curpos += ctrlutil::s(dt, 0, curvel, curacc, 0/*math::MathUtils::Sign(curacc-oldacc) * jerk*/); + time += dt; + brakingDist = brakingDistance(curvel, dec); + posAfterBraking = curpos + brakingDist; + } + + void updatePos(double newPos) + { + BOOST_CHECK(std::isfinite(newPos)); + //save old + oldvel = curvel; + oldpos = curpos; + oldacc = curacc; + + //update + curvel = (newPos - oldpos) / dt; + curacc = (curvel - oldvel) / dt; + jerk = (curacc - oldacc) / dt; + curpos = newPos; time += dt; brakingDist = brakingDistance(curvel, dec); posAfterBraking = curpos + brakingDist; @@ -195,13 +218,13 @@ struct Simulation if (std::abs(oldvel) > std::abs(curvel)) { // deceleration check with PID controller for motion-finish does not work -> check disabled - // //we decelerated - // if (!(curacc < dec * 1.01)) - // { - // std::cout << "Time[" << time << "] violated deceleration bound! vold " << oldvel << " / vnew " << curvel << " / dv " << std::abs(oldvel - curvel) << " / dt " << dt << " / dec " << dec - // << " / (targetv " << targvel << ")\n"; - // } - // BOOST_CHECK_LE(curacc, dec * 1.01); + //we decelerated + if (!(curacc < dec * 1.01)) + { + std::cout << "Time[" << time << "] violated deceleration bound! vold " << oldvel << " / vnew " << curvel << " / dv " << std::abs(oldvel - curvel) << " / dt " << dt << " / dec " << dec + << " / (targetv " << targvel << ")\n"; + } + BOOST_CHECK_LE(curacc, dec * 1.01); } else { @@ -219,182 +242,159 @@ struct Simulation void writeHeader(const std::string& name) { LOG_CONTROLLER_DATA_WRITE_TO(name); - LOG_CONTROLLER_DATA("time curpos targpos posHiHard posLoHard posHi posLo curvel targvel maxvel curacc acc dec brakingDist posAfterBraking"); + LOG_CONTROLLER_DATA("time curpos targpos posHiHard posLoHard posHi posLo curvel targvel maxv curacc acc dec brakingDist posAfterBraking"); reset(); } void log() { - //output a neg val for dec and a pos val for acc - float outputacc; - if (sign(curvel) != sign(oldvel)) - { - // cant determine the current acceleration correctly (since we have an acceleration and deceleration phase) - outputacc = 0; - } - else - { - outputacc = curacc; - if (std::abs(oldvel) > std::abs(curvel)) - { - //we decelerated -> negative sign - outputacc *= -1; - } - } + // //output a neg val for dec and a pos val for acc + // double outputacc; + // if (sign(curvel) != sign(oldvel)) + // { + // // cant determine the current acceleration correctly (since we have an acceleration and deceleration phase) + // outputacc = 0; + // } + // else + // { + // outputacc = curacc; + // if (std::abs(oldvel) > std::abs(curvel)) + // { + // //we decelerated -> negative sign + // outputacc *= -1; + // } + // } LOG_CONTROLLER_DATA(time << " " << curpos << " " << targpos << " " << posHiHard << " " << posLoHard << " " << posHi << " " << posLo << " " << curvel << " " << targvel << " " << maxvel << " " << - outputacc << " " << acc << " " << -dec << " " << + curacc << " " << acc << " " << -dec << " " << brakingDist << " " << posAfterBraking); } }; - -BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationBoundsTest) +BOOST_AUTO_TEST_CASE(VelocityControllerWithRampedAccelerationAndPositionBoundsTest) { - return; - std::cout << "starting velocityControlWithAccelerationBoundsTest \n"; - Simulation s; - s.posHi = 0; - s.posLo = 0; - s.posHiHard = 0; - s.posLoHard = 0; - - float directSetVLimit = 0.005; - - - auto testTick = [&] - { - VelocityControllerWithAccelerationBounds ctrl; - ctrl.dt = s.dt; - ctrl.maxDt = s.maxDt; - ctrl.currentV = s.curvel; - ctrl.targetV = s.targvel; - ctrl.maxV = s.maxvel; - ctrl.acceleration = s.acc; - ctrl.deceleration = s.dec; - ctrl.directSetVLimit = directSetVLimit; - BOOST_CHECK(ctrl.validParameters()); - // s.updateVel(velocityControlWithAccelerationBounds(s.dt, s.maxDt, s.curvel, s.targvel, s.maxvel, s.acc, s.dec, directSetVLimit)); - s.updateVel(ctrl.run()); - s.checkVel(); - s.checkAcc(); - }; - std::cout << "random tests\n"; - for (std::size_t try_ = 0; try_ < tries; ++ try_) - { - s.writeHeader("velCtrl+acc_random_" + to_string(try_)); - s.curvel = s.vDist(gen); - s.targvel = s.vDist(gen); - s.acc = s.aDist(gen); - s.dec = s.aDist(gen); - directSetVLimit = (std::min(s.acc, s.dec) * s.maxDt * 0.75f / 2.f) * 0.99f; // vlimit = accmin*dtmin/2 - - s.log(); - for (std::size_t tick = 0; tick < ticks; ++tick) - { - s.tick(testTick); - } - BOOST_CHECK_EQUAL(s.curvel, s.targvel); - } - std::cout << "bounds tests\n"; - std::cout << "TODO\n"; - ///TODO - - std::cout << "done velocityControlWithAccelerationBoundsTest \n"; -} - -BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationAndPositionBoundsTest) -{ - return; - std::cout << "starting velocityControlWithAccelerationAndPositionBoundsTest \n"; + std::cout << "starting VelocityControllerWithRampedAccelerationTest \n"; Simulation s; + // s.posHi = 0; + // s.posLo = 0; - // const float positionSoftLimitViolationVelocity = 0.1; - float directSetVLimit = 0.005; - s.posLo = s.posLoHard * 0.99; - s.posHi = s.posHiHard * 0.99; + auto distPosLo = std::uniform_real_distribution<double> { -M_PI, -M_PI_4}; + auto distPosHi = std::uniform_real_distribution<double> { M_PI_4, M_PI}; + s.posLo = distPosLo(gen); + s.posHi = distPosHi(gen); + double p = 20.5; + VelocityControllerWithRampedAccelerationAndPositionBounds ctrl; auto testTick = [&] { - VelocityControllerWithAccelerationAndPositionBounds ctrl; ctrl.dt = s.dt; ctrl.maxDt = s.maxDt; - ctrl.currentV = s.curvel; - ctrl.targetV = s.targvel; + // ctrl.currentV = s.curvel; + // ctrl.currentAcc = s.curacc; ctrl.maxV = s.maxvel; - ctrl.acceleration = s.acc; - ctrl.deceleration = s.dec; - ctrl.directSetVLimit = directSetVLimit; - ctrl.currentPosition = s.curpos; - ctrl.positionLimitLoSoft = s.posLo; + ctrl.targetV = s.targvel; ctrl.positionLimitHiSoft = s.posHi; + ctrl.positionLimitLoSoft = s.posLo; + ctrl.currentPosition = s.curpos; + ctrl.deceleration = 40; + + // ctrl.accuracy = 0.00001; + // ctrl.pControlPosErrorLimit = pControlPosErrorLimit; + // ctrl.pControlVelLimit = pControlVelLimit; + // ctrl.p = p;//ctrl.calculateProportionalGain(); + // ctrl.usePIDAtEnd = false; + // ARMARX_INFO << VAROUT(ctrl.p); BOOST_CHECK(ctrl.validParameters()); - s.updateVel(ctrl.run()); - //s.updateVel(velocityControlWithAccelerationAndPositionBounds( + auto r = ctrl.run(); + // ARMARX_INFO << "State: " << (int)ctrl.calcState() << " " << VAROUT(r.acceleration) << VAROUT(r.velocity) << VAROUT(r.jerk); + s.updateVel(r.velocity); + ctrl.currentV = r.velocity; + ctrl.currentAcc = r.acceleration; + //s.updateVel(positionThroughVelocityControlWithAccelerationBounds( //s.dt, s.maxDt, - //s.curvel, s.targvel, s.maxvel, + //s.curvel, s.maxvel, //s.acc, s.dec, - //directSetVLimit, - //s.curpos, - //s.posLo, s.posHi, - //// positionSoftLimitViolationVelocity, - //s.posLoHard, s.posHiHard + //s.curpos, s.targpos, + //pControlPosErrorLimit, pControlVelLimit, p //)); - s.checkPos(); - s.checkVel(); + // s.checkVel(); + // s.checkAcc(); }; std::cout << "random tests\n"; for (std::size_t try_ = 0; try_ < tries; ++ try_) { - s.writeHeader("velCtrl+acc+pos_random_" + to_string(try_)); - s.curvel = s.vDist(gen); + ctrl = VelocityControllerWithRampedAccelerationAndPositionBounds(); + s.writeHeader("RampedAccPositionLimitedVelCtrl+acc_random_" + to_string(try_)); + s.maxvel = 5;//std::abs(s.vDist(gen)) + 1; + s.curvel = 5;//armarx::math::MathUtils::LimitTo(s.vDist(gen), s.maxvel); + ctrl.currentV = s.curvel; s.curpos = s.pDist(gen); - s.targvel = s.vDist(gen); - s.acc = s.aDist(gen); - s.dec = s.aDist(gen); - directSetVLimit = (std::min(s.acc, s.dec) * s.maxDt * 0.75f / 2.f) * 0.99f; // vlimit = accmin*dtmin/2 + ctrl.currentAcc = s.pDist(gen); + s.curacc = ctrl.currentAcc; + ARMARX_INFO << VAROUT(s.dt) << VAROUT(s.curpos) << VAROUT(s.targvel) << " " << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); + // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 + ctrl.jerk = 100;//(random() % 100) + 10; s.log(); + double stopTime = -1; for (std::size_t tick = 0; tick < ticks; ++tick) { + // std::cout << std::endl; + // s.targpos = tick > 900 ? 3 : 5; + s.tick(testTick); + ARMARX_INFO << /*deactivateSpam(0.01) <<*/ VAROUT(s.dt * tick) << VAROUT(s.curpos) << VAROUT(s.targvel) << VAROUT(s.curacc) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); + if (std::abs(s.curvel - s.targvel) < 0.001 && stopTime < 0) + { + stopTime = s.dt * (tick + 20); // let it run for some more ticks to make sure the velocity is stable + } + if (stopTime > 0 && s.dt * tick > stopTime) + { + break; + } } + BOOST_CHECK_LE(std::abs(s.curvel - s.targvel), 0.001); // allow error of 0.5729577951308232 deg } std::cout << "bounds tests\n"; std::cout << "TODO\n"; ///TODO - std::cout << "done velocityControlWithAccelerationAndPositionBoundsTest \n"; + std::cout << "done positionThroughVelocityControlWithAccelerationBounds \n"; } -BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsTest) +BOOST_AUTO_TEST_CASE(VelocityControllerWithRampedAccelerationTest) { - std::cout << "starting positionThroughVelocityControlWithAccelerationBounds \n"; + return; + std::cout << "starting VelocityControllerWithRampedAccelerationTest \n"; Simulation s; s.posHi = 0; s.posLo = 0; - float p = 20.5; + double p = 20.5; + VelocityControllerWithRampedAcceleration ctrl; auto testTick = [&] { - PositionThroughVelocityControllerWithAccelerationBounds ctrl; ctrl.dt = s.dt; ctrl.maxDt = s.maxDt; - ctrl.currentV = s.curvel; + // ctrl.currentV = s.curvel; + // ctrl.currentAcc = s.curacc; ctrl.maxV = s.maxvel; - ctrl.acceleration = s.acc; - ctrl.deceleration = s.dec; - ctrl.currentPosition = s.curpos; - ctrl.targetPosition = s.targpos; - ctrl.accuracy = 0.0; + ctrl.targetV = s.targvel; + // ctrl.accuracy = 0.00001; // ctrl.pControlPosErrorLimit = pControlPosErrorLimit; // ctrl.pControlVelLimit = pControlVelLimit; - ctrl.p = p; + // ctrl.p = p;//ctrl.calculateProportionalGain(); + // ctrl.usePIDAtEnd = false; + // ARMARX_INFO << VAROUT(ctrl.p); BOOST_CHECK(ctrl.validParameters()); - s.updateVel(ctrl.run()); + auto r = ctrl.run(); + // ARMARX_INFO << "State: " << (int)ctrl.calcState() << " " << VAROUT(r.acceleration) << VAROUT(r.velocity) << VAROUT(r.jerk); + s.updateVel(r.velocity); + ctrl.currentV = r.velocity; + ctrl.currentAcc = r.acceleration; //s.updateVel(positionThroughVelocityControlWithAccelerationBounds( //s.dt, s.maxDt, //s.curvel, s.maxvel, @@ -402,33 +402,42 @@ BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsTest) //s.curpos, s.targpos, //pControlPosErrorLimit, pControlVelLimit, p //)); - s.checkVel(); - s.checkAcc(); + // s.checkVel(); + // s.checkAcc(); }; std::cout << "random tests\n"; for (std::size_t try_ = 0; try_ < tries; ++ try_) { - s.writeHeader("posViaVelCtrl+acc_random_" + to_string(try_)); + ctrl = VelocityControllerWithRampedAcceleration(); + s.writeHeader("RampedAccVelCtrl+acc_random_" + to_string(try_)); s.maxvel = std::abs(s.vDist(gen)) + 1; s.curvel = armarx::math::MathUtils::LimitTo(s.vDist(gen), s.maxvel); - s.curpos = s.pDist(gen); - s.targpos = s.pDist(gen); - s.acc = s.aDist(gen); - s.dec = s.aDist(gen); - ARMARX_INFO << VAROUT(s.curpos) << "TargetPos: " << s.targpos << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel); + ctrl.currentV = s.curvel; + s.curpos = 1;//s.pDist(gen); + ctrl.currentAcc = s.pDist(gen); + ARMARX_INFO << VAROUT(s.dt) << VAROUT(s.curpos) << VAROUT(s.targvel) << " " << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 + ctrl.jerk = (random() % 100) + 10; s.log(); + double stopTime = -1; for (std::size_t tick = 0; tick < ticks; ++tick) { + // std::cout << std::endl; + // s.targpos = tick > 900 ? 3 : 5; + s.tick(testTick); - ARMARX_INFO << deactivateSpam(0.01) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel); - if (std::abs(s.curpos - s.targpos) < 0.01) + // ARMARX_INFO << /*deactivateSpam(0.01) <<*/ VAROUT(s.dt * tick) << VAROUT(s.curpos) << VAROUT(s.targvel) << VAROUT(s.curacc) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); + if (std::abs(s.curvel - s.targvel) < 0.001 && stopTime < 0) + { + stopTime = s.dt * (tick + 20); // let it run for some more ticks to make sure the velocity is stable + } + if (stopTime > 0 && s.dt * tick > stopTime) { break; } } - BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); // allow error of 0.5729577951308232 deg + BOOST_CHECK_LE(std::abs(s.curvel - s.targvel), 0.001); // allow error of 0.5729577951308232 deg } std::cout << "bounds tests\n"; std::cout << "TODO\n"; @@ -437,140 +446,505 @@ BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsTest) std::cout << "done positionThroughVelocityControlWithAccelerationBounds \n"; } -BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationAndPositionBoundsTest) + +//BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationBoundsTest) +//{ +// std::cout << "starting velocityControlWithAccelerationBoundsTest \n"; +// Simulation s; +// s.posHi = 0; +// s.posLo = 0; +// s.posHiHard = 0; +// s.posLoHard = 0; + +// double directSetVLimit = 0.005; + + +// auto testTick = [&] +// { +// VelocityControllerWithAccelerationBounds ctrl; +// ctrl.dt = s.dt; +// ctrl.maxDt = s.maxDt; +// ctrl.currentV = s.curvel; +// ctrl.targetV = s.targvel; +// ctrl.maxV = s.maxvel; +// ctrl.acceleration = s.acc; +// ctrl.deceleration = s.dec; +// ctrl.directSetVLimit = directSetVLimit; +// BOOST_CHECK(ctrl.validParameters()); +// // s.updateVel(velocityControlWithAccelerationBounds(s.dt, s.maxDt, s.curvel, s.targvel, s.maxvel, s.acc, s.dec, directSetVLimit)); +// s.updateVel(ctrl.run()); +// s.checkVel(); +// s.checkAcc(); +// }; + +// std::cout << "random tests\n"; +// for (std::size_t try_ = 0; try_ < tries; ++ try_) +// { +// s.writeHeader("velCtrl+acc_random_" + to_string(try_)); +// s.curvel = s.vDist(gen); +// s.targvel = s.vDist(gen); +// s.acc = s.aDist(gen); +// s.dec = s.aDist(gen); +// directSetVLimit = (std::min(s.acc, s.dec) * s.maxDt * 0.75f / 2.f) * 0.99f; // vlimit = accmin*dtmin/2 + +// s.log(); +// for (std::size_t tick = 0; tick < ticks; ++tick) +// { +// s.tick(testTick); +// } +// BOOST_CHECK(std::abs(s.curvel - s.targvel) < 0.001); +// } +// std::cout << "bounds tests\n"; +// std::cout << "TODO\n"; +// ///TODO + +// std::cout << "done velocityControlWithAccelerationBoundsTest \n"; +//} + +//BOOST_AUTO_TEST_CASE(velocityControlWithAccelerationAndPositionBoundsTest) +//{ +// std::cout << "starting velocityControlWithAccelerationAndPositionBoundsTest \n"; +// Simulation s; + +// // const double positionSoftLimitViolationVelocity = 0.1; +// double directSetVLimit = 0.005; +// s.posLo = s.posLoHard * 0.99; +// s.posHi = s.posHiHard * 0.99; + +// auto testTick = [&] +// { +// VelocityControllerWithAccelerationAndPositionBounds ctrl; +// ctrl.dt = s.dt; +// ctrl.maxDt = s.maxDt; +// ctrl.currentV = s.curvel; +// ctrl.targetV = s.targvel; +// ctrl.maxV = s.maxvel; +// ctrl.acceleration = s.acc; +// ctrl.deceleration = s.dec; +// ctrl.directSetVLimit = directSetVLimit; +// ctrl.currentPosition = s.curpos; +// ctrl.positionLimitLoSoft = s.posLo; +// ctrl.positionLimitHiSoft = s.posHi; +// BOOST_CHECK(ctrl.validParameters()); +// s.updateVel(ctrl.run()); +// //s.updateVel(velocityControlWithAccelerationAndPositionBounds( +// //s.dt, s.maxDt, +// //s.curvel, s.targvel, s.maxvel, +// //s.acc, s.dec, +// //directSetVLimit, +// //s.curpos, +// //s.posLo, s.posHi, +// //// positionSoftLimitViolationVelocity, +// //s.posLoHard, s.posHiHard +// //)); +// s.checkPos(); +// s.checkVel(); +// }; + +// std::cout << "random tests\n"; +// for (std::size_t try_ = 0; try_ < tries; ++ try_) +// { +// s.writeHeader("velCtrl+acc+pos_random_" + to_string(try_)); +// s.curvel = s.vDist(gen); +// s.curpos = s.pDist(gen); +// s.targvel = s.vDist(gen); +// s.acc = s.aDist(gen); +// s.dec = s.aDist(gen); +// directSetVLimit = (std::min(s.acc, s.dec) * s.maxDt * 0.75f / 2.f) * 0.99f; // vlimit = accmin*dtmin/2 +// s.log(); +// for (std::size_t tick = 0; tick < ticks; ++tick) +// { +// s.tick(testTick); +// } +// } +// std::cout << "bounds tests\n"; +// std::cout << "TODO\n"; +// ///TODO + +// std::cout << "done velocityControlWithAccelerationAndPositionBoundsTest \n"; +//} + +//BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithConstantJerkTest) +//{ +// return; + +// std::cout << "starting positionThroughVelocityControlWithConstantJerk \n"; +// Simulation s; +// s.posHi = 0; +// s.posLo = 0; + + +// double p = 20.5; +// PositionThroughVelocityControllerWithAccelerationRamps ctrl; + +// auto testTick = [&] +// { +// ctrl.dt = s.dt; +// ctrl.maxDt = s.maxDt; +// // ctrl.currentV = s.curvel; +// // ctrl.currentAcc = s.curacc; +// ctrl.maxV = s.maxvel; +// ctrl.acceleration = s.acc; +// ctrl.deceleration = s.dec; +// ctrl.currentPosition = s.curpos; +// ctrl.setTargetPosition(s.targpos); +// ctrl.accuracy = 0.00001; +// ctrl.jerk = 100; +// // ctrl.pControlPosErrorLimit = pControlPosErrorLimit; +// // ctrl.pControlVelLimit = pControlVelLimit; +// ctrl.p = p;//ctrl.calculateProportionalGain(); +// ctrl.usePIDAtEnd = false; +// // ARMARX_INFO << VAROUT(ctrl.p); +// BOOST_CHECK(ctrl.validParameters()); +// auto r = ctrl.run(); +// // ARMARX_INFO << "State: " << (int)ctrl.calcState() << " " << VAROUT(r.acceleration) << VAROUT(r.velocity) << VAROUT(r.jerk); +// s.updateVel(r.velocity); +// ctrl.currentV = r.velocity; +// ctrl.currentAcc = r.acceleration; +// //s.updateVel(positionThroughVelocityControlWithAccelerationBounds( +// //s.dt, s.maxDt, +// //s.curvel, s.maxvel, +// //s.acc, s.dec, +// //s.curpos, s.targpos, +// //pControlPosErrorLimit, pControlVelLimit, p +// //)); +// s.checkVel(); +// s.checkAcc(); +// }; + +// std::cout << "random tests\n"; +// for (std::size_t try_ = 0; try_ < tries; ++ try_) +// { +// ctrl = PositionThroughVelocityControllerWithAccelerationRamps(); +// s.writeHeader("posViaVelCtrl+acc_random_" + to_string(try_)); +// s.maxvel = 1.49;//std::abs(s.vDist(gen)) + 1; +// s.curvel = -1.49;//armarx::math::MathUtils::LimitTo(s.vDist(gen), s.maxvel); +// ctrl.currentV = s.curvel; +// s.curpos = 1;//s.pDist(gen); +// // s.targpos = 5;//s.pDist(gen); +// s.acc = 10;//s.aDist(gen); +// s.dec = 20;//s.aDist(gen); +//// ARMARX_INFO << VAROUT(s.dt) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << " " << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); +//// p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 +// s.log(); +// for (std::size_t tick = 0; tick < ticks; ++tick) +// { +//// std::cout << std::endl; +// s.targpos = tick > 300 ? 3 : 5; + +// s.tick(testTick); +//// ARMARX_INFO << /*deactivateSpam(0.01) <<*/ VAROUT(s.dt) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << " " << VAROUT(s.curacc) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); +// if (std::abs(s.curpos - s.targpos) < 0.00001 /*|| s.curpos > s.targpos*/) +// { +// break; +// } +// } +// BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.00001); // allow error of 0.5729577951308232 deg +// } +// std::cout << "bounds tests\n"; +// std::cout << "TODO\n"; +// ///TODO + +// std::cout << "done positionThroughVelocityControlWithAccelerationBounds \n"; +//} + +BOOST_AUTO_TEST_CASE(MinJerkPositionControllerTest) { - return; - std::cout << "starting positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; + + std::cout << "starting MinJerkPositionControllerTest \n"; Simulation s; s.posHi = 0; s.posLo = 0; - float p = 0.5; + double p = 20.5; + MinJerkPositionController ctrl; auto testTick = [&] { - PositionThroughVelocityControllerWithAccelerationAndPositionBounds ctrl; ctrl.dt = s.dt; ctrl.maxDt = s.maxDt; - ctrl.currentV = s.curvel; + // ctrl.currentV = s.curvel; + // ctrl.currentAcc = s.curacc; ctrl.maxV = s.maxvel; - ctrl.acceleration = s.acc; - ctrl.deceleration = s.dec; + ctrl.desiredDeceleration = s.dec; ctrl.currentPosition = s.curpos; - ctrl.targetPosition = s.targpos; - ctrl.p = p; - ctrl.positionLimitLo = s.posLoHard; - ctrl.positionLimitHi = s.posHiHard; - BOOST_CHECK(ctrl.validParameters()); - s.updateVel(ctrl.run()); - //s.updateVel(positionThroughVelocityControlWithAccelerationAndPositionBounds( + ctrl.setTargetPosition(s.targpos); + // ctrl.accuracy = 0.00001; + ctrl.desiredJerk = 100; + // ctrl.pControlPosErrorLimit = pControlPosErrorLimit; + // ctrl.pControlVelLimit = pControlVelLimit; + // ctrl.p = p;//ctrl.calculateProportionalGain(); + // ctrl.usePIDAtEnd = false; + // ARMARX_INFO << VAROUT(ctrl.p); + // BOOST_CHECK(ctrl.validParameters()); + auto r = ctrl.run(); + // ARMARX_INFO << "State: " << (int)ctrl.calcState() << " " << VAROUT(r.acceleration) << VAROUT(r.velocity) << VAROUT(r.jerk); + // s.updateVel(r.velocity); + s.updatePos(r.position); + ctrl.currentV = r.velocity; + ctrl.currentAcc = r.acceleration; + //s.updateVel(positionThroughVelocityControlWithAccelerationBounds( //s.dt, s.maxDt, //s.curvel, s.maxvel, //s.acc, s.dec, //s.curpos, s.targpos, - //pControlPosErrorLimit, pControlVelLimit, p, - //s.posLoHard, s.posHiHard)); - s.checkPos(); + //pControlPosErrorLimit, pControlVelLimit, p + //)); + // s.checkVel(); + // s.checkAcc(); }; std::cout << "random tests\n"; for (std::size_t try_ = 0; try_ < tries; ++ try_) { - s.writeHeader("posViaVelCtrl+acc+pos_random_" + to_string(try_)); - s.curvel = s.vDist(gen); - s.curpos = s.pDist(gen); - s.targpos = s.pDist(gen); - s.acc = s.aDist(gen); + ctrl = MinJerkPositionController(); + s.writeHeader("minJerkPosCtrl+acc_random_" + to_string(try_)); + s.maxvel = std::abs(s.vDist(gen)) + 1; + s.curvel = armarx::math::MathUtils::LimitTo(s.vDist(gen), s.maxvel); + s.curacc = s.vDist(gen); s.dec = s.aDist(gen); + ctrl.currentAcc = s.curacc; + ctrl.currentV = s.curvel; + s.curpos = 1;//s.pDist(gen); + ARMARX_INFO << VAROUT(s.dt) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << " " << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 s.log(); + double stopTime = -1; for (std::size_t tick = 0; tick < ticks; ++tick) { + // std::cout << std::endl; + s.targpos = tick > 900 ? 3 : 5; + s.tick(testTick); + // ARMARX_INFO << /*deactivateSpam(0.01) <<*/ VAROUT(s.dt * tick) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << " " << VAROUT(s.curacc) << VAROUT(s.maxvel) << VAROUT(s.curvel) << VAROUT(s.jerk); + if (std::abs(s.curpos - s.targpos) < 0.01 && stopTime < 0) + { + stopTime = s.dt * (tick + 20); // let it run for some more ticks to make sure the velocity is stable + ARMARX_INFO << VAROUT(s.time) << VAROUT(stopTime); + } + if (stopTime > 0 && s.dt * tick > stopTime) + { + break; + } } - BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.05); + BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); // allow error of 0.5729577951308232 deg } std::cout << "bounds tests\n"; std::cout << "TODO\n"; ///TODO - std::cout << "done positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; + std::cout << "done positionThroughVelocityControlWithAccelerationBounds \n"; } -//BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPositionTest) + + +//BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsTest) //{ -// std::cout << "starting positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; +// return; +// std::cout << "starting positionThroughVelocityControlWithAccelerationBounds \n"; // Simulation s; // s.posHi = 0; // s.posLo = 0; -// // float p = 0.05; -// // const float pControlPosErrorLimit = 0.02; -// // const float pControlVelLimit = 0.02; -// float p = 0.1; -// const float pControlPosErrorLimit = 0.01; -// const float pControlVelLimit = 0.01; -// float direction; + + +// double p = 0.5; // auto testTick = [&] // { -// PositionThroughVelocityControllerWithAccelerationAndPositionBounds ctrl; +// PositionThroughVelocityControllerWithAccelerationBounds ctrl; // ctrl.dt = s.dt; // ctrl.maxDt = s.maxDt; // ctrl.currentV = s.curvel; +// // ctrl.currentAcc = s.curacc; // ctrl.maxV = s.maxvel; // ctrl.acceleration = s.acc; // ctrl.deceleration = s.dec; // ctrl.currentPosition = s.curpos; // ctrl.targetPosition = s.targpos; -// ctrl.pControlPosErrorLimit = pControlPosErrorLimit; -// ctrl.pControlVelLimit = pControlVelLimit; -// ctrl.p = p; -// ctrl.positionLimitLo = s.posLoHard; -// ctrl.positionLimitHi = s.posHiHard; +//// ctrl.accuracy = 0.0; +// // ctrl.jerk = 100; +// // ctrl.pControlPosErrorLimit = pControlPosErrorLimit; +// // ctrl.pControlVelLimit = pControlVelLimit; +// ctrl.pid->Kp = p; // BOOST_CHECK(ctrl.validParameters()); -// s.updateVel(ctrl.run()); -// //s.updateVel(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition( +// auto result = ctrl.run(); +// s.updateVel(result); +// //s.updateVel(positionThroughVelocityControlWithAccelerationBounds( // //s.dt, s.maxDt, // //s.curvel, s.maxvel, // //s.acc, s.dec, // //s.curpos, s.targpos, -// //pControlPosErrorLimit, pControlVelLimit, p, -// //direction, -// //s.posLoHard, s.posHiHard)); +// //pControlPosErrorLimit, pControlVelLimit, p +// //)); // s.checkVel(); // s.checkAcc(); -// s.curpos = periodicClamp(s.curpos, s.posLoHard, s.posHiHard); // }; -// auto rngTestRuns = [&](int d) +// std::cout << "random tests\n"; +// for (std::size_t try_ = 0; try_ < tries; ++ try_) // { -// std::cout << "random tests (dir=" << to_string(d) << "\n"; -// for (std::size_t try_ = 0; try_ < tries; ++ try_) +// s.writeHeader("posViaVelCtrl+acc_random_" + to_string(try_)); +// s.maxvel = std::abs(s.vDist(gen)) + 1; +// s.curvel = //armarx::math::MathUtils::LimitTo(s.vDist(gen), s.maxvel); +// s.curpos = s.pDist(gen); +// s.targpos = s.pDist(gen); +// s.acc = s.aDist(gen); +// s.dec = s.aDist(gen); +// ARMARX_INFO << VAROUT(s.dt) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << VAROUT(s.acc) << VAROUT(s.dec) << VAROUT(s.maxvel) << VAROUT(s.curvel); +// // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 +// s.log(); +// for (std::size_t tick = 0; tick < ticks; ++tick) // { -// s.writeHeader("posViaVelCtrl+acc+periodicPos+dir" + to_string(d) + "_random_" + to_string(try_)); -// direction = d; -// s.curvel = s.vDist(gen); -// s.curpos = periodicClamp(s.pDist(gen), s.posLoHard, s.posHiHard); -// s.targpos = periodicClamp(s.pDist(gen), s.posLoHard, s.posHiHard); -// s.acc = s.aDist(gen); -// s.dec = s.aDist(gen); -// s.log(); -// for (std::size_t tick = 0; tick < ticks; ++tick) +// s.tick(testTick); +//// ARMARX_INFO << /*deactivateSpam(0.01) <<*/ VAROUT(s.dt) << VAROUT(s.curpos) << "TargetPos: " << s.targpos << VAROUT(s.curacc) << VAROUT(s.maxvel) << VAROUT(s.curvel); +// if (std::abs(s.curpos - s.targpos) < 0.01) // { -// s.tick(testTick); +// break; // } -// BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); -// BOOST_CHECK_LE(std::abs(s.curvel), 0.1); // } +// BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); // allow error of 0.5729577951308232 deg +// } +// std::cout << "bounds tests\n"; +// std::cout << "TODO\n"; +// ///TODO +// std::cout << "done positionThroughVelocityControlWithAccelerationBounds \n"; +//} + +//BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationAndPositionBoundsTest) +//{ +// std::cout << "starting positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; +// Simulation s; +// s.posHi = 0; +// s.posLo = 0; + +// double p = 0.5; + + +// auto testTick = [&] +// { +// PositionThroughVelocityControllerWithAccelerationAndPositionBounds ctrl; +// ctrl.dt = s.dt; +// ctrl.maxDt = s.maxDt; +// ctrl.currentV = s.curvel; +// ctrl.maxV = s.maxvel; +// ctrl.acceleration = s.acc; +// ctrl.deceleration = s.dec; +// ctrl.currentPosition = s.curpos; +// ctrl.targetPosition = s.targpos; +// ctrl.pid->Kp = p; +// ctrl.positionLimitLo = s.posLoHard; +// ctrl.positionLimitHi = s.posHiHard; +// BOOST_REQUIRE(ctrl.validParameters()); +// s.updateVel(ctrl.run()); +// //s.updateVel(positionThroughVelocityControlWithAccelerationAndPositionBounds( +// //s.dt, s.maxDt, +// //s.curvel, s.maxvel, +// //s.acc, s.dec, +// //s.curpos, s.targpos, +// //pControlPosErrorLimit, pControlVelLimit, p, +// //s.posLoHard, s.posHiHard)); +// s.checkPos(); // }; -// rngTestRuns(0); +// std::cout << "random tests\n"; +// for (std::size_t try_ = 0; try_ < tries; ++ try_) +// { +// s.writeHeader("posViaVelCtrl+acc+pos_random_" + to_string(try_)); +// s.curvel = s.vDist(gen); +// s.curpos = s.pDist(gen); +// s.targpos = s.pDist(gen); +// s.acc = s.aDist(gen); +// s.dec = s.aDist(gen); +// // p = ((std::min(s.acc,s.dec)*s.maxDt*0.75-pControlVelLimit)/pControlPosErrorLimit) * 0.99f; // sometimes <0 +// s.log(); +// for (std::size_t tick = 0; tick < ticks; ++tick) +// { +// s.tick(testTick); +// } +// BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.05); +// } // std::cout << "bounds tests\n"; // std::cout << "TODO\n"; // ///TODO -// std::cout << "done positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; +// std::cout << "done positionThroughVelocityControlWithAccelerationAndPositionBounds \n"; //} +////BOOST_AUTO_TEST_CASE(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPositionTest) +////{ +//// std::cout << "starting positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; +//// Simulation s; +//// s.posHi = 0; +//// s.posLo = 0; +//// // double p = 0.05; +//// // const double pControlPosErrorLimit = 0.02; +//// // const double pControlVelLimit = 0.02; +//// double p = 0.1; +//// const double pControlPosErrorLimit = 0.01; +//// const double pControlVelLimit = 0.01; +//// double direction; + +//// auto testTick = [&] +//// { +//// PositionThroughVelocityControllerWithAccelerationAndPositionBounds ctrl; +//// ctrl.dt = s.dt; +//// ctrl.maxDt = s.maxDt; +//// ctrl.currentV = s.curvel; +//// ctrl.maxV = s.maxvel; +//// ctrl.acceleration = s.acc; +//// ctrl.deceleration = s.dec; +//// ctrl.currentPosition = s.curpos; +//// ctrl.targetPosition = s.targpos; +//// ctrl.pControlPosErrorLimit = pControlPosErrorLimit; +//// ctrl.pControlVelLimit = pControlVelLimit; +//// ctrl.p = p; +//// ctrl.positionLimitLo = s.posLoHard; +//// ctrl.positionLimitHi = s.posHiHard; +//// BOOST_CHECK(ctrl.validParameters()); +//// s.updateVel(ctrl.run()); +//// //s.updateVel(positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition( +//// //s.dt, s.maxDt, +//// //s.curvel, s.maxvel, +//// //s.acc, s.dec, +//// //s.curpos, s.targpos, +//// //pControlPosErrorLimit, pControlVelLimit, p, +//// //direction, +//// //s.posLoHard, s.posHiHard)); +//// s.checkVel(); +//// s.checkAcc(); +//// s.curpos = periodicClamp(s.curpos, s.posLoHard, s.posHiHard); +//// }; + +//// auto rngTestRuns = [&](int d) +//// { +//// std::cout << "random tests (dir=" << to_string(d) << "\n"; +//// for (std::size_t try_ = 0; try_ < tries; ++ try_) +//// { +//// s.writeHeader("posViaVelCtrl+acc+periodicPos+dir" + to_string(d) + "_random_" + to_string(try_)); +//// direction = d; +//// s.curvel = s.vDist(gen); +//// s.curpos = periodicClamp(s.pDist(gen), s.posLoHard, s.posHiHard); +//// s.targpos = periodicClamp(s.pDist(gen), s.posLoHard, s.posHiHard); +//// s.acc = s.aDist(gen); +//// s.dec = s.aDist(gen); +//// s.log(); +//// for (std::size_t tick = 0; tick < ticks; ++tick) +//// { +//// s.tick(testTick); +//// } +//// BOOST_CHECK_LE(std::abs(s.curpos - s.targpos), 0.01); +//// BOOST_CHECK_LE(std::abs(s.curvel), 0.1); +//// } + +//// }; +//// rngTestRuns(0); + +//// std::cout << "bounds tests\n"; +//// std::cout << "TODO\n"; +//// ///TODO + +//// std::cout << "done positionThroughVelocityControlWithAccelerationBoundsAndPeriodicPosition \n"; +////} + diff --git a/source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt b/source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt index 6a3394ce80fb3c67a1670d555ae04fee7a0b0517..9c43b6455ece5f1448f25ac224d207a8593314b3 100644 --- a/source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt +++ b/source/RobotAPI/components/units/RobotUnit/test/CMakeLists.txt @@ -1,5 +1,5 @@ set(LIBS ${LIBS} RobotUnit ) armarx_add_test(BasicControllerTest BasicControllerTest.cpp "${LIBS}") -armarx_add_test(SingleBasicControllerTest SingleBasicControllerTest.cpp "${LIBS}") +#armarx_add_test(SingleBasicControllerTest SingleBasicControllerTest.cpp "${LIBS}") diff --git a/source/RobotAPI/components/units/RobotUnit/test/eval_script.inc b/source/RobotAPI/components/units/RobotUnit/test/eval_script.inc index 0c13aa8f3ad99cf5623697f3ba8a3c3778f74ed0..e6a9129a6847ff59514f00d32aed7d81bae94820 100644 --- a/source/RobotAPI/components/units/RobotUnit/test/eval_script.inc +++ b/source/RobotAPI/components/units/RobotUnit/test/eval_script.inc @@ -62,8 +62,8 @@ def consume_file(fname, save=True, show=True): add(pos,'brakingDist',clr='orange', style=':') add(pos,'posAfterBraking',clr='magenta', style=':') - add(vel,'curvel',clr='teal') - add(vel,'targvel',clr='teal', style='-.') + add(vel,'curvel',clr='blue') + add(vel,'targvel',clr='blue', style='-.') add(vel,'maxv',clr='blue', style='--') add(vel,'maxv',factor=-1,clr='blue', style='--') @@ -72,7 +72,7 @@ def consume_file(fname, save=True, show=True): add(acc,'dec',clr='darkgreen', style='--') plt.draw() - if save: plt.savefig(fname+'.png') + if save: plt.savefig(fname+'.svg') if show: plt.show() if not show: plt.close() @@ -88,7 +88,8 @@ def handle_path(p, show=True): show=False for subdir, dirs, files in os.walk(p): for f in files: - handle_path(subdir+f,show=show) + if not ".svg" in f: + handle_path(subdir+f,show=show) if len(sys.argv) >= 2: handle_path(sys.argv[1]) diff --git a/source/RobotAPI/components/units/RobotUnit/util/CtrlUtil.h b/source/RobotAPI/components/units/RobotUnit/util/CtrlUtil.h new file mode 100644 index 0000000000000000000000000000000000000000..16b1680121581308cbe687f344f21e5b057cf9fd --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/util/CtrlUtil.h @@ -0,0 +1,186 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http{//www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2019 + * @copyright http{//www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +//pragma once +#include <cmath> +#include <RobotAPI/libraries/core/math/MathUtils.h> +#include <iostream> +namespace armarx +{ + namespace ctrlutil + { + double s(double t, double s0, double v0, double a0, double j) + { + return s0 + v0 * t + 0.5 * a0 * t * t + 1.0 / 6.0 * j * t * t * t; + } + double v(double t, double v0, double a0, double j) + { + return v0 + a0 * t + 0.5 * j * t * t; + } + double a(double t, double a0, double j) + { + return a0 + j * t; + } + + + double t_to_v(double v, double a, double j) + { + // time to accelerate to v with jerk j starting at acceleration a0 + //return 2*math.sqrt(a0**2+2*j*(v/2))-a0/j + // 0 = (-v + a0*t) /(0.5*j) + t*t + // 0 = -2v/j + 2a0t/j + t*t + // --> p = 2a0/j; q = -2v/j + // t = - a0/j +- sqrt((a0/j)**2 +2v/j) + double tmp = a / j; + return - a / j + std::sqrt(tmp * tmp + 2 * v / j); + } + + + + double t_to_v_with_wedge_acc(double v, double a0, double j) + { + // ramp up and down of acceleration (i.e. a0=at); returns t to achieve delta v + return 2 * t_to_v(v / 2.0, a0, j); + } + + struct BrakingData + { + double s_total, v1, v2, v3, dt1, dt2, dt3; + }; + + double brakingDistance(double v0, double a0, double j) + { + auto signV = math::MathUtils::Sign(v0); + auto t = t_to_v(v0, -signV * a0, signV * j); + return s(t, 0, v0, a0, -signV * j); + } + + BrakingData brakingDistance(double goal, double s0, double v0, double a0, double v_max, double a_max, double j, double dec_max) + { + double d = math::MathUtils::Sign(goal - s0); // if v0 == 0.0 else abs(v0)/a_max + dec_max *= -d; + // std::cout << "dec max: " << (dec_max) << " d: " << d << std::endl; + double dt1 = std::abs((dec_max - a0) / (-j * d)); + // double dt1 = t_to_v(v_max-v0, a0, j); + // double a1 = a(dt1, a0, -d * j); + double v1 = v(dt1, v0, a0, -d * j); + double acc_duration = std::abs(dec_max) / j; + double v2 = v(acc_duration, 0, 0, d * j);// # inverse of time to accelerate from 0 to max v + v2 = math::MathUtils::LimitTo(v2, v_max); + // v2 = v1 + dv2 + double dt2 = d * ((v1 - v2) / dec_max); + // double a2 = a(dt2, a1, 0); + + double dt3 = t_to_v(std::abs(v2), 0, j); + double s1 = s(dt1, 0, v0, a0, d * j); + double s2 = s(dt2, 0, v1, dec_max, 0); + double s3 = s(dt3, 0, v2, dec_max, d * j); + double v3 = v(dt3, v2, dec_max, d * j); + double s_total = s1 + s2 + s3; + + if (dt2 < 0)// # we dont have a phase with a=a_max and need to reduce the a_max + { + double excess_time = t_to_v_with_wedge_acc(std::abs(v1), std::abs(dec_max), j); + double delta_a = a(excess_time / 2, 0, d * j); + a_max -= d * delta_a; + dec_max = std::abs(dec_max) - std::abs(delta_a); + dec_max *= -d; + return brakingDistance(goal, s0, v0, a0, v_max, a_max, j, dec_max); + } + + return {s_total * d, v1, v2, v3, dt1, dt2, dt3}; + // return (s_total, v1, v2, v3, dt1, dt2, dt3); + } + + struct WedgeBrakingData + { + double s_total, s1, s2, v1, v2, a1, a2, t, dt1, dt2; + }; + bool isPossibleToBrake(double v0, double a0, double j) + { + return j * v0 - a0 * a0 / 2 > 0.0f; + } + + double jerk(double t, double s0, double v0, double a0) + { + return (6 * s0 - 3 * t * (a0 * t + 2 * v0)) / (t * t * t); + } + + std::tuple<double, double, double> calcAccAndJerk(double s0, double v0) + { + s0 *= -1; + double t = - (3 * s0) / v0; + double a0 = - (2 * v0) / t; + double j = 2 * v0 / (t * t); + return std::make_tuple(t, a0, j); + } + + + + WedgeBrakingData braking_distance_with_wedge(double v0, double a0, double j) + { + // # v0 = v1 + v2 + // # v1 = t1 * a0 + 0.5*j*t1**2 + // # v2 = 0.5*j*t2**2 + // # a1 = t2 * j ^ a1 = a0 - t1 * j -> t2 * j = a0 - t1 * j + // # t2 = (a0 - t1 * j) / j + // # t2 = a0/j - t1 + // # t1_1 = -(math.sqrt(2*j**2 * (a0**2 + 2*j*v0)) + 2*a0*j)/(2*j**2) + // # t1_2 = (math.sqrt(2*j**2 * (a0**2 + 2*j*v0)) - 2*a0*j)/(2*j**2) + // # t1 = t1_2 + double d = math::MathUtils::Sign(v0); + v0 *= d; + a0 *= d; + // j *= d; + double part = j * v0 - a0 * a0 / 2.0; + if (part < 0) + { + WedgeBrakingData r; + r.s_total = -1; + r.dt2 = -1; + return r; + throw std::runtime_error("unsuitable parameters! : j: " + std::to_string(j) + + " a0: " + std::to_string(a0) + + " v0: " + std::to_string(v0)); + } + double t1 = std::sqrt(part) / j; + double t2 = (a0 / j) + t1; + double v1 = v(t1, v0, a0, -j); + // double dv1 = v(t1, 0, a0, -j); + // double diff_v1 = v0 - v1; + double a1 = a(t1, -a0, -j); + // double a1_2 = a(t2, 0, j); + double v2 = v(t2, v1, a1, j); + // double dv2 = v(t2, 0, 0, j); + // double v_sum = abs(dv1)+dv2; + double a2 = a(t2, a1, j); + // assert(abs(v_sum - v0) < 0.001); + // assert(abs(v2) < 0.0001); + double s1 = s(t1, 0, v0, a0, -j); + double s2 = s(t2, 0, v1, a1, j); + double s_total = s1 + s2; + return {s_total, s1, s2, d * v1, d * v2, d * a1, d * a2, t1 + t2, t1, t2}; + } + } + +} diff --git a/source/RobotAPI/components/units/RobotUnit/util/DynamicsHelper.cpp b/source/RobotAPI/components/units/RobotUnit/util/DynamicsHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f52c9276e2876c52264a09831c212d5b9db2ffd5 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/util/DynamicsHelper.cpp @@ -0,0 +1,126 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#include "DynamicsHelper.h" + +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/observers/filters/rtfilters/RTFilterBase.h> + +namespace armarx +{ + + DynamicsHelper::DynamicsHelper(RobotUnit* robotUnit): robotUnit(robotUnit) + { + + ARMARX_CHECK_EXPRESSION(robotUnit); + } + + void DynamicsHelper::addRobotPart(VirtualRobot::RobotNodeSetPtr rnsJoints, VirtualRobot::RobotNodeSetPtr rnsBodies, + rtfilters::RTFilterBasePtr velocityFilter, + rtfilters::RTFilterBasePtr accelerationFilter) + { + DynamicsData data(rnsJoints, rnsBodies); + for (size_t i = 0; i < rnsJoints->getSize(); i++) + { + auto selectedJoint = robotUnit->getSensorDevice(rnsJoints->getNode(i)->getName()); + if (selectedJoint) + { + auto sensorValue = const_cast<SensorValue1DoFActuator*>(selectedJoint->getSensorValue()->asA<SensorValue1DoFActuator>()); + ARMARX_CHECK_EXPRESSION_W_HINT(sensorValue, rnsJoints->getNode(i)->getName()); + ARMARX_DEBUG << "will calculate inverse dynamics for robot node " << rnsJoints->getNode(i)->getName(); + data.nodeSensorVec.push_back(std::make_pair(rnsJoints->getNode(i), sensorValue)); + } + else + { + ARMARX_INFO << "Joint " << rnsJoints->getNode(i)->getName() << " not found"; + data.nodeSensorVec.push_back(std::make_pair(rnsJoints->getNode(i), nullptr)); + } + if (velocityFilter) + { + data.velocityFilter.push_back(velocityFilter->clone()); + } + if (accelerationFilter) + { + data.accelerationFilter.push_back(accelerationFilter->clone()); + } + } + dataMap.emplace(std::make_pair(rnsJoints, data)); + } + + void DynamicsHelper::update(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + for (auto& pair : dataMap) + { + // first get current state of the robot (position, velocity, acceleration) + auto& data = pair.second; + auto& dynamics = data.dynamics; + { + size_t i = 0; + for (auto& node : data.nodeSensorVec) + { + ARMARX_CHECK_EXPRESSION_W_HINT(node.second, node.first->getName()); + data.q(i) = node.second->position; + if (i < data.velocityFilter.size() && data.velocityFilter.at(i) && !std::isnan(node.second->velocity)) + { + data.qDot(i) = data.velocityFilter.at(i)->update(sensorValuesTimestamp, node.second->velocity); + } + else + { + data.qDot(i) = node.second->velocity; + } + if (i < data.accelerationFilter.size() && data.accelerationFilter.at(i) && !std::isnan(node.second->acceleration)) + { + data.qDDot(i) = data.accelerationFilter.at(i)->update(sensorValuesTimestamp, node.second->acceleration); + } + else + { + data.qDDot(i) = node.second->acceleration; + } + i++; + } + } + // calculate external torques (tau) applied to robot induced by dynamics + dynamics.getGravityMatrix(data.q, data.tauGravity); + dynamics.getInverseDynamics(data.q, + data.qDot, + data.qDDot, + data.tau); + // update virtual sensor values + size_t i = 0; + for (auto& node : data.nodeSensorVec) + { + auto torque = data.tau(i); + auto gravityTorque = data.tauGravity(i); + if (node.second) + { + node.second->inverseDynamicsTorque = -torque; + node.second->gravityTorque = -gravityTorque; + node.second->inertiaTorque = -(torque - gravityTorque); + } + i++; + } + } + } + +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/util/DynamicsHelper.h b/source/RobotAPI/components/units/RobotUnit/util/DynamicsHelper.h new file mode 100644 index 0000000000000000000000000000000000000000..4212b3ae5db79ac102f164860810a31483d1363d --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/util/DynamicsHelper.h @@ -0,0 +1,69 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#pragma once +#include <VirtualRobot/RobotNodeSet.h> +#include <VirtualRobot/Dynamics/Dynamics.h> +#include <Eigen/Core> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> + +namespace armarx +{ + namespace rtfilters + { + class RTFilterBase; + using RTFilterBasePtr = std::shared_ptr<RTFilterBase>; + } + class RobotUnit; + class DynamicsHelper + { + public: + struct DynamicsData + { + DynamicsData(VirtualRobot::RobotNodeSetPtr rnsJoints, VirtualRobot::RobotNodeSetPtr rnsBodies) : + rnsJoints(rnsJoints), rnsBodies(rnsBodies), + dynamics(rnsJoints, rnsBodies) + { + q = qDot = qDDot = tau = tauGravity = Eigen::VectorXd::Zero(rnsJoints->getSize()); + } + DynamicsData(const DynamicsData& r) = default; + VirtualRobot::RobotNodeSetPtr rnsJoints, rnsBodies; + VirtualRobot::Dynamics dynamics; + Eigen::VectorXd q, qDot, qDDot, tau, tauGravity; + std::vector<std::pair<VirtualRobot::RobotNodePtr, SensorValue1DoFActuator*>> nodeSensorVec; + std::vector<rtfilters::RTFilterBasePtr> velocityFilter; + std::vector<rtfilters::RTFilterBasePtr> accelerationFilter; + }; + + DynamicsHelper(RobotUnit* robotUnit); + ~DynamicsHelper() = default; + void addRobotPart(VirtualRobot::RobotNodeSetPtr rnsJoints, VirtualRobot::RobotNodeSetPtr rnsBodies, + rtfilters::RTFilterBasePtr velocityFilter = rtfilters::RTFilterBasePtr(), + rtfilters::RTFilterBasePtr accelerationFilter = rtfilters::RTFilterBasePtr()); + void update(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + protected: + RobotUnit* robotUnit; + std::map<VirtualRobot::RobotNodeSetPtr, DynamicsData> dataMap; + }; + +} diff --git a/source/RobotAPI/components/units/RobotUnit/util/KeyValueVector.h b/source/RobotAPI/components/units/RobotUnit/util/KeyValueVector.h index 799baf84595d6a6eb042cbc5f7d6c52755b640ad..be82c776f26e326f76642dda211d662a2b9e13f9 100644 --- a/source/RobotAPI/components/units/RobotUnit/util/KeyValueVector.h +++ b/source/RobotAPI/components/units/RobotUnit/util/KeyValueVector.h @@ -20,224 +20,5 @@ * GNU General Public License */ #pragma once +#include <ArmarXCore/util/CPPUtility/KeyValueVector.h> -#include <ArmarXCore/core/util/TemplateMetaProgramming.h> -#include <vector> -#include <map> - -namespace armarx -{ - /** - * @brief This class is pretty much similar to a map. - * - * This class stores keys and values in two vectors and uses a map to map the keys to the index of the corresponding value - * This enables map acces and index access. - * The index of an inserted element never changes (this is different to a map). - * Index access may be required in a high frequency case (e.g. an rt control loop) - */ - template < class KeyT, class ValT, - class KeyContT = std::vector<KeyT>, - class ValContT = std::vector<ValT>, - class IdxT = typename ValContT::size_type, - class MapT = std::map<KeyT, IdxT >> - class KeyValueVector - { - public: - void add(KeyT key, ValT value); - IdxT index(const KeyT& k) const; - IdxT size() const; - - ValT& at(IdxT i); - ValT const& at(IdxT i) const; - ValT& at(const KeyT& k); - ValT const& at(const KeyT& k) const; - ValT& at(const KeyT& k, ValT& defaultVal); - ValT const& at(const KeyT& k, const ValT& defaultVal) const; - - ValContT const& values() const; - MapT const& indices() const; - KeyContT const& keys() const; - IdxT count(const KeyT& k) const; - bool has(const KeyT& k) const; - void clear(); - - using iterator = typename ValContT::iterator; - using const_iterator = typename ValContT::const_iterator; - - iterator begin(); - iterator end(); - - const_iterator begin() const; - const_iterator end() const; - - const_iterator cbegin() const; - const_iterator cend() const; - - - private: - template<class T, class = typename std::enable_if<meta::HasToString<T>::value>::type> - static std::string DescribeKey(const T* key); - static std::string DescribeKey(...); - - MapT indices_; - KeyContT keys_; - ValContT values_; - }; -} - -// implementation -namespace armarx -{ - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - void KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::add(KeyT key, ValT value) - { - if (indices_.find(key) != indices_.end()) - { - throw std::invalid_argument {"Already added a value for this key" + DescribeKey(&key)}; - } - indices_.emplace(key, size()); - values_.emplace_back(std::move(value)); - keys_.emplace_back(std::move(key)); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - IdxT KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::index(const KeyT& k) const - { - return indices_.at(k); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - ValT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::at(IdxT i) - { - return values_.at(i); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - const ValT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::at(IdxT i) const - { - return values_.at(i); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - ValT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::at(const KeyT& k) - { - return at(index(k)); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - const ValT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::at(const KeyT& k) const - { - return at(index(k)); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - ValT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::at(const KeyT& k, ValT& defaultVal) - { - return has(k) ? at(index(k)) : defaultVal; - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - const ValT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::at(const KeyT& k, const ValT& defaultVal) const - { - return has(k) ? at(index(k)) : defaultVal; - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - IdxT KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::size() const - { - return values_.size(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - const ValContT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::values() const - { - return values_; - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - const MapT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::indices() const - { - return indices_; - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - const KeyContT& KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::keys() const - { - return keys_; - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - IdxT KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::count(const KeyT& k) const - { - return indices_.count(k); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - bool KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::has(const KeyT& k) const - { - return indices_.count(k); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - void KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::clear() - { - indices_.clear(); - keys_.clear(); - values_.clear(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - typename KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::iterator - KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::begin() - { - return values_.begin(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - typename KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::iterator - KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::end() - { - return values_.end(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - typename KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::const_iterator - KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::begin() const - { - return values_.cbegin(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - typename KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::const_iterator - KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::end() const - { - return values_.cend(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - typename KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::const_iterator - KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::cbegin() const - { - return begin(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - typename KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::const_iterator - KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::cend() const - { - return end(); - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - std::string KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::DescribeKey(...) - { - return ""; - } - - template < class KeyT, class ValT, class KeyContT, class ValContT, class IdxT, class MapT> - template<class T, class> - std::string KeyValueVector<KeyT, ValT, KeyContT, ValContT, IdxT, MapT>::DescribeKey(const T* key) - { - return ": " + to_string(*key); - } -} diff --git a/source/RobotAPI/components/units/SensorActorUnit.h b/source/RobotAPI/components/units/SensorActorUnit.h index e41418d70f7f4a5beb3bcd4b847d0cc65c2322d3..bd6485fc64dc62047bbb84d6f4ac2a776b8bc70d 100644 --- a/source/RobotAPI/components/units/SensorActorUnit.h +++ b/source/RobotAPI/components/units/SensorActorUnit.h @@ -59,7 +59,7 @@ namespace armarx * * \param c Ice context provided by the Ice framework */ - void init(const Ice::Current& c = ::Ice::Current()) override; + void init(const Ice::Current& c = Ice::emptyCurrent) override; /** * Set execution state to eStarted @@ -71,7 +71,7 @@ namespace armarx * * \param c Ice context provided by the Ice framework */ - void start(const Ice::Current& c = ::Ice::Current()) override; + void start(const Ice::Current& c = Ice::emptyCurrent) override; /** * Set execution state to eStopped @@ -83,7 +83,7 @@ namespace armarx * * \param c Ice context provided by the Ice framework */ - void stop(const Ice::Current& c = ::Ice::Current()) override; + void stop(const Ice::Current& c = Ice::emptyCurrent) override; /** * Retrieve current execution state @@ -91,21 +91,21 @@ namespace armarx * \param c Ice context provided by the Ice framework * \return current execution state */ - UnitExecutionState getExecutionState(const Ice::Current& c = ::Ice::Current()) override; + UnitExecutionState getExecutionState(const Ice::Current& c = Ice::emptyCurrent) override; /** * Request exclusive access to current unit. Throws ResourceUnavailableException on error. * * \param c Ice context provided by the Ice framework */ - void request(const Ice::Current& c = ::Ice::Current()) override; + void request(const Ice::Current& c = Ice::emptyCurrent) override; /** * Release exclusive access to current unit. Throws ResourceUnavailableException on error. * * \param c Ice context provided by the Ice framework */ - void release(const Ice::Current& c = ::Ice::Current()) override; + void release(const Ice::Current& c = Ice::emptyCurrent) override; protected: void onExitComponent() override; diff --git a/source/RobotAPI/components/units/SpeechObserver.cpp b/source/RobotAPI/components/units/SpeechObserver.cpp index 15b2766b4f07989833590f8a3e2a0aa3bbdd134a..26c215dbfa745af1213c3b1f52db4c5a7aa761d0 100644 --- a/source/RobotAPI/components/units/SpeechObserver.cpp +++ b/source/RobotAPI/components/units/SpeechObserver.cpp @@ -1,6 +1,6 @@ /* * This file is part of ArmarX. - * + * * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), * Karlsruhe Institute of Technology (KIT), all rights reserved. * @@ -22,6 +22,7 @@ */ #include "SpeechObserver.h" +#include <ArmarXCore/util/json/JSONObject.h> using namespace armarx; @@ -33,6 +34,8 @@ void SpeechObserver::onInitObserver() { usingTopic(getProperty<std::string>("TextToSpeechTopicName").getValue()); usingTopic(getProperty<std::string>("TextToSpeechStateTopicName").getValue()); + + } void SpeechObserver::onConnectObserver() @@ -42,12 +45,18 @@ void SpeechObserver::onConnectObserver() offerDataFieldWithDefault("TextToSpeech", "TextChangeCounter", Variant(reportTextCounter), "Counter for text updates"); offerDataFieldWithDefault("TextToSpeech", "State", Variant(""), "Current TTS state"); - offerDataFieldWithDefault("TextToSpeech", "StateChangeCounter", Variant(reportStateCounter), "Counter for text updates"); + offerDataFieldWithDefault("TextToSpeech", "StateChangeCounter", Variant(reportStateCounter), "Counter for state updates"); + + offerChannel("SpeechToText", "SpeechToText channel"); + offerDataFieldWithDefault("SpeechToText", "RecognizedText", Variant(std::string()), "Text recognized by the SpeechRecogntion"); + + auto proxy = getObjectAdapter()->addWithUUID(new SpeechListenerImpl(this)); + getIceManager()->subscribeTopic(proxy, "Speech_Commands"); } std::string SpeechObserver::SpeechStateToString(TextToSpeechStateType state) { - switch(state) + switch (state) { case eIdle: return "Idle"; @@ -70,7 +79,7 @@ void SpeechObserver::reportState(TextToSpeechStateType state, const Ice::Current updateChannel("TextToSpeech"); } -void SpeechObserver::reportText(const std::string& text, const Ice::Current&) +void SpeechObserver::reportText(const std::string& text, const Ice::Current& c) { ScopedLock lock(dataMutex); reportTextCounter++; @@ -84,3 +93,24 @@ void SpeechObserver::reportTextWithParams(const std::string& text, const Ice::St ScopedLock lock(dataMutex); ARMARX_WARNING << "reportTextWithParams is not implemented"; } + +SpeechListenerImpl::SpeechListenerImpl(SpeechObserver* obs) : + obs(obs) +{ + +} + + +void armarx::SpeechListenerImpl::reportText(const std::string& text, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + JSONObject json; + json.fromString(text); + obs->setDataField("SpeechToText", "RecognizedText", Variant(json.getString("text"))); +} + +void armarx::SpeechListenerImpl::reportTextWithParams(const std::string&, const Ice::StringSeq&, const Ice::Current&) +{ + ScopedLock lock(dataMutex); + ARMARX_WARNING << "reportTextWithParams is not implemented"; +} diff --git a/source/RobotAPI/components/units/SpeechObserver.h b/source/RobotAPI/components/units/SpeechObserver.h index 769677e2759f0f7934ced448a3db51ef48db82e1..43e3bbc6fc37511b052d590fde29a9e8a09fabd9 100644 --- a/source/RobotAPI/components/units/SpeechObserver.h +++ b/source/RobotAPI/components/units/SpeechObserver.h @@ -1,6 +1,6 @@ /* * This file is part of ArmarX. - * + * * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), * Karlsruhe Institute of Technology (KIT), all rights reserved. * @@ -39,11 +39,25 @@ namespace armarx defineOptionalProperty<std::string>("TextToSpeechStateTopicName", "TextToSpeechState", "Name of the TextToSpeechStateTopic"); } }; + class SpeechObserver; + class SpeechListenerImpl : public TextListenerInterface + { + public: + SpeechListenerImpl(SpeechObserver* obs); + protected: + SpeechObserver* obs; + Mutex dataMutex; + // TextListenerInterface interface + public: + void reportText(const std::string&, const Ice::Current&) override; + void reportTextWithParams(const std::string&, const Ice::StringSeq&, const Ice::Current&) override; + }; class SpeechObserver : - virtual public Observer, - virtual public SpeechObserverInterface + virtual public Observer, + virtual public SpeechObserverInterface { + friend class SpeechListenerImpl; public: SpeechObserver(); @@ -58,9 +72,9 @@ namespace armarx void onInitObserver() override; void onConnectObserver() override; - virtual void reportState(armarx::TextToSpeechStateType state, const Ice::Current& = Ice::Current()) override; - virtual void reportText(const std::string& text, const Ice::Current& = Ice::Current()) override; - virtual void reportTextWithParams(const std::string& text, const Ice::StringSeq& params, const Ice::Current& = Ice::Current()) override; + virtual void reportState(armarx::TextToSpeechStateType state, const Ice::Current& = Ice::emptyCurrent) override; + virtual void reportText(const std::string& text, const Ice::Current& = Ice::emptyCurrent) override; + virtual void reportTextWithParams(const std::string& text, const Ice::StringSeq& params, const Ice::Current& = Ice::emptyCurrent) override; static std::string SpeechStateToString(TextToSpeechStateType state); private: diff --git a/source/RobotAPI/components/units/TCPControlUnit.h b/source/RobotAPI/components/units/TCPControlUnit.h index ea2482acedb199503014992e36c7352f1080a379..6d254bba5031b1d1b3ddc3dfe2b0316a409f42e8 100644 --- a/source/RobotAPI/components/units/TCPControlUnit.h +++ b/source/RobotAPI/components/units/TCPControlUnit.h @@ -93,7 +93,7 @@ namespace armarx * \param milliseconds New cycle time. * \param c Ice Context, leave blank. */ - void setCycleTime(Ice::Int milliseconds, const Ice::Current& c = Ice::Current()) override; + void setCycleTime(Ice::Int milliseconds, const Ice::Current& c = Ice::emptyCurrent) override; /** * \brief Sets the cartesian velocity of a node in a nodeset for translation and/or orientation. @@ -110,32 +110,32 @@ namespace armarx * * \see request(), release() */ - void setTCPVelocity(const std::string& nodeSetName, const std::string& tcpName, const::armarx::FramedDirectionBasePtr& translationVelocity, const::armarx::FramedDirectionBasePtr& orientationVelocityRPY, const Ice::Current& c = Ice::Current()) override; + void setTCPVelocity(const std::string& nodeSetName, const std::string& tcpName, const::armarx::FramedDirectionBasePtr& translationVelocity, const::armarx::FramedDirectionBasePtr& orientationVelocityRPY, const Ice::Current& c = Ice::emptyCurrent) override; // UnitExecutionManagementInterface interface /** * \brief Does not do anything at the moment. * \param c */ - void init(const Ice::Current& c = Ice::Current()) override; + void init(const Ice::Current& c = Ice::emptyCurrent) override; /** * \brief Does not do anything at the moment. * \param c */ - void start(const Ice::Current& c = Ice::Current()) override; + void start(const Ice::Current& c = Ice::emptyCurrent) override; /** * \brief Does not do anything at the moment. * \param c */ - void stop(const Ice::Current& c = Ice::Current()) override; - UnitExecutionState getExecutionState(const Ice::Current& c = Ice::Current()) override; + void stop(const Ice::Current& c = Ice::emptyCurrent) override; + UnitExecutionState getExecutionState(const Ice::Current& c = Ice::emptyCurrent) override; // UnitResourceManagementInterface interface /** * \brief Triggers the calculation loop for using cartesian velocity. Call once before/after setting a tcp velocity with SetTCPVelocity. * \param c Ice Context, leave blank. */ - void request(const Ice::Current& c = Ice::Current()) override; + void request(const Ice::Current& c = Ice::emptyCurrent) override; /** * \brief Releases and stops the recalculation and updating of joint velocities. @@ -143,9 +143,9 @@ namespace armarx * all node set will be deleted in this function. * \param c Ice Context, leave blank. */ - void release(const Ice::Current& c = Ice::Current()) override; + void release(const Ice::Current& c = Ice::emptyCurrent) override; - bool isRequested(const Ice::Current& c = Ice::Current()) override; + bool isRequested(const Ice::Current& c = Ice::emptyCurrent) override; protected: diff --git a/source/RobotAPI/components/units/TCPControlUnitObserver.h b/source/RobotAPI/components/units/TCPControlUnitObserver.h index e62e4377e6b076d6da3adcd464952a97cc5bb01f..88cdf43c30f840a00c3b82857bf51b059aba8ad0 100644 --- a/source/RobotAPI/components/units/TCPControlUnitObserver.h +++ b/source/RobotAPI/components/units/TCPControlUnitObserver.h @@ -73,8 +73,8 @@ namespace armarx public: // TCPControlUnitListener interface - void reportTCPPose(const FramedPoseBaseMap& poseMap, const Ice::Current& c = Ice::Current()) override; - void reportTCPVelocities(const FramedDirectionMap& tcpTranslationVelocities, const FramedDirectionMap& tcpOrientationVelocities, const Ice::Current& c = Ice::Current()) override; + void reportTCPPose(const FramedPoseBaseMap& poseMap, const Ice::Current& c = Ice::emptyCurrent) override; + void reportTCPVelocities(const FramedDirectionMap& tcpTranslationVelocities, const FramedDirectionMap& tcpOrientationVelocities, const Ice::Current& c = Ice::emptyCurrent) override; Mutex dataMutex; }; diff --git a/source/RobotAPI/drivers/CMakeLists.txt b/source/RobotAPI/drivers/CMakeLists.txt index c51339f3adc5b0c16a5e217c959ffaad43884ebc..731765e4bbcbdcb597b34f5fb6de5a5b9ae7b0ed 100644 --- a/source/RobotAPI/drivers/CMakeLists.txt +++ b/source/RobotAPI/drivers/CMakeLists.txt @@ -6,4 +6,4 @@ add_subdirectory(OrientedTactileSensor) add_subdirectory(GamepadUnit) add_subdirectory(MetaWearIMU) add_subdirectory(KITProstheticHandDriver) - +add_subdirectory(KITProsthesisIceDriver) diff --git a/source/RobotAPI/drivers/CyberGloveUnit/serial.tgz b/source/RobotAPI/drivers/CyberGloveUnit/serial.tgz new file mode 100644 index 0000000000000000000000000000000000000000..5f224fa51a52d01c1a74e4d387a612e0ded79620 Binary files /dev/null and b/source/RobotAPI/drivers/CyberGloveUnit/serial.tgz differ diff --git a/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.cpp b/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.cpp index a840e0c0663fdc3746f717712a8cc98edd73f423..436a81ed1097096b6ee905fd50e9f847af13d095 100644 --- a/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.cpp +++ b/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.cpp @@ -37,6 +37,25 @@ void GamepadUnit::onInitComponent() void GamepadUnit::onConnectComponent() { topicPrx = getTopic<GamepadUnitListenerPrx>(getProperty<std::string>("GamepadTopicName").getValue()); + sendTask = new SimplePeriodicTask<>([&] + { + ScopedLock lock(mutex); + if (!js.opened()) + return; + auto age = IceUtil::Time::now() - dataTimestamp->toTime(); + if (age.toMilliSeconds() < getProperty<int>("PublishTimeout").getValue()) + topicPrx->reportGamepadState(deviceName, js.name, data, dataTimestamp); + else + { + ARMARX_INFO << deactivateSpam(100000, std::to_string(dataTimestamp->getTimestamp())) << "No new signal from gamepad for " << age.toMilliSecondsDouble() << " milliseconds. Not sending data. Timeout: " << getProperty<int>("PublishTimeout").getValue() << " ms"; + } + }, 30); + sendTask->start(); + openGamepadConnection(); +} + +bool GamepadUnit::openGamepadConnection() +{ if (js.open(deviceName)) { ARMARX_INFO << "opened a gamepad named " << js.name << " with " << js.numberOfAxis << " axis and " << js.numberOfButtons << " buttons."; @@ -46,29 +65,37 @@ void GamepadUnit::onConnectComponent() } else { - ARMARX_WARNING << "this is not our trusty logitech gamepad you moron."; + ARMARX_WARNING << "this is not our trusty logitech gamepad."; + js.close(); + return false; } } else { ARMARX_WARNING << "Could not open gamepad device " << deviceName; + js.close(); + return false; } + return true; } void GamepadUnit::run() { while (readTask->isRunning()) { - GamepadData data; - if (!js.pollEvent()) { - ARMARX_WARNING << "failed to read gamepad data"; - break; + ARMARX_WARNING << "failed to read gamepad data - trying to reconnect"; + js.close(); + sleep(1); + while (readTask->isRunning() && !openGamepadConnection()) + { + sleep(1); + } } - + ScopedLock lock(mutex); IceUtil::Time now = IceUtil::Time::now(); - TimestampVariantPtr nowTimestamp = new TimestampVariant(now); + dataTimestamp = new TimestampVariant(now); //mapping found with command line tool jstest <device file> @@ -95,9 +122,7 @@ void GamepadUnit::run() data.leftStickButton = js.buttonsPressed[9]; data.rightStickButton = js.buttonsPressed[10]; - ARMARX_IMPORTANT << "left x (integer): " << js.axis[0] << " left x (float): " << data.leftStickX; - - topicPrx->reportGamepadState(deviceName, js.name, data, nowTimestamp); + ARMARX_VERBOSE << "left x (integer): " << js.axis[0] << " left x (float): " << data.leftStickX << " right trigger: " << data.rightTrigger; //usleep(1000); // 10ms } @@ -106,7 +131,14 @@ void GamepadUnit::run() void GamepadUnit::onDisconnectComponent() { - + if (sendTask) + { + sendTask->stop(); + } + if (readTask) + { + readTask->stop(); + } } diff --git a/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.h b/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.h index 1db681dc79cdb0b5a071390a63b0b72828692311..ee0e66a2e7e10ed62e1a634cc4787f48239db3de 100644 --- a/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.h +++ b/source/RobotAPI/drivers/GamepadUnit/GamepadUnit.h @@ -25,10 +25,12 @@ #include<linux/joystick.h> #include<sys/stat.h> #include<fcntl.h> +#include <ArmarXCore/observers/variant/TimestampVariant.h> #include <ArmarXCore/core/Component.h> #include <ArmarXCore/core/services/tasks/RunningTask.h> +#include <ArmarXCore/core/services/tasks/TaskUtil.h> #include <RobotAPI/interface/units/GamepadUnit.h> @@ -51,6 +53,7 @@ namespace armarx //defineOptionalProperty<std::string>("PropertyName", "DefaultValue", "Description"); defineOptionalProperty<std::string>("GamepadTopicName", "GamepadValues", "Name of the Gamepad Topic"); defineOptionalProperty<std::string>("GamepadDeviceName", "/dev/input/js2", "device that will be opened as a gamepad"); + defineOptionalProperty<int>("PublishTimeout", 2000, "In Milliseconds. Timeout after which the gamepad data is not published after, if no new data was read from the gamepad"); } }; @@ -103,12 +106,19 @@ namespace armarx */ armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; + bool openGamepadConnection(); + private: GamepadUnitListenerPrx topicPrx; RunningTask<GamepadUnit>::pointer_type readTask; + SimplePeriodicTask<>::pointer_type sendTask; + void run(); + Mutex mutex; std::string deviceName; Joystick js; + GamepadData data; + TimestampVariantPtr dataTimestamp; }; } diff --git a/source/RobotAPI/drivers/GamepadUnit/Joystick.h b/source/RobotAPI/drivers/GamepadUnit/Joystick.h index 132bb751dc2eb7d78c978532c3179b6644e3f2ca..1f39074b7a1449f9d58b643d42be68f2c123c445 100644 --- a/source/RobotAPI/drivers/GamepadUnit/Joystick.h +++ b/source/RobotAPI/drivers/GamepadUnit/Joystick.h @@ -62,6 +62,11 @@ namespace armarx return fd != -1; } + bool opened() const + { + return fd != -1; + } + bool pollEvent() { int bytes = read(fd, &event, sizeof(event)); @@ -87,6 +92,7 @@ namespace armarx void close() { ::close(fd); + fd = -1; } }; } diff --git a/source/RobotAPI/drivers/HokuyoLaserUnit/HokuyoLaserUnit.cpp b/source/RobotAPI/drivers/HokuyoLaserUnit/HokuyoLaserUnit.cpp index c257210ebe454a3cbe88c405bb90aab5e0af1582..03b9f860ca100b7ae6a95b8a54ff39a2423f6ea0 100644 --- a/source/RobotAPI/drivers/HokuyoLaserUnit/HokuyoLaserUnit.cpp +++ b/source/RobotAPI/drivers/HokuyoLaserUnit/HokuyoLaserUnit.cpp @@ -23,6 +23,7 @@ #include "HokuyoLaserUnit.h" #include <ArmarXCore/observers/variant/TimestampVariant.h> +#include <sys/resource.h> #include <boost/algorithm/string/split.hpp> #include <HokuyoLaserScannerDriver/urg_utils.h> @@ -32,6 +33,11 @@ using namespace armarx; void HokuyoLaserUnit::onInitComponent() { + // ARMARX_INFO << "Priority: " << getpriority(PRIO_PROCESS, LogSender::getProcessId()); + // // setpriority(PRIO_PROCESS, LogSender::getProcessId(), -10); + // int prio = -20; + // auto ret = nice(prio); + // ARMARX_INFO << "Priority after: " << getpriority(PRIO_PROCESS, LogSender::getProcessId()) << " ret: " << ret; offeringTopic(getProperty<std::string>("RobotHealthTopicName").getValue()); topicName = getProperty<std::string>("LaserScannerTopicName").getValue(); @@ -172,8 +178,12 @@ void HokuyoLaserUnit::updateScanData() { ARMARX_ERROR << "Device " << device.ip << " has too many consecutive errors!"; // assume dead - device.connected = false; - + if (device.connected) + { + ARMARX_INFO << "Disconnecting from laser scanner with IP " << device.ip; + urg_close(&device.urg); + device.connected = false; + } ARMARX_WARNING << "Reconnecting to " << device.ip << ":" << device.port; int ret = urg_open(&device.urg, URG_ETHERNET, device.ip.c_str(), device.port); device.connected = (ret == 0); @@ -227,7 +237,10 @@ void HokuyoLaserUnit::updateScanData() device.errorCounter = 0; topic->reportSensorValues(device.ip, device.frame, scan, now); - robotHealthTopic->heartbeat(getName() + "_" + device.ip, RobotHealthHeartbeatArgs()); + RobotHealthHeartbeatArgs args; + args.maximumCycleTimeWarningMS = 500; + args.maximumCycleTimeErrorMS = 800; + robotHealthTopic->heartbeat(getName() + "_" + device.ip, args); } } } diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.cpp b/source/RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25b5f9972efd5decb236f520eca28920ac741d19 --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.cpp @@ -0,0 +1,69 @@ +#include "KITProsthesisIceDriver.h" +#include <iostream> +#include <memory> + +using namespace KITProsthesis; + +void KITProsthesisIceDriver::sendGrasp(Ice::Int n, const Ice::Current&) +{ + iface.sendGrasp(n); +} +void KITProsthesisIceDriver::sendThumbPWM(const ProsthesisMotorValues& motorValues, const Ice::Current&) +{ + iface.sendThumbPWM(motorValues.v, motorValues.maxPWM, motorValues.pos); +} + +void KITProsthesisIceDriver::sendFingerPWM(const ProsthesisMotorValues& motorValues, const Ice::Current&) +{ + iface.sendFingerPWM(motorValues.v, motorValues.maxPWM, motorValues.pos); +} + +ProsthesisSensorValues KITProsthesisIceDriver::getSensorValues(const Ice::Current&) +{ + ProsthesisSensorValues value; + switch (iface.getState()) + { + case BLEProthesisInterface::State::Created: + value.state = ProsthesisState::Created; + break; + case BLEProthesisInterface::State::DiscoveringDevices: + value.state = ProsthesisState::DiscoveringDevices; + break; + case BLEProthesisInterface::State::DiscoveringDevicesDone: + value.state = ProsthesisState::DiscoveringDevicesDone; + break; + case BLEProthesisInterface::State::Disconnected: + value.state = ProsthesisState::Disconnected; + break; + case BLEProthesisInterface::State::Connecting: + value.state = ProsthesisState::Connecting; + break; + case BLEProthesisInterface::State::ConnectingDone: + value.state = ProsthesisState::ConnectingDone; + break; + case BLEProthesisInterface::State::DiscoveringServices: + value.state = ProsthesisState::DiscoveringServices; + break; + case BLEProthesisInterface::State::DiscoveringServicesDone: + value.state = ProsthesisState::DiscoveringServicesDone; + break; + case BLEProthesisInterface::State::ConnectingService: + value.state = ProsthesisState::ConnectingService; + break; + case BLEProthesisInterface::State::Running: + value.state = ProsthesisState::Running; + break; + case BLEProthesisInterface::State::Killed: + value.state = ProsthesisState::Killed; + break; + } + + value.thumbPWM = iface.getThumbPWM(); + value.thumbPos = iface.getThumbPos(); + + value.fingerPWM = iface.getFingerPWM(); + value.fingerPos = iface.getFingerPos(); + + //add IMU here + return value; +} diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.h b/source/RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.h new file mode 100644 index 0000000000000000000000000000000000000000..3d8ec5dc028ef543d3f2aa8212c2f5620ab8c512 --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.h @@ -0,0 +1,52 @@ +/* + * 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::::drivers::KITProsthesisIceDriver + * @author Julia Starke ( julia dot starke at kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/interface/units/KITProstheticHandInterface.h> +#include <RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h> +#include <Ice/Ice.h> +#include <Ice/Object.h> +#include <QCoreApplication> +#include <iostream> +#include <thread> + +[[maybe_unused]] static constexpr auto prosthesis = "CB:43:34:8F:3C:0A"; +[[maybe_unused]] static constexpr auto prosthesis_old = "DF:70:E8:81:DB:D6"; + +class KITProsthesisIceDriver: + virtual public KITProsthesis::KITProstheticHandInterface +{ +public: + KITProsthesisIceDriver(const std::string& mac = prosthesis_old) : iface{mac} + {} + + // sender interface + void sendGrasp(Ice::Int, const Ice::Current& = Ice::emptyCurrent) override; + void sendThumbPWM(const KITProsthesis::ProsthesisMotorValues&, const ::Ice::Current& = ::Ice::emptyCurrent) override; + void sendFingerPWM(const KITProsthesis::ProsthesisMotorValues&, const ::Ice::Current& = ::Ice::emptyCurrent) override; + + KITProsthesis::ProsthesisSensorValues getSensorValues(const Ice::Current&) override; +private: + BLEProthesisInterface iface; + +}; diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/example/CMakeLists.txt b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8f8dd417c314d7e53badf1d85150f35dd4c47265 --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/CMakeLists.txt @@ -0,0 +1,10 @@ +armarx_component_set_name("KITProsthesisIceDriverExampleNoArmarX") + +set(COMPONENT_LIBS + KITProsthesisIceDriver + RobotAPIInterfaces +) + +set(EXE_SOURCE KITProsthesisIceDriverExample.cpp) + +armarx_add_component_executable("${EXE_SOURCE}") diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/example/KITProsthesisIceDriverExample.cpp b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/KITProsthesisIceDriverExample.cpp new file mode 100644 index 0000000000000000000000000000000000000000..10928b88eb88b13a09239e5cda8226c2245c8d8b --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/KITProsthesisIceDriverExample.cpp @@ -0,0 +1,35 @@ +#include <Ice/Ice.h> +#include <RobotAPI/drivers/KITProsthesisIceDriver/KITProsthesisIceDriver.h> +#include <RobotAPI/interface/units/KITProstheticHandInterface.h> +#include <QCoreApplication> +#include <thread> +int main(int argc, char* argv[]) +{ + for (int i = 0; i < argc; ++i) + { + if (std::string(argv[i]) == "-h" || + std::string(argv[i]) == "--print-options") // needed for ArmarX Doc generation + { + // TODO: print help + return 0; + } + } + + QCoreApplication a(argc, argv); + try + { + Ice::CommunicatorHolder iceServer(argc, argv); + Ice::ObjectAdapterPtr adapter = iceServer->createObjectAdapterWithEndpoints("KITProsthesisControlAdapter", "default -h localhost -p 10000"); + KITProsthesis::KITProstheticHandInterfacePtr object = new KITProsthesisIceDriver; + Ice::ObjectPrx prx = adapter->add(object, Ice::stringToIdentity("KITProsthesisControl")); + std::cout << prx->ice_toString() << std::endl; + adapter->activate(); + iceServer->waitForShutdown(); + } + catch (const std::exception& e) + { + std::cerr << e.what() << std::endl; + return 1; + } + return 0; +} diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/example/KITProstheticHandInterface.ice b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/KITProstheticHandInterface.ice new file mode 120000 index 0000000000000000000000000000000000000000..a480161f79b731fbd1b7cb14308c378e93dbcffb --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/KITProstheticHandInterface.ice @@ -0,0 +1 @@ +../../../interface/units/KITProstheticHandInterface.ice \ No newline at end of file diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/example/ProsthesisNetworkControl.py b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/ProsthesisNetworkControl.py new file mode 100755 index 0000000000000000000000000000000000000000..fb7a7d86f033118eed3bb43317c278f0151432f1 --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/ProsthesisNetworkControl.py @@ -0,0 +1,69 @@ +#!/usr/bin/python +import sys, traceback, Ice +import IceStorm +import time + +#from bmp280 import BMP280 + + +def main(): + status = 0 + ic = None + prosthesis = None + ########################################################## ICE part + try: + properties = Ice.createProperties(sys.argv) + properties.load("./default.generated.cfg") + properties.load("./default.cfg") + + init_data = Ice.InitializationData() + init_data.properties = properties + + ic = Ice.initialize(init_data) + + Ice.loadSlice('-I%s %s -DMININTERFACE' % (Ice.getSliceDir(), 'KITProstheticHandInterface.ice')) + + print("loadSlice") + import KITProsthesis + + + obj = ic.stringToProxy("IceStorm/TopicManager") + + print("stringToProxy") + base = ic.stringToProxy("KITProsthesisControl -t -e 1.1:tcp -h localhost -p 10000 -t 60000") + print("base = ", base.ice_toString()) + print("cast proxy") + prosthesis = KITProsthesis.KITProstheticHandInterfacePrx.checkedCast(base) + if not prosthesis: + raise RuntimeError("Invalid proxy") + + except: + traceback.print_exc() + status = 1 + + if prosthesis is not None: + def graspAndStatus(grasp, sleep = 3): + prosthesis.sendGrasp(grasp) + val = prosthesis.getSensorValues() + print("state ", val.state) + print("thumbPWM ", val.thumbPWM) + print("thumbPos ", val.thumbPos) + print("fingerPWM ", val.fingerPWM) + print("fingerPos ", val.fingerPos) + time.sleep(sleep) + + graspAndStatus(0) + graspAndStatus(1) + graspAndStatus(0) + + if ic: + #clean up + try: + ic.destroy() + except: + traceback.print_exc() + status = 1 + + sys.exit(status) + +main() diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/example/default.cfg b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/default.cfg new file mode 100644 index 0000000000000000000000000000000000000000..63cd81810cc833c4242a3acce28b189a1bc1b90a --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/default.cfg @@ -0,0 +1,3 @@ +Ice.Default.Locator=IceGrid/Locator:tcp -p 10000 -h localhost +IceGrid.Registry.Client.Endpoints=tcp -p 10000 + diff --git a/source/RobotAPI/drivers/KITProsthesisIceDriver/example/default.generated.cfg b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/default.generated.cfg new file mode 100644 index 0000000000000000000000000000000000000000..36a1225e22ae5531d59066713e162ac7e1ce4a24 --- /dev/null +++ b/source/RobotAPI/drivers/KITProsthesisIceDriver/example/default.generated.cfg @@ -0,0 +1,16 @@ +# Do not edit this file! +# This file is automatically generated and overwritten regularly. +IceStormAdmin.TopicManager.Default=IceStorm/TopicManager + +Ice.ThreadPool.Server.SizeMax=20 +Ice.ThreadPool.Client.SizeMax=20 +Ice.PrintStackTraces=1 +Ice.MessageSizeMax=20000000 + +# Server Deactivation Timeout & custom Process Facet instantiation +# https://doc.zeroc.com/display/Ice34/IceGrid+and+the+Administrative+Facility#IceGridandtheAdministrativeFacility-DeactivatingaDeployedServer +IceGrid.Node.WaitTime=10 +Ice.Admin.DelayCreation=1 + +# comment out for Ice 3.4 to remove warning +Ice.Default.EncodingVersion=1.0 diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp index e8c024398fb5aa539b09cfa043fd28dea1e14cc3..fab724f4b1cc29732eed10bfa1c291237284c8ec 100644 --- a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.cpp @@ -135,7 +135,7 @@ void BLEProthesisInterface::sendFingerPWM(uint64_t v, uint64_t mxPWM, uint64_t p std::to_string(getMaxPWM()) + "]" }; } - if (pos > maxPosF()) + if (pos > getMaxPosFingers()) { throw std::invalid_argument { @@ -143,7 +143,7 @@ void BLEProthesisInterface::sendFingerPWM(uint64_t v, uint64_t mxPWM, uint64_t p " , mxPWM = " + std::to_string(mxPWM) + " , pos = " + std::to_string(pos) + " ): The interval for pos is [0, " + - std::to_string(maxPosF()) + "]" + std::to_string(getMaxPosFingers()) + "]" }; } std::stringstream str; diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h index cf96515661f2e6497c1de67386010d0563fb83ac..51797fe9b5b1e2ee3300b53221ab43a8c9675052 100644 --- a/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/BLEProthesisInterface.h @@ -83,7 +83,7 @@ public: { return 100'000; } - static constexpr std::uint64_t maxPosF() + static constexpr std::uint64_t getMaxPosFingers() { return 200'000; } diff --git a/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp b/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp index 3b04f047f8524aa3e8a71733e92bdc6b7de1626f..a8a524a0dff47ce79912f1b25b80093cc7f45ae5 100644 --- a/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp +++ b/source/RobotAPI/drivers/KITProstheticHandDriver/example/KITProstheticHandDriverExampleNoArmarX.cpp @@ -11,6 +11,15 @@ int main(int argc, char* argv[]) { + for (int i = 0; i < argc; ++i) + { + if (std::string(argv[i]) == "-h" || + std::string(argv[i]) == "--print-options") // needed for ArmarX Doc generation + { + // TODO: print help + return 0; + } + } QCoreApplication a(argc, argv); { BLEProthesisInterface iface{dlr}; diff --git a/source/RobotAPI/drivers/ProsthesisObserver/ProsthesisInterface.py b/source/RobotAPI/drivers/ProsthesisObserver/ProsthesisInterface.py index be697e920434d1034dd11e9031974907fd810544..133b62afc9ab7cd136bb8355ab877b9265cb551c 100755 --- a/source/RobotAPI/drivers/ProsthesisObserver/ProsthesisInterface.py +++ b/source/RobotAPI/drivers/ProsthesisObserver/ProsthesisInterface.py @@ -2,14 +2,14 @@ # Example of interaction with a BLE UART device using a UART service # implementation. # Author: Tony DiCola -import Adafruit_BluefruitLE -from Adafruit_BluefruitLE.services import UART +#import Adafruit_BluefruitLE +#from Adafruit_BluefruitLE.services import UART import re import numpy as np # Get the BLE provider for the current platform. -ble = Adafruit_BluefruitLE.get_provider() +#ble = Adafruit_BluefruitLE.get_provider() import sys, traceback, Ice import IceStorm @@ -77,7 +77,7 @@ def main(): except: traceback.print_exc() status = 1 - + ''' ############################################################## BLE part motorData = np.zeros((2,2)) @@ -178,11 +178,15 @@ def main(): status = 1 sys.exit(status) - + ''' # Initialize the BLE system. MUST be called before other BLE calls! -ble.initialize() +#ble.initialize() # Start the mainloop to process BLE events, and run the provided function in # a background thread. When the provided main function stops running, returns # an integer status code, or throws an error the program will exit. -ble.run_mainloop_with(main) +#ble.run_mainloop_with(main) +main() +values = armarx.ProsthesisMotorValues(name = "motorValues", position1 = 0, pwm1 = 1, position2 = 2, pwm2 = 3) + #values.name = "Test" +prosthesis.reportMotorValues(values) diff --git a/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticUnit.cpp b/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticUnit.cpp index ad34a2dedd3f4368e8bbd6ad4b2bd4a15082e230..9cf656e32b1280b6ad3c60024094a4016cff08c6 100644 --- a/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticUnit.cpp +++ b/source/RobotAPI/drivers/WeissHapticSensor/WeissHapticUnit.cpp @@ -26,7 +26,7 @@ #include "WeissHapticUnit.h" #include <boost/regex.hpp> -#include <boost/filesystem.hpp> +#include <filesystem> using namespace armarx; @@ -58,12 +58,12 @@ std::vector< std::string > WeissHapticUnit::getDevices() std::vector< std::string > files; - boost::filesystem::directory_iterator end_itr; // Default ctor yields past-the-end + std::filesystem::directory_iterator end_itr; // Default ctor yields past-the-end - for (boost::filesystem::directory_iterator i(target_path); i != end_itr; ++i) + for (std::filesystem::directory_iterator i(target_path); i != end_itr; ++i) { // Skip if not a file - //if( !boost::filesystem::is_( i->status() ) ) continue; + //if( !std::filesystem::is_( i->status() ) ) continue; boost::smatch what; diff --git a/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.cpp b/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.cpp index 24bd2463237d88cc3623fca7e4434828597dd9fb..11d77b7f1ff415cc7391979947a6846ba8f23ea2 100644 --- a/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.cpp @@ -264,6 +264,10 @@ QString DebugDrawerViewerWidgetController::makeLayerItemText(const LayerInformat { annotations.push_back("empty"); } + else + { + annotations.push_back(std::to_string(layer.elementCount)); + } if (!layer.visible) { annotations.push_back("hidden"); diff --git a/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.h b/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.h index a3e46e54d5232bc381915311f15c2c512b7090c7..b8c58435fb792f97ef8ae3f88c518bb10fd407c2 100644 --- a/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.h +++ b/source/RobotAPI/gui-plugins/DebugDrawerViewer/DebugDrawerViewerWidgetController.h @@ -22,7 +22,7 @@ #pragma once -#include "ui_DebugDrawerViewerWidget.h" +#include <RobotAPI/gui-plugins/DebugDrawerViewer/ui_DebugDrawerViewerWidget.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h> diff --git a/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitConfigDialog.cpp b/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitConfigDialog.cpp index 1c29574a9c4a0e85d4de52ed1528f5beba14066a..0a4d7031aad04aaf9cf42a044b1d1667b3c02ce3 100644 --- a/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitConfigDialog.cpp +++ b/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitConfigDialog.cpp @@ -21,7 +21,7 @@ */ #include "HandUnitConfigDialog.h" -#include "ui_HandUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/HandUnitPlugin/ui_HandUnitConfigDialog.h> #include <RobotAPI/components/units/HandUnit.h> #include <IceUtil/UUID.h> diff --git a/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.cpp b/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.cpp index 4bd8d2d25bcbbf1652ec3d8611b851b03efe753a..8629a33ff0ae22a834ff13a08a3978c418f797fb 100644 --- a/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.cpp +++ b/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.cpp @@ -23,7 +23,7 @@ */ #include "HandUnitGuiPlugin.h" #include "HandUnitConfigDialog.h" -#include "ui_HandUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/HandUnitPlugin/ui_HandUnitConfigDialog.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> // Qt headers diff --git a/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.h b/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.h index d233f9ee9d3da3d0b6916656b32c9bdeac50b8f0..f57fd6d3ed15f50d5d0cc812b8b005f96ae45ec0 100644 --- a/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.h +++ b/source/RobotAPI/gui-plugins/HandUnitPlugin/HandUnitGuiPlugin.h @@ -24,7 +24,7 @@ #pragma once /* ArmarX headers */ -#include "ui_HandUnitGuiPlugin.h" +#include <RobotAPI/gui-plugins/HandUnitPlugin/ui_HandUnitGuiPlugin.h> #include <ArmarXCore/core/Component.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h> diff --git a/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitConfigDialog.cpp b/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitConfigDialog.cpp index d450ab1607339c95e83fe9eea17976c5d1ab35b7..5b7f350afdc769c246cc6c8021953389859c0784 100644 --- a/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitConfigDialog.cpp +++ b/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitConfigDialog.cpp @@ -21,7 +21,7 @@ */ #include "HapticUnitConfigDialog.h" -#include "ui_HapticUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/HapticUnitPlugin/ui_HapticUnitConfigDialog.h> armarx::HapticUnitConfigDialog::HapticUnitConfigDialog(QWidget* parent) : QDialog(parent), diff --git a/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.cpp b/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.cpp index f4ecec0573e109358f656efca57c7c04b25db56e..e5e60d26036935a8fc20bc902d7ad0de976178ca 100644 --- a/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.cpp +++ b/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.cpp @@ -23,7 +23,7 @@ */ #include "HapticUnitGuiPlugin.h" #include "HapticUnitConfigDialog.h" -#include "ui_HapticUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/HapticUnitPlugin/ui_HapticUnitConfigDialog.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> #include <ArmarXCore/observers/variant/SingleTypeVariantList.h> diff --git a/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.h b/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.h index 760874e360336bd3cd9d93a5cb90a1f841e9c547..44b2f33227d22d45fd5c14af1abb579a6ceb0774 100644 --- a/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.h +++ b/source/RobotAPI/gui-plugins/HapticUnitPlugin/HapticUnitGuiPlugin.h @@ -24,7 +24,7 @@ #pragma once /* ArmarX headers */ -#include "ui_HapticUnitGuiPlugin.h" +#include <RobotAPI/gui-plugins/HapticUnitPlugin/ui_HapticUnitGuiPlugin.h> #include <ArmarXCore/core/Component.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h> diff --git a/source/RobotAPI/gui-plugins/HapticUnitPlugin/MatrixDisplayWidget.cpp b/source/RobotAPI/gui-plugins/HapticUnitPlugin/MatrixDisplayWidget.cpp index 9ea47061f22f74cd68895bbc8b27132a41dc1be5..8f5bc0d16dad87c97d243453e4c53f1645390dc5 100644 --- a/source/RobotAPI/gui-plugins/HapticUnitPlugin/MatrixDisplayWidget.cpp +++ b/source/RobotAPI/gui-plugins/HapticUnitPlugin/MatrixDisplayWidget.cpp @@ -22,7 +22,7 @@ * GNU General Public License */ #include "MatrixDisplayWidget.h" -#include "ui_MatrixDisplayWidget.h" +#include <RobotAPI/gui-plugins/HapticUnitPlugin/ui_MatrixDisplayWidget.h> #include <QPainter> #include <pthread.h> diff --git a/source/RobotAPI/gui-plugins/HomogeneousMatrixCalculator/HomogeneousMatrixCalculatorWidgetController.h b/source/RobotAPI/gui-plugins/HomogeneousMatrixCalculator/HomogeneousMatrixCalculatorWidgetController.h index f58937e8f8de83e025ed7cd41cd2b2154605fd36..8c726c50144ee12b5b1b31e2bfeacd77f8cafa20 100644 --- a/source/RobotAPI/gui-plugins/HomogeneousMatrixCalculator/HomogeneousMatrixCalculatorWidgetController.h +++ b/source/RobotAPI/gui-plugins/HomogeneousMatrixCalculator/HomogeneousMatrixCalculatorWidgetController.h @@ -22,7 +22,7 @@ #pragma once -#include "ui_HomogeneousMatrixCalculatorWidget.h" +#include <RobotAPI/gui-plugins/HomogeneousMatrixCalculator/ui_HomogeneousMatrixCalculatorWidget.h> #include <VirtualRobot/MathTools.h> diff --git a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitConfigDialog.cpp b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitConfigDialog.cpp index 72cf349da009f7070eec4dd01b62b6a3abb708f9..f224f04b1ec1de9895a94315e2b1bdd24595d7cc 100644 --- a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitConfigDialog.cpp +++ b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitConfigDialog.cpp @@ -21,7 +21,7 @@ */ #include "KinematicUnitConfigDialog.h" -#include "ui_KinematicUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/KinematicUnitPlugin/ui_KinematicUnitConfigDialog.h> #include <QTimer> #include <QPushButton> diff --git a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp index 5263f02f8bc2439f9454aefc2b6cabdda3173770..5d362e12b6615b34fe0831d73249bee23ca2a490 100644 --- a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp +++ b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp @@ -61,7 +61,7 @@ #include <iostream> #include <cmath> -#include <boost/filesystem.hpp> +#include <filesystem> //#define KINEMATIC_UNIT_FILE_DEFAULT std::string("RobotAPI/robots/Armar3/ArmarIII.xml") @@ -337,6 +337,7 @@ void KinematicUnitWidgetController::loadSettings(QSettings* settings) { kinematicUnitName = settings->value("kinematicUnitName", KINEMATIC_UNIT_NAME_DEFAULT).toString().toStdString(); enableValueValidator = settings->value("enableValueValidator", true).toBool(); + viewerEnabled = settings->value("viewerEnabled", true).toBool(); historyTime = settings->value("historyTime", 100).toInt() * 1000; currentValueMax = settings->value("currentValueMax", 5.0).toFloat(); } @@ -345,6 +346,7 @@ void KinematicUnitWidgetController::saveSettings(QSettings* settings) { settings->setValue("kinematicUnitName", QString::fromStdString(kinematicUnitName)); settings->setValue("enableValueValidator", enableValueValidator); + settings->setValue("viewerEnabled", viewerEnabled); assert(historyTime % 1000 == 0); settings->setValue("historyTime", (int)(historyTime / 1000)); settings->setValue("currentValueMax", currentValueMax); @@ -1392,33 +1394,13 @@ void KinematicUnitWidgetController::reportJointStatuses(const NameStatusMap& joi void KinematicUnitWidgetController::updateModel() { - - // ARMARX_INFO << "updateModel()" << flush; boost::recursive_mutex::scoped_lock lock(mutexNodeSet); if (!robotNodeSet) { return; } - std::vector< RobotNodePtr > rn2 = robotNodeSet->getAllRobotNodes(); - - std::vector< RobotNodePtr > usedNodes; - std::vector< float > jv; - - for (unsigned int i = 0; i < rn2.size(); i++) - { - VirtualRobot::RobotNodePtr node = robot->getRobotNode(rn2[i]->getName()); - NameValueMap::const_iterator it; - it = reportedJointAngles.find(node->getName()); - - if (it != reportedJointAngles.end()) - { - usedNodes.push_back(node); - jv.push_back(it->second); - } - } - - robot->setJointValues(usedNodes, jv); + robot->setJointValues(reportedJointAngles); } void KinematicUnitWidgetController::highlightCriticalValues() diff --git a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/kinematicunitguiplugin.ui b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/kinematicunitguiplugin.ui index 3eaba382cc0c67a5e92dfbcda4b3d40cafa70c5c..ece04b7de3fedbf030a0354c6442c6a487589b11 100644 --- a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/kinematicunitguiplugin.ui +++ b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/kinematicunitguiplugin.ui @@ -206,7 +206,7 @@ <number>3</number> </property> <property name="maximum"> - <double>1.000000000000000</double> + <double>10.000000000000000</double> </property> <property name="singleStep"> <double>0.010000000000000</double> diff --git a/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitConfigDialog.cpp b/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitConfigDialog.cpp index 8683057dd13c94900addaf91f665bb8e6428d5d1..cd15a2e3a54a9010ee38c1486986b8f3c890da11 100644 --- a/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitConfigDialog.cpp +++ b/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitConfigDialog.cpp @@ -21,7 +21,7 @@ */ #include "PlatformUnitConfigDialog.h" -#include "ui_PlatformUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/PlatformUnitPlugin/ui_PlatformUnitConfigDialog.h> #include <IceUtil/UUID.h> diff --git a/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.cpp b/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.cpp index 95d957f85249fe874ac6229a0b20177ce69712a2..f23423f6cfaa89711123bfbfa612269105d8e123 100644 --- a/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.cpp +++ b/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.cpp @@ -23,7 +23,7 @@ */ #include "PlatformUnitGuiPlugin.h" #include "PlatformUnitConfigDialog.h" -#include "ui_PlatformUnitConfigDialog.h" +#include <RobotAPI/gui-plugins/PlatformUnitPlugin/ui_PlatformUnitConfigDialog.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> // Qt headers diff --git a/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.h b/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.h index 9e63f997a91b9d6b1b21382f93f41d6ffcac88bd..775e254616cf8dfd62f6abdb14bf7da3d361c00d 100644 --- a/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.h +++ b/source/RobotAPI/gui-plugins/PlatformUnitPlugin/PlatformUnitGuiPlugin.h @@ -24,7 +24,7 @@ #pragma once /* ArmarX headers */ -#include "ui_PlatformUnitGuiPlugin.h" +#include <RobotAPI/gui-plugins/PlatformUnitPlugin/ui_PlatformUnitGuiPlugin.h> #include <ArmarXGui/libraries/ArmarXGuiBase/widgets/JoystickControlWidget.h> #include <ArmarXCore/core/Component.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h> @@ -118,9 +118,9 @@ namespace armarx void onExitComponent() override; // slice interface implementation - void reportPlatformPose(::Ice::Float currentPlatformPositionX, ::Ice::Float currentPlatformPositionY, ::Ice::Float currentPlatformRotation, const Ice::Current& c = ::Ice::Current()) override; - void reportNewTargetPose(::Ice::Float newPlatformPositionX, ::Ice::Float newPlatformPositionY, ::Ice::Float newPlatformRotation, const Ice::Current& c = ::Ice::Current()) override; - void reportPlatformVelocity(::Ice::Float currentPlatformVelocityX, ::Ice::Float currentPlatformVelocityY, ::Ice::Float currentPlatformVelocityRotation, const Ice::Current& c = ::Ice::Current()) override; + void reportPlatformPose(::Ice::Float currentPlatformPositionX, ::Ice::Float currentPlatformPositionY, ::Ice::Float currentPlatformRotation, const Ice::Current& c = Ice::emptyCurrent) override; + void reportNewTargetPose(::Ice::Float newPlatformPositionX, ::Ice::Float newPlatformPositionY, ::Ice::Float newPlatformRotation, const Ice::Current& c = Ice::emptyCurrent) override; + void reportPlatformVelocity(::Ice::Float currentPlatformVelocityX, ::Ice::Float currentPlatformVelocityY, ::Ice::Float currentPlatformVelocityRotation, const Ice::Current& c = Ice::emptyCurrent) override; void reportPlatformOdometryPose(Ice::Float, Ice::Float, Ice::Float, const Ice::Current&) override; // inherited of ArmarXWidget diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/ControlDevicesWidget.h b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/ControlDevicesWidget.h index b06897427de80b4cfd60e6283ddde502770752a1..2754c47b43f49c728a59ca1c112cf1bb06c430b0 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/ControlDevicesWidget.h +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/ControlDevicesWidget.h @@ -41,7 +41,7 @@ #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> #include "RobotUnitWidgetBase.h" -#include "ui_ControlDevicesWidget.h" +#include <RobotAPI/gui-plugins/RobotUnitPlugin/ui_ControlDevicesWidget.h> namespace armarx { diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.cpp b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.cpp index 400d2884deb8652f7b09750c3f4ead94a5f1bcdf..b0bb952eab4ac87d74aa687e47d7c66ce33ce6a9 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.cpp +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.cpp @@ -22,7 +22,7 @@ #include <thread> -#include <boost/filesystem.hpp> +#include <filesystem> #include "NJointControllerClassesWidget.h" #include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> @@ -44,7 +44,7 @@ namespace armarx //get package hints { - using namespace boost::filesystem; + using namespace std::filesystem; std::string homeDir = QDir::homePath().toStdString(); path p = path {homeDir} / ".cmake" / "packages"; if (is_directory(p)) diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.h b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.h index 21e44e0a86287660b5eeb842563ae5e5327f4073..47057c1f8381aed2c7cdb71a707401f5d36d9cb7 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.h +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllerClassesWidget.h @@ -41,7 +41,7 @@ #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> #include "RobotUnitWidgetBase.h" -#include "ui_NJointControllerClassesWidget.h" +#include <RobotAPI/gui-plugins/RobotUnitPlugin/ui_NJointControllerClassesWidget.h> namespace armarx { diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllersWidget.h b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllersWidget.h index d6abed0dafda4047a269018ec7386438700de496..0db9a473257734bd978fc1864ae583de5fee318e 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllersWidget.h +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/NJointControllersWidget.h @@ -42,7 +42,7 @@ #include <RobotAPI/interface/units/RobotUnit/NJointController.h> #include "RobotUnitWidgetBase.h" -#include "ui_NJointControllersWidget.h" +#include <RobotAPI/gui-plugins/RobotUnitPlugin/ui_NJointControllersWidget.h> namespace armarx { diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/SensorDevicesWidget.h b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/SensorDevicesWidget.h index bccbbda1f8e57b5562e477634812d0ca66522a0a..7f55ab7298bf72d3d6e78c625deb381640948731 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/SensorDevicesWidget.h +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/QWidgets/SensorDevicesWidget.h @@ -40,7 +40,7 @@ #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> #include "RobotUnitWidgetBase.h" -#include "ui_SensorDevicesWidget.h" +#include <RobotAPI/gui-plugins/RobotUnitPlugin/ui_SensorDevicesWidget.h> namespace armarx { diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.cpp b/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.cpp index db997c5d2ac8006245a44ddadb27c775c23836e1..8fdc20d0a779d19dd37c410d025c24f6f7aa03e6 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.cpp @@ -25,7 +25,7 @@ #include <string> -#include <boost/filesystem.hpp> +#include <filesystem> #include <QDir> #include <QSortFilterProxyModel> diff --git a/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.h b/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.h index 03b8671112b067a5cee7037b1494e6cbced15b7e..fa9a52c448997605b83c88dfbcca7c344543b6a4 100644 --- a/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.h +++ b/source/RobotAPI/gui-plugins/RobotUnitPlugin/RobotUnitPlugin/RobotUnitPluginWidgetController.h @@ -22,7 +22,7 @@ #pragma once -#include "ui_RobotUnitPluginWidget.h" +#include <RobotAPI/gui-plugins/RobotUnitPlugin/ui_RobotUnitPluginWidget.h> #include <mutex> #include <map> diff --git a/source/RobotAPI/gui-plugins/RobotViewerPlugin/RobotViewerGuiPlugin.cpp b/source/RobotAPI/gui-plugins/RobotViewerPlugin/RobotViewerGuiPlugin.cpp index 3b463172e9858b9524901a193017cf82abaa31db..8d116f2d67e90fcc60b1415701599be945688a85 100644 --- a/source/RobotAPI/gui-plugins/RobotViewerPlugin/RobotViewerGuiPlugin.cpp +++ b/source/RobotAPI/gui-plugins/RobotViewerPlugin/RobotViewerGuiPlugin.cpp @@ -53,7 +53,7 @@ #include <iostream> #include <cmath> -#include <boost/filesystem.hpp> +#include <filesystem> #define TIMER_MS 33 #define ROBOTSTATE_NAME_DEFAULT "RobotStateComponent" diff --git a/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMover.h b/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMover.h index 20e4166eab415e086fb844cf93c46800e60927da..e3a78e725f07cf73ac8fc10d478c3abf914ee7f6 100644 --- a/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMover.h +++ b/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMover.h @@ -24,7 +24,7 @@ /** ArmarX headers **/ #include "TCPMoverConfigDialog.h" -#include "ui_TCPMover.h" +#include <RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ui_TCPMover.h> // ArmarX includes #include <RobotAPI/interface/units/TCPMoverUnitInterface.h> diff --git a/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMoverConfigDialog.cpp b/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMoverConfigDialog.cpp index 4ec8ad2b5b6d31bf2091cb013c5d1eadded6e607..2f906a864348c1ea87b97f5b79a78d7a984c5252 100644 --- a/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMoverConfigDialog.cpp +++ b/source/RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ArmarXTCPMover/TCPMoverConfigDialog.cpp @@ -21,7 +21,7 @@ */ #include "TCPMoverConfigDialog.h" -#include "ui_TCPMoverConfigDialog.h" +#include <RobotAPI/gui-plugins/SensorActorWidgetsPlugin/ui_TCPMoverConfigDialog.h> #include <IceUtil/UUID.h> diff --git a/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionConfigDialog.cpp b/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionConfigDialog.cpp index 248c260f25cb20e117a8698785a357bdc6ad8d94..d221e2ff267cbcbf7768d2be6bdf5e65872c81e6 100644 --- a/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionConfigDialog.cpp +++ b/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionConfigDialog.cpp @@ -1,5 +1,5 @@ #include "ViewSelectionConfigDialog.h" -#include "ui_ViewSelectionConfigDialog.h" +#include <RobotAPI/gui-plugins/ViewSelection/ui_ViewSelectionConfigDialog.h> #include <RobotAPI/interface/components/ViewSelectionInterface.h> diff --git a/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionWidgetController.h b/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionWidgetController.h index 1b5f87d8b5223ceed8b5ea0aea043f92c7f4e066..d95095edc7862d2386ca1d188aa252a339398871 100644 --- a/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionWidgetController.h +++ b/source/RobotAPI/gui-plugins/ViewSelection/ViewSelectionWidgetController.h @@ -22,7 +22,7 @@ #pragma once -#include "ui_ViewSelectionWidget.h" +#include <RobotAPI/gui-plugins/ViewSelection/ui_ViewSelectionWidget.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXGuiPlugin.h> #include <ArmarXGui/libraries/ArmarXGuiBase/ArmarXComponentWidgetController.h> @@ -87,9 +87,9 @@ namespace armarx void configured() override; - void onActivateAutomaticViewSelection(const Ice::Current& c = Ice::Current()) override; - void onDeactivateAutomaticViewSelection(const Ice::Current& c = Ice::Current()) override; - void nextViewTarget(Ice::Long, const Ice::Current& c = Ice::Current()) override; + void onActivateAutomaticViewSelection(const Ice::Current& c = Ice::emptyCurrent) override; + void onDeactivateAutomaticViewSelection(const Ice::Current& c = Ice::emptyCurrent) override; + void nextViewTarget(Ice::Long, const Ice::Current& c = Ice::emptyCurrent) override; /** * Returns the Widget name displayed in the ArmarXGui to create an diff --git a/source/RobotAPI/interface/CMakeLists.txt b/source/RobotAPI/interface/CMakeLists.txt index 32308c8a90c3ed3bb0a5f85e6de9df50c2354586..772a12aff4eb357a993a40db93c7ff121fcb332d 100644 --- a/source/RobotAPI/interface/CMakeLists.txt +++ b/source/RobotAPI/interface/CMakeLists.txt @@ -8,6 +8,7 @@ set(SLICE_FILES observers/KinematicUnitObserverInterface.ice observers/PlatformUnitObserverInterface.ice observers/ObserverFilters.ice + observers/GraspCandidateObserverInterface.ice core/PoseBase.ice core/OrientedPoint.ice @@ -17,11 +18,14 @@ set(SLICE_FILES core/RobotStateObserverInterface.ice core/Trajectory.ice core/CartesianSelection.ice + core/CartesianWaypointControllerConfig.ice + core/CartesianPositionControllerConfig.ice selflocalisation/SelfLocalisationProcess.ice speech/SpeechInterface.ice observers/SpeechObserverInterface.ice + observers/GraspCandidateObserverInterface.ice units/ForceTorqueUnit.ice units/InertialMeasurementUnit.ice @@ -42,10 +46,10 @@ set(SLICE_FILES units/GamepadUnit.ice units/MetaWearIMUInterface.ice units/MetaWearIMU.ice - units/ProsthesisInterface.ice - units/ProsthesisObserverInterface.ice units/CyberGloveInterface.ice units/CyberGloveObserverInterface.ice + units/GraspCandidateProviderInterface.ice + units/KITProstheticHandInterface.ice units/RobotUnit/NJointController.ice units/RobotUnit/NJointTrajectoryController.ice units/RobotUnit/NJointCartesianVelocityController.ice @@ -61,11 +65,14 @@ set(SLICE_FILES units/RobotUnit/NJointTaskSpaceDMPController.ice units/RobotUnit/NJointBimanualForceMPController.ice + units/RobotUnit/DSControllerBase.ice + units/RobotUnit/TaskSpaceActiveImpedanceControl.ice components/ViewSelectionInterface.ice components/TrajectoryPlayerInterface.ice components/RobotNameServiceInterface.ice components/RobotHealthInterface.ice + components/FrameTrackingInterface.ice visualization/DebugDrawerInterface.ice @@ -83,3 +90,6 @@ set(SLICE_FILES_ADDITIONAL_SOURCES # generate the interface library armarx_interfaces_generate_library(RobotAPI "${ROBOTAPI_INTERFACE_DEPEND}") + +target_link_libraries(RobotAPIInterfaces PUBLIC ArmarXCore) + diff --git a/source/RobotAPI/interface/units/ProsthesisObserverInterface.ice b/source/RobotAPI/interface/components/FrameTrackingInterface.ice similarity index 50% rename from source/RobotAPI/interface/units/ProsthesisObserverInterface.ice rename to source/RobotAPI/interface/components/FrameTrackingInterface.ice index 8f5b3e34415855379be9f12b40f96856aeec6e25..a892a5aecfff626ef9de02d2a811be89cd8da696 100644 --- a/source/RobotAPI/interface/units/ProsthesisObserverInterface.ice +++ b/source/RobotAPI/interface/components/FrameTrackingInterface.ice @@ -1,8 +1,6 @@ /* * This file is part of ArmarX. * - * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. - * * ArmarX is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. @@ -15,27 +13,32 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * - * @package RobotAPI - * @author Peter Kaiser <peter dot kaiser at kit dot edu> - * @date 2014 + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#ifndef _ARMARX_ROBOTAPI_PROSTHESIS_OBSERVER_INTERFACE_SLICE_ -#define _ARMARX_ROBOTAPI_PROSTHESIS_OBSERVER_INTERFACE_SLICE_ -#include <ArmarXCore/interface/observers/ObserverInterface.ice> -#include <RobotAPI/interface/units/ProsthesisInterface.ice> +#pragma once + +#include <ArmarXCore/interface/core/BasicVectorTypes.ice> module armarx -{ - /** - * Implements an interface to an OptoForceUnitObserver. - **/ - interface ProsthesisObserverInterface extends ObserverInterface, ProsthesisListenerInterface +{ + interface FrameTrackingInterface { + void enableTracking(bool enable); + void setFrame(string frameName); + ["cpp:const"] + idempotent + string getFrame(); + + void lookAtFrame(string frameName); + void lookAtPointInGlobalFrame(Vector3f point); + void lookAtPointInRobotFrame(Vector3f point); + + void scanInConfigurationSpace(float yawFrom, float yawTo, ::Ice::FloatSeq pitchValues, float velocity); + void scanInWorkspace(::armarx::Vector3fSeq points, float maxVelocity, float acceleration); }; }; - -#endif diff --git a/source/RobotAPI/interface/core/CartesianPositionControllerConfig.ice b/source/RobotAPI/interface/core/CartesianPositionControllerConfig.ice new file mode 100644 index 0000000000000000000000000000000000000000..0c1418a257091bead62d86a0629967ba8bfde77b --- /dev/null +++ b/source/RobotAPI/interface/core/CartesianPositionControllerConfig.ice @@ -0,0 +1,49 @@ +/* +* This file is part of ArmarX. +* +* ArmarX is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* ArmarX is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package ArmarX::RobotAPI +* @author Martin Miller +* @copyright 2015 Humanoids Group, KIT +* @license http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#pragma once + +#include <ArmarXCore/interface/observers/VariantBase.ice> +#include <ArmarXCore/interface/observers/Filters.ice> + +module armarx +{ + /** + * [CartesianPositionControllerConfigBase] defines the config for CartesianPositionController + */ + class CartesianPositionControllerConfigBase extends VariantDataClass + { + float KpPos = 1; + float KpOri = 1; + float maxPosVel = -1; + float maxOriVel = -1; + + float thresholdOrientationNear = -1; + float thresholdOrientationReached = -1; + float thresholdPositionNear = -1; + float thresholdPositionReached = -1; + }; + + + +}; + diff --git a/source/RobotAPI/interface/core/CartesianWaypointControllerConfig.ice b/source/RobotAPI/interface/core/CartesianWaypointControllerConfig.ice new file mode 100644 index 0000000000000000000000000000000000000000..1e2a1a8e9ea01ee32c0b6d229eeada60cda1c0aa --- /dev/null +++ b/source/RobotAPI/interface/core/CartesianWaypointControllerConfig.ice @@ -0,0 +1,44 @@ +/* +* This file is part of ArmarX. +* +* ArmarX is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* ArmarX is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package ArmarX::RobotAPI +* @author Raphael Grimm +* @copyright 2019 Humanoids Group, H2T, KIT +* @license http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#pragma once + +module armarx +{ + struct CartesianWaypointControllerConfig + { + float kpJointLimitAvoidance = 1; + float jointLimitAvoidanceScale = 2; + + float thresholdOrientationNear = 0.1; + float thresholdOrientationReached = 0.05; + float thresholdPositionNear = 50; + float thresholdPositionReached = 5; + + float maxOriVel = 1; + float maxPosVel = 80; + float kpOri = 1; + float kpPos = 1; + }; +}; + + diff --git a/source/RobotAPI/interface/observers/GraspCandidateObserverInterface.ice b/source/RobotAPI/interface/observers/GraspCandidateObserverInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..4f500a4520a8fc0cc7b85a424d2fcfd1b79874c1 --- /dev/null +++ b/source/RobotAPI/interface/observers/GraspCandidateObserverInterface.ice @@ -0,0 +1,67 @@ +/** +* 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 as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package RobotAPI +* @author Simon Ottenhaus +* @copyright 2019 Humanoids Group, H2T, KIT +* @license http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#pragma once + + +#include <ArmarXCore/interface/observers/ObserverInterface.ice> +#include <RobotAPI/interface/units/GraspCandidateProviderInterface.ice> + +module armarx +{ + module grasping + { + class CandidateFilterCondition + { + string providerName = "*"; + float minimumSuccessProbability = 0; + ObjectTypeEnum objectType = AnyObject; + ApertureType preshape = AnyAperture; + ApproachType approach = AnyApproach; + + }; + + sequence<string> StringSeq; + dictionary<string, int> IntMap; + + interface GraspCandidateObserverInterface extends ObserverInterface, GraspCandidatesTopicInterface + { + InfoMap getAvailableProvidersWithInfo(); + StringSeq getAvailableProviderNames(); + ProviderInfo getProviderInfo(string providerName); + bool hasProvider(string providerName); + GraspCandidateSeq getAllCandidates(); + GraspCandidateSeq getCandidatesByProvider(string providerName); + GraspCandidateSeq getCandidatesByFilter(CandidateFilterCondition filter); + int getUpdateCounterByProvider(string providerName); + IntMap getAllUpdateCounters(); + bool setProviderConfig(string providerName, StringVariantBaseMap config); + + // for execution + void setSelectedCandidates(GraspCandidateSeq candidates); + GraspCandidateSeq getSelectedCandidates(); + }; + }; + +}; + diff --git a/source/RobotAPI/interface/units/GraspCandidateProviderInterface.ice b/source/RobotAPI/interface/units/GraspCandidateProviderInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..153810cd5c7e0a4d2bfd27a3e5238ef716d63adf --- /dev/null +++ b/source/RobotAPI/interface/units/GraspCandidateProviderInterface.ice @@ -0,0 +1,122 @@ +/** +* 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 as +* published by the Free Software Foundation; either version 2 of +* the License, or (at your option) any later version. +* +* 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 Lesser General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see <http://www.gnu.org/licenses/>. +* +* @package RobotAPI +* @author Simon Ottenhaus +* @copyright 2019 Humanoids Group, H2T, KIT +* @license http://www.gnu.org/licenses/gpl-2.0.txt +* GNU General Public License +*/ + +#pragma once + +#include <ArmarXCore/interface/core/BasicTypes.ice> +#include <RobotAPI/interface/core/FramedPoseBase.ice> +#include <ArmarXCore/interface/observers/VariantBase.ice> +#include <ArmarXCore/interface/observers/RequestableService.ice> + +module armarx +{ + module grasping + { + enum ObjectTypeEnum { + AnyObject, KnownObject, UnknownObject + }; + enum ApproachType { + AnyApproach, TopApproach, SideApproach + }; + enum ApertureType { + AnyAperture, OpenAperture, PreshapedAperture + }; + + class BoundingBox + { + Vector3Base center; + Vector3Base ha1; // HalfAxis1 : longest half axis + Vector3Base ha2; // HalfAxis2 : middle half axis + Vector3Base ha3; // HalfAxis3 : shortest half axis + }; + + class GraspCandidateSourceInfo + { + PoseBase referenceObjectPose; + string referenceObjectName; + int segmentationLabelID = -1; + BoundingBox bbox; + }; + class GraspCandidateExecutionHints + { + ApertureType preshape; + ApproachType approach; + string graspTrajectoryName; + }; + class GraspCandidateReachabilityInfo + { + bool reachable = false; + float minimumJointLimitMargin = -1; + Ice::FloatSeq jointLimitMargins; + float maxPosError = 0; + float maxOriError = 0; + }; + + + + class GraspCandidate + { + PoseBase graspPose; + PoseBase robotPose; + Vector3Base approachVector; + + string sourceFrame; // frame where graspPose is located + string targetFrame; // frame which should be moved to graspPose + string side; + + float graspSuccessProbability; + + ObjectTypeEnum objectType; + int groupNr = -1; // used to match candidates that belog together, e.g. from the same object or point cloud segment + string providerName; + + GraspCandidateSourceInfo sourceInfo; // (optional) + GraspCandidateExecutionHints executionHints; // (optional) + GraspCandidateReachabilityInfo reachabilityInfo; // (optional) + }; + + + + class ProviderInfo + { + ObjectTypeEnum objectType = AnyObject; + StringVariantBaseMap currentConfig; + }; + + sequence<GraspCandidate> GraspCandidateSeq; + dictionary<string, ProviderInfo> InfoMap; + + + interface GraspCandidatesTopicInterface + { + void reportProviderInfo(string providerName, ProviderInfo info); + void reportGraspCandidates(string providerName, GraspCandidateSeq candidates); + }; + + interface GraspCandidateProviderInterface extends RequestableServiceListenerInterface + { + }; + }; + +}; + diff --git a/source/RobotAPI/interface/units/KITProstheticHandInterface.ice b/source/RobotAPI/interface/units/KITProstheticHandInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..1babfdfe0b919ab38cba8b3f379db26260282f04 --- /dev/null +++ b/source/RobotAPI/interface/units/KITProstheticHandInterface.ice @@ -0,0 +1,76 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI + * @author Julia Starke <julia dot starke at kit dot edu> + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +module KITProsthesis +{ + module ProsthesisState + { + //the enclosing namespace scopes the c-style enum values + enum State + { + Created, + DiscoveringDevices, + DiscoveringDevicesDone, + Disconnected, + Connecting, + ConnectingDone, + DiscoveringServices, + DiscoveringServicesDone, + ConnectingService, + Running, + Killed + }; + }; + + struct ProsthesisMotorValues + { + long v; + long maxPWM; + long pos; + }; + + struct ProsthesisSensorValues + { + ProsthesisState::State state; + + long thumbPWM; + long thumbPos; + + long fingerPWM; + long fingerPos; + + //add IMU here + }; + + interface KITProstheticHandInterface + { + void sendGrasp(int n); + void sendThumbPWM(ProsthesisMotorValues motorValues); + void sendFingerPWM(ProsthesisMotorValues motorValues); + + ProsthesisSensorValues getSensorValues(); + }; +}; diff --git a/source/RobotAPI/interface/units/ProsthesisInterface.ice b/source/RobotAPI/interface/units/ProsthesisInterface.ice deleted file mode 100644 index 28219519e85357d13747a234352dd594d6f4c805..0000000000000000000000000000000000000000 --- a/source/RobotAPI/interface/units/ProsthesisInterface.ice +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of ArmarX. - * - * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. - * - * ArmarX is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ArmarX is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * @package RobotAPI - * @author Peter Kaiser <peter dot kaiser at kit dot edu> - * @date 2014 - * @copyright http://www.gnu.org/licenses/gpl-2.0.txt - * GNU General Public License - */ - -#ifndef _ARMARX_ROBOTAPI_SIMPLE_HAPTIC_INTERFACE_SLICE_ -#define _ARMARX_ROBOTAPI_SIMPLE_HAPTIC_INTERFACE_SLICE_ - -module armarx -{ - struct ProsthesisMotorValues - { - string name; - float position1; - float pwm1; - float position2; - float pwm2; - }; - - interface ProsthesisListenerInterface - { - void reportMotorValues(ProsthesisMotorValues motorValues); - /*void reportMotorValues(string name, float position1, float pwm1, float position2, float pwm2);*/ - }; - - interface ProsthesisInterface - { - string getTopicName(); - }; -}; - -#endif diff --git a/source/RobotAPI/interface/units/RobotUnit/DSControllerBase.ice b/source/RobotAPI/interface/units/RobotUnit/DSControllerBase.ice new file mode 100644 index 0000000000000000000000000000000000000000..1ae23199e5281780ec263c0ec62b0dbf3e6faabf --- /dev/null +++ b/source/RobotAPI/interface/units/RobotUnit/DSControllerBase.ice @@ -0,0 +1,137 @@ +/* + * 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::NJointControllerInterface + * @author Mirko Waechter ( mirko dot waechter at kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/interface/units/RobotUnit/NJointController.ice> + +module armarx +{ + class DSControllerConfig extends NJointControllerConfig + { + float posiKp = 5; + float v_max = 0.15; + float posiDamping = 10; + + float oriDamping; + float oriKp; + + float filterTimeConstant = 0.01; + float torqueLimit = 1; + + + string nodeSetName = ""; + string tcpName = ""; + + Ice::FloatSeq desiredPosition; + Ice::FloatSeq desiredQuaternion; + + Ice::FloatSeq qnullspaceVec; + + float nullspaceKp; + float nullspaceDamping; + + + Ice::StringSeq gmmParamsFiles; + float positionErrorTolerance; + + float dsAdaptorEpsilon; + + }; + + interface DSControllerInterface extends NJointControllerInterface + { + + }; + + class DSRTBimanualControllerConfig extends NJointControllerConfig + { + float posiKp = 5; + float v_max = 0.15; + Ice::FloatSeq posiDamping; + float couplingStiffness = 10; + float couplingForceLimit = 50; + + float forwardGain = 1; + + float oriDamping; + float oriKp; + + float filterTimeConstant = 0.01; + float torqueLimit = 1; + float NullTorqueLimit = 0.2; + +// string tcpName = ""; + +// Ice::FloatSeq desiredPosition; + + Ice::FloatSeq left_desiredQuaternion; + Ice::FloatSeq right_desiredQuaternion; + + Ice::FloatSeq leftarm_qnullspaceVec; + Ice::FloatSeq rightarm_qnullspaceVec; + + float nullspaceKp; + float nullspaceDamping; + + + + string gmmParamsFile = ""; + float positionErrorTolerance; + + float contactForce; + float guardTargetZUp; + float guardTargetZDown; + float loweringForce; + float liftingForce; + bool guardDesiredDirection; + float highPassFilterFactor; + + + Ice::FloatSeq left_oriUp; + Ice::FloatSeq left_oriDown; + Ice::FloatSeq right_oriUp; + Ice::FloatSeq right_oriDown; + float forceFilterCoeff; + float forceErrorZDisturbance; + float forceErrorZThreshold; + float forceErrorZGain; + float desiredTorqueDisturbance; + float TorqueFilterConstant; + float leftForceOffsetZ; + float rightForceOffsetZ; + float contactDistanceTolerance; + float mountingDistanceTolerance; + float mountingCorrectionFilterFactor; + + Ice::FloatSeq forceLeftOffset; + Ice::FloatSeq forceRightOffset; + }; + + + interface DSBimanualControllerInterface extends NJointControllerInterface + { + void setToDefaultTarget(); + + }; + +}; diff --git a/source/RobotAPI/interface/units/RobotUnit/TaskSpaceActiveImpedanceControl.ice b/source/RobotAPI/interface/units/RobotUnit/TaskSpaceActiveImpedanceControl.ice index 04cc5fae69b1c830c4d64e172a97e40314462f11..34e01876aff6c4d9cdb0e02d3ed79fde22c5d300 100644 --- a/source/RobotAPI/interface/units/RobotUnit/TaskSpaceActiveImpedanceControl.ice +++ b/source/RobotAPI/interface/units/RobotUnit/TaskSpaceActiveImpedanceControl.ice @@ -48,6 +48,7 @@ module armarx interface NJointTaskSpaceImpedanceControlInterface extends NJointControllerInterface { void setTarget(Ice::FloatSeq target); + void setImpedanceParameters(string paraName, Ice::FloatSeq vals); }; }; diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractData.h b/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractData.h index f4a0e8c3162f72ec60777e220ec1e233cf6cbd37..50207f96b4df5d3cfc58d302564c6ca188dacd97 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractData.h +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractData.h @@ -48,16 +48,29 @@ namespace armarx * @param node * @param defaultValue * @param offsetBeforeFactor if true the offset is added before multiplying with factor. If false: the other way around. + * @param nameForDebugging This name is printend in case an error is encountered (Its only purpose is to ease debugging) */ - void init(T* raw, const DefaultRapidXmlReaderNode& node, float defaultValue = std::nan("1"), bool offsetBeforeFactor = true) + void init(T* raw, const DefaultRapidXmlReaderNode& node, float defaultValue = std::nan("1"), bool offsetBeforeFactor = true, const char* nameForDebugging = "") { float factor = node.attribute_as_float("factor"); float offset = node.attribute_as_float("offset"); - init(raw, factor, offset, defaultValue, offsetBeforeFactor); + init(raw, factor, offset, defaultValue, offsetBeforeFactor, nameForDebugging); } - void init(T* raw, float factor, float offset, float defaultValue = std::nan("1"), bool offsetBeforeFactor = true) + void init(T* raw, float factor, float offset, float defaultValue = std::nan("1"), bool offsetBeforeFactor = true, const char* nameForDebugging = "") { + const auto rawAsInt = reinterpret_cast<std::uint64_t>(raw); + ARMARX_CHECK_EXPRESSION_W_HINT( + (rawAsInt % alignof(T)) == 0, + "\nThe alignment is wrong!\nIt has to be " + << alignof(T) << ", but the data is aligned with " + << rawAsInt % alignof(std::max_align_t) + << "!\nThis is an offset of " << (rawAsInt % alignof(T)) + << " bytes!\nThe datatype is " << GetTypeString<T>() + << "\nIts size is " << sizeof(T) + << "\nraw = " << raw + << " bytes\nThe name is " << nameForDebugging + ); this->offsetBeforeFactor = offsetBeforeFactor; this->factor = factor; this->offset = offset; diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractSlave.h b/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractSlave.h index ffb694ab40b87caf32c05d0f761f0bc1e50dd7fe..fb8868b0d0044514b29702b9d144af9e75209531 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractSlave.h +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/AbstractSlave.h @@ -110,7 +110,7 @@ namespace armarx public: using AbstractSlave::AbstractSlave; - void setInputPDO(void *ptr) override + void setInputPDO(void* ptr) override { const auto ptrAsInt = reinterpret_cast<std::uint64_t>(ptr); ARMARX_CHECK_EXPRESSION_W_HINT( @@ -125,7 +125,7 @@ namespace armarx ); inputs = static_cast<InputT*>(ptr); } - void setOutputPDO(void *ptr) override + void setOutputPDO(void* ptr) override { const auto ptrAsInt = reinterpret_cast<std::uint64_t>(ptr); ARMARX_CHECK_EXPRESSION_W_HINT( diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.cpp b/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.cpp index 792583d9d314e3b6135e226d028b2da7d1367b85..d85ca3490aa355e853dc7b65ec873a0710b4e668 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.cpp +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.cpp @@ -119,4 +119,9 @@ namespace armarx return returnList; } + std::vector<AbstractFunctionalDevicePtr> DeviceContainer::getAllFunctionalDevices() const + { + return devices; + } + } // namespace armarx diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.h b/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.h index 2ae55ba257f5e88c426d715ad74e342b7e0763a3..52288ccc8797644cf80038d71f2eacfd97d02cb1 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.h +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/DeviceContainer.h @@ -52,7 +52,7 @@ namespace armarx } std::vector<AbstractFunctionalDevicePtr> getAllInitializedFunctionalDevices() const; std::vector<AbstractFunctionalDevicePtr> getAllUninitializedFunctionalDevices() const; - virtual std::vector<AbstractFunctionalDevicePtr> getAllFunctionalDevices() const = 0; + virtual std::vector<AbstractFunctionalDevicePtr> getAllFunctionalDevices() const; protected: std::vector<AbstractFunctionalDevicePtr> devices; }; diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.cpp b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.cpp index a522aa295ee936e907aec5da8d3c4b24252e1444..4fffde8837104f7ecc90c71ad39d0e84d9ab1b79 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.cpp +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.cpp @@ -21,13 +21,20 @@ #include "DeviceContainer.h" #include "EtherCATDeviceFactory.h" //include all soem stuff here so no one should be able to use soem functions unintentionally because he has to include some first +#define EC_VER1 +#include <ethercattype.h> +#include <nicdrv.h> +#include <osal.h> +#include <osal_defs.h> +#include <ethercatmain.h> +#include <ethercatbase.h> +#include <ArmarXCore/core/util/OnScopeExit.h> //EtherCAT definitions only used for the ethercat stuff #define SDO_READ_TIMEOUT 100000 #define SDO_WRITE_TIMEOUT 50000 #define EC_TIMEOUTMON 2000 - using namespace armarx; @@ -65,6 +72,7 @@ EtherCAT::EtherCAT() : expectedWKC(-1), deviceContainerPtr(nullptr), mainUnitPtr(nullptr) { + checkErrorCountersOnWKCError = false; //writing zeros to the IOMap for (size_t i = 0; i < IOmapSize; ++i) { @@ -183,6 +191,7 @@ bool EtherCAT::startBus(bool createDevices) { slave->prepareForSafeOp(); } + ARMARX_INFO << "Finishing Preparing for safe op now"; for (auto slave : slaves) { slave->finishPreparingForSafeOp(); @@ -190,10 +199,11 @@ bool EtherCAT::startBus(bool createDevices) osal_usleep(500000); - ///// going to SAFE_OP //do the mapping + ARMARX_INFO << "Mapping...."; actualMappedSize = ec_config_map(IOmap.data()); + ARMARX_INFO << "Going to safe op now"; //wait to get all slaves to SAFE-OP ec_statecheck(0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4); ARMARX_INFO << "IOmapping done, size: " << actualMappedSize << " - all Slaves are in SAFE-OP now\n"; @@ -231,7 +241,7 @@ bool EtherCAT::startBus(bool createDevices) /// setting the data pointer in the functional devices for (AbstractFunctionalDevicePtr& device : functionalDevices) { - ARMARX_INFO << "init for " << device->getClassName(); + ARMARX_INFO << "init for device type '" << device->getClassName() << "'"; device->initData(); } @@ -366,8 +376,10 @@ bool EtherCAT::setPDOMappings() int byteSum = 0; for (std::shared_ptr<AbstractSlave>& slave : slaves) { + ARMARX_INFO << "Checking mapping for slave " << slave->getSlaveNumber() << " name: " << slave->getSlaveIdentifier().humanName; if (!slave->hasPDOMapping()) { + ARMARX_INFO << "No mapping needed for " << slave->getSlaveIdentifier().humanName; continue; } byteSum += ec_slave[slave->getSlaveNumber()].Obytes + ec_slave[slave->getSlaveNumber()].Ibytes; @@ -419,6 +431,7 @@ std::pair<std::vector<ControlDevicePtr>, std::vector<SensorDevicePtr>> EtherCAT: } auto etherCATFactoryNames = EtherCATDeviceFactory::getAvailableClasses(); ARMARX_INFO << "PPP: " << etherCATFactoryNames; + std::map<std::string, std::vector<uint16_t>> devicesOfType; for (uint16_t currentSlaveIndex = 1; currentSlaveIndex <= ec_slavecount; currentSlaveIndex++) { const std::string messageSlaveIdentifier = "[Slave index: " + std::to_string(currentSlaveIndex) + "] "; @@ -439,10 +452,13 @@ std::pair<std::vector<ControlDevicePtr>, std::vector<SensorDevicePtr>> EtherCAT: auto device = EtherCATDeviceFactory::fromName(facName, std::make_tuple(this, currentSlaveIndex, deviceContainerPtr)); if (device) { - ARMARX_INFO << "Found device of type: " << device->getClassName(); - ARMARX_CHECK_GREATER(device->getSlaves().size(), 0); - currentSlaveIndex += device->getSlaves().size() - 1; - slaves.insert(slaves.end(), device->getSlaves().begin(), device->getSlaves().end()); + devicesOfType[device->getClassName()].emplace_back(currentSlaveIndex); + + auto newSlaves = device->getSlaves(); + ARMARX_INFO << "Found device of type: " << device->getClassName() << " with " << newSlaves.size() << " slaves"; + ARMARX_CHECK_GREATER(newSlaves.size(), 0); + currentSlaveIndex += newSlaves.size() - 1; + slaves.insert(slaves.end(), newSlaves.begin(), newSlaves.end()); ctrlDevs.insert(device->getCtrlDevs().begin(), device->getCtrlDevs().end()); sensDevs.insert(device->getSensDevs().begin(), device->getSensDevs().end()); foundDevice = true; @@ -458,12 +474,24 @@ std::pair<std::vector<ControlDevicePtr>, std::vector<SensorDevicePtr>> EtherCAT: } if (!foundDevice) { + devicesOfType["<NO FACTORY FOUND>"].emplace_back(currentSlaveIndex); ARMARX_ERROR << "Could not find a corresponding factory for " << messageSlaveIdentifier << " product id: " << (int)ec_slave[currentSlaveIndex].eep_id << "vendor id: " << (int)ec_slave[currentSlaveIndex].eep_man; } } - - ARMARX_INFO << slaves.size() << " usable slave devices are initialized!" << std::endl; + ARMARX_INFO << "Summary of devices and factory types:\n" << ARMARX_STREAM_PRINTER + { + for (const auto& [factoryName, ids] : devicesOfType) + { + out << "---- " << factoryName << ": #" << ids.size() << " (ids:"; + for (auto id : ids) + { + out << ' ' << id; + } + out << ")\n"; + } + } + << '\n' << slaves.size() << " usable slave devices are initialized!" << std::endl; return {std::vector<ControlDevicePtr>(ctrlDevs.begin(), ctrlDevs.end()), std::vector<SensorDevicePtr>(sensDevs.begin(), sensDevs.end())}; } @@ -496,7 +524,10 @@ bool EtherCAT::updateBus(bool doErrorHandling) } else if (switchON_OFF == OFF) { - ARMARX_WARNING << "Could not update bus because it switched off - closing bus (again?)"; + if (!isSDOWorking) + { + ARMARX_WARNING << "Could not update bus because it switched off - closing bus (again?)"; + } closeBus(); return false; } @@ -590,7 +621,7 @@ void EtherCAT::switchBusOFF() ARMARX_VERBOSE << deactivateSpam(1) << "Bus is already switched off"; return; } - ARMARX_VERBOSE << "Switching off bus"; + ARMARX_INFO << "Switching off bus"; switchON_OFF = OFF; } @@ -689,20 +720,54 @@ void EtherCAT::errorHandling() uint16 slave; //if the bus is in already in safe op then we have an error and don't need any more stuff to do error = ec_slave[0].state == EC_STATE_SAFE_OP; - if ((lastWorkCounter == expectedWKC)) + + if (lastWorkCounter == expectedWKC) { noErrorIterations++; - } + ARMARX_ON_SCOPE_EXIT + { + firstErrorCheck = false; + }; if (((lastWorkCounter < expectedWKC) || ec_group[currentGroup].docheckstate) && !error) { - ARMARX_RT_LOGF_WARN("RECOVERY - Wkc: %s - %d, state: %s - iterations without error: %d", ((lastWorkCounter < expectedWKC) ? "NOT OK" : "OK"), - lastWorkCounter, (ec_group[currentGroup].docheckstate ? "NOT OK" : "OK"), noErrorIterations); - noErrorIterations = 0; + + ARMARX_RT_LOGF_WARN("RECOVERY - Wkc: %s - %d/%d, state: %s - iterations without error: %d", ((lastWorkCounter < expectedWKC) ? "NOT OK" : "OK"), + lastWorkCounter, expectedWKC, (ec_group[currentGroup].docheckstate ? "NOT OK" : "OK"), noErrorIterations); + //actually there is something wrong so we have an error lets see if we can find an fix it error = true; + if (checkErrorCountersOnWKCError || (!firstErrorCheck && noErrorIterations == 0)) + { + IceUtil::Time start = IceUtil::Time::now(IceUtil::Time::Monotonic); + for (uint16 slaveIndex = 1; slaveIndex <= *(ecx_context.slavecount); slaveIndex++) + { + auto ADPh = (uint16)(1 - slaveIndex); + uint16_t configAddr = ecx_APRDw(ecx_context.port, ADPh, ECT_REG_STADR, 100000); + for (int i = 0; i < 4; i++) + { + + + uint16_t rxErrorField = ecx_APRDw(ecx_context.port, ADPh, 0x300 + i * 2, 100000); + uint16_t forwardedRxErrorField = ecx_APRDw(ecx_context.port, ADPh, 0x308 + i, 100000); + uint8_t invalidFrameCounter = rxErrorField >> 8; + uint8_t rxError = rxErrorField; + if (rxError > 0 || invalidFrameCounter > 0 || forwardedRxErrorField > 0) + { + ARMARX_INFO << "non zero frame error counter found: Slavenumber " << slaveIndex << " config addr: " << (int)configAddr << ": port " << i << ": " << VAROUT((int)invalidFrameCounter) << VAROUT((int)rxError) << VAROUT((int)forwardedRxErrorField); + } + } + if ((IceUtil::Time::now(IceUtil::Time::Monotonic) - start).toMilliSeconds() > 10) + { + updatePDO(); + ARMARX_RT_LOGF_INFO("Updated BUS to prevent timeout").deactivateSpam(1); + start = IceUtil::Time::now(IceUtil::Time::Monotonic); + } + } + } + noErrorIterations = 0; /* one ore more slaves are not responding */ //This is hard SOEM code not the easy stuff, but works... @@ -1428,4 +1493,13 @@ bool EtherCAT::isEmergencyStopActive() const } +bool EtherCAT::getCheckErrorCountersOnWKCError() const +{ + return checkErrorCountersOnWKCError; +} + +void EtherCAT::setCheckErrorCountersOnWKCError(bool value) +{ + checkErrorCountersOnWKCError = value; +} diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.h b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.h index e0a231dce019a0e72b4f99482a6ce94416e339bc..20b5bf9ee8aafb71837cb024bda2ed56b9521579 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.h +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCAT.h @@ -147,6 +147,10 @@ namespace armarx std::vector<SensorDevicePtr> getSensDevs() const; + + bool getCheckErrorCountersOnWKCError() const; + void setCheckErrorCountersOnWKCError(bool value); + private: //Hiding the constructor to avoid illegal creation of the Bus (singelton pattern) EtherCAT(); @@ -193,7 +197,7 @@ namespace armarx //! Socketfiledescriptor on which the ethercat connection is running int socketFileDescriptor = -1; /** @brief IOmap the IO map where the process data are mapped in */ - std::array<char, IOmapSize> IOmap; + alignas(alignof(std::max_align_t)) std::array<char, IOmapSize> IOmap; /** @brief switchON_OFF this flag is used to switch the bus off an close it */ std::atomic_bool switchON_OFF; /** current Bus group we are working on */ @@ -208,6 +212,7 @@ namespace armarx /** the working counter from the last transmission */ int lastWorkCounter; int noErrorIterations = 0; + bool firstErrorCheck = true; /** List of all functional/internal Units */ std::vector<AbstractFunctionalDevicePtr> functionalDevices; @@ -215,6 +220,8 @@ namespace armarx int actualMappedSize; + std::atomic_bool checkErrorCountersOnWKCError; + /** Indicates if the bus got broken and is not recoverable, so we don't need to switch it down correct it already went away...*/ std::atomic_bool isBusDead; diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp index 705f140e22df531b583bacc3f5bfaa7b7fe8ad73..548d42be36a4a4832493c259e8f9b48d3ec0ed52 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.cpp @@ -31,7 +31,6 @@ #include <sys/mman.h> #include <sys/stat.h> -#include <boost/filesystem/path.hpp> #include <boost/algorithm/clamp.hpp> #include <VirtualRobot/Tools/Gravity.h> @@ -78,12 +77,20 @@ EtherCATRTUnit::EtherCATRTUnit() : } - +void EtherCATRTUnit::componentPropertiesUpdated(const std::set<std::string>& changedProperties) +{ + if (changedProperties.count("checkErrorCountersOnEtherCATError")) + { + auto val = getProperty<bool>("checkErrorCountersOnEtherCATError").getValue(); + ARMARX_INFO << "Changing checkErrorCountersOnEtherCATError to " << val; + EtherCAT::getBus().setCheckErrorCountersOnWKCError(val); + } +} void EtherCATRTUnit::onInitRobotUnit() { - + rtWarningFactor = getProperty<float>("RTLoopTimingCheckToleranceFactor").getValue(); rtLoopTime = getProperty<int>("RTLoopFrequency").getValue(); @@ -100,7 +107,7 @@ void EtherCATRTUnit::onInitRobotUnit() memset(dummy, 0, MAX_SAFE_STACK); ARMARX_INFO << "EtherCATRTUnit initializing now"; - robot = rtGetRobot()->clone(); + publishRobot = rtGetRobot()->clone(); rtRobotJointSet = rtGetRobot()->getRobotNodeSet("RobotJoints"); rtRobotBodySet = rtGetRobot()->getRobotNodeSet("RobotCol"); for (auto& node : *rtRobotJointSet) @@ -209,8 +216,8 @@ void EtherCATRTUnit::joinControlThread() { ARMARX_INFO << "stopping control task"; taskRunning = false; - EtherCAT& bus = EtherCAT::getBus(); - bus.switchBusOFF(); + // EtherCAT& bus = EtherCAT::getBus(); + // bus.switchBusOFF(); rtTask.join(); ARMARX_INFO << "rt task stopped"; } @@ -219,40 +226,40 @@ void EtherCATRTUnit::publish(StringVariantBaseMap debugObserverMap, StringVarian { RobotUnit::publish(std::move(debugObserverMap), std::move(timingMap)); - for (auto& name : getSensorDeviceNames()) - { - auto data = getSensorDevice(name)->getSensorValue()->asA<SensorValue1DoFActuatorPosition>(); - if (!data) - { - continue; - } - robot->getRobotNode(name)->setJointValueNoUpdate(data->position); - } - robot->applyJointValues(); - for (auto& name : getSensorDeviceNames()) - { - auto data = getSensorDevice(name)->getSensorValue()->asA<SensorValue1DoFActuatorTorque>(); - if (!data) - { - continue; - } - auto node = robot->getRobotNode(name); - ARMARX_CHECK_EXPRESSION(node); - auto joint = boost::dynamic_pointer_cast<VirtualRobot::RobotNodeRevolute>(node); - ARMARX_CHECK_EXPRESSION(joint); - Vector3Ptr pos = new Vector3(joint->getGlobalPosition()); - Vector3Ptr axis = new Vector3(joint->getJointRotationAxis()); - float percent = data->torque / 4.0f; - if (std::abs(percent) > 0.1) - { - getDebugDrawerProxy()->setCircleArrowVisu("TorqueEstimation", name, pos, axis, 100, percent, 5, DrawColor{0, 1, 0, 1}); - } - else - { - getDebugDrawerProxy()->removeCircleVisu("TorqueEstimation", name); - } - - } + // for (auto& name : getSensorDeviceNames()) + // { + // auto data = getSensorDevice(name)->getSensorValue()->asA<SensorValue1DoFActuatorPosition>(); + // if (!data) + // { + // continue; + // } + // publishRobot->getRobotNode(name)->setJointValueNoUpdate(data->position); + // } + // publishRobot->applyJointValues(); + // for (auto& name : getSensorDeviceNames()) + // { + // auto data = getSensorDevice(name)->getSensorValue()->asA<SensorValue1DoFActuatorTorque>(); + // if (!data) + // { + // continue; + // } + // auto node = publishRobot->getRobotNode(name); + // ARMARX_CHECK_EXPRESSION(node); + // auto joint = boost::dynamic_pointer_cast<VirtualRobot::RobotNodeRevolute>(node); + // ARMARX_CHECK_EXPRESSION(joint); + // Vector3Ptr pos = new Vector3(joint->getGlobalPosition()); + // Vector3Ptr axis = new Vector3(joint->getJointRotationAxis()); + // float percent = data->torque / 4.0f; + // if (std::abs(percent) > 0.1) + // { + // getDebugDrawerProxy()->setCircleArrowVisu("TorqueEstimation", name, pos, axis, 100, percent, 5, DrawColor {0, 1, 0, 1}); + // } + // else + // { + // getDebugDrawerProxy()->removeCircleVisu("TorqueEstimation", name); + // } + + // } } armarx::PropertyDefinitionsPtr EtherCATRTUnit::createPropertyDefinitions() @@ -274,9 +281,19 @@ void EtherCATRTUnit::rtRun() ARMARX_ON_SCOPE_EXIT { + ARMARX_INFO << "Leaving rtRun scope"; //switching off the bus and be sure that the bus will receive bus.switchBusOFF(); bus.updateBus(); + if (getProperty<int>("SocketFileDescriptor").isSet() && getProperty<int>("SocketFileDescriptor").getValue() > 0) + { + ARMARX_INFO << "Closing raw socket with FD " << getProperty<int>("SocketFileDescriptor").getValue(); + int ret = close(getProperty<int>("SocketFileDescriptor").getValue()); + if (ret) + { + ARMARX_INFO << "Failed to close raw socket file handle: " << ret << " errno: " << strerror(errno); + } + } }; @@ -408,9 +425,9 @@ void EtherCATRTUnit::rtRun() // unitInitTask.join(); controlLoopRTThread(); - //switching off the bus and be sure that the bus will receive - bus.switchBusOFF(); - bus.updateBus(); + // //switching off the bus and be sure that the bus will receive + // bus.switchBusOFF(); + // bus.updateBus(); while (getObjectScheduler() && !getObjectScheduler()->isTerminationRequested()) { @@ -425,24 +442,24 @@ void EtherCATRTUnit::icePropertiesInitialized() } -MultiNodeRapidXMLReader EtherCATRTUnit::readHardwareConfigFile(std::string busConfigFilePath) +MultiNodeRapidXMLReader EtherCATRTUnit::ReadHardwareConfigFile(std::string busConfigFilePath, std::string rootNodeName) { if (!ArmarXDataPath::getAbsolutePath(busConfigFilePath, busConfigFilePath)) { throw LocalException("could not find BusConfigFilePath: ") << busConfigFilePath; } - ARMARX_DEBUG << "read the config"; + ARMARX_DEBUG_S << "read the config"; //reading the config for the Bus and create all the robot objects in the robot container - auto busConfigFilePathDir = boost::filesystem::path(busConfigFilePath).parent_path(); + auto busConfigFilePathDir = std::filesystem::path(busConfigFilePath).parent_path(); auto rapidXmlReaderPtr = RapidXmlReader::FromFile(busConfigFilePath); - auto rootNode = rapidXmlReaderPtr->getRoot("Armar6"); + auto rootNode = rapidXmlReaderPtr->getRoot(rootNodeName.c_str()); MultiNodeRapidXMLReader multiNode({rootNode}); for (RapidXmlReaderNode& includeNode : rootNode.nodes("include")) { auto relPath = includeNode.attribute_value("file"); - boost::filesystem::path filepath = (busConfigFilePathDir / relPath); - if (!boost::filesystem::exists(filepath)) + std::filesystem::path filepath = (busConfigFilePathDir / relPath); + if (!std::filesystem::exists(filepath)) { std::string absPath; if (!ArmarXDataPath::getAbsolutePath(relPath, absPath)) @@ -450,7 +467,7 @@ MultiNodeRapidXMLReader EtherCATRTUnit::readHardwareConfigFile(std::string busCo throw LocalException("Could not find config file at path ") << relPath; } } - multiNode.addNode(RapidXmlReader::FromFile(filepath.string())->getRoot("Armar6")); + multiNode.addNode(RapidXmlReader::FromFile(filepath.string())->getRoot(rootNodeName.c_str())); } return multiNode; } @@ -540,21 +557,21 @@ void EtherCATRTUnit::controlLoopRTThread() RT_TIMING_START(RunControllers); RT_TIMING_START(SwitchControllers); rtSwitchControllerSetup(); - RT_TIMING_CEND(SwitchControllers, 0.3); + RT_TIMING_CEND(SwitchControllers, 0.3 * rtWarningFactor); RT_TIMING_START(ResettingTargets); rtResetAllTargets(); - RT_TIMING_CEND(ResettingTargets, 0.3); + RT_TIMING_CEND(ResettingTargets, 0.3 * rtWarningFactor); RT_TIMING_START(RunNJointControllers); rtRunNJointControllers(currentPDOUpdateTimestamp, cappedDeltaT); - RT_TIMING_CEND(RunNJointControllers, 0.3); + RT_TIMING_CEND(RunNJointControllers, 0.3 * rtWarningFactor); RT_TIMING_START(CheckInvalidTargets); rtHandleInvalidTargets(); - RT_TIMING_CEND(CheckInvalidTargets, 0.3); + RT_TIMING_CEND(CheckInvalidTargets, 0.3 * rtWarningFactor); RT_TIMING_START(RunJointControllers); rtRunJointControllers(currentPDOUpdateTimestamp, cappedDeltaT); - RT_TIMING_CEND(RunJointControllers, 0.3); - RT_TIMING_CEND(RunControllers, 0.3); + RT_TIMING_CEND(RunJointControllers, 0.3 * rtWarningFactor); + RT_TIMING_CEND(RunControllers, 0.3 * rtWarningFactor); } //bus update @@ -577,20 +594,20 @@ void EtherCATRTUnit::controlLoopRTThread() rtSetEmergencyStopState(EmergencyStopState::eEmergencyStopActive); } } - RT_TIMING_CEND(updateBus, 0.7); + RT_TIMING_CEND(updateBus, 0.7 * rtWarningFactor); rtGetRTThreadTimingsSensorDevice().rtMarkRtBusSendReceiveEnd(); RT_TIMING_START(ReadSensorValues); rtReadSensorDeviceValues(currentPDOUpdateTimestamp, cappedDeltaT); - RT_TIMING_CEND(ReadSensorValues, 0.7); + RT_TIMING_CEND(ReadSensorValues, 0.7 * rtWarningFactor); lastMonoticTimestamp = currentMonotonicTimestamp; currentMonotonicTimestamp = armarx::rtNow(); RT_TIMING_START(Publish); rtUpdateSensorAndControlBuffer(currentPDOUpdateTimestamp, cappedDeltaT); - RT_TIMING_CEND(Publish, 0.15); + RT_TIMING_CEND(Publish, 0.15 * rtWarningFactor); RT_TIMING_START(ComputeGravityTorques); gravity.computeGravityTorque(gravityValues); @@ -605,7 +622,7 @@ void EtherCATRTUnit::controlLoopRTThread() i++; } - RT_TIMING_CEND(ComputeGravityTorques, 0.2); + RT_TIMING_CEND(ComputeGravityTorques, 0.2 * rtWarningFactor); // copyEtherCATBufferOut(); @@ -614,11 +631,11 @@ void EtherCATRTUnit::controlLoopRTThread() const auto loopPreSleepTime = armarx::rtNow(); RT_TIMING_START(RTLoopWaiting); cycleKeeper.waitForCycleDuration(); - RT_TIMING_CEND(RTLoopWaiting, rtLoopTime * 1.3); + RT_TIMING_CEND(RTLoopWaiting, rtLoopTime * 1.3 * rtWarningFactor); const auto loopPostSleepTime = armarx::rtNow(); const auto loopDuration = armarx::rtNow() - loopStartTime; - if (loopDuration.toMicroSeconds() > rtLoopTime + 500) + if (loopDuration.toMicroSeconds() > (rtLoopTime + 500) * rtWarningFactor) { const SensorValueRTThreadTimings* sval = rtGetRTThreadTimingsSensorDevice().getSensorValue()->asA<SensorValueRTThreadTimings>(); ARMARX_CHECK_NOT_NULL(sval); @@ -714,6 +731,7 @@ void EtherCATRTUnit::controlLoopRTThread() ARMARX_FATAL << "exception in control thread!" << std::flush; //TODO emergency stop } + ARMARX_INFO << "Leaving control loop function"; } DeviceContainerPtr EtherCATRTUnit::getDeviceContainerPtr() const diff --git a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h index faf3d505d9bb5596faa045e8cad5bff4052256d0..1619c7c1708301607ebf7588a99363bca23f1d35 100644 --- a/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h +++ b/source/RobotAPI/libraries/ArmarXEtherCAT/EtherCATRTUnit.h @@ -68,7 +68,10 @@ namespace armarx defineOptionalProperty<bool>("UseTorqueVelocityModeAsDefault", false, "If true, the KinematicUnit will use TorqueVelocity mode for velocity mode"); defineOptionalProperty<int>("RTLoopFrequency", 1000, "Desired frequency of real-time control loop"); + defineOptionalProperty<float>("RTLoopTimingCheckToleranceFactor", 1.0f, "Factor by which the timing checks are multiplied. Higher value -> less warning outputs"); + defineOptionalProperty<bool>("checkErrorCountersOnEtherCATError", false, "If true, the EtherCAT bus will be checked for receive errors on bus error. This is slow; it should be active in normal usage.", PropertyDefinitionBase::eModifiable); + defineOptionalProperty<bool>("VisualizeTorques", false, "If true, EtherCATRTUnit::publish will draw joint torques on the debug drawer"); } }; @@ -123,9 +126,10 @@ namespace armarx armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; void icePropertiesInitialized() override; - protected: + void componentPropertiesUpdated(const std::set<std::string>& changedProperties) override; - MultiNodeRapidXMLReader readHardwareConfigFile(std::string hardwareConfigFilePath); + protected: + static MultiNodeRapidXMLReader ReadHardwareConfigFile(std::string hardwareConfigFilePath, std::string rootNodeName); //all the stuff to run the rt Thread void startRTThread(); @@ -138,23 +142,24 @@ namespace armarx bool initBusRTThread(); void controlLoopRTThread(); + + void computeInertiaTorque(); DebugDrawerInterfacePrx dd; std::thread rtTask; std::atomic_bool taskRunning {false}; - std::atomic_int rtLoopTime; + std::atomic_int rtLoopTime{1000}; + float rtWarningFactor{1}; + //timestamps for the pdo updates IceUtil::Time currentPDOUpdateTimestamp; - - VirtualRobot::RobotPtr robot; + VirtualRobot::RobotPtr publishRobot; DeviceContainerPtr deviceContainerPtr; - VirtualRobot::RobotNodeSetPtr rtRobotJointSet, rtRobotBodySet; std::vector<std::pair<VirtualRobot::RobotNodePtr, SensorValue1DoFGravityTorque*>> nodeJointDataVec; - int latency_target_fd = -1; void set_latency_target(int32_t latency_target_value = 0); diff --git a/source/RobotAPI/libraries/CMakeLists.txt b/source/RobotAPI/libraries/CMakeLists.txt index d5d62998ce906e8e70412f60e13afb01478f16c3..e6c6c6e3be9facfce693eaf70fe28707c49686a0 100644 --- a/source/RobotAPI/libraries/CMakeLists.txt +++ b/source/RobotAPI/libraries/CMakeLists.txt @@ -8,5 +8,9 @@ add_subdirectory(RobotAPIVariantWidget) add_subdirectory(RobotAPINJointControllers) add_subdirectory(DMPController) +# disabled until Mathlib include is fixed... +add_subdirectory(DSControllers) add_subdirectory(RobotStatechartHelpers) + +add_subdirectory(DebugDrawerConfigWidget) \ No newline at end of file diff --git a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp index 03da7f366f5f612ae660cedfd55489f66c6d0504..25c74c7958249e38595a64e6c67df2aa8de11f8b 100644 --- a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp +++ b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.cpp @@ -22,29 +22,33 @@ #include "TaskSpaceDMPController.h" +#include <ArmarXCore/core/system/ArmarXDataPath.h> + using namespace armarx; void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentPose, const Eigen::VectorXf& twist) +{ + canVal = flow(canVal, deltaT, currentPose, twist); +} + +double TaskSpaceDMPController::flow(double canVal, double deltaT, const Eigen::Matrix4f& currentPose, const Eigen::VectorXf& twist) { if (paused) { targetVel.setZero(); return; } - if (canVal < 1e-8) + if (canVal < 0.1 && config.DMPStyle == "Periodic") { - if (config.DMPStyle == "Periodic") - { - prepareExecution(eigen4f2vec(currentPose), goalPoseVec); - } - else - { - targetVel.setZero(); - return; - } + canVal = config.motionTimeDuration; + } + if (canVal < 1e-8 && config.DMPStyle == "Discrete") + { + targetVel.setZero(); + return canVal; } Eigen::Vector3f currentPosition; @@ -81,24 +85,20 @@ void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentP isDisturbance = false; } - double tau = dmpPtr->getTemporalFactor(); double timeDuration = config.motionTimeDuration; - canVal -= deltaT * 1 / (1 + phaseStop) ; + canVal -= tau * deltaT * 1;// / (1 + phaseStop) ; + + DMP::Vec<DMP::DMPState > temporalState = dmpPtr->calculateDirectlyVelocity(currentState, canVal / timeDuration, deltaT / timeDuration, targetPoseVec); - for (size_t i = 0; i < 7; ++i) + // scale translation velocity + for (size_t i = 0; i < 3; ++i) { - currentState[i].vel = currentState[i].vel * config.DMPAmplitude; + currentState[i].vel = tau * temporalState[i].vel * config.DMPAmplitude / timeDuration; + currentState[i].pos += deltaT * currentState[i].vel; } - currentState = dmpPtr->calculateDirectlyVelocity(currentState, canVal / (tau * timeDuration), deltaT / (tau * timeDuration), targetPoseVec); - - // for (size_t i = 0; i < 7; ++i) - // { - // targetPoseVec[i] = currentState[i].pos; - // } - - + // define the translation velocity if (isPhaseStopControl) { float vel0, vel1; @@ -107,7 +107,7 @@ void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentP linearVel << twist(0), twist(1), twist(2); for (size_t i = 0; i < 3; i++) { - vel0 = currentState[i].vel / (timeDuration * tau); + vel0 = currentState[i].vel; vel1 = config.phaseStopParas.Kpos * (targetPoseVec[i] - currentPosition(i)) - config.phaseStopParas.Dpos * linearVel(i); targetVel(i) = mpcFactor * vel0 + (1 - mpcFactor) * vel1; } @@ -116,15 +116,18 @@ void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentP { for (size_t i = 0; i < 3; i++) { - targetVel(i) = currentState[i].vel / (timeDuration * tau); + targetVel(i) = currentState[i].vel; } } + + + // define the rotation velocity Eigen::Quaterniond dmpQuaternionVel; - dmpQuaternionVel.w() = currentState[3].vel; - dmpQuaternionVel.x() = currentState[4].vel; - dmpQuaternionVel.y() = currentState[5].vel; - dmpQuaternionVel.z() = currentState[6].vel; + dmpQuaternionVel.w() = temporalState[3].vel; + dmpQuaternionVel.x() = temporalState[4].vel; + dmpQuaternionVel.y() = temporalState[5].vel; + dmpQuaternionVel.z() = temporalState[6].vel; Eigen::Quaterniond dmpQuaternionPosi; dmpQuaternionPosi.w() = currentState[3].pos; @@ -154,33 +157,50 @@ void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentP } oldDMPAngularVelocity = angularVel0; - Eigen::Vector3f currentAngularVel; - currentAngularVel << twist(3), twist(4), twist(5); + // scale orientation velocity + angularVel0.w() = 0; + angularVel0.x() = tau * angularVel0.x() * config.oriAmplitude / timeDuration; + angularVel0.y() = tau * angularVel0.y() * config.oriAmplitude / timeDuration; + angularVel0.z() = tau * angularVel0.z() * config.oriAmplitude / timeDuration; - Eigen::Quaternionf targetQuaternionf; - targetQuaternionf.w() = targetPoseVec[3]; - targetQuaternionf.x() = targetPoseVec[4]; - targetQuaternionf.y() = targetPoseVec[5]; - targetQuaternionf.z() = targetPoseVec[6]; - - Eigen::Matrix3f desiredMat(targetQuaternionf); - Eigen::Matrix3f currentMat = currentPose.block<3, 3>(0, 0); - Eigen::Matrix3f diffMat = desiredMat * currentMat.inverse(); - Eigen::Vector3f diffRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat); - Eigen::Vector3f angularVel1 = config.phaseStopParas.Kori * diffRPY - config.phaseStopParas.Dori * currentAngularVel; + // Eigen::Quaterniond scaledQuat = (angularVel0 * dmpQuaternionPosi); + // currentState[3].vel = 0.5 * scaledQuat.w(); + // currentState[4].vel = 0.5 * scaledQuat.x(); + // currentState[6].vel = 0.5 * scaledQuat.z(); + // currentState[5].vel = 0.5 * scaledQuat.y(); + for (size_t i = 3; i < 7; ++i) + { + currentState[i].vel = tau * temporalState[i].vel * config.oriAmplitude / timeDuration; + currentState[i].pos += currentState[i].vel * deltaT; + } if (isPhaseStopControl) { - targetVel(3) = angularVel0.x() / (timeDuration * tau); - targetVel(4) = angularVel0.y() / (timeDuration * tau); - targetVel(5) = angularVel0.z() / (timeDuration * tau); + Eigen::Vector3f currentAngularVel; + currentAngularVel << twist(3), twist(4), twist(5); + + Eigen::Quaternionf targetQuaternionf; + targetQuaternionf.w() = targetPoseVec[3]; + targetQuaternionf.x() = targetPoseVec[4]; + targetQuaternionf.y() = targetPoseVec[5]; + targetQuaternionf.z() = targetPoseVec[6]; + + Eigen::Matrix3f desiredMat(targetQuaternionf); + Eigen::Matrix3f currentMat = currentPose.block<3, 3>(0, 0); + Eigen::Matrix3f diffMat = desiredMat * currentMat.inverse(); + Eigen::Vector3f diffRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat); + Eigen::Vector3f angularVel1 = config.phaseStopParas.Kori * diffRPY - config.phaseStopParas.Dori * currentAngularVel; + + targetVel(3) = mpcFactor * angularVel0.x() / timeDuration + (1 - mpcFactor) * angularVel1(0); + targetVel(4) = mpcFactor * angularVel0.y() / timeDuration + (1 - mpcFactor) * angularVel1(1); + targetVel(5) = mpcFactor * angularVel0.z() / timeDuration + (1 - mpcFactor) * angularVel1(2); } else { - targetVel(3) = mpcFactor * angularVel0.x() / (timeDuration * tau) + (1 - mpcFactor) * angularVel1(0); - targetVel(4) = mpcFactor * angularVel0.y() / (timeDuration * tau) + (1 - mpcFactor) * angularVel1(1); - targetVel(5) = mpcFactor * angularVel0.z() / (timeDuration * tau) + (1 - mpcFactor) * angularVel1(2); + targetVel(3) = angularVel0.x() ; + targetVel(4) = angularVel0.y(); + targetVel(5) = angularVel0.z(); } debugData.canVal = canVal; @@ -188,6 +208,8 @@ void TaskSpaceDMPController::flow(double deltaT, const Eigen::Matrix4f& currentP debugData.posiError = posiError; debugData.mpcFactor = mpcFactor; debugData.poseError = poseError; + + return canVal; } void TaskSpaceDMPController::learnDMPFromFiles(const std::vector<std::string>& fileNames, const std::vector<double>& ratios) @@ -205,7 +227,9 @@ void TaskSpaceDMPController::learnDMPFromFiles(const std::vector<std::string>& f for (size_t i = 0; i < fileNames.size(); ++i) { DMP::SampledTrajectoryV2 traj; - traj.readFromCSVFile(fileNames.at(i)); + std::string absPath; + ArmarXDataPath::getAbsolutePath(fileNames.at(i), absPath); + traj.readFromCSVFile(absPath); traj = DMP::SampledTrajectoryV2::normalizeTimestamps(traj, 0, 1); trajs.push_back(traj); @@ -248,6 +272,28 @@ void TaskSpaceDMPController::learnDMPFromFiles(const std::vector<std::string>& f learnDMPFromFiles(fileNames, ratios); } +void TaskSpaceDMPController::learnDMPFromTrajectory(const TrajectoryPtr& traj) +{ + ARMARX_CHECK_EQUAL(traj->dim(), 7); + DMP::SampledTrajectoryV2 dmpTraj; + + DMP::DVec timestamps(traj->getTimestamps()); + for (size_t i = 0; i < traj->dim(); ++i) + { + DMP::DVec dimData(traj->getDimensionData(i, 0)); + dmpTraj.addDimension(timestamps, dimData); + } + + DMP::Vec<DMP::SampledTrajectoryV2 > trajs; + + dmpTraj = DMP::SampledTrajectoryV2::normalizeTimestamps(dmpTraj, 0, 1); + trajs.push_back(dmpTraj); + DMP::DVec ratiosVec; + ratiosVec.push_back(1.0); + dmpPtr->learnFromTrajectories(trajs); + dmpPtr->styleParas = dmpPtr->getStyleParasWithRatio(ratiosVec); +} + void TaskSpaceDMPController::setViaPose(double canVal, const Eigen::Matrix4f& viaPose) { @@ -281,6 +327,7 @@ void TaskSpaceDMPController::prepareExecution(const std::vector<double>& current ARMARX_CHECK_EQUAL(currentPoseVec.size(), 7); ARMARX_CHECK_EQUAL(goalPoseVec.size(), 7); + ARMARX_IMPORTANT << "prepareExecution: currentPoseVec: " << currentPoseVec; for (size_t i = 0; i < currentPoseVec.size(); ++i) { currentState[i].pos = currentPoseVec.at(i); @@ -298,12 +345,21 @@ void TaskSpaceDMPController::prepareExecution(const std::vector<double>& current void TaskSpaceDMPController::setSpeed(double times) { - if (times == 0) + if (times <= 0) { - return; + ARMARX_WARNING << "TaskSpaceDMPController: cannot set non-positive speed times"; } - config.motionTimeDuration /= times; + tau = times; +} + +void TaskSpaceDMPController::setAmplitude(double amp) +{ + if (amp <= 0) + { + ARMARX_WARNING << "TaskSpaceDMPController: cannot set non-positive amplitude"; + } + config.DMPAmplitude = amp; } std::vector<double> TaskSpaceDMPController::eigen4f2vec(const Eigen::Matrix4f& pose) diff --git a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h index eb8d5c541cb09936b664701d75ae09b4b23d339e..81e87e88794a0ad1cdfdafed8fdd5467a89a107a 100644 --- a/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h +++ b/source/RobotAPI/libraries/DMPController/TaskSpaceDMPController.h @@ -30,7 +30,7 @@ #include <VirtualRobot/RobotNodeSet.h> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> - +#include <RobotAPI/libraries/core/Trajectory.h> namespace armarx { @@ -54,6 +54,7 @@ namespace armarx std::string DMPMode = "Linear"; std::string DMPStyle = "Discrete"; float DMPAmplitude = 1; + float oriAmplitude = 1; float motionTimeDuration = 10; PhaseStopParams phaseStopParas; }; @@ -107,6 +108,7 @@ namespace armarx this->isPhaseStopControl = isPhaseStopControl; dmpName = name; this->paused = false; + tau = 1; } std::string getName() @@ -116,6 +118,7 @@ namespace armarx void flow(double deltaT, const Eigen::Matrix4f& currentPose, const Eigen::VectorXf& twist); + double flow(double canVal, double deltaT, const Eigen::Matrix4f& currentPose, const Eigen::VectorXf& twist); Eigen::VectorXf getTargetVelocity() { @@ -137,9 +140,24 @@ namespace armarx return res; } + Eigen::Matrix4f getIntegratedPoseMat() + { + Eigen::Matrix4f res = VirtualRobot::MathTools::quat2eigen4f(currentState.at(4).pos, + currentState.at(5).pos, + currentState.at(6).pos, + currentState.at(3).pos); + res(0, 3) = currentState.at(0).pos; + res(1, 3) = currentState.at(1).pos; + res(2, 3) = currentState.at(2).pos; + + return res; + } + void learnDMPFromFiles(const std::vector<std::string>& fileNames, const std::vector<double>& ratios); void learnDMPFromFiles(const std::vector<std::string>& fileNames); + void learnDMPFromTrajectory(const TrajectoryPtr& traj); + void setViaPose(double canVal, const Eigen::Matrix4f& viaPose); void setViaPose(double canVal, const std::vector<double>& viaPoseWithQuaternion); @@ -149,6 +167,7 @@ namespace armarx void prepareExecution(const std::vector<double>& currentPoseVec, const std::vector<double>& goalPoseVec, double tau = 1); void setSpeed(double times); + void setAmplitude(double amp); void setGoalPose(const Eigen::Matrix4f& goalPose) { @@ -185,7 +204,7 @@ namespace armarx private: - + double tau; DMP::DVec goalPoseVec; Eigen::VectorXf targetVel; diff --git a/source/RobotAPI/libraries/DSControllers/CMakeLists.txt b/source/RobotAPI/libraries/DSControllers/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8708dc17072abcadda62797f3167d7b0fa82de4f --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/CMakeLists.txt @@ -0,0 +1,55 @@ +set(LIB_NAME DSControllers) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +find_package(Eigen3 QUIET) +find_package(Simox ${ArmarX_Simox_VERSION} QUIET) +find_package(MATHLIB QUIET) + +armarx_build_if(Eigen3_FOUND "Eigen3 not available") +armarx_build_if(Simox_FOUND "Simox-VirtualRobot not available") +armarx_build_if(MATHLIB_FOUND "MATHLIB not available") + +if (Eigen3_FOUND AND Simox_FOUND AND MATHLIB_FOUND) + include_directories(${Simox_INCLUDE_DIRS}) + include_directories(SYSTEM ${Eigen3_INCLUDE_DIR}) + include_directories(${MATHLIB_INCLUDE_DIRS}) +endif() + + + + +message(STATUS "mathlib is ${MATHLIB_LIBS}") +set(LIBS ArmarXCoreObservers ArmarXCoreStatechart ArmarXCoreEigen3Variants + VirtualRobot + Saba + SimDynamics + RobotUnit + RobotAPIUnits + RobotAPICore + RobotAPIInterfaces + ${MATHLIB_LIB} +) + + + +set(LIB_FILES +./DSRTBimanualController.cpp +./DSRTController.cpp +./GMRDynamics.cpp +./Gaussians.cpp +#@TEMPLATE_LINE@@COMPONENT_PATH@/@COMPONENT_NAME@.cpp +) +set(LIB_HEADERS +./DSRTBimanualController.h +./DSRTController.h +./GMRDynamics.h +./Gaussians.h +#@TEMPLATE_LINE@@COMPONENT_PATH@/@COMPONENT_NAME@.h +) + + +armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") + +# add unit tests diff --git a/source/RobotAPI/libraries/DSControllers/DSRTBimanualController.cpp b/source/RobotAPI/libraries/DSControllers/DSRTBimanualController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..249b951e8fc383b76d616b78f6f4255b62b24655 --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/DSRTBimanualController.cpp @@ -0,0 +1,1009 @@ +/* + * 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 DSController::ArmarXObjects::DSRTBimanualController + * @author Mahdi Khoramshahi ( m80 dot khoramshahi at gmail dot com ) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "DSRTBimanualController.h" +#include <ArmarXCore/core/time/CycleUtil.h> + +using namespace armarx; + +NJointControllerRegistration<DSRTBimanualController> registrationControllerDSRTBimanualController("DSRTBimanualController"); + +void DSRTBimanualController::onInitNJointController() +{ + ARMARX_INFO << "init ..."; + controllerStopRequested = false; + controllerRunFinished = false; + runTask("DSRTBimanualControllerTask", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted && !controllerStopRequested) + { + ARMARX_INFO << deactivateSpam(1) << "control fct"; + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + controllerRunFinished = true; + ARMARX_INFO << "Control Fct finished"; + }); + + +} + +void DSRTBimanualController::onDisconnectNJointController() +{ + ARMARX_INFO << "disconnect"; + controllerStopRequested = true; + while (!controllerRunFinished) + { + ARMARX_INFO << deactivateSpam(1) << "waiting for run function"; + usleep(10000); + } +} + + +DSRTBimanualController::DSRTBimanualController(const RobotUnitPtr& robotUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) +{ + cfg = DSRTBimanualControllerConfigPtr::dynamicCast(config); + useSynchronizedRtRobot(); + + VirtualRobot::RobotNodeSetPtr left_ns = rtGetRobot()->getRobotNodeSet("LeftArm"); + VirtualRobot::RobotNodeSetPtr right_ns = rtGetRobot()->getRobotNodeSet("RightArm"); + + left_jointNames.clear(); + right_jointNames.clear(); + + ARMARX_CHECK_EXPRESSION_W_HINT(left_ns, "LeftArm"); + ARMARX_CHECK_EXPRESSION_W_HINT(right_ns, "RightArm"); + + // for left arm + for (size_t i = 0; i < left_ns->getSize(); ++i) + { + std::string jointName = left_ns->getNode(i)->getName(); + left_jointNames.push_back(jointName); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Torque1DoF); // ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Velocity1DoF); + ARMARX_CHECK_EXPRESSION(ct); + const SensorValueBase* sv = useSensorValue(jointName); + ARMARX_CHECK_EXPRESSION(sv); + auto casted_ct = ct->asA<ControlTarget1DoFActuatorTorque>(); // auto casted_ct = ct->asA<ControlTarget1DoFActuatorVelocity>(); + ARMARX_CHECK_EXPRESSION(casted_ct); + left_torque_targets.push_back(casted_ct); + + const SensorValue1DoFActuatorTorque* torqueSensor = sv->asA<SensorValue1DoFActuatorTorque>(); + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFGravityTorque* gravityTorqueSensor = sv->asA<SensorValue1DoFGravityTorque>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + if (!torqueSensor) + { + ARMARX_WARNING << "No Torque sensor available for " << jointName; + } + if (!gravityTorqueSensor) + { + ARMARX_WARNING << "No Gravity Torque sensor available for " << jointName; + } + + left_torqueSensors.push_back(torqueSensor); + left_gravityTorqueSensors.push_back(gravityTorqueSensor); + left_velocitySensors.push_back(velocitySensor); + left_positionSensors.push_back(positionSensor); + }; + + // for right arm + for (size_t i = 0; i < right_ns->getSize(); ++i) + { + std::string jointName = right_ns->getNode(i)->getName(); + right_jointNames.push_back(jointName); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Torque1DoF); // ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Velocity1DoF); + ARMARX_CHECK_EXPRESSION(ct); + const SensorValueBase* sv = useSensorValue(jointName); + ARMARX_CHECK_EXPRESSION(sv); + auto casted_ct = ct->asA<ControlTarget1DoFActuatorTorque>(); // auto casted_ct = ct->asA<ControlTarget1DoFActuatorVelocity>(); + ARMARX_CHECK_EXPRESSION(casted_ct); + right_torque_targets.push_back(casted_ct); + + const SensorValue1DoFActuatorTorque* torqueSensor = sv->asA<SensorValue1DoFActuatorTorque>(); + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFGravityTorque* gravityTorqueSensor = sv->asA<SensorValue1DoFGravityTorque>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + if (!torqueSensor) + { + ARMARX_WARNING << "No Torque sensor available for " << jointName; + } + if (!gravityTorqueSensor) + { + ARMARX_WARNING << "No Gravity Torque sensor available for " << jointName; + } + + right_torqueSensors.push_back(torqueSensor); + right_gravityTorqueSensors.push_back(gravityTorqueSensor); + right_velocitySensors.push_back(velocitySensor); + right_positionSensors.push_back(positionSensor); + }; + + + const SensorValueBase* svlf = useSensorValue("FT L"); + leftForceTorque = svlf->asA<SensorValueForceTorque>(); + const SensorValueBase* svrf = useSensorValue("FT R"); + rightForceTorque = svrf->asA<SensorValueForceTorque>(); + + ARMARX_INFO << "Initialized " << left_torque_targets.size() << " targets for the left arm"; + ARMARX_INFO << "Initialized " << right_torque_targets.size() << " targets for the right arm"; + + left_arm_tcp = left_ns->getTCP(); + right_arm_tcp = right_ns->getTCP(); + + left_sensor_frame = left_ns->getRobot()->getRobotNode("ArmL8_Wri2"); + right_sensor_frame = right_ns->getRobot()->getRobotNode("ArmR8_Wri2"); + + left_ik.reset(new VirtualRobot::DifferentialIK(left_ns, rtGetRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + right_ik.reset(new VirtualRobot::DifferentialIK(right_ns, rtGetRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + + + // ?? not sure about this + DSRTBimanualControllerSensorData initSensorData; + + initSensorData.left_tcpPose = left_arm_tcp->getPoseInRootFrame(); + initSensorData.currentTime = 0; + controllerSensorData.reinitAllBuffers(initSensorData); + + initSensorData.right_tcpPose = right_arm_tcp->getPoseInRootFrame(); + initSensorData.currentTime = 0; + controllerSensorData.reinitAllBuffers(initSensorData); + + + left_oldPosition = left_arm_tcp->getPositionInRootFrame(); + left_oldOrientation = left_arm_tcp->getPoseInRootFrame().block<3, 3>(0, 0); + + right_oldPosition = right_arm_tcp->getPositionInRootFrame(); + right_oldOrientation = right_arm_tcp->getPoseInRootFrame().block<3, 3>(0, 0); + + + std::vector<float> left_desiredQuaternionVec = cfg->left_desiredQuaternion; + ARMARX_CHECK_EQUAL(left_desiredQuaternionVec.size(), 4); + + std::vector<float> right_desiredQuaternionVec = cfg->right_desiredQuaternion; + ARMARX_CHECK_EQUAL(right_desiredQuaternionVec.size(), 4); + + left_desiredQuaternion.w() = left_desiredQuaternionVec.at(0); + left_desiredQuaternion.x() = left_desiredQuaternionVec.at(1); + left_desiredQuaternion.y() = left_desiredQuaternionVec.at(2); + left_desiredQuaternion.z() = left_desiredQuaternionVec.at(3); + + right_desiredQuaternion.w() = right_desiredQuaternionVec.at(0); + right_desiredQuaternion.x() = right_desiredQuaternionVec.at(1); + right_desiredQuaternion.y() = right_desiredQuaternionVec.at(2); + right_desiredQuaternion.z() = right_desiredQuaternionVec.at(3); + + + // set initial joint velocities filter + + left_jointVelocity_filtered.resize(left_torque_targets.size()); + left_jointVelocity_filtered.setZero(); + right_jointVelocity_filtered.resize(left_torque_targets.size()); + right_jointVelocity_filtered.setZero(); + + // do we need to duplicate this? + DSRTBimanualControllerControlData initData; + for (size_t i = 0; i < 3; ++i) + { + initData.left_tcpDesiredLinearVelocity(i) = 0; + initData.right_tcpDesiredLinearVelocity(i) = 0; + + } + + for (size_t i = 0; i < 3; ++i) + { + initData.left_tcpDesiredAngularError(i) = 0; + initData.right_tcpDesiredAngularError(i) = 0; + + } + reinitTripleBuffer(initData); + + + // initial filter + left_desiredTorques_filtered.resize(left_torque_targets.size()); + left_desiredTorques_filtered.setZero(); + right_desiredTorques_filtered.resize(right_torque_targets.size()); + right_desiredTorques_filtered.setZero(); + + + left_currentTCPLinearVelocity_filtered.setZero(); + right_currentTCPLinearVelocity_filtered.setZero(); + + left_currentTCPAngularVelocity_filtered.setZero(); + right_currentTCPAngularVelocity_filtered.setZero(); + + left_tcpDesiredTorque_filtered.setZero(); + right_tcpDesiredTorque_filtered.setZero(); + + + smooth_startup = 0; + + + filterTimeConstant = cfg->filterTimeConstant; + posiKp = cfg->posiKp; + v_max = cfg->v_max; + Damping = cfg->posiDamping; + Coupling_stiffness = cfg->couplingStiffness; + Coupling_force_limit = cfg->couplingForceLimit; + forward_gain = cfg->forwardGain; + torqueLimit = cfg->torqueLimit; + null_torqueLimit = cfg->NullTorqueLimit; + oriKp = cfg->oriKp; + oriDamping = cfg->oriDamping; + contactForce = cfg->contactForce; + + left_oriUp.w() = cfg->left_oriUp[0]; + left_oriUp.x() = cfg->left_oriUp[1]; + left_oriUp.y() = cfg->left_oriUp[2]; + left_oriUp.z() = cfg->left_oriUp[3]; + + left_oriDown.w() = cfg->left_oriDown[0]; + left_oriDown.x() = cfg->left_oriDown[1]; + left_oriDown.y() = cfg->left_oriDown[2]; + left_oriDown.z() = cfg->left_oriDown[3]; + + right_oriUp.w() = cfg->right_oriUp[0]; + right_oriUp.x() = cfg->right_oriUp[1]; + right_oriUp.y() = cfg->right_oriUp[2]; + right_oriUp.z() = cfg->right_oriUp[3]; + + right_oriDown.w() = cfg->right_oriDown[0]; + right_oriDown.x() = cfg->right_oriDown[1]; + right_oriDown.y() = cfg->right_oriDown[2]; + right_oriDown.z() = cfg->right_oriDown[3]; + + + guardTargetZUp = cfg->guardTargetZUp; + guardTargetZDown = cfg->guardTargetZDown; + guardDesiredZ = guardTargetZUp; + guard_mounting_correction_z = 0; + + guardXYHighFrequency = 0; + left_force_old.setZero(); + right_force_old.setZero(); + + left_contactForceZ_correction = 0; + right_contactForceZ_correction = 0; + + + std::vector<float> leftarm_qnullspaceVec = cfg->leftarm_qnullspaceVec; + std::vector<float> rightarm_qnullspaceVec = cfg->rightarm_qnullspaceVec; + + left_qnullspace.resize(leftarm_qnullspaceVec.size()); + right_qnullspace.resize(rightarm_qnullspaceVec.size()); + + for (size_t i = 0; i < leftarm_qnullspaceVec.size(); ++i) + { + left_qnullspace(i) = leftarm_qnullspaceVec[i]; + right_qnullspace(i) = rightarm_qnullspaceVec[i]; + } + + nullspaceKp = cfg->nullspaceKp; + nullspaceDamping = cfg->nullspaceDamping; + + + //set GMM parameters ... + if (cfg->gmmParamsFile == "") + { + ARMARX_ERROR << "gmm params file cannot be empty string ..."; + } + gmmMotionGenerator.reset(new BimanualGMMMotionGen(cfg->gmmParamsFile)); + positionErrorTolerance = cfg->positionErrorTolerance; + forceFilterCoeff = cfg->forceFilterCoeff; + for (size_t i = 0 ; i < 3; ++i) + { + leftForceOffset[i] = cfg->forceLeftOffset.at(i); + rightForceOffset[i] = cfg->forceRightOffset.at(i); + } + isDefaultTarget = false; + ARMARX_INFO << "Initialization done"; +} + +void DSRTBimanualController::controllerRun() +{ + if (!controllerSensorData.updateReadBuffer()) + { + return; + } + + + // receive the measurements + Eigen::Matrix4f left_currentTCPPose = controllerSensorData.getReadBuffer().left_tcpPose; + Eigen::Matrix4f right_currentTCPPose = controllerSensorData.getReadBuffer().right_tcpPose; + + Eigen::Vector3f left_force = controllerSensorData.getReadBuffer().left_force; + Eigen::Vector3f right_force = controllerSensorData.getReadBuffer().right_force; + Eigen::Vector3f both_arm_force_ave = - 0.5 * (left_force + right_force); // negative sign for the direction + + + float left_force_z = left_force(2); + float right_force_z = right_force(2); + + Eigen::Vector3f left_currentTCPPositionInMeter; + Eigen::Vector3f right_currentTCPPositionInMeter; + + left_currentTCPPositionInMeter << left_currentTCPPose(0, 3), left_currentTCPPose(1, 3), left_currentTCPPose(2, 3); + right_currentTCPPositionInMeter << right_currentTCPPose(0, 3), right_currentTCPPose(1, 3), right_currentTCPPose(2, 3); + + left_currentTCPPositionInMeter = 0.001 * left_currentTCPPositionInMeter; + right_currentTCPPositionInMeter = 0.001 * right_currentTCPPositionInMeter; + + + // updating the desired height for the guard based on the interaction forces + float both_arm_height_z_ave = 0.5 * (left_currentTCPPositionInMeter(2) + right_currentTCPPositionInMeter(2)); + + + float adaptive_ratio = 1; + float force_error_z = 0; + float guard_mounting_correction_x = 0; + float guard_mounting_correction_y = 0; + + + if (cfg->guardDesiredDirection) + { + adaptive_ratio = 1; + force_error_z = both_arm_force_ave(2) - adaptive_ratio * cfg->liftingForce; + + // meausures the high-frequency components of the interaction forces + float diff_norm = (left_force - left_force_old).squaredNorm() + (right_force - right_force_old).squaredNorm(); + // diff_norm = deadzone(diff_norm,0.1,2); + guardXYHighFrequency = cfg->highPassFilterFactor * guardXYHighFrequency + 0.1 * diff_norm; + + guardXYHighFrequency = (guardXYHighFrequency > 10) ? 10 : guardXYHighFrequency; + + left_force_old = left_force; + right_force_old = right_force; + + if (fabs(both_arm_height_z_ave - guardTargetZUp) < cfg->mountingDistanceTolerance) + { + guard_mounting_correction_z = (1 - cfg->mountingCorrectionFilterFactor) * guard_mounting_correction_z + cfg->mountingCorrectionFilterFactor * (- 0.1 * (guardXYHighFrequency - 8)); + guard_mounting_correction_z = deadzone(guard_mounting_correction_z, 0, 0.1); + + + guard_mounting_correction_x = guard_mounting_correction_x - cfg->forceErrorZGain * deadzone(both_arm_force_ave(0), 1, 3); + guard_mounting_correction_y = guard_mounting_correction_y - cfg->forceErrorZGain * deadzone(both_arm_force_ave(1), 1, 3); + + guard_mounting_correction_x = deadzone(guard_mounting_correction_x, 0, 0.1); + guard_mounting_correction_y = deadzone(guard_mounting_correction_y, 0, 0.1); + + + } + + } + else + { + adaptive_ratio = (0.5 * (both_arm_height_z_ave - guardTargetZDown) / (guardTargetZUp - guardTargetZDown) + 0.5); + force_error_z = both_arm_force_ave(2) - adaptive_ratio * cfg->loweringForce; + + } + + + + force_error_z = deadzone(force_error_z, cfg->forceErrorZDisturbance, cfg->forceErrorZThreshold); + guardDesiredZ = guardDesiredZ - cfg->forceErrorZGain * (force_error_z); + + guardDesiredZ = (guardDesiredZ > guardTargetZUp) ? guardTargetZUp : guardDesiredZ; + guardDesiredZ = (guardDesiredZ < guardTargetZDown) ? guardTargetZDown : guardDesiredZ; + + if (isDefaultTarget) + { + guardDesiredZ = guardTargetZDown; + } + + if (!cfg->guardDesiredDirection && guardDesiredZ < 1.5) + { + guard_mounting_correction_y = -0.1; + } + + // update the desired velocity + gmmMotionGenerator->updateDesiredVelocity( + left_currentTCPPositionInMeter, + right_currentTCPPositionInMeter, + positionErrorTolerance, + guardDesiredZ, + guard_mounting_correction_x, + guard_mounting_correction_y, + guard_mounting_correction_z); + + // ARMARX_INFO << "tcpDesiredLinearVelocity: " << gmmMotionGenerator->left_DS_DesiredVelocity; + // ARMARX_INFO << "tcpDesiredLinearVelocity: " << gmmMotionGenerator->right_DS_DesiredVelocity; + + + Eigen::Vector3f left_tcpDesiredAngularError; + Eigen::Vector3f right_tcpDesiredAngularError; + + left_tcpDesiredAngularError << 0, 0, 0; + right_tcpDesiredAngularError << 0, 0, 0; + + + + Eigen::Matrix3f left_currentRotMat = left_currentTCPPose.block<3, 3>(0, 0); + Eigen::Matrix3f right_currentRotMat = right_currentTCPPose.block<3, 3>(0, 0); + + + float lqratio = (left_currentTCPPositionInMeter(2) - guardTargetZDown) / (guardTargetZUp - guardTargetZDown); + float rqratio = (right_currentTCPPositionInMeter(2) - guardTargetZDown) / (guardTargetZUp - guardTargetZDown); + + lqratio = (lqratio > 1) ? 1 : lqratio; + lqratio = (lqratio < 0) ? 0 : lqratio; + rqratio = (rqratio > 1) ? 1 : rqratio; + rqratio = (rqratio < 0) ? 0 : rqratio; + + + Eigen::Quaternionf left_desiredQuaternion = quatSlerp(lqratio, left_oriDown, left_oriUp); + Eigen::Quaternionf right_desiredQuaternion = quatSlerp(rqratio, right_oriDown, right_oriUp); + + + Eigen::Matrix3f left_desiredRotMat = left_desiredQuaternion.normalized().toRotationMatrix(); + Eigen::Matrix3f right_desiredRotMat = right_desiredQuaternion.normalized().toRotationMatrix(); + + Eigen::Matrix3f left_orientationError = left_currentRotMat * left_desiredRotMat.inverse(); + Eigen::Matrix3f right_orientationError = right_currentRotMat * right_desiredRotMat.inverse(); + + Eigen::Quaternionf left_diffQuaternion(left_orientationError); + Eigen::Quaternionf right_diffQuaternion(right_orientationError); + + Eigen::AngleAxisf left_angleAxis(left_diffQuaternion); + Eigen::AngleAxisf right_angleAxis(right_diffQuaternion); + + left_tcpDesiredAngularError = left_angleAxis.angle() * left_angleAxis.axis(); + right_tcpDesiredAngularError = right_angleAxis.angle() * right_angleAxis.axis(); + + + // writing to the buffer + getWriterControlStruct().left_tcpDesiredLinearVelocity = gmmMotionGenerator->left_DS_DesiredVelocity; + getWriterControlStruct().right_tcpDesiredLinearVelocity = gmmMotionGenerator->right_DS_DesiredVelocity; + getWriterControlStruct().left_right_distanceInMeter = gmmMotionGenerator->left_right_position_errorInMeter; + + getWriterControlStruct().left_tcpDesiredAngularError = left_tcpDesiredAngularError; + getWriterControlStruct().right_tcpDesiredAngularError = right_tcpDesiredAngularError; + + writeControlStruct(); + debugCtrlDataInfo.getWriteBuffer().desredZ = guardDesiredZ; + debugCtrlDataInfo.getWriteBuffer().force_error_z = force_error_z; + debugCtrlDataInfo.getWriteBuffer().guardXYHighFrequency = guardXYHighFrequency; + debugCtrlDataInfo.getWriteBuffer().guard_mounting_correction_x = guard_mounting_correction_x; + debugCtrlDataInfo.getWriteBuffer().guard_mounting_correction_y = guard_mounting_correction_y; + debugCtrlDataInfo.getWriteBuffer().guard_mounting_correction_z = guard_mounting_correction_z; + debugCtrlDataInfo.commitWrite(); + +} + + +void DSRTBimanualController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) +{ + // reading the control objectives from the other threads (desired velocities) + Eigen::Vector3f left_tcpDesiredLinearVelocity = rtGetControlStruct().left_tcpDesiredLinearVelocity; + Eigen::Vector3f right_tcpDesiredLinearVelocity = rtGetControlStruct().right_tcpDesiredLinearVelocity; + Eigen::Vector3f left_tcpDesiredAngularError = rtGetControlStruct().left_tcpDesiredAngularError; + Eigen::Vector3f right_tcpDesiredAngularError = rtGetControlStruct().right_tcpDesiredAngularError; + Eigen::Vector3f left_right_distanceInMeter = rtGetControlStruct().left_right_distanceInMeter; + + double deltaT = timeSinceLastIteration.toSecondsDouble(); + + + // measure the sesor data for the other threads + + Eigen::Matrix4f leftSensorFrame = left_sensor_frame->getPoseInRootFrame(); + Eigen::Matrix4f rightSensorFrame = right_sensor_frame->getPoseInRootFrame(); + + // ARMARX_IMPORTANT << "left force offset: " << leftForceOffset; + // ARMARX_IMPORTANT << "right force offset: " << rightForceOffset; + + Eigen::Vector3f left_forceInRoot = leftSensorFrame.block<3, 3>(0, 0) * (leftForceTorque->force - leftForceOffset); + Eigen::Vector3f right_forceInRoot = rightSensorFrame.block<3, 3>(0, 0) * (rightForceTorque->force - rightForceOffset); + // Eigen::Vector3f left_torqueInRoot = leftSensorFrame.block<3, 3>(0, 0) * leftForceTorque->torque; + // Eigen::Vector3f right_torqueInRoot = rightSensorFrame.block<3, 3>(0, 0) * rightForceTorque->torque; + left_forceInRoot(2) = left_forceInRoot(2) + cfg->leftForceOffsetZ; // - 4 + 8.5; + right_forceInRoot(2) = right_forceInRoot(2) + cfg->rightForceOffsetZ; // + 30 - 5.2; + + + Eigen::Matrix4f left_currentTCPPose = left_arm_tcp->getPoseInRootFrame(); + Eigen::Matrix4f right_currentTCPPose = right_arm_tcp->getPoseInRootFrame(); + + + Eigen::MatrixXf left_jacobi = left_ik->getJacobianMatrix(left_arm_tcp, VirtualRobot::IKSolver::CartesianSelection::All); + Eigen::MatrixXf right_jacobi = right_ik->getJacobianMatrix(right_arm_tcp, VirtualRobot::IKSolver::CartesianSelection::All); + + Eigen::VectorXf left_qpos; + Eigen::VectorXf left_qvel; + + Eigen::VectorXf right_qpos; + Eigen::VectorXf right_qvel; + + left_qpos.resize(left_positionSensors.size()); + left_qvel.resize(left_velocitySensors.size()); + + right_qpos.resize(right_positionSensors.size()); + right_qvel.resize(right_velocitySensors.size()); + + int jointDim = left_positionSensors.size(); + + for (size_t i = 0; i < left_velocitySensors.size(); ++i) + { + left_qpos(i) = left_positionSensors[i]->position; + left_qvel(i) = left_velocitySensors[i]->velocity; + + right_qpos(i) = right_positionSensors[i]->position; + right_qvel(i) = right_velocitySensors[i]->velocity; + } + + Eigen::VectorXf left_tcptwist = left_jacobi * left_qvel; + Eigen::VectorXf right_tcptwist = right_jacobi * right_qvel; + + Eigen::Vector3f left_currentTCPLinearVelocity; + Eigen::Vector3f right_currentTCPLinearVelocity; + Eigen::Vector3f left_currentTCPAngularVelocity; + Eigen::Vector3f right_currentTCPAngularVelocity; + left_currentTCPLinearVelocity << 0.001 * left_tcptwist(0), 0.001 * left_tcptwist(1), 0.001 * left_tcptwist(2); + right_currentTCPLinearVelocity << 0.001 * right_tcptwist(0), 0.001 * right_tcptwist(1), 0.001 * right_tcptwist(2); + left_currentTCPAngularVelocity << left_tcptwist(3), left_tcptwist(4), left_tcptwist(5); + right_currentTCPAngularVelocity << right_tcptwist(3), right_tcptwist(4), right_tcptwist(5); + double filterFactor = deltaT / (filterTimeConstant + deltaT); + left_currentTCPLinearVelocity_filtered = (1 - filterFactor) * left_currentTCPLinearVelocity_filtered + filterFactor * left_currentTCPLinearVelocity; + right_currentTCPLinearVelocity_filtered = (1 - filterFactor) * right_currentTCPLinearVelocity_filtered + filterFactor * right_currentTCPLinearVelocity; + left_currentTCPAngularVelocity_filtered = (1 - filterFactor) * left_currentTCPAngularVelocity_filtered + left_currentTCPAngularVelocity; + right_currentTCPAngularVelocity_filtered = (1 - filterFactor) * right_currentTCPAngularVelocity_filtered + right_currentTCPAngularVelocity; + left_jointVelocity_filtered = (1 - filterFactor) * left_jointVelocity_filtered + filterFactor * left_qvel; + right_jointVelocity_filtered = (1 - filterFactor) * right_jointVelocity_filtered + filterFactor * right_qvel; + + // writing into the bufffer for other threads + controllerSensorData.getWriteBuffer().left_tcpPose = left_currentTCPPose; + controllerSensorData.getWriteBuffer().right_tcpPose = right_currentTCPPose; + controllerSensorData.getWriteBuffer().left_force = left_forceInRoot; + controllerSensorData.getWriteBuffer().right_force = right_forceInRoot; + controllerSensorData.getWriteBuffer().currentTime += deltaT; + controllerSensorData.commitWrite(); + + + + + // inverse dynamics: + + + // reading the tcp orientation + + + + + // computing the task-specific forces + Eigen::Vector3f left_DS_force = -(1.2 * left_currentTCPLinearVelocity_filtered - forward_gain * left_tcpDesiredLinearVelocity); + Eigen::Vector3f right_DS_force = -(1.2 * right_currentTCPLinearVelocity_filtered - forward_gain * right_tcpDesiredLinearVelocity); + for (int i = 0; i < 3; ++i) + { + left_DS_force(i) = left_DS_force(i) * Damping[i]; + right_DS_force(i) = right_DS_force(i) * Damping[i]; + + left_DS_force(i) = deadzone(left_DS_force(i), 0.1, 100); + right_DS_force(i) = deadzone(right_DS_force(i), 0.1, 100); + + } + + // computing coupling forces + Eigen::Vector3f coupling_force = - Coupling_stiffness * left_right_distanceInMeter; + float coupling_force_norm = coupling_force.norm(); + + if (coupling_force_norm > Coupling_force_limit) + { + coupling_force = (Coupling_force_limit / coupling_force_norm) * coupling_force; + } + + coupling_force(0) = deadzone(coupling_force(0), 0.1, 100); + coupling_force(1) = deadzone(coupling_force(1), 0.1, 100); + coupling_force(2) = deadzone(coupling_force(2), 0.1, 100); + + + + + double v_both = left_currentTCPLinearVelocity_filtered.norm() + right_currentTCPLinearVelocity_filtered.norm(); + float force_contact_switch = 0; + float left_height = left_currentTCPPose(2, 3) * 0.001; + float right_height = right_currentTCPPose(2, 3) * 0.001; + + float disTolerance = cfg->contactDistanceTolerance; + bool isUp = fabs(left_height - guardTargetZUp) < disTolerance && fabs(right_height - guardTargetZUp) < disTolerance; + if (v_both < disTolerance && isUp) + { + force_contact_switch = 1; + } + else if (v_both < 0.05 && isUp) + { + force_contact_switch = (0.05 - v_both) / (0.05 - disTolerance); + } + else if (v_both >= 0.05) + { + force_contact_switch = 0; + } + + // computing for contact forces + float left_force_error = force_contact_switch * (-left_forceInRoot(2) - contactForce); + float right_force_error = force_contact_switch * (-right_forceInRoot(2) - contactForce); + + // float left_force_error_limited = left_force_error; + // float right_force_error_limited = right_force_error; + + // left_force_error_limited = (left_force_error_limited > 2) ? 2 : left_force_error_limited; + // left_force_error_limited = (left_force_error_limited < -2) ? -2 : left_force_error_limited; + + // right_force_error_limited = (right_force_error_limited > 2) ? 2 : right_force_error_limited; + // right_force_error_limited = (right_force_error_limited < -2) ? -2 : right_force_error_limited; + + + left_force_error = deadzone(left_force_error, 0.5, 2); + right_force_error = deadzone(right_force_error, 0.5, 2); + + left_contactForceZ_correction = left_contactForceZ_correction - forceFilterCoeff * left_force_error; + right_contactForceZ_correction = right_contactForceZ_correction - forceFilterCoeff * right_force_error; + + left_contactForceZ_correction = (left_contactForceZ_correction > 30) ? 30 : left_contactForceZ_correction; + right_contactForceZ_correction = (right_contactForceZ_correction > 30) ? 30 : right_contactForceZ_correction; + + left_contactForceZ_correction = (left_contactForceZ_correction < -30) ? -contactForce : left_contactForceZ_correction; + right_contactForceZ_correction = (right_contactForceZ_correction < -30) ? -contactForce : right_contactForceZ_correction; + + Eigen::Vector3f left_tcpDesiredForce = left_DS_force + coupling_force; + Eigen::Vector3f right_tcpDesiredForce = right_DS_force - coupling_force; + + left_tcpDesiredForce(2) += force_contact_switch * (contactForce + left_contactForceZ_correction); + right_tcpDesiredForce(2) += force_contact_switch * (contactForce + right_contactForceZ_correction); + + + + Eigen::Vector3f left_tcpDesiredTorque = - oriKp * left_tcpDesiredAngularError - oriDamping * left_currentTCPAngularVelocity_filtered; + Eigen::Vector3f right_tcpDesiredTorque = - oriKp * right_tcpDesiredAngularError - oriDamping * right_currentTCPAngularVelocity_filtered; + + // for (int i = 0; i < left_tcpDesiredTorque.size(); ++i) + // { + // left_tcpDesiredTorque(i) = deadzone(left_tcpDesiredTorque(i), ) + + // } + + + left_tcpDesiredTorque_filtered = (1 - filterFactor) * left_tcpDesiredTorque_filtered + filterFactor * left_tcpDesiredTorque; + right_tcpDesiredTorque_filtered = (1 - filterFactor) * right_tcpDesiredTorque_filtered + filterFactor * right_tcpDesiredTorque; + + + Eigen::Vector6f left_tcpDesiredWrench; + Eigen::Vector6f right_tcpDesiredWrench; + + left_tcpDesiredWrench << 0.001 * left_tcpDesiredForce, left_tcpDesiredTorque_filtered; + right_tcpDesiredWrench << 0.001 * right_tcpDesiredForce, right_tcpDesiredTorque_filtered; + + + // calculate the null-spcae torque + Eigen::MatrixXf I = Eigen::MatrixXf::Identity(jointDim, jointDim); + + float lambda = 0.2; + Eigen::MatrixXf left_jtpinv = left_ik->computePseudoInverseJacobianMatrix(left_jacobi.transpose(), lambda); + Eigen::MatrixXf right_jtpinv = right_ik->computePseudoInverseJacobianMatrix(right_jacobi.transpose(), lambda); + + + Eigen::VectorXf left_nullqerror = left_qpos - left_qnullspace; + Eigen::VectorXf right_nullqerror = right_qpos - right_qnullspace; + + for (int i = 0; i < left_nullqerror.size(); ++i) + { + if (fabs(left_nullqerror(i)) < 0.09) + { + left_nullqerror(i) = 0; + } + + if (fabs(right_nullqerror(i)) < 0.09) + { + right_nullqerror(i) = 0; + } + } + + + Eigen::VectorXf left_nullspaceTorque = - nullspaceKp * left_nullqerror - nullspaceDamping * left_jointVelocity_filtered; + Eigen::VectorXf right_nullspaceTorque = - nullspaceKp * right_nullqerror - nullspaceDamping * right_jointVelocity_filtered; + + Eigen::VectorXf left_projectedNullTorque = (I - left_jacobi.transpose() * left_jtpinv) * left_nullspaceTorque; + Eigen::VectorXf right_projectedNullTorque = (I - right_jacobi.transpose() * right_jtpinv) * right_nullspaceTorque; + + float left_nulltorque_norm = left_projectedNullTorque.norm(); + float right_nulltorque_norm = right_projectedNullTorque.norm(); + + if (left_nulltorque_norm > null_torqueLimit) + { + left_projectedNullTorque = (null_torqueLimit / left_nulltorque_norm) * left_projectedNullTorque; + } + + if (right_nulltorque_norm > null_torqueLimit) + { + right_projectedNullTorque = (null_torqueLimit / right_nulltorque_norm) * right_projectedNullTorque; + } + + Eigen::VectorXf left_jointDesiredTorques = left_jacobi.transpose() * left_tcpDesiredWrench + left_projectedNullTorque; + Eigen::VectorXf right_jointDesiredTorques = right_jacobi.transpose() * right_tcpDesiredWrench + right_projectedNullTorque; + + + right_desiredTorques_filtered = (1 - cfg->TorqueFilterConstant) * right_desiredTorques_filtered + cfg->TorqueFilterConstant * right_jointDesiredTorques; + + + for (size_t i = 0; i < left_torque_targets.size(); ++i) + { + float desiredTorque = smooth_startup * left_jointDesiredTorques(i); + desiredTorque = deadzone(desiredTorque, cfg->desiredTorqueDisturbance, torqueLimit); + left_desiredTorques_filtered(i) = (1 - cfg->TorqueFilterConstant) * left_desiredTorques_filtered(i) + cfg->TorqueFilterConstant * desiredTorque; + + std::string names = left_jointNames[i] + "_desiredTorques"; + debugDataInfo.getWriteBuffer().desired_torques[names] = desiredTorque; + names = names + "_filtered"; + debugDataInfo.getWriteBuffer().desired_torques[names] = left_desiredTorques_filtered(i); + + if (fabs(left_desiredTorques_filtered(i)) > torqueLimit) + { + left_torque_targets.at(i)->torque = 0; + } + else + { + left_torque_targets.at(i)->torque = left_desiredTorques_filtered(i); // targets.at(i)->velocity = desiredVelocity; + } + } + + for (size_t i = 0; i < right_torque_targets.size(); ++i) + { + float desiredTorque = smooth_startup * right_jointDesiredTorques(i); + desiredTorque = deadzone(desiredTorque, cfg->desiredTorqueDisturbance, torqueLimit); + right_desiredTorques_filtered(i) = (1 - cfg->TorqueFilterConstant) * right_desiredTorques_filtered(i) + cfg->TorqueFilterConstant * desiredTorque; + + std::string names = right_jointNames[i] + "_desiredTorques"; + debugDataInfo.getWriteBuffer().desired_torques[names] = desiredTorque; + names = names + "_filtered"; + debugDataInfo.getWriteBuffer().desired_torques[names] = right_desiredTorques_filtered(i); + + if (fabs(right_desiredTorques_filtered(i)) > torqueLimit) + { + right_torque_targets.at(i)->torque = 0; + } + else + { + right_torque_targets.at(i)->torque = right_desiredTorques_filtered(i); // targets.at(i)->velocity = desiredVelocity; + } + } + + smooth_startup = smooth_startup + 0.001 * (1.1 - smooth_startup); + smooth_startup = (smooth_startup > 1) ? 1 : smooth_startup; + smooth_startup = (smooth_startup < 0) ? 0 : smooth_startup; + + + + debugDataInfo.getWriteBuffer().left_realForce_x = left_forceInRoot(0); + debugDataInfo.getWriteBuffer().left_realForce_y = left_forceInRoot(1); + debugDataInfo.getWriteBuffer().left_realForce_z = left_forceInRoot(2); + debugDataInfo.getWriteBuffer().right_realForce_x = right_forceInRoot(0); + debugDataInfo.getWriteBuffer().right_realForce_y = right_forceInRoot(1); + debugDataInfo.getWriteBuffer().right_realForce_z = right_forceInRoot(2); + + debugDataInfo.getWriteBuffer().left_force_error = left_force_error; + debugDataInfo.getWriteBuffer().right_force_error = right_force_error; + + + debugDataInfo.getWriteBuffer().left_tcpDesiredTorque_x = left_tcpDesiredTorque_filtered(0); + debugDataInfo.getWriteBuffer().left_tcpDesiredTorque_y = left_tcpDesiredTorque_filtered(1); + debugDataInfo.getWriteBuffer().left_tcpDesiredTorque_z = left_tcpDesiredTorque_filtered(2); + debugDataInfo.getWriteBuffer().right_tcpDesiredTorque_x = right_tcpDesiredTorque_filtered(0); + debugDataInfo.getWriteBuffer().right_tcpDesiredTorque_y = right_tcpDesiredTorque_filtered(1); + debugDataInfo.getWriteBuffer().right_tcpDesiredTorque_z = right_tcpDesiredTorque_filtered(2); + // debugDataInfo.getWriteBuffer().desiredForce_x = tcpDesiredForce(0); + // debugDataInfo.getWriteBuffer().desiredForce_y = tcpDesiredForce(1); + // debugDataInfo.getWriteBuffer().desiredForce_z = tcpDesiredForce(2); + + + // debugDataInfo.getWriteBuffer().tcpDesiredTorque_x = tcpDesiredTorque(0); + // debugDataInfo.getWriteBuffer().tcpDesiredTorque_y = tcpDesiredTorque(1); + // debugDataInfo.getWriteBuffer().tcpDesiredTorque_z = tcpDesiredTorque(2); + + // debugDataInfo.getWriteBuffer().tcpDesiredAngularError_x = tcpDesiredAngularError(0); + // debugDataInfo.getWriteBuffer().tcpDesiredAngularError_y = tcpDesiredAngularError(1); + // debugDataInfo.getWriteBuffer().tcpDesiredAngularError_z = tcpDesiredAngularError(2); + + // debugDataInfo.getWriteBuffer().currentTCPAngularVelocity_x = currentTCPAngularVelocity(0); + // debugDataInfo.getWriteBuffer().currentTCPAngularVelocity_y = currentTCPAngularVelocity(1); + // debugDataInfo.getWriteBuffer().currentTCPAngularVelocity_z = currentTCPAngularVelocity(2); + + // debugDataInfo.getWriteBuffer().currentTCPLinearVelocity_x = currentTCPLinearVelocity_filtered(0); + // debugDataInfo.getWriteBuffer().currentTCPLinearVelocity_y = currentTCPLinearVelocity_filtered(1); + // debugDataInfo.getWriteBuffer().currentTCPLinearVelocity_z = currentTCPLinearVelocity_filtered(2); + + debugDataInfo.commitWrite(); + + // } + // else + // { + // for (size_t i = 0; i < targets.size(); ++i) + // { + // targets.at(i)->torque = 0; + + // } + // } + + + +} + +float DSRTBimanualController::deadzone(float input, float disturbance, float threshold) +{ + if (input > 0) + { + input = input - disturbance; + input = (input < 0) ? 0 : input; + input = (input > threshold) ? threshold : input; + } + else if (input < 0) + { + input = input + disturbance; + input = (input > 0) ? 0 : input; + input = (input < -threshold) ? -threshold : input; + } + + + return input; +} + +Eigen::Quaternionf DSRTBimanualController::quatSlerp(double t, const Eigen::Quaternionf& q0, const Eigen::Quaternionf& q1) +{ + double cosHalfTheta = q0.w() * q1.w() + q0.x() * q1.x() + q0.y() * q1.y() + q0.z() * q1.z(); + + + Eigen::Quaternionf q1x = q1; + if (cosHalfTheta < 0) + { + q1x.w() = -q1.w(); + q1x.x() = -q1.x(); + q1x.y() = -q1.y(); + q1x.z() = -q1.z(); + } + + + if (fabs(cosHalfTheta) >= 1.0) + { + return q0; + } + + double halfTheta = acos(cosHalfTheta); + double sinHalfTheta = sqrt(1.0 - cosHalfTheta * cosHalfTheta); + + + Eigen::Quaternionf result; + if (fabs(sinHalfTheta) < 0.001) + { + result.w() = (1 - t) * q0.w() + t * q1x.w(); + result.x() = (1 - t) * q0.x() + t * q1x.x(); + result.y() = (1 - t) * q0.y() + t * q1x.y(); + result.z() = (1 - t) * q0.z() + t * q1x.z(); + + } + else + { + double ratioA = sin((1 - t) * halfTheta) / sinHalfTheta; + double ratioB = sin(t * halfTheta) / sinHalfTheta; + + result.w() = ratioA * q0.w() + ratioB * q1x.w(); + result.x() = ratioA * q0.x() + ratioB * q1x.x(); + result.y() = ratioA * q0.y() + ratioB * q1x.y(); + result.z() = ratioA * q0.z() + ratioB * q1x.z(); + } + + return result; +} + +void DSRTBimanualController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx& debugObs) +{ + + StringVariantBaseMap datafields; + auto values = debugDataInfo.getUpToDateReadBuffer().desired_torques; + for (auto& pair : values) + { + datafields[pair.first] = new Variant(pair.second); + } + + // std::string nameJacobi = "jacobiori"; + // std::string nameJacobipos = "jacobipos"; + + // std::vector<float> jacobiVec = debugDataInfo.getUpToDateReadBuffer().jacobiVec; + // std::vector<float> jacobiPos = debugDataInfo.getUpToDateReadBuffer().jacobiPos; + + // for (size_t i = 0; i < jacobiVec.size(); ++i) + // { + // std::stringstream ss; + // ss << i; + // std::string name = nameJacobi + ss.str(); + // datafields[name] = new Variant(jacobiVec[i]); + // std::string namepos = nameJacobipos + ss.str(); + // datafields[namepos] = new Variant(jacobiPos[i]); + + // } + + + datafields["left_tcpDesiredTorque_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_tcpDesiredTorque_x); + datafields["left_tcpDesiredTorque_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_tcpDesiredTorque_y); + datafields["left_tcpDesiredTorque_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_tcpDesiredTorque_z); + datafields["right_tcpDesiredTorque_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_tcpDesiredTorque_x); + datafields["right_tcpDesiredTorque_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_tcpDesiredTorque_y); + datafields["right_tcpDesiredTorque_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_tcpDesiredTorque_z); + + datafields["left_realForce_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_realForce_x); + datafields["left_realForce_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_realForce_y); + datafields["left_realForce_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_realForce_z); + datafields["right_realForce_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_realForce_x); + datafields["right_realForce_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_realForce_y); + datafields["right_realForce_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_realForce_z); + + datafields["left_force_error"] = new Variant(debugDataInfo.getUpToDateReadBuffer().left_force_error); + datafields["right_force_error"] = new Variant(debugDataInfo.getUpToDateReadBuffer().right_force_error); + + + datafields["Desired_Guard_Z"] = new Variant(debugCtrlDataInfo.getUpToDateReadBuffer().desredZ); + datafields["Force_Error_Z"] = new Variant(debugCtrlDataInfo.getUpToDateReadBuffer().force_error_z); + datafields["guardXYHighFrequency"] = new Variant(debugCtrlDataInfo.getUpToDateReadBuffer().guardXYHighFrequency); + datafields["guard_mounting_correction_x"] = new Variant(debugCtrlDataInfo.getUpToDateReadBuffer().guard_mounting_correction_x); + datafields["guard_mounting_correction_y"] = new Variant(debugCtrlDataInfo.getUpToDateReadBuffer().guard_mounting_correction_y); + datafields["guard_mounting_correction_z"] = new Variant(debugCtrlDataInfo.getUpToDateReadBuffer().guard_mounting_correction_z); + + + + // datafields["desiredForce_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().desiredForce_x); + // datafields["desiredForce_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().desiredForce_y); + // datafields["desiredForce_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().desiredForce_z); + + // datafields["tcpDesiredTorque_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredTorque_x); + // datafields["tcpDesiredTorque_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredTorque_y); + // datafields["tcpDesiredTorque_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredTorque_z); + + // datafields["tcpDesiredAngularError_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredAngularError_x); + // datafields["tcpDesiredAngularError_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredAngularError_y); + // datafields["tcpDesiredAngularError_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredAngularError_z); + + // datafields["currentTCPAngularVelocity_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPAngularVelocity_x); + // datafields["currentTCPAngularVelocity_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPAngularVelocity_y); + // datafields["currentTCPAngularVelocity_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPAngularVelocity_z); + + + // datafields["currentTCPLinearVelocity_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPLinearVelocity_x); + // datafields["currentTCPLinearVelocity_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPLinearVelocity_y); + // datafields["currentTCPLinearVelocity_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPLinearVelocity_z); + + + debugObs->setDebugChannel("DSBimanualControllerOutput", datafields); + +} + +void DSRTBimanualController::rtPreActivateController() +{ + +} + +void DSRTBimanualController::rtPostDeactivateController() +{ + +} + +void armarx::DSRTBimanualController::setToDefaultTarget(const Ice::Current&) +{ + isDefaultTarget = true; +} diff --git a/source/RobotAPI/libraries/DSControllers/DSRTBimanualController.h b/source/RobotAPI/libraries/DSControllers/DSRTBimanualController.h new file mode 100644 index 0000000000000000000000000000000000000000..02fc3f8ab609b6cbcdd6529256e6955e126467a8 --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/DSRTBimanualController.h @@ -0,0 +1,548 @@ +/* + * 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 DSController::ArmarXObjects::DSRTBimanualController + * @author Mahdi Khoramshahi ( m80 dot khoramshahi at gmail dot com ) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_DSController_DSRTBimanualController_H +#define _ARMARX_LIB_DSController_DSRTBimanualController_H + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h> +#include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h> +#include <VirtualRobot/IK/DifferentialIK.h> +#include <VirtualRobot/Tools/Gravity.h> + +#include <RobotAPI/interface/units/RobotUnit/DSControllerBase.h> +#include "GMRDynamics.h" +#include <ArmarXCore/util/json/JSONObject.h> + +#include "MathLib.h" +//#include <ArmarXGui/libraries/StructuralJson/JPathNavigator.h> +//#include <ArmarXGui/libraries/StructuralJson/StructuralJsonParser.h> + + +namespace armarx +{ + class DSRTBimanualControllerControlData + { + public: + Eigen::Vector3f left_tcpDesiredLinearVelocity; + Eigen::Vector3f left_tcpDesiredAngularError; + + Eigen::Vector3f right_tcpDesiredLinearVelocity; + Eigen::Vector3f right_tcpDesiredAngularError; + + Eigen::Vector3f left_right_distanceInMeter; + + }; + + typedef boost::shared_ptr<GMRDynamics> BimanualGMMPtr; + + struct BimanualGMRParameters + { + int K_gmm_; + int dim_; + std::vector<double> Priors_; + std::vector<double> Mu_; + std::vector<double> Sigma_; + std::vector<double> attractor_; + double dt_; + }; + + + class BimanualGMMMotionGen + { + public: + BimanualGMMMotionGen() {} + + BimanualGMMMotionGen(const std::string& fileName) + { + getGMMParamsFromJsonFile(fileName); + } + + BimanualGMMPtr leftarm_gmm; + BimanualGMMPtr rightarm_gmm; + + BimanualGMRParameters leftarm_gmmParas; + BimanualGMRParameters rightarm_gmmParas; + + Eigen::Vector3f leftarm_Target; + Eigen::Vector3f rightarm_Target; + + Eigen::Vector3f left_DS_DesiredVelocity; + Eigen::Vector3f right_DS_DesiredVelocity; + Eigen::Vector3f left_right_position_errorInMeter; + + + + float scaling; + float v_max; + + void getGMMParamsFromJsonFile(const std::string& fileName) + { + std::ifstream infile { fileName }; + std::string objDefs = { std::istreambuf_iterator<char>(infile), std::istreambuf_iterator<char>() }; + // StructuralJsonParser jsonParser(objDefs); + // jsonParser.parse(); + // JPathNavigator jpnav(jsonParser.parsedJson); + // float k = jpnav.selectSingleNode("left/K").asFloat(); + // ARMARX_INFO << "k: " << k ; + // jpnav.selectSingleNode("left") + JSONObjectPtr json = new JSONObject(); + json->fromString(objDefs); + + // leftarm_gmmParas.K_gmm_ = AbstractObjectSerializerPtr::dynamicCast<JSONObjectPtr>(json->getElement("left"))->getInt("K"); + // leftarm_gmmParas.dim_ = json->getElement("left")->getInt("dim"); + // boost::dynamic_pointer_cast<JSONObjectPtr>(json->getElement("left"))->getArray<double>("Priors", leftarm_gmmParas.Priors_); + + // json->getElement("left")->getArray<double>("Mu", leftarm_gmmParas.Mu_); + // json->getElement("left")->getArray<double>("attractor", leftarm_gmmParas.attractor_); + // json->getElement("left")->getArray<double>("Sigma", leftarm_gmmParas.Sigma_); + + // rightarm_gmmParas.K_gmm_ = json->getElement("right")->getInt("K"); + // rightarm_gmmParas.dim_ = json->getElement("right")->getInt("dim"); + // json->getElement("right")->getArray<double>("Priors", rightarm_gmmParas.Priors_); + // json->getElement("right")->getArray<double>("Mu", rightarm_gmmParas.Mu_); + // json->getElement("right")->getArray<double>("attractor", rightarm_gmmParas.attractor_); + // json->getElement("right")->getArray<double>("Sigma", rightarm_gmmParas.Sigma_); + + + leftarm_gmmParas.K_gmm_ = json->getInt("leftK"); + leftarm_gmmParas.dim_ = json->getInt("leftDim"); + json->getArray<double>("leftPriors", leftarm_gmmParas.Priors_); + json->getArray<double>("leftMu", leftarm_gmmParas.Mu_); + json->getArray<double>("leftAttractor", leftarm_gmmParas.attractor_); + json->getArray<double>("leftSigma", leftarm_gmmParas.Sigma_); + + + rightarm_gmmParas.K_gmm_ = json->getInt("rightK"); + rightarm_gmmParas.dim_ = json->getInt("rightDim"); + json->getArray<double>("rightPriors", rightarm_gmmParas.Priors_); + json->getArray<double>("rightMu", rightarm_gmmParas.Mu_); + json->getArray<double>("rightAttractor", rightarm_gmmParas.attractor_); + json->getArray<double>("rightSigma", rightarm_gmmParas.Sigma_); + + + scaling = json->getDouble("Scaling"); + v_max = json->getDouble("MaxVelocity"); + + leftarm_gmm.reset(new GMRDynamics(leftarm_gmmParas.K_gmm_, leftarm_gmmParas.dim_, leftarm_gmmParas.dt_, leftarm_gmmParas.Priors_, leftarm_gmmParas.Mu_, leftarm_gmmParas.Sigma_)); + leftarm_gmm->initGMR(0, 2, 3, 5); + + rightarm_gmm.reset(new GMRDynamics(rightarm_gmmParas.K_gmm_, rightarm_gmmParas.dim_, rightarm_gmmParas.dt_, rightarm_gmmParas.Priors_, rightarm_gmmParas.Mu_, rightarm_gmmParas.Sigma_)); + rightarm_gmm->initGMR(0, 2, 3, 5); + + + // if (leftarm_gmmParas.attractor_.size() < 3) + // { + // ARMARX_ERROR << "attractor in json file for the left arm should be 6 dimension vector ... "; + // } + + // if (rightarm_gmmParas.attractor_.size() < 3) + // { + // ARMARX_ERROR << "attractor in json file for the left arm should be 6 dimension vector ... "; + // } + + std::cout << "line 162." << std::endl; + + + for (int i = 0; i < 3; ++i) + { + leftarm_Target(i) = leftarm_gmmParas.attractor_.at(i); + rightarm_Target(i) = rightarm_gmmParas.attractor_.at(i); + } + + std::cout << "Finished GMM." << std::endl; + + } + + + void updateDesiredVelocity( + const Eigen::Vector3f& leftarm_PositionInMeter, + const Eigen::Vector3f& rightarm_PositionInMeter, + float positionErrorToleranceInMeter, + float desiredZ, + float correction_x, + float correction_y, + float correction_z) + { + MathLib::Vector position_error; + position_error.Resize(3); + + MathLib::Vector desired_vel; + desired_vel.Resize(3); + + + + Eigen::Vector3f leftarm_Target_corrected = leftarm_Target; + Eigen::Vector3f rightarm_Target_corrected = rightarm_Target; + + leftarm_Target_corrected(0) += correction_x; + rightarm_Target_corrected(0) += correction_x; + + leftarm_Target_corrected(1) += correction_y; + rightarm_Target_corrected(1) += correction_y; + + leftarm_Target_corrected(2) = desiredZ + correction_z; + rightarm_Target_corrected(2) = desiredZ + correction_z; + + + + // for the left arm + Eigen::Vector3f leftarm_PositionError = leftarm_PositionInMeter - leftarm_Target_corrected; + if (leftarm_PositionError.norm() < positionErrorToleranceInMeter) + { + leftarm_PositionError.setZero(); + } + + for (int i = 0; i < 3; ++i) + { + position_error(i) = leftarm_PositionError(i); + } + + desired_vel = leftarm_gmm->getVelocity(position_error); + + Eigen::Vector3f leftarm_desired_vel; + leftarm_desired_vel << desired_vel(0), desired_vel(1), desired_vel(2); + + leftarm_desired_vel = scaling * leftarm_desired_vel; + + float lenVec = leftarm_desired_vel.norm(); + + if (std::isnan(lenVec)) + { + leftarm_desired_vel.setZero(); + } + + if (desiredZ < 1.5) + { + v_max = 0.2; + } + else + { + v_max = 0.5; + } + + if (lenVec > v_max) + { + leftarm_desired_vel = (v_max / lenVec) * leftarm_desired_vel; + } + + left_DS_DesiredVelocity = leftarm_desired_vel; + + + // for the right arm + Eigen::Vector3f rightarm_PositionError = rightarm_PositionInMeter - rightarm_Target_corrected; + if (rightarm_PositionError.norm() < positionErrorToleranceInMeter) + { + rightarm_PositionError.setZero(); + } + + for (int i = 0; i < 3; ++i) + { + position_error(i) = rightarm_PositionError(i); + } + + desired_vel = rightarm_gmm->getVelocity(position_error); + + Eigen::Vector3f rightarm_desired_vel; + rightarm_desired_vel << desired_vel(0), desired_vel(1), desired_vel(2); + + rightarm_desired_vel = scaling * rightarm_desired_vel; + + lenVec = rightarm_desired_vel.norm(); + if (std::isnan(lenVec)) + { + rightarm_desired_vel.setZero(); + } + + if (lenVec > v_max) + { + rightarm_desired_vel = (v_max / lenVec) * rightarm_desired_vel; + } + + right_DS_DesiredVelocity = rightarm_desired_vel; + + left_right_position_errorInMeter = leftarm_PositionError - rightarm_PositionError; + + } + + + + }; + + typedef boost::shared_ptr<BimanualGMMMotionGen> BimanualGMMMotionGenPtr; + + + + + /** + * @defgroup Library-DSRTBimanualController DSRTBimanualController + * @ingroup DSController + * A description of the library DSRTBimanualController. + * + * @class DSRTBimanualController + * @ingroup Library-DSRTBimanualController + * @brief Brief description of class DSRTBimanualController. + * + * Detailed description of class DSRTBimanualController. + */ + + class DSRTBimanualController : public NJointControllerWithTripleBuffer<DSRTBimanualControllerControlData>, public DSBimanualControllerInterface + { + + // ManagedIceObject interface + protected: + void onInitNJointController(); + void onDisconnectNJointController(); + + + void controllerRun(); + + + + // NJointControllerInterface interface + public: + using ConfigPtrT = DSRTBimanualControllerConfigPtr; + + DSRTBimanualController(const RobotUnitPtr& robotUnit, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + + std::string getClassName(const Ice::Current&) const + { + return "DSRTBimanualController"; + } + + // NJointController interface + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + void setToDefaultTarget(const Ice::Current&); + private: + + float deadzone(float currentValue, float targetValue, float threshold); + Eigen::Quaternionf quatSlerp(double t, const Eigen::Quaternionf& q0, const Eigen::Quaternionf& q1); + + // PeriodicTask<DSRTBimanualController>::pointer_type controllerTask; + BimanualGMMMotionGenPtr gmmMotionGenerator; + struct DSRTBimanualControllerSensorData + { + Eigen::Matrix4f left_tcpPose; + Eigen::Matrix4f right_tcpPose; + + // Eigen::Vector3f left_linearVelocity; + // Eigen::Vector3f right_linearVelocity; + + Eigen::Vector3f left_force; + Eigen::Vector3f right_force; + + + double currentTime; + + + }; + TripleBuffer<DSRTBimanualControllerSensorData> controllerSensorData; + + struct DSCtrlDebugInfo + { + float desredZ; + float force_error_z; + float guardXYHighFrequency; + float guard_mounting_correction_x; + float guard_mounting_correction_y; + float guard_mounting_correction_z; + }; + TripleBuffer<DSCtrlDebugInfo> debugCtrlDataInfo; + + struct DSRTDebugInfo + { + StringFloatDictionary desired_torques; + float desiredForce_x; + float desiredForce_y; + float desiredForce_z; + float tcpDesiredTorque_x; + float tcpDesiredTorque_y; + float tcpDesiredTorque_z; + + float tcpDesiredAngularError_x; + float tcpDesiredAngularError_y; + float tcpDesiredAngularError_z; + + float currentTCPAngularVelocity_x; + float currentTCPAngularVelocity_y; + float currentTCPAngularVelocity_z; + + float currentTCPLinearVelocity_x; + float currentTCPLinearVelocity_y; + float currentTCPLinearVelocity_z; + + // force torque sensor in root + float left_realForce_x; + float left_realForce_y; + float left_realForce_z; + float right_realForce_x; + float right_realForce_y; + float right_realForce_z; + float left_force_error; + float right_force_error; + + float left_tcpDesiredTorque_x; + float left_tcpDesiredTorque_y; + float left_tcpDesiredTorque_z; + float right_tcpDesiredTorque_x; + float right_tcpDesiredTorque_y; + float right_tcpDesiredTorque_z; + + }; + TripleBuffer<DSRTDebugInfo> debugDataInfo; + + + std::vector<const SensorValue1DoFActuatorTorque*> left_torqueSensors; + std::vector<const SensorValue1DoFGravityTorque*> left_gravityTorqueSensors; + std::vector<const SensorValue1DoFActuatorVelocity*> left_velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> left_positionSensors; + + std::vector<const SensorValue1DoFActuatorTorque*> right_torqueSensors; + std::vector<const SensorValue1DoFGravityTorque*> right_gravityTorqueSensors; + std::vector<const SensorValue1DoFActuatorVelocity*> right_velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> right_positionSensors; + + const SensorValueForceTorque* leftForceTorque; + const SensorValueForceTorque* rightForceTorque; + + std::vector<ControlTarget1DoFActuatorTorque*> left_torque_targets; + std::vector<ControlTarget1DoFActuatorTorque*> right_torque_targets; + + + VirtualRobot::RobotNodePtr left_arm_tcp; + VirtualRobot::RobotNodePtr right_arm_tcp; + + VirtualRobot::RobotNodePtr left_sensor_frame; + VirtualRobot::RobotNodePtr right_sensor_frame; + + VirtualRobot::DifferentialIKPtr left_ik; + Eigen::MatrixXf left_jacobip; + Eigen::MatrixXf left_jacobio; + + VirtualRobot::DifferentialIKPtr right_ik; + Eigen::MatrixXf right_jacobip; + Eigen::MatrixXf right_jacobio; + + Eigen::Quaternionf left_desiredQuaternion; + Eigen::Vector3f left_oldPosition; + Eigen::Matrix3f left_oldOrientation; + + Eigen::Quaternionf right_desiredQuaternion; + Eigen::Vector3f right_oldPosition; + Eigen::Matrix3f right_oldOrientation; + + Eigen::Vector3f left_currentTCPLinearVelocity_filtered; + Eigen::Vector3f left_currentTCPAngularVelocity_filtered; + + Eigen::VectorXf left_jointVelocity_filtered; + Eigen::VectorXf right_jointVelocity_filtered; + + Eigen::VectorXf left_desiredTorques_filtered; + Eigen::VectorXf right_desiredTorques_filtered; + + + Eigen::Vector3f left_tcpDesiredTorque_filtered; + Eigen::Vector3f right_tcpDesiredTorque_filtered; + + Eigen::Vector3f leftForceOffset; + Eigen::Vector3f rightForceOffset; + + float left_contactForceZ_correction; + float right_contactForceZ_correction; + + float smooth_startup; + + DSRTBimanualControllerConfigPtr cfg; + + Eigen::Vector3f right_currentTCPLinearVelocity_filtered; + Eigen::Vector3f right_currentTCPAngularVelocity_filtered; + + float filterTimeConstant; + + std::vector<std::string> left_jointNames; + std::vector<std::string> right_jointNames; + + float posiKp; + float v_max; + std::vector<float> Damping; + float torqueLimit; + float null_torqueLimit; + + float Coupling_stiffness; + float Coupling_force_limit; + + float forward_gain; + + float oriKp; + float oriDamping; + + float nullspaceKp; + float nullspaceDamping; + + float contactForce; + + float guardTargetZUp; + float guardTargetZDown; + float guardDesiredZ; + float guard_mounting_correction_z; + + float guardXYHighFrequency; + Eigen::Vector3f left_force_old; + Eigen::Vector3f right_force_old; + + Eigen::VectorXf left_qnullspace; + Eigen::VectorXf right_qnullspace; + + Eigen::Quaternionf left_oriUp; + Eigen::Quaternionf left_oriDown; + Eigen::Quaternionf right_oriUp; + Eigen::Quaternionf right_oriDown; + + // std::vector<BimanualGMMMotionGenPtr> BimanualGMMMotionGenList; + + + float forceFilterCoeff; + + float positionErrorTolerance; + bool controllerStopRequested = false; + bool controllerRunFinished = false; + + bool isDefaultTarget = true; + + // NJointController interface + protected: + void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); + + // NJointController interface + protected: + void rtPreActivateController(); + void rtPostDeactivateController(); + }; + +} + +#endif diff --git a/source/RobotAPI/libraries/DSControllers/DSRTController.cpp b/source/RobotAPI/libraries/DSControllers/DSRTController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..efe9bf5231456b0b743ac46c8b830d3958e79812 --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/DSRTController.cpp @@ -0,0 +1,442 @@ +/* + * 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 DSController::ArmarXObjects::DSRTController + * @author Mahdi Khoramshahi ( m80 dot khoramshahi at gmail dot com ) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "DSRTController.h" +#include <ArmarXCore/core/time/CycleUtil.h> + +using namespace armarx; + +NJointControllerRegistration<DSRTController> registrationControllerDSRTController("DSRTController"); + +void DSRTController::onInitNJointController() +{ + ARMARX_INFO << "init ..."; + + runTask("DSRTControllerTask", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); + + +} + +void DSRTController::onDisconnectNJointController() +{ + +} + + +DSRTController::DSRTController(const RobotUnitPtr& robotUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) +{ + DSControllerConfigPtr cfg = DSControllerConfigPtr::dynamicCast(config); + useSynchronizedRtRobot(); + VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); + + jointNames.clear(); + ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName); + for (size_t i = 0; i < rns->getSize(); ++i) + { + std::string jointName = rns->getNode(i)->getName(); + jointNames.push_back(jointName); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Torque1DoF); // ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Velocity1DoF); + ARMARX_CHECK_EXPRESSION(ct); + const SensorValueBase* sv = useSensorValue(jointName); + ARMARX_CHECK_EXPRESSION(sv); + auto casted_ct = ct->asA<ControlTarget1DoFActuatorTorque>(); // auto casted_ct = ct->asA<ControlTarget1DoFActuatorVelocity>(); + ARMARX_CHECK_EXPRESSION(casted_ct); + targets.push_back(casted_ct); + + const SensorValue1DoFActuatorTorque* torqueSensor = sv->asA<SensorValue1DoFActuatorTorque>(); + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFGravityTorque* gravityTorqueSensor = sv->asA<SensorValue1DoFGravityTorque>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + if (!torqueSensor) + { + ARMARX_WARNING << "No Torque sensor available for " << jointName; + } + if (!gravityTorqueSensor) + { + ARMARX_WARNING << "No Gravity Torque sensor available for " << jointName; + } + + torqueSensors.push_back(torqueSensor); + gravityTorqueSensors.push_back(gravityTorqueSensor); + velocitySensors.push_back(velocitySensor); + positionSensors.push_back(positionSensor); + }; + ARMARX_INFO << "Initialized " << targets.size() << " targets"; + tcp = rns->getTCP(); + ARMARX_CHECK_EXPRESSION_W_HINT(tcp, cfg->tcpName); + + + ik.reset(new VirtualRobot::DifferentialIK(rns, rtGetRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + + DSRTControllerSensorData initSensorData; + initSensorData.tcpPose = tcp->getPoseInRootFrame(); + initSensorData.currentTime = 0; + controllerSensorData.reinitAllBuffers(initSensorData); + + + oldPosition = tcp->getPositionInRootFrame(); + oldOrientation = tcp->getPoseInRootFrame().block<3, 3>(0, 0); + + std::vector<float> desiredPositionVec = cfg->desiredPosition; + for (size_t i = 0; i < 3; ++i) + { + desiredPosition(i) = desiredPositionVec[i]; + } + ARMARX_INFO << "ik reseted "; + + std::vector<float> desiredQuaternionVec = cfg->desiredQuaternion; + ARMARX_CHECK_EQUAL(desiredQuaternionVec.size(), 4); + + desiredQuaternion.x() = desiredQuaternionVec.at(0); + desiredQuaternion.y() = desiredQuaternionVec.at(1); + desiredQuaternion.z() = desiredQuaternionVec.at(2); + desiredQuaternion.w() = desiredQuaternionVec.at(3); + + + + DSRTControllerControlData initData; + for (size_t i = 0; i < 3; ++i) + { + initData.tcpDesiredLinearVelocity(i) = 0; + } + + for (size_t i = 0; i < 3; ++i) + { + initData.tcpDesiredAngularError(i) = 0; + } + reinitTripleBuffer(initData); + + // initial filter + currentTCPLinearVelocity_filtered.setZero(); + currentTCPAngularVelocity_filtered.setZero(); + filterTimeConstant = cfg->filterTimeConstant; + posiKp = cfg->posiKp; + v_max = cfg->v_max; + posiDamping = cfg->posiDamping; + torqueLimit = cfg->torqueLimit; + oriKp = cfg->oriKp; + oriDamping = cfg->oriDamping; + + + std::vector<float> qnullspaceVec = cfg->qnullspaceVec; + + qnullspace.resize(qnullspaceVec.size()); + + for (size_t i = 0; i < qnullspaceVec.size(); ++i) + { + qnullspace(i) = qnullspaceVec[i]; + } + + nullspaceKp = cfg->nullspaceKp; + nullspaceDamping = cfg->nullspaceDamping; + + + //set GMM parameters ... + std::vector<std::string> gmmParamsFiles = cfg->gmmParamsFiles; + if (gmmParamsFiles.size() == 0) + { + ARMARX_ERROR << "no gmm found ... "; + } + + gmmMotionGenList.clear(); + float sumBelief = 0; + for (size_t i = 0; i < gmmParamsFiles.size(); ++i) + { + gmmMotionGenList.push_back(GMMMotionGenPtr(new GMMMotionGen(gmmParamsFiles.at(i)))); + sumBelief += gmmMotionGenList[i]->belief; + } + ARMARX_CHECK_EQUAL(gmmMotionGenList.size(), 2); + + dsAdaptorPtr.reset(new DSAdaptor(gmmMotionGenList, cfg->dsAdaptorEpsilon)); + positionErrorTolerance = cfg->positionErrorTolerance; + + + ARMARX_INFO << "Initialization done"; +} + + +void DSRTController::controllerRun() +{ + if (!controllerSensorData.updateReadBuffer()) + { + return; + } + + + Eigen::Matrix4f currentTCPPose = controllerSensorData.getReadBuffer().tcpPose; + Eigen::Vector3f realVelocity = controllerSensorData.getReadBuffer().linearVelocity; + + Eigen::Vector3f currentTCPPositionInMeter; + currentTCPPositionInMeter << currentTCPPose(0, 3), currentTCPPose(1, 3), currentTCPPose(2, 3); + currentTCPPositionInMeter = 0.001 * currentTCPPositionInMeter; + + dsAdaptorPtr->updateDesiredVelocity(currentTCPPositionInMeter, positionErrorTolerance); + Eigen::Vector3f tcpDesiredLinearVelocity = dsAdaptorPtr->totalDesiredVelocity; + dsAdaptorPtr->updateBelief(realVelocity); + + + + float vecLen = tcpDesiredLinearVelocity.norm(); + if (vecLen > v_max) + { + + tcpDesiredLinearVelocity = tcpDesiredLinearVelocity / vecLen * v_max; + } + + ARMARX_INFO << "tcpDesiredLinearVelocity: " << tcpDesiredLinearVelocity; + + debugDataInfo.getWriteBuffer().belief0 = dsAdaptorPtr->task0_belief; + debugDataInfo.getWriteBuffer().belief1 = gmmMotionGenList[0]->belief; + debugDataInfo.getWriteBuffer().belief2 = gmmMotionGenList[1]->belief; + debugDataInfo.commitWrite(); + + Eigen::Vector3f tcpDesiredAngularError; + tcpDesiredAngularError << 0, 0, 0; + + Eigen::Matrix3f currentOrientation = currentTCPPose.block<3, 3>(0, 0); + Eigen::Matrix3f desiredOrientation = desiredQuaternion.normalized().toRotationMatrix(); + Eigen::Matrix3f orientationError = currentOrientation * desiredOrientation.inverse(); + Eigen::Quaternionf diffQuaternion(orientationError); + Eigen::AngleAxisf angleAxis(diffQuaternion); + tcpDesiredAngularError = angleAxis.angle() * angleAxis.axis(); + + getWriterControlStruct().tcpDesiredLinearVelocity = tcpDesiredLinearVelocity; + getWriterControlStruct().tcpDesiredAngularError = tcpDesiredAngularError; + + writeControlStruct(); +} + + +void DSRTController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) +{ + double deltaT = timeSinceLastIteration.toSecondsDouble(); + if (deltaT != 0) + { + + Eigen::Matrix4f currentTCPPose = tcp->getPoseInRootFrame(); + + + Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All); + + Eigen::VectorXf qpos; + Eigen::VectorXf qvel; + qpos.resize(positionSensors.size()); + qvel.resize(velocitySensors.size()); + + int jointDim = positionSensors.size(); + + for (size_t i = 0; i < velocitySensors.size(); ++i) + { + qpos(i) = positionSensors[i]->position; + qvel(i) = velocitySensors[i]->velocity; + } + + Eigen::VectorXf tcptwist = jacobi * qvel; + + Eigen::Vector3f currentTCPLinearVelocity; + currentTCPLinearVelocity << 0.001 * tcptwist(0), 0.001 * tcptwist(1), 0.001 * tcptwist(2); + double filterFactor = deltaT / (filterTimeConstant + deltaT); + currentTCPLinearVelocity_filtered = (1 - filterFactor) * currentTCPLinearVelocity_filtered + filterFactor * currentTCPLinearVelocity; + + controllerSensorData.getWriteBuffer().tcpPose = currentTCPPose; + controllerSensorData.getWriteBuffer().currentTime += deltaT; + controllerSensorData.getWriteBuffer().linearVelocity = currentTCPLinearVelocity_filtered; + controllerSensorData.commitWrite(); + + + Eigen::Vector3f currentTCPAngularVelocity; + currentTCPAngularVelocity << tcptwist(3), tcptwist(4), tcptwist(5); + // // calculate linear velocity + // Eigen::Vector3f currentTCPPosition; + // currentTCPPosition << currentTCPPose(0, 3), currentTCPPose(1, 3), currentTCPPose(2, 3); + // Eigen::Vector3f currentTCPLinearVelocity_raw = (currentTCPPosition - oldPosition) / deltaT; + // oldPosition = currentTCPPosition; + + // // calculate angular velocity + // Eigen::Matrix3f currentTCPOrientation = currentTCPPose.block<3, 3>(0, 0); + // Eigen::Matrix3f currentTCPDiff = currentTCPOrientation * oldOrientation.inverse(); + // Eigen::AngleAxisf currentAngleAxisDiff(currentTCPDiff); + // Eigen::Vector3f currentTCPAngularVelocity_raw = currentAngleAxisDiff.angle() * currentAngleAxisDiff.axis(); + // oldOrientation = currentTCPOrientation; + // currentTCPAngularVelocity_filtered = (1 - filterFactor) * currentTCPAngularVelocity_filtered + filterFactor * currentTCPAngularVelocity_raw; + + + Eigen::Vector3f tcpDesiredLinearVelocity = rtGetControlStruct().tcpDesiredLinearVelocity; + Eigen::Vector3f tcpDesiredAngularError = rtGetControlStruct().tcpDesiredAngularError; + + // calculate desired tcp force + Eigen::Vector3f tcpDesiredForce = -posiDamping * (currentTCPLinearVelocity_filtered - tcpDesiredLinearVelocity); + + // calculate desired tcp torque + Eigen::Vector3f tcpDesiredTorque = - oriKp * tcpDesiredAngularError - oriDamping * currentTCPAngularVelocity; + + Eigen::Vector6f tcpDesiredWrench; + tcpDesiredWrench << 0.001 * tcpDesiredForce, tcpDesiredTorque; + + // calculate desired joint torque + Eigen::MatrixXf I = Eigen::MatrixXf::Identity(jointDim, jointDim); + + float lambda = 2; + Eigen::MatrixXf jtpinv = ik->computePseudoInverseJacobianMatrix(jacobi.transpose(), lambda); + + Eigen::VectorXf nullqerror = qpos - qnullspace; + + for (int i = 0; i < nullqerror.rows(); ++i) + { + if (fabs(nullqerror(i)) < 0.09) + { + nullqerror(i) = 0; + } + } + Eigen::VectorXf nullspaceTorque = - nullspaceKp * nullqerror - nullspaceDamping * qvel; + + Eigen::VectorXf jointDesiredTorques = jacobi.transpose() * tcpDesiredWrench + (I - jacobi.transpose() * jtpinv) * nullspaceTorque; + + for (size_t i = 0; i < targets.size(); ++i) + { + float desiredTorque = jointDesiredTorques(i); + + desiredTorque = (desiredTorque > torqueLimit) ? torqueLimit : desiredTorque; + desiredTorque = (desiredTorque < -torqueLimit) ? -torqueLimit : desiredTorque; + + debugDataInfo.getWriteBuffer().desired_torques[jointNames[i]] = jointDesiredTorques(i); + + targets.at(i)->torque = desiredTorque; // targets.at(i)->velocity = desiredVelocity; + } + + + debugDataInfo.getWriteBuffer().desiredForce_x = tcpDesiredForce(0); + debugDataInfo.getWriteBuffer().desiredForce_y = tcpDesiredForce(1); + debugDataInfo.getWriteBuffer().desiredForce_z = tcpDesiredForce(2); + + + debugDataInfo.getWriteBuffer().tcpDesiredTorque_x = tcpDesiredTorque(0); + debugDataInfo.getWriteBuffer().tcpDesiredTorque_y = tcpDesiredTorque(1); + debugDataInfo.getWriteBuffer().tcpDesiredTorque_z = tcpDesiredTorque(2); + + debugDataInfo.getWriteBuffer().tcpDesiredAngularError_x = tcpDesiredAngularError(0); + debugDataInfo.getWriteBuffer().tcpDesiredAngularError_y = tcpDesiredAngularError(1); + debugDataInfo.getWriteBuffer().tcpDesiredAngularError_z = tcpDesiredAngularError(2); + + debugDataInfo.getWriteBuffer().currentTCPAngularVelocity_x = currentTCPAngularVelocity(0); + debugDataInfo.getWriteBuffer().currentTCPAngularVelocity_y = currentTCPAngularVelocity(1); + debugDataInfo.getWriteBuffer().currentTCPAngularVelocity_z = currentTCPAngularVelocity(2); + + debugDataInfo.getWriteBuffer().currentTCPLinearVelocity_x = currentTCPLinearVelocity_filtered(0); + debugDataInfo.getWriteBuffer().currentTCPLinearVelocity_y = currentTCPLinearVelocity_filtered(1); + debugDataInfo.getWriteBuffer().currentTCPLinearVelocity_z = currentTCPLinearVelocity_filtered(2); + + debugDataInfo.commitWrite(); + + } + else + { + for (size_t i = 0; i < targets.size(); ++i) + { + targets.at(i)->torque = 0; + + } + } + + + +} + +void DSRTController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx& debugObs) +{ + + StringVariantBaseMap datafields; + auto values = debugDataInfo.getUpToDateReadBuffer().desired_torques; + for (auto& pair : values) + { + datafields[pair.first] = new Variant(pair.second); + } + + // std::string nameJacobi = "jacobiori"; + // std::string nameJacobipos = "jacobipos"; + + // std::vector<float> jacobiVec = debugDataInfo.getUpToDateReadBuffer().jacobiVec; + // std::vector<float> jacobiPos = debugDataInfo.getUpToDateReadBuffer().jacobiPos; + + // for (size_t i = 0; i < jacobiVec.size(); ++i) + // { + // std::stringstream ss; + // ss << i; + // std::string name = nameJacobi + ss.str(); + // datafields[name] = new Variant(jacobiVec[i]); + // std::string namepos = nameJacobipos + ss.str(); + // datafields[namepos] = new Variant(jacobiPos[i]); + + // } + + + + datafields["desiredForce_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().desiredForce_x); + datafields["desiredForce_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().desiredForce_y); + datafields["desiredForce_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().desiredForce_z); + + datafields["tcpDesiredTorque_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredTorque_x); + datafields["tcpDesiredTorque_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredTorque_y); + datafields["tcpDesiredTorque_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredTorque_z); + + datafields["tcpDesiredAngularError_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredAngularError_x); + datafields["tcpDesiredAngularError_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredAngularError_y); + datafields["tcpDesiredAngularError_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().tcpDesiredAngularError_z); + + datafields["currentTCPAngularVelocity_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPAngularVelocity_x); + datafields["currentTCPAngularVelocity_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPAngularVelocity_y); + datafields["currentTCPAngularVelocity_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPAngularVelocity_z); + + + datafields["currentTCPLinearVelocity_x"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPLinearVelocity_x); + datafields["currentTCPLinearVelocity_y"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPLinearVelocity_y); + datafields["currentTCPLinearVelocity_z"] = new Variant(debugDataInfo.getUpToDateReadBuffer().currentTCPLinearVelocity_z); + + datafields["belief_0"] = new Variant(debugDataInfo.getUpToDateReadBuffer().belief0); + datafields["belief_1"] = new Variant(debugDataInfo.getUpToDateReadBuffer().belief1); + datafields["belief_2"] = new Variant(debugDataInfo.getUpToDateReadBuffer().belief2); + + debugObs->setDebugChannel("DSControllerOutput", datafields); + +} + +void DSRTController::rtPreActivateController() +{ + +} + +void DSRTController::rtPostDeactivateController() +{ + +} diff --git a/source/RobotAPI/libraries/DSControllers/DSRTController.h b/source/RobotAPI/libraries/DSControllers/DSRTController.h new file mode 100644 index 0000000000000000000000000000000000000000..d60552cef0a5bb6bf563b3d161a93185c0560220 --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/DSRTController.h @@ -0,0 +1,491 @@ +/* + * 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 DSController::ArmarXObjects::DSRTController + * @author Mahdi Khoramshahi ( m80 dot khoramshahi at gmail dot com ) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#ifndef _ARMARX_LIB_DSController_DSRTController_H +#define _ARMARX_LIB_DSController_DSRTController_H + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h> +#include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include <VirtualRobot/IK/DifferentialIK.h> +#include <VirtualRobot/Tools/Gravity.h> + +#include <RobotAPI/interface/units/RobotUnit/DSControllerBase.h> +#include "GMRDynamics.h" +#include <ArmarXCore/util/json/JSONObject.h> + +#include "MathLib.h" + +namespace armarx +{ + class DSRTControllerControlData + { + public: + Eigen::Vector3f tcpDesiredLinearVelocity; + Eigen::Vector3f tcpDesiredAngularError; + }; + + typedef boost::shared_ptr<GMRDynamics> GMMPtr; + + struct GMRParameters + { + int K_gmm_; + int dim_; + std::vector<double> Priors_; + std::vector<double> Mu_; + std::vector<double> Sigma_; + std::vector<double> attractor_; + double dt_; + }; + + + class GMMMotionGen + { + public: + GMMMotionGen() {} + + GMMMotionGen(const std::string& fileName) + { + getGMMParamsFromJsonFile(fileName); + } + + GMMPtr gmm; + GMRParameters gmmParas; + Eigen::Vector3f desiredDefaultTarget; + float scaling; + + float belief; + float v_max; + + void getGMMParamsFromJsonFile(const std::string& fileName) + { + std::ifstream infile { fileName }; + std::string objDefs = { std::istreambuf_iterator<char>(infile), std::istreambuf_iterator<char>() }; + JSONObjectPtr json = new JSONObject(); + json->fromString(objDefs); + + + gmmParas.K_gmm_ = json->getInt("K"); + gmmParas.dim_ = json->getInt("dim"); + json->getArray<double>("Priors", gmmParas.Priors_); + json->getArray<double>("Mu", gmmParas.Mu_); + json->getArray<double>("attractor", gmmParas.attractor_); + json->getArray<double>("Sigma", gmmParas.Sigma_); + + scaling = json->getDouble("Scaling"); + belief = json->getDouble("InitBelief"); + belief = 0; + v_max = json->getDouble("MaxVelocity"); + + gmm.reset(new GMRDynamics(gmmParas.K_gmm_, gmmParas.dim_, gmmParas.dt_, gmmParas.Priors_, gmmParas.Mu_, gmmParas.Sigma_)); + gmm->initGMR(0, 2, 3, 5); + + if (gmmParas.attractor_.size() < 3) + { + ARMARX_ERROR << "attractor in json file should be 6 dimension vector ... "; + } + + for (int i = 0; i < 3; ++i) + { + desiredDefaultTarget(i) = gmmParas.attractor_.at(i); + } + } + + void updateDesiredVelocity(const Eigen::Vector3f& currentPositionInMeter, float positionErrorToleranceInMeter) + { + Eigen::Vector3f PositionError = currentPositionInMeter - desiredDefaultTarget; + if (PositionError.norm() < positionErrorToleranceInMeter) + { + PositionError.setZero(); + } + + MathLib::Vector position_error; + position_error.Resize(3); + + for (int i = 0; i < 3; ++i) + { + position_error(i) = PositionError(i); + } + + MathLib::Vector desired_vel; + desired_vel.Resize(3); + desired_vel = gmm->getVelocity(position_error); + + Eigen::Vector3f tcpDesiredLinearVelocity; + tcpDesiredLinearVelocity << desired_vel(0), desired_vel(1), desired_vel(2); + + currentDesiredVelocity = scaling * tcpDesiredLinearVelocity; + + + float lenVec = tcpDesiredLinearVelocity.norm(); + if (std::isnan(lenVec)) + { + tcpDesiredLinearVelocity.setZero(); + } + + if (lenVec > v_max) + { + tcpDesiredLinearVelocity = (v_max / lenVec) * tcpDesiredLinearVelocity; + } + } + + + + Eigen::Vector3f currentDesiredVelocity; + }; + + typedef boost::shared_ptr<GMMMotionGen> GMMMotionGenPtr; + + class DSAdaptor + { + public: + float task0_belief; + float epsilon; + DSAdaptor() {} + + DSAdaptor(std::vector<GMMMotionGenPtr> gmmMotionGenList, float epsilon) + { + task0_belief = 1; + this->gmmMotionGenList = gmmMotionGenList; + + ARMARX_INFO << "epsilon: " << epsilon; + this->epsilon = epsilon; + + totalDesiredVelocity.setZero(); + } + + Eigen::Vector3f totalDesiredVelocity; + std::vector<GMMMotionGenPtr> gmmMotionGenList; + + + void updateDesiredVelocity(const Eigen::Vector3f& currentPositionInMeter, float positionErrorToleranceInMeter) + { + totalDesiredVelocity.setZero(); + for (size_t i = 0; i < gmmMotionGenList.size(); ++i) + { + gmmMotionGenList[i]->updateDesiredVelocity(currentPositionInMeter, positionErrorToleranceInMeter); + totalDesiredVelocity += gmmMotionGenList[i]->belief * gmmMotionGenList[i]->currentDesiredVelocity; + } + } + + void updateBelief(const Eigen::Vector3f& realVelocity) + { + std::vector<float> beliefUpdate; + beliefUpdate.resize(gmmMotionGenList.size()); + + float nullInnerSimilarity = 0; + for (size_t i = 0; i < gmmMotionGenList.size(); ++i) + { + + GMMMotionGenPtr currentGMM = gmmMotionGenList[i]; + + float belief = currentGMM->belief; + Eigen::Vector3f OtherTasks = totalDesiredVelocity - belief * currentGMM->currentDesiredVelocity; + float innerSimilarity = 2 * OtherTasks.dot(currentGMM->currentDesiredVelocity); + float outerDisSimilarity = (realVelocity - currentGMM->currentDesiredVelocity).squaredNorm(); + + if (innerSimilarity > nullInnerSimilarity) + { + nullInnerSimilarity = innerSimilarity; + } + + beliefUpdate[i] = - outerDisSimilarity - innerSimilarity; + + + } + + + + + float nullOuterSimilarity = realVelocity.squaredNorm(); + float zeroTaskRawBeliefUpdate = - nullInnerSimilarity - nullOuterSimilarity; + + if (zeroTaskRawBeliefUpdate < 0.2) + { + zeroTaskRawBeliefUpdate -= 1000; + } + + + beliefUpdate.insert(beliefUpdate.begin(), zeroTaskRawBeliefUpdate); + + WinnerTakeAll(beliefUpdate); + task0_belief += epsilon * beliefUpdate[0]; + if (task0_belief > 1) + { + task0_belief = 1; + } + else if (task0_belief < 0) + { + task0_belief = 0; + } + + float beliefSum = task0_belief; + + for (size_t i = 0; i < gmmMotionGenList.size(); ++i) + { + gmmMotionGenList[i]->belief += epsilon * beliefUpdate[i + 1]; + + + if (gmmMotionGenList[i]->belief > 1) + { + gmmMotionGenList[i]->belief = 1; + } + else if (gmmMotionGenList[i]->belief < 0) + { + gmmMotionGenList[i]->belief = 0; + } + + beliefSum += gmmMotionGenList[i]->belief; + + } + + for (size_t i = 0; i < gmmMotionGenList.size(); ++i) + { + gmmMotionGenList[i]->belief /= beliefSum; + } + + task0_belief /= beliefSum; + } + + private: + + void WinnerTakeAll(std::vector<float>& UpdateBeliefs_) + { + // std::fill(UpdateBeliefs_.begin(), UpdateBeliefs_.end(), 0); + + size_t winner_index = 0; + + for (size_t i = 1; i < UpdateBeliefs_.size(); i++) + { + if (UpdateBeliefs_[i] > UpdateBeliefs_[winner_index]) + { + winner_index = i; + } + } + + float winner_belief = task0_belief; + + if (winner_index != 0) + { + winner_belief = gmmMotionGenList[winner_index - 1]->belief; + } + + if (winner_belief == 1) + { + return; + } + + int runnerUp_index = 0; + + if (winner_index == 0) + { + runnerUp_index = 1; + } + + for (size_t i = 0; i < UpdateBeliefs_.size(); i++) + { + if (i == winner_index) + { + continue; + } + + if (UpdateBeliefs_[i] > UpdateBeliefs_[runnerUp_index]) + { + runnerUp_index = i; + } + } + + float offset = 0.5 * (UpdateBeliefs_[winner_index] + UpdateBeliefs_[runnerUp_index]); + + for (size_t i = 0; i < UpdateBeliefs_.size(); i++) + { + UpdateBeliefs_[i] -= offset; + } + + float UpdateSum = 0; + + for (size_t i = 0; i < UpdateBeliefs_.size(); i++) + { + float belief = task0_belief; + if (i != 0) + { + belief = gmmMotionGenList[i - 1]->belief; + } + + if (belief != 0 || UpdateBeliefs_[i] > 0) + { + UpdateSum += UpdateBeliefs_[i]; + } + } + + UpdateBeliefs_[winner_index] -= UpdateSum; + } + }; + + typedef boost::shared_ptr<DSAdaptor> DSAdaptorPtr; + + + + /** + * @defgroup Library-DSRTController DSRTController + * @ingroup DSController + * A description of the library DSRTController. + * + * @class DSRTController + * @ingroup Library-DSRTController + * @brief Brief description of class DSRTController. + * + * Detailed description of class DSRTController. + */ + + class DSRTController : public NJointControllerWithTripleBuffer<DSRTControllerControlData>, public DSControllerInterface + { + + // ManagedIceObject interface + protected: + void onInitNJointController(); + void onDisconnectNJointController(); + + + void controllerRun(); + + + + // NJointControllerInterface interface + public: + using ConfigPtrT = DSControllerConfigPtr; + + DSRTController(const RobotUnitPtr& robotUnit, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + + std::string getClassName(const Ice::Current&) const + { + return "DSRTController"; + } + + // NJointController interface + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + private: + // PeriodicTask<DSRTController>::pointer_type controllerTask; + + struct DSRTControllerSensorData + { + Eigen::Matrix4f tcpPose; + double currentTime; + + Eigen::Vector3f linearVelocity; + + }; + TripleBuffer<DSRTControllerSensorData> controllerSensorData; + + struct DSRTDebugInfo + { + StringFloatDictionary desired_torques; + float desiredForce_x; + float desiredForce_y; + float desiredForce_z; + float tcpDesiredTorque_x; + float tcpDesiredTorque_y; + float tcpDesiredTorque_z; + + float tcpDesiredAngularError_x; + float tcpDesiredAngularError_y; + float tcpDesiredAngularError_z; + + float currentTCPAngularVelocity_x; + float currentTCPAngularVelocity_y; + float currentTCPAngularVelocity_z; + + float currentTCPLinearVelocity_x; + float currentTCPLinearVelocity_y; + float currentTCPLinearVelocity_z; + + float belief0; + float belief1; + float belief2; + }; + TripleBuffer<DSRTDebugInfo> debugDataInfo; + + std::vector<const SensorValue1DoFActuatorTorque*> torqueSensors; + std::vector<const SensorValue1DoFGravityTorque*> gravityTorqueSensors; + std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> positionSensors; + + std::vector<ControlTarget1DoFActuatorTorque*> targets; + + + VirtualRobot::RobotNodePtr tcp; + + VirtualRobot::DifferentialIKPtr ik; + Eigen::MatrixXf jacobip; + Eigen::MatrixXf jacobio; + + Eigen::Vector3f desiredPosition; + + Eigen::Quaternionf desiredQuaternion; + Eigen::Vector3f oldPosition; + + Eigen::Matrix3f oldOrientation; + + Eigen::Vector3f currentTCPLinearVelocity_filtered; + Eigen::Vector3f currentTCPAngularVelocity_filtered; + + float filterTimeConstant; + + std::vector<std::string> jointNames; + + float posiKp; + float v_max; + float posiDamping; + float torqueLimit; + + float oriKp; + float oriDamping; + + float nullspaceKp; + float nullspaceDamping; + + Eigen::VectorXf qnullspace; + + std::vector<GMMMotionGenPtr> gmmMotionGenList; + + DSAdaptorPtr dsAdaptorPtr; + + float positionErrorTolerance; + + + // NJointController interface + protected: + void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); + + // NJointController interface + protected: + void rtPreActivateController(); + void rtPostDeactivateController(); + }; + +} + +#endif diff --git a/source/RobotAPI/libraries/DSControllers/GMRDynamics.cpp b/source/RobotAPI/libraries/DSControllers/GMRDynamics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1941fd2b8dda74b2fe4124f2786629eff8c526df --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/GMRDynamics.cpp @@ -0,0 +1,132 @@ +/* + * GMRDynamics.cpp + * + * Created on: Nov 20, 2011 + * Author: Seungsu KIM + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "GMRDynamics.h" + + +GMRDynamics::GMRDynamics(int nStates, int nVar, double delta_t, const char* f_mu, const char* f_sigma, const char* f_prio) +{ + this->delta_t = delta_t; + GMM = new Gaussians(nStates, nVar, f_mu, f_sigma, f_prio); +} + +GMRDynamics::GMRDynamics(int nStates, int nVar, double delta_t, const vector<double> pri_vec, const vector<double> mu_vec, const vector<double> sig_vec) +{ + this->delta_t = delta_t; + GMM = new Gaussians(nStates, nVar, pri_vec, mu_vec, sig_vec); +} + + +void GMRDynamics::initGMR(int first_inindex, int last_inindex, int first_outindex, int last_outindex) +{ + GMM->InitFastGMR(first_inindex, last_inindex, first_outindex, last_outindex); + + gDim = last_inindex - first_inindex + 1; + if (gDim != static_cast<unsigned int>(last_outindex - first_outindex + 1)) + { + cout << "dynamics dimension is not matching" << endl; + } + + gXi.Resize(gDim); + target.Resize(gDim); + + gXi.Zero(); + target.Zero(); +} + +void GMRDynamics::setStateTarget(Vector state, Vector target) +{ + setTarget(target); + setState(state); +} + +void GMRDynamics::setTarget(Vector target, double target_t) +{ + this->target_t = target_t; + + //gXi += (this->target - target); + this->target = target; +} + +Vector GMRDynamics::getTarget(void) +{ + return target; +} + +double GMRDynamics::getTargetT(void) +{ + return target_t; +} + +void GMRDynamics::setState(Vector state) +{ + gXi = state; +} + +Vector GMRDynamics::getState(void) +{ + return gXi; +} + +void GMRDynamics::setCurrentTime(double current_t) +{ + this->current_t = current_t; +} + +double GMRDynamics::getCurrentTime(void) +{ + return current_t; +} + +Vector GMRDynamics::getVelocity(Vector x) +{ + return GMM->Regression(x); + + +} + + +Vector GMRDynamics::getNextState(void) +{ + return getNextState(1.0); +} + +Vector GMRDynamics::getNextState(double lamda) +{ + // target time + target_t -= (delta_t* lamda); + + gXi += (getVelocity(gXi - target) * (delta_t* lamda)); + + return gXi; +} + +double GMRDynamics::getReachingTime(double lamda) +{ + unsigned int frame = 0; + unsigned int li = 0; + Vector xi(3); + xi.Set(gXi); + + for (frame = 0; frame < REACHING_ITERATION_MAX; frame++) + { + for (li = 0; li < INTEGRATION_L; li++) + { + xi += getVelocity(xi - target) * delta_t / (double)INTEGRATION_L * lamda; + + if ((xi - target).Norm() < GMR_ERROR_TOLERANCE) + { + return (double)(frame * INTEGRATION_L + li) * delta_t / (double)INTEGRATION_L; + } + } + } + return (double)(frame * INTEGRATION_L + li) * delta_t / (double)INTEGRATION_L; +} + diff --git a/source/RobotAPI/libraries/DSControllers/GMRDynamics.h b/source/RobotAPI/libraries/DSControllers/GMRDynamics.h new file mode 100644 index 0000000000000000000000000000000000000000..de826ca48963846616753dd86912f5c28d4c962b --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/GMRDynamics.h @@ -0,0 +1,56 @@ +/* + * GMRDynamics.h + * + * Created on: Nov 20, 2011 + * Author: Seungsu KIM + */ + +#ifndef __GMRDYNAMICS_H__ +#define __GMRDYNAMICS_H__ + +#include "Gaussians.h" + +#define GMR_ERROR_TOLERANCE 0.001 +#define INTEGRATION_L 5 +#define REACHING_ITERATION_MAX 500 +#define REAL_MIN (1e-30) + +// GMR Dynamics +class GMRDynamics +{ +private: + Gaussians* GMM; + + double delta_t; + double target_t; + double current_t; + + Vector gXi; + Vector target; + unsigned int gDim; + +public: + GMRDynamics(int nStates, int nVar, double delta_t, const char* f_mu, const char* f_sigma, const char* f_prio); + GMRDynamics(int nStates, int nVar, double delta_t, const vector<double> pri_vec, const vector<double> mu_vec, const vector<double> sig_vec); + + void initGMR(int first_inindex, int last_inindex, int first_outindex, int last_outindex); + + void setStateTarget(Vector state, Vector target); + void setTarget(Vector target, double target_t = -1.0); + Vector getTarget(void); + double getTargetT(void); + void setState(Vector state); + Vector getState(void); + void setCurrentTime(double current_t); + double getCurrentTime(void); + + Vector getVelocity(Vector x); + + Vector getNextState(void); + Vector getNextState(double lamda); + double getReachingTime(double lamda); +}; + + + +#endif //__GMRDYNAMICS_H__ diff --git a/source/RobotAPI/libraries/DSControllers/Gaussians.cpp b/source/RobotAPI/libraries/DSControllers/Gaussians.cpp new file mode 100644 index 0000000000000000000000000000000000000000..200605424145ced271fc45acb4bea5044e2b3d38 --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/Gaussians.cpp @@ -0,0 +1,518 @@ +/* + * Gaussians.cpp + * + * Created on: Nov 19, 2011 + * Author: Seungsu KIM + */ + +#include <math.h> +#include <iostream> +#include <fstream> + +#include "Gaussians.h" + +using namespace std; +/* +Gaussians::Gaussians(GMMs *model) +{ + this->model.nbStates = model->nbStates; + this->model.nbDim = model->nbDim; + + this->model.States = (GMMState *)malloc(model->nbStates*sizeof(GMMState) ); + + for(int s=0; s<model->nbStates; s++ ){ + this->model.States[s].Mu = model->GMMState[s].Mu; + this->model.States[s].Sigma = model->GMMState[s].Sigma; + this->model.States[s].Prio = model->GMMState[s].Prio; + } +} +*/ + +Gaussians::Gaussians(const char *f_mu, const char *f_sigma, const char *f_prio) +{ + int s, i, j; + int nbStates; + int nbDim; + + Matrix fMatrix; + fMatrix.Load(f_prio); + if ( fMatrix(0, fMatrix.ColumnSize() - 1) > 0.0 ) + { + nbStates = fMatrix.ColumnSize(); + } + else + { + nbStates = fMatrix.ColumnSize() - 1; + } + + for ( s = 0; s < nbStates; s++ ) { + model.States[s].Prio = fMatrix(0, s); + } + + fMatrix.Load(f_mu); + nbDim = fMatrix.RowSize(); + model.nbStates = nbStates; + model.nbDim = nbDim; + + + for ( s = 0; s < nbStates; s++ ) { + model.States[s].Mu.Resize(nbDim); + model.States[s].Sigma.Resize(nbDim, nbDim ); + } + + for ( s = 0; s < nbStates; s++ ) { + model.States[s].Mu = fMatrix.GetColumn(s); + } + + fMatrix.Load(f_sigma); + j = 0; + for ( s = 0; s < nbStates; s++ ) { + for ( i = 0; i < nbDim; i++ ) { + model.States[s].Sigma.SetRow(fMatrix.GetRow(j), i); + j++; + } + } +} + +Gaussians::Gaussians(int nbStates, int nbDim, const char *f_mu, const char *f_sigma, const char *f_prio) +{ + + int s, i, j; + + model.nbStates = nbStates; + model.nbDim = nbDim; + + for ( s = 0; s < nbStates; s++ ) { + model.States[s].Mu.Resize(nbDim); + model.States[s].Sigma.Resize(nbDim, nbDim ); + } + + Matrix fMatrix(nbDim, nbStates); + fMatrix.Load(f_mu); + for ( s = 0; s < nbStates; s++ ) { + model.States[s].Mu = fMatrix.GetColumn(s); + } + + fMatrix.Resize(nbStates * nbDim, nbDim); + fMatrix.Load(f_sigma); + j = 0; + for ( s = 0; s < nbStates; s++ ) { + for ( i = 0; i < nbDim; i++ ) { + model.States[s].Sigma.SetRow(fMatrix.GetRow(j), i); + j++; + } + } + + fMatrix.Resize(1, nbStates); + fMatrix.Load(f_prio); + Vector fVector(nbStates); + for ( s = 0; s < nbStates; s++ ) { + model.States[s].Prio = fMatrix(0, s); + } + +} + +Gaussians::Gaussians(const int nbStates, const int nbDim, const vector<double> pri_vec, const vector<double> mu_vec, const vector<double> sig_vec) { + + model.nbStates = nbStates; + model.nbDim = nbDim; + + for ( int s = 0; s < nbStates; s++ ) { + model.States[s].Mu.Resize(nbDim); + model.States[s].Sigma.Resize(nbDim, nbDim ); + } + + for ( int s = 0; s < nbStates; s++ ) { + model.States[s].Prio = pri_vec[s]; + } + // cout << endl << "Printing the constructed Priors" << endl; + // for ( int s = 0; s < nbStates; s++ ) { + // cout << model.States[s].Prio << "\t"; + // } + // cout << endl; + + + for ( int s = 0; s < nbStates; s++ ) { + for (int d = 0; d < nbDim; d++) { + model.States[s].Mu[d] = mu_vec[s * nbDim + d]; + } + } + + + // cout << endl << "Printing the constructed Mu" << endl; + // for ( int s = 0; s < nbStates; s++ ) { + // for (int d = 0; d < nbDim; d++) { + // cout << model.States[s].Mu[d] << "\t"; + // } + // cout << endl; + // } + + for ( int s = 0; s < nbStates; s++ ) { + for (int row = 0; row < nbDim; row++) { + for (int col = 0; col < nbDim; col++) { + int ind = s * nbDim * nbDim + row * nbDim + col; + model.States[s].Sigma(row, col) = sig_vec[ind]; + } + } + } + + + // cout << endl << "Printing the constructed Sigma" << endl; + // for ( int s = 0; s < nbStates; s++ ) { + // for (int row = 0; row < nbDim; row++) { + // for (int col = 0; col < nbDim; col++) { + // cout << model.States[s].Sigma(row, col) << "\t"; + // } + // cout <<endl; + // } + // cout << endl; + // } + + + + +} + + + + +void Gaussians::setGMMs(GMMs *model) +{ + for (unsigned int s = 0; s < model->nbStates; s++ ) { + this->model.States[s].Mu = model->States[s].Mu; + this->model.States[s].Sigma = model->States[s].Sigma; + this->model.States[s].Prio = model->States[s].Prio; + } +} + + +void Gaussians::InitFastGaussians(int first_inindex, int last_inindex) +{ + double det; + int nbIN = last_inindex - first_inindex + 1; + + for (unsigned int s = 0; s < model.nbStates; s++ ) { + gmmpinv[s].MuI.Resize(nbIN); + gmmpinv[s].SigmaII.Resize(nbIN, nbIN); + gmmpinv[s].SigmaIIInv.Resize(nbIN, nbIN); + } + + for (unsigned int s = 0; s < model.nbStates; s++ ) { + for ( int i = first_inindex; i <= last_inindex; i++ ) gmmpinv[s].MuI(i - first_inindex) = model.States[s].Mu(i); + for ( int i = first_inindex; i <= last_inindex; i++ ) + for ( int j = first_inindex; j <= last_inindex; j++ ) + gmmpinv[s].SigmaII(i - first_inindex, j - first_inindex) = model.States[s].Sigma(i, j); + + gmmpinv[s].SigmaII.Inverse(gmmpinv[s].SigmaIIInv, &det); + //gmmpinv[s].SigmaIIInv = gmmpinv[s].SigmaII.Inverse(); + //gmmpinv[s].SigmaII.Inverse(&det); + if (det < 0) det = 1e-30; + gmmpinv[s].detSigmaII = det; + + } + + nbDimI = last_inindex - first_inindex + 1; + gfDiff.Resize(nbDimI); + gfDiffp.Resize(nbDimI); + gDer.Resize(nbDimI); + +} + +double Gaussians::GaussianPDFFast(int state, Vector x) +{ + double p; + gfDiff = x - gmmpinv[state].MuI; + gfDiffp = gmmpinv[state].SigmaIIInv * gfDiff; + + p = exp(-0.5 * gfDiff.Dot(gfDiffp)) / sqrt(pow(2.0 * PI, nbDimI) * ( gmmpinv[state].detSigmaII + 1e-30)); + + return p; +} + +double Gaussians::GaussianProbFast(Vector x) +{ + double totalP = 0; + for (unsigned int s = 0; s < model.nbStates; s++ ) { + totalP += model.States[s].Prio * GaussianPDFFast(s, x); + } + return totalP; +} + +Vector Gaussians::GaussianDerProbFast(Vector x) +{ + gDer.Zero(); + for (unsigned int s = 0; s < model.nbStates; s++ ) { + gDer += (gmmpinv[s].SigmaIIInv * (x - gmmpinv[s].MuI)) * model.States[s].Prio * GaussianPDFFast(s, x); + } + return -gDer; +} + +void Gaussians::InitFastGMR(int first_inindex, int last_inindex, int first_outindex, int last_outindex) +{ + double det; + int nbIN = last_inindex - first_inindex + 1; + int nbOUT = last_outindex - first_outindex + 1; + + gPdf.Resize(model.nbStates); + + for (unsigned int s = 0; s < model.nbStates; s++ ) { + gmmpinv[s].MuI.Resize(nbIN); + gmmpinv[s].SigmaII.Resize(nbIN, nbIN); + gmmpinv[s].SigmaIIInv.Resize(nbIN, nbIN); + + gmmpinv[s].muO.Resize(nbOUT); + gmmpinv[s].SigmaIO.Resize(nbIN, nbOUT); + gmmpinv[s].SigmaIOInv.Resize(nbOUT, nbOUT); + } + + for (unsigned int s = 0; s < model.nbStates; s++ ) { + for ( int i = first_inindex; i <= last_inindex; i++ ) { + gmmpinv[s].MuI(i - first_inindex) = model.States[s].Mu(i); + + for ( int j = first_inindex; j <= last_inindex; j++ ) { + gmmpinv[s].SigmaII(i - first_inindex, j - first_inindex) = model.States[s].Sigma(i, j); + } + for ( int j = first_outindex; j <= last_outindex; j++ ) { + gmmpinv[s].SigmaIO(i - first_inindex, j - first_outindex) = model.States[s].Sigma(i, j); + } + } + + for ( int i = first_outindex; i <= last_outindex; i++ ) { + gmmpinv[s].muO(i - first_outindex) = model.States[s].Mu(i); + } + + gmmpinv[s].SigmaII.Inverse(gmmpinv[s].SigmaIIInv, &det); + if (det < 0) det = 1e-30; + gmmpinv[s].detSigmaII = det; + (gmmpinv[s].SigmaIO).Transpose().Inverse(gmmpinv[s].SigmaIOInv, &det); + } + + nbDimI = last_inindex - first_inindex + 1; + gfDiff.Resize(nbDimI); + gfDiffp.Resize(nbDimI); + gDer.Resize(nbDimI); + +} + +void Gaussians::Regression(const Vector & indata, Vector & outdata, Matrix & derGMR) +{ + Regression(indata, outdata); + cout << "derivative is not implemented yet " << endl; +} + +void Gaussians::Regression(const Vector & indata, Vector & outdata) +{ + double pdfall; + Vector h(model.nbStates); + Vector r_diff(outdata.Size()); + + for (unsigned int s = 0; s < model.nbStates; s++) { + gPdf(s) = model.States[s].Prio * GaussianPDFFast(s, indata); + } + pdfall = gPdf.Sum(); + + outdata.Zero(); + for (unsigned int s = 0; s < model.nbStates; s++) { + //h(s) = gPdf(s)/(pdfall + 1e-30 ); + h(s) = gPdf(s) / (pdfall ); + r_diff = gmmpinv[s].SigmaIO.Transpose() * gmmpinv[s].SigmaIIInv * (indata - gmmpinv[s].MuI); + + for (unsigned int i = 0; i < r_diff.Size(); i++ ) { + outdata(i) += h(s) * (r_diff(i) + gmmpinv[s].muO(i)); + } + } +} + +Vector Gaussians::Regression(const Vector & indata) +{ + Vector outdata(indata.Size()); + Regression(indata, outdata); + return outdata; +} + + +/* +#include <math.h> +#include "Gaussians.h" + +#include "armadillo" + +using namespace arma; +using namespace std; + +Gaussians::Gaussians(GMMs *model) +{ + this->model.nbStates = model->nbStates; + this->model.nbDim = model->nbDim; + + this->model.States = (GMMState *)malloc(model->nbStates*sizeof(GMMState) ); + + for(int s=0; s<model->nbStates; s++ ){ + this->model.States[s].Mu = model->GMMState[s].Mu; + this->model.States[s].Sigma = model->GMMState[s].Sigma; + this->model.States[s].Prio = model->GMMState[s].Prio; + } +} + +Gaussians::Gaussians(int nbStates, int nbDim, char *f_mu, char *f_sigma, char *f_prio) +{ + + int s, i, j; + + model.nbStates = nbStates; + model.nbDim = nbDim; + model.States = (GMMState *)malloc(nbStates*sizeof(GMMState) ); + + for( s=0; s<nbStates; s++ ){ + model.States[s].Mu = zeros<vec>(nbDim); + model.States[s].Sigma = zeros<mat>(nbDim, nbDim ); + } + + // f_mu + ifstream fin; + fin.open(f_mu); + for( i=0; i<nbDim; i++ ){ + for( s=0; s<nbStates; s++ ){ + fin >> model.States[s].Mu(i); + } + } + fin.close(); + + // f_sigma + fin.open(f_sigma); + for( s=0; s<nbStates; s++ ){ + for( i=0; i<nbDim; i++ ){ + for( j=0; j<nbDim; j++ ){ + fin >> model.States[s].Sigma(i,j); + } + } + } + fin.close(); + + // f_prio + fin.open(f_prio); + for( s=0; s<nbStates; s++ ){ + fin >>model.States[s].Prio; + } + fin.close(); +} + +void Gaussians::setGMMs(GMMs *model) +{ + for(int s=0; s<model->nbStates; s++ ){ + this->model.States[s].Mu = model->GMMState[s].Mu; + this->model.States[s].Sigma = model->GMMState[s].Sigma; + this->model.States[s].Prio = model->GMMState[s].Prio; + } +} + + +void Gaussians::InitFastGaussians(int first_inindex, int last_inindex) +{ + gmmpinv = (GMMStateP *)malloc(model.nbStates*sizeof(GMMStateP) ); + + for(int s=0; s<model.nbStates; s++ ){ + gmmpinv[s].MuI = model.States[s].Mu.subvec(first_inindex, last_inindex); + gmmpinv[s].SigmaII = model.States[s].Sigma.submat(first_inindex, first_inindex, last_inindex, last_inindex); + gmmpinv[s].SigmaIIInv = pinv(gmmpinv[s].SigmaII); + gmmpinv[s].detSigmaII = det(gmmpinv[s].SigmaII); + } + + nbDimI = last_inindex - first_inindex +1; + gfDiff = zeros<vec>(nbDimI); + gfDiffp = zeros<vec>(nbDimI); + gDer = zeros<vec>(nbDimI); +} + +double Gaussians::GaussianPDFFast(int state, vec x) +{ + double p; + gfDiff = x - gmmpinv[state].MuI; + gfDiffp = gmmpinv[state].SigmaIIInv * gfDiff; + + p = exp(-0.5*dot(gfDiff, gfDiffp)) / sqrt(pow(2.0*math::pi(), nbDimI)*( gmmpinv[state].detSigmaII +1e-30)); + + return p; +} + +double Gaussians::GaussianProbFast(vec x) +{ + double totalP=0; + for(int s=0; s<model.nbStates; s++ ){ + totalP += model.States[s].Prio*GaussianPDFFast(s,x); + } + return totalP; +} + +vec Gaussians::GaussianDerProbFast(vec x) +{ + gDer.zeros(); + for(int s=0; s<model.nbStates; s++ ){ + gDer += model.States[s].Prio * gmmpinv[s].SigmaIIInv *(x-gmmpinv[s].MuI)*GaussianPDFFast(s,x); + } + return -gDer; +} + +//------------------------------------------------------------------------------------------------------- +void AllocateGMMs(GMMs *model, int nbDim, int nbStates) +{ + model->nbDim = nbDim; + model->nbStates = nbStates; + model->GMMState = (GMMState *)malloc(nbStates*sizeof(GMMState) ); + + for(int s=0; s<nbStates; s++ ){ + model->GMMState[s].Mu = zeros<vec>(nbDim); + model->GMMState[s].Sigma = zeros<mat>(nbDim, nbDim ); + } +} + + +double GaussianPDF(vec x, vec Mu, mat Sigma) +{ + double p; + vec diff = x - Mu; + vec diffp = pinv( Sigma ) * diff; + int nbDim = x.size(); + + p = exp(-0.5*dot(diff, diffp)) / sqrt(pow(2.0*math::pi(), nbDim)*( abs(det(Sigma)) +1e-30)); + + if(p < 1e-30){ + return 1e-30; + } + else{ + return p; + } +} + +void GaussianMux(GMMs *modelK, GMMs *modelL, GMMs *modelOut) +{ + int k,l,j; + int K = modelK->nbStates; + int L = modelL->nbStates; + int J = K*L; + + //modelOut->nbDim = modelK->nbDim; + //modelOut->nbStates = J; + //modelOut->GMMState = (GMMState *)malloc(J*sizeof(GMMState) ); + + j=0; + for(k=0; k<K; k++){ + for(l=0; l<L; l++){ + modelOut->GMMState[j].Sigma = pinv( pinv(modelK->GMMState[k].Sigma) + pinv( modelL->GMMState[l].Sigma) ); + modelOut->GMMState[j].Mu = modelOut->GMMState[j].Sigma *( pinv(modelK->GMMState[k].Sigma) * modelK->GMMState[k].Mu + pinv(modelL->GMMState[l].Sigma) * modelL->GMMState[l].Mu ); + modelOut->GMMState[j].Prio = modelK->GMMState[k].Prio* modelL->GMMState[l].Prio * GaussianPDF( modelK->GMMState[k].Mu, modelL->GMMState[l].Mu, modelK->GMMState[k].Sigma + modelL->GMMState[l].Sigma); + j++; + } + } +} + +void GaussianRotate(GMMs *model, arma::vec P, arma::mat R, GMMs *modelOut) +{ + for(int s=0; s<model->nbStates; s++){ + modelOut->GMMState[s].Mu = R*model->GMMState[s].Mu + P; + modelOut->GMMState[s].Sigma = R*model->GMMState[s].Sigma*trans(R); + } + //modelOut->nbDim = model->nbDim; + //modelOut->nbStates = model->nbStates; +} +*/ diff --git a/source/RobotAPI/libraries/DSControllers/Gaussians.h b/source/RobotAPI/libraries/DSControllers/Gaussians.h new file mode 100644 index 0000000000000000000000000000000000000000..b72890a832d7f81b619a24655b236daa869acbad --- /dev/null +++ b/source/RobotAPI/libraries/DSControllers/Gaussians.h @@ -0,0 +1,83 @@ +/* + * Gaussians.h + * + * Created on: Nov 19, 2011 + * Author: Seungsu KIM + */ + +#ifndef __GAUSSIANSM_H__ +#define __GAUSSIANSM_H__ + +#include "MathLib.h" + +#define GAUSSIAN_MAXIMUM_NUMBER 50 + +using namespace MathLib; + +struct GMMState +{ + Vector Mu; + Matrix Sigma; + double Prio; +}; + +struct GMMStateP +{ + Vector MuI; + Matrix SigmaII; + Matrix SigmaIIInv; + double detSigmaII; + + // for GMR + Vector muO; + Matrix SigmaIO; + Matrix SigmaIOInv; +}; + +struct GMMs +{ + unsigned int nbStates; + unsigned int nbDim; + + GMMState States[GAUSSIAN_MAXIMUM_NUMBER]; +}; + +class Gaussians +{ +private: + GMMStateP gmmpinv[GAUSSIAN_MAXIMUM_NUMBER]; + +public: + GMMs model; + + Gaussians(const char* f_mu, const char* f_sigma, const char* f_prio); + Gaussians(int nbStates, int nbDim, const char* f_mu, const char* f_sigma, const char* f_prio); + Gaussians(const int nbStates, const int nbDim, const vector<double> pri_vec, const vector<double> mu_vec, const vector<double> sig_vec); + Gaussians(GMMs* model); + + void setGMMs(GMMs* model); + + // For fast computation of GaussianPDF + Vector gfDiff, gfDiffp; + Vector gDer; + Vector gPdf; + int nbDimI; + + + void InitFastGaussians(int first_inindex, int last_inindex); + double GaussianPDFFast(int state, Vector x); + double GaussianProbFast(Vector x); + Vector GaussianDerProbFast(Vector x); + + void InitFastGMR(int first_inindex, int last_inindex, int first_outindex, int last_outindex); + void Regression(const Vector& indata, Vector& outdata, Matrix& derGMR); + void Regression(const Vector& indata, Vector& outdata); + Vector Regression(const Vector& indata); + +}; +/* +void GaussianMux(GMMs *modelK, GMMs *modelL, GMMs *modelOut); +void GaussianRotate(GMMs *model, Vector P, Matrix R, GMMs *modelOut); +*/ + +#endif //__GAUSSIANS_H__ diff --git a/source/RobotAPI/libraries/DebugDrawerConfigWidget/CMakeLists.txt b/source/RobotAPI/libraries/DebugDrawerConfigWidget/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..02e56dcab691c35572a94ac66ed4822e231d57a6 --- /dev/null +++ b/source/RobotAPI/libraries/DebugDrawerConfigWidget/CMakeLists.txt @@ -0,0 +1,35 @@ +set(LIB_NAME DebugDrawerConfigWidget) + +armarx_build_if(ArmarXGui_FOUND "ArmarXGui not available") + +armarx_find_qt(QtCore QtGui QtDesigner) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +set(LIBS + ArmarXCoreInterfaces + ArmarXCore + DebugDrawer + ${QT_LIBRARIES} +) + +set(SOURCES +./DebugDrawerConfigWidget.cpp +) +set(HEADERS +./DebugDrawerConfigWidget.h +) +set(GUI_MOC_HDRS +./DebugDrawerConfigWidget.h +) +set(GUI_UIS +./DebugDrawerConfigWidget.ui +) + +if(ArmarXGui_FOUND) + armarx_gui_library("${LIB_NAME}" "${SOURCES}" "${GUI_MOC_HDRS}" "${GUI_UIS}" "" "${LIBS}") + + # add unit tests + add_subdirectory(test) +endif() diff --git a/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.cpp b/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..504409aee28f308fddce88de06155340af1b527e --- /dev/null +++ b/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.cpp @@ -0,0 +1,160 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::DebugDrawerConfigWidget + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "DebugDrawerConfigWidget.h" +#include <QInputDialog> + +namespace armarx +{ + + DebugDrawerConfigWidget::DebugDrawerConfigWidget(const DebugDrawerComponentPtr& debugDrawer, QWidget* parent) + : QWidget(parent), + debugDrawer(debugDrawer) + { + ui.setupUi(this); + refresh(); + + connect(ui.pushButtonRefresh, &QPushButton::released, this, &DebugDrawerConfigWidget::refresh); + connect(ui.listWidgetLayerVisibility, &QListWidget::itemChanged, this, &DebugDrawerConfigWidget::onVisibilityChanged); + connect(ui.checkBoxDefaultLayerVisibility, &QCheckBox::stateChanged, this, &DebugDrawerConfigWidget::onDefaultLayerVisibilityChanged); + connect(ui.listWidgetDefaultLayerVisibility, &QListWidget::itemChanged, this, &DebugDrawerConfigWidget::onDefaultLayerVisibilityPerLayerChanged); + connect(ui.pushButtonDefaultLayerVisibilityAdd, &QPushButton::released, this, &DebugDrawerConfigWidget::onAddDefaultLayerVisibility); + connect(ui.pushButtonDefaultLayerVisibilityRemove, &QPushButton::released, this, &DebugDrawerConfigWidget::onRemoveDefaultLayerVisibility); + } + + void DebugDrawerConfigWidget::loadSettings(QSettings* settings) + { + ui.checkBoxDefaultLayerVisibility->setCheckState(settings->value(QString::fromStdString("DefaultLayerVisibility")).toBool() ? Qt::Checked : Qt::Unchecked); + settings->beginGroup("layer"); + for (const auto& key : settings->allKeys()) + { + QListWidgetItem* item = new QListWidgetItem(key, ui.listWidgetDefaultLayerVisibility); + item->setCheckState(settings->value(key).toBool() ? Qt::Checked : Qt::Unchecked); + ui.listWidgetDefaultLayerVisibility->addItem(item); + } + settings->endGroup(); + } + + void DebugDrawerConfigWidget::saveSettings(QSettings* settings) + { + settings->setValue(QString::fromStdString("DefaultLayerVisibility"), ui.checkBoxDefaultLayerVisibility->checkState() == Qt::Checked); + settings->beginGroup("layer"); + for (const auto& layerInfo : debugDrawer->getAllDefaultLayerVisibilities()) + { + settings->setValue(QString::fromStdString(layerInfo.first), layerInfo.second); + } + settings->endGroup(); + } + + void DebugDrawerConfigWidget::setDebugDrawer(const DebugDrawerComponentPtr& debugDrawer) + { + this->debugDrawer = debugDrawer; + onDefaultLayerVisibilityChanged(ui.checkBoxDefaultLayerVisibility->checkState()); + for (int i = 0; i < ui.listWidgetDefaultLayerVisibility->count(); ++i) + { + onDefaultLayerVisibilityPerLayerChanged(ui.listWidgetDefaultLayerVisibility->item(i)); + } + refresh(); + } + + DebugDrawerComponentPtr DebugDrawerConfigWidget::getDebugDrawer() const + { + return debugDrawer; + } + + void DebugDrawerConfigWidget::refresh() + { + if (!debugDrawer) + { + return; + } + ui.listWidgetLayerVisibility->clear(); + for (const auto& layerInfo : debugDrawer->layerInformation()) + { + QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(layerInfo.layerName), ui.listWidgetLayerVisibility); + item->setCheckState(layerInfo.visible ? Qt::Checked : Qt::Unchecked); + ui.listWidgetLayerVisibility->addItem(item); + } + ui.checkBoxDefaultLayerVisibility->setCheckState(debugDrawer->getDefaultLayerVisibility() ? Qt::Checked : Qt::Unchecked); + ui.listWidgetDefaultLayerVisibility->clear(); + for (const auto& layerInfo : debugDrawer->getAllDefaultLayerVisibilities()) + { + QListWidgetItem* item = new QListWidgetItem(QString::fromStdString(layerInfo.first), ui.listWidgetDefaultLayerVisibility); + item->setCheckState(layerInfo.second ? Qt::Checked : Qt::Unchecked); + ui.listWidgetDefaultLayerVisibility->addItem(item); + } + } + + void DebugDrawerConfigWidget::onVisibilityChanged(QListWidgetItem* item) + { + if (!debugDrawer) + { + return; + } + debugDrawer->enableLayerVisu(item->text().toStdString(), item->checkState() == Qt::Checked); + } + + void DebugDrawerConfigWidget::onDefaultLayerVisibilityChanged(int checkState) + { + if (!debugDrawer) + { + return; + } + debugDrawer->setDefaultLayerVisibility(checkState != Qt::Unchecked); + } + + void DebugDrawerConfigWidget::onDefaultLayerVisibilityPerLayerChanged(QListWidgetItem* item) + { + if (!debugDrawer) + { + return; + } + debugDrawer->setDefaultLayerVisibility(item->text().toStdString(), item->checkState() == Qt::Checked); + } + + void DebugDrawerConfigWidget::onAddDefaultLayerVisibility() + { + bool ok; + QString text = QInputDialog::getText(this, "Layer name", + "Layer name:", QLineEdit::Normal, + ui.listWidgetLayerVisibility->selectedItems().empty() ? "" : ui.listWidgetLayerVisibility->selectedItems().front()->text(), &ok); + if (ok && !text.isEmpty()) + { + QListWidgetItem* item = new QListWidgetItem(text, ui.listWidgetDefaultLayerVisibility); + item->setCheckState(debugDrawer->getDefaultLayerVisibility() ? Qt::Checked : Qt::Unchecked); + ui.listWidgetDefaultLayerVisibility->addItem(item); + } + } + + void DebugDrawerConfigWidget::onRemoveDefaultLayerVisibility() + { + for (const auto& selected : ui.listWidgetDefaultLayerVisibility->selectedItems()) + { + if (debugDrawer) + { + debugDrawer->removeDefaultLayerVisibility(selected->text().toStdString()); + } + ui.listWidgetDefaultLayerVisibility->takeItem(ui.listWidgetDefaultLayerVisibility->row(selected)); + } + } + +} diff --git a/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.h b/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..6680464000af1a9706c765955979dc18ea8d297c --- /dev/null +++ b/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.h @@ -0,0 +1,73 @@ +/* + * 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::DebugDrawerConfigWidget + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/components/DebugDrawer/DebugDrawerComponent.h> + +#include <RobotAPI/libraries/DebugDrawerConfigWidget/ui_DebugDrawerConfigWidget.h> + +#include <QWidget> +#include <QSettings> + + +namespace armarx +{ + /** + * @defgroup Library-DebugDrawerConfigWidget DebugDrawerConfigWidget + * @ingroup RobotAPI + * A description of the library DebugDrawerConfigWidget. + * + * @class DebugDrawerConfigWidget + * @ingroup Library-DebugDrawerConfigWidget + * @brief Brief description of class DebugDrawerConfigWidget. + * + * Detailed description of class DebugDrawerConfigWidget. + */ + class DebugDrawerConfigWidget : public QWidget + { + public: + DebugDrawerConfigWidget(const DebugDrawerComponentPtr& debugDrawer, QWidget* parent = nullptr); + + void loadSettings(QSettings* settings); + void saveSettings(QSettings* settings); + + void setDebugDrawer(const DebugDrawerComponentPtr& debugDrawer); + DebugDrawerComponentPtr getDebugDrawer() const; + + public slots: + void refresh(); + + void onVisibilityChanged(QListWidgetItem* item); + + void onDefaultLayerVisibilityChanged(int checkState); + void onDefaultLayerVisibilityPerLayerChanged(QListWidgetItem* item); + + void onAddDefaultLayerVisibility(); + void onRemoveDefaultLayerVisibility(); + + protected: + DebugDrawerComponentPtr debugDrawer; + Ui::DebugDrawerConfigWidget ui; + }; + +} diff --git a/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.ui b/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.ui new file mode 100644 index 0000000000000000000000000000000000000000..0fe625be17d0592a87153ce1b8873307bdf722f9 --- /dev/null +++ b/source/RobotAPI/libraries/DebugDrawerConfigWidget/DebugDrawerConfigWidget.ui @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>DebugDrawerConfigWidget</class> + <widget class="QWidget" name="DebugDrawerConfigWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>421</width> + <height>236</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QGroupBox" name="groupBox"> + <property name="styleSheet"> + <string notr="true">QGroupBox{border:2px solid gray;border-radius:5px;margin-top: 1ex;} QGroupBox::title{subcontrol-origin: margin;subcontrol-position:top center;padding:0 3px;}</string> + </property> + <property name="title"> + <string>DebugDrawer configuration</string> + </property> + <layout class="QFormLayout" name="formLayout_2"> + <item row="3" column="0"> + <widget class="QLabel" name="labelLayerVisibility"> + <property name="text"> + <string>Layer visibility</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="labelDefaultLayerVisibility"> + <property name="text"> + <string>Default layer visibility</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QListWidget" name="listWidgetLayerVisibility"/> + </item> + <item row="4" column="1"> + <widget class="QCheckBox" name="checkBoxDefaultLayerVisibility"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QPushButton" name="pushButtonRefresh"> + <property name="text"> + <string>Refresh</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QListWidget" name="listWidgetDefaultLayerVisibility"/> + </item> + <item row="5" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="pushButtonDefaultLayerVisibilityAdd"> + <property name="text"> + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButtonDefaultLayerVisibilityRemove"> + <property name="text"> + <string>Remove</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/source/RobotAPI/libraries/DebugDrawerConfigWidget/test/CMakeLists.txt b/source/RobotAPI/libraries/DebugDrawerConfigWidget/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..dc326fdc7c93ba3e1bb38bd4f2316ee4aa4bc530 --- /dev/null +++ b/source/RobotAPI/libraries/DebugDrawerConfigWidget/test/CMakeLists.txt @@ -0,0 +1,5 @@ + +# Libs required for the tests +SET(LIBS ${LIBS} ArmarXCore DebugDrawerConfigWidget) + +armarx_add_test(DebugDrawerConfigWidgetTest DebugDrawerConfigWidgetTest.cpp "${LIBS}") \ No newline at end of file diff --git a/source/RobotAPI/libraries/DebugDrawerConfigWidget/test/DebugDrawerConfigWidgetTest.cpp b/source/RobotAPI/libraries/DebugDrawerConfigWidget/test/DebugDrawerConfigWidgetTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e10bd7c4b3ba362cb6ef5dab600b0660351f54d8 --- /dev/null +++ b/source/RobotAPI/libraries/DebugDrawerConfigWidget/test/DebugDrawerConfigWidgetTest.cpp @@ -0,0 +1,36 @@ +/* + * 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::DebugDrawerConfigWidget + * @author Adrian Knobloch ( adrian dot knobloch at student dot kit dot edu ) + * @date 2019 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#define BOOST_TEST_MODULE RobotAPI::ArmarXLibraries::DebugDrawerConfigWidget + +#define ARMARX_BOOST_TEST + +#include <RobotAPI/Test.h> +#include "../DebugDrawerConfigWidget.h" + +#include <iostream> + +BOOST_AUTO_TEST_CASE(testExample) +{ + + BOOST_CHECK_EQUAL(true, true); +} diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.cpp index 6ed80f25a5b54fe6de3e8d7d0e871971f82aa7cc..3bbf673c04a8e6bb1fdaf9d921ad713eeaa65bfa 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.cpp @@ -6,10 +6,21 @@ namespace armarx JointKITGripperEmergencyStopController::JointKITGripperEmergencyStopController(ActorDataPtr dataPtr) : dataPtr(dataPtr) { - + pid.reset(new PIDController(0, 5000, 0)); + pid->maxIntegral = 0.1; } void JointKITGripperEmergencyStopController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { + pid->update(timeSinceLastIteration.toSecondsDouble(), dataPtr->getVelocity(), 0.0); + float targetPwm = pid->getControlValue(); + if (std::isnan(targetPwm)) + { + targetPwm = 0.0f; + } + // dataPtr->setTargetPWM(targetPwm); + lastPWM *= 0.9997; + // ARMARX_RT_LOGF_INFO("old pwm %d, new pwm: %.2f", dataPtr->getTargetPWM(), lastPWM); + dataPtr->setTargetPWM(lastPWM); } @@ -21,7 +32,8 @@ namespace armarx void JointKITGripperEmergencyStopController::rtPreActivateController() { // ARMARX_INFO << "Stopping gripper!"; - dataPtr->setTargetPWM(0); + // dataPtr->setTargetPWM(0); + lastPWM = math::MathUtils::LimitTo(dataPtr->getTargetPWM(), 500); } diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.h index 043b63f39628f90f50d68ba4599403350b6c389c..df1c76a4a52997ba35bddfdd15dc7a539b537b20 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperEmergencyStopController.h @@ -1,6 +1,7 @@ #pragma once #include <RobotAPI/components/units/RobotUnit/JointControllers/JointController.h> +#include <RobotAPI/libraries/core/PIDController.h> #include "../KITGripperBasisBoardData.h" @@ -28,6 +29,8 @@ namespace armarx private: DummyControlTargetEmergencyStop target; ActorDataPtr dataPtr; + PIDControllerPtr pid; + float lastPWM = 0.0f; }; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperPWMPassThroughController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperPWMPassThroughController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5e5441eaaf750affbce122d8f65a24fe94a7262f --- /dev/null +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperPWMPassThroughController.cpp @@ -0,0 +1,29 @@ +#include "JointKITGripperPWMPassThroughController.h" +#include <boost/algorithm/clamp.hpp> +#include "../KITGripperBasisBoard.h" + +namespace armarx +{ + ControlTargetBase* JointKITGripperPWMPassThroughController::getControlTarget() + { + return ⌖ + } + + JointKITGripperPWMPassThroughController::JointKITGripperPWMPassThroughController(const std::string deviceName, ActorDataPtr jointData) : + jointData(jointData) + {} + + void JointKITGripperPWMPassThroughController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + if (target.isValid()) + { + jointData->setTargetPWM(target.current * 1000); + } + } + + void JointKITGripperPWMPassThroughController::rtPreActivateController() + { + jointData->setTargetPWM(0); + + } +} diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperPWMPassThroughController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperPWMPassThroughController.h new file mode 100644 index 0000000000000000000000000000000000000000..9a367bfec1e2af2d9d4223b0246e2f91d35c734f --- /dev/null +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointKITGripperPWMPassThroughController.h @@ -0,0 +1,31 @@ +#pragma once + +#include <RobotAPI/components/units/RobotUnit/JointControllers/JointController.h> +#include "../KITGripperBasisBoardData.h" +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> + +namespace armarx +{ + + class JointKITGripperPWMPassThroughController; + typedef std::shared_ptr<JointKITGripperPWMPassThroughController> JointKITGripperPWMPassThroughControllerPtr; + + class JointKITGripperPWMPassThroughController : public armarx::JointController + { + public: + JointKITGripperPWMPassThroughController(const std::string deviceName, ActorDataPtr jointData); + + // JointController interface + ControlTargetBase* getControlTarget() override; + protected: + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override; + private: + ControlTarget1DoFActuatorCurrent target; + ActorDataPtr jointData; + + // JointController interface + protected: + void rtPreActivateController() override; + }; + +} diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp index 2daabdda37d71cd46eb11e48fcbadddebfd432f7..e4bf73b8cad298cb7909024d8d2b18f6452e90b7 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.cpp @@ -5,7 +5,7 @@ #include "../KITGripperBasisBoard.h" #include <RobotAPI/components/units/RobotUnit/util/ControlThreadOutputBuffer.h> #include <ArmarXCore/core/ManagedIceObject.h> - +#include <boost/algorithm/clamp.hpp> #include <ArmarXGui/libraries/RemoteGui/WidgetProxy.h> #include <ArmarXGui/libraries/RemoteGui/WidgetBuilder.h> @@ -14,17 +14,21 @@ #include <RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleDevices.h> +#include <ArmarXCore/observers/filters/rtfilters/AverageFilter.h> + using namespace armarx; JointPWMPositionController::JointPWMPositionController(const std::string deviceName, KITGripperBasisBoardPtr board, ActorDataPtr jointData, - PWMVelocityControllerConfigurationCPtr velocityControllerConfigDataPtr) : JointController(), - config(velocityControllerConfigDataPtr), - controller(velocityControllerConfigDataPtr), + PWMPositionControllerConfigurationCPtr positionControllerConfigDataPtr) : JointController(), + config(positionControllerConfigDataPtr), + // controller(positionControllerConfigDataPtr), target(), board(board), deviceName(deviceName) { actorIndex = board->getActorIndex(deviceName); + sensorValue = board->getDevices().at(actorIndex)->getSensorValue()->asA<SensorValue1DoFActuator>(); + ARMARX_CHECK_EXPRESSION_W_HINT(sensorValue, deviceName); dataPtr = jointData; // add limitless joint controller to solve the flipping sensor value problem @@ -43,18 +47,25 @@ JointPWMPositionController::JointPWMPositionController(const std::string deviceN // ARMARX_INFO << "maxDt: " << limitlessController.maxDt << ", maxV: " << limitlessController.maxV; // ARMARX_INFO << "acceleration: " << limitlessController.acceleration << ", deceleration: " << limitlessController.deceleration; - posController.acceleration = velocityControllerConfigDataPtr->maxAccelerationRad; - posController.deceleration = velocityControllerConfigDataPtr->maxDecelerationRad; - posController.maxDt = velocityControllerConfigDataPtr->maxDt; - posController.maxV = velocityControllerConfigDataPtr->maxVelocityRad; + posController.desiredDeceleration = positionControllerConfigDataPtr->maxDecelerationRad; + posController.desiredJerk = 100; + posController.maxDt = positionControllerConfigDataPtr->maxDt; + posController.maxV = positionControllerConfigDataPtr->maxVelocityRad; + posController.p = 4; + posController.phase2SwitchDistance = 0.1; ARMARX_CHECK_GREATER_EQUAL(jointData->getSoftLimitHi(), jointData->getSoftLimitLo()); // controller.positionLimitHiHard = dataPtr->getHardLimitHi(); - posController.positionLimitHi = jointData->getSoftLimitHi(); + // posController.positionLimitHi = jointData->getSoftLimitHi(); // controller.positionLimitLoHard = dataPtr->getHardLimitLo(); - posController.positionLimitLo = jointData->getSoftLimitLo(); - posController.p = velocityControllerConfigDataPtr->posControlP; //3.0f; + // posController.positionLimitLo = jointData->getSoftLimitLo(); + // posController.pControlPosErrorLimit = 0.02f; + // posController.pid->Kp = posController.calculateProportionalGain(); + // ARMARX_IMPORTANT << "position Kp " << posController.pid->Kp; + this->isLimitless = jointData->isLimitless(); - // ARMARX_INFO << "limitless: " << this->isLimitless; + pidPosController.reset(new PIDController(positionControllerConfigDataPtr->p, positionControllerConfigDataPtr->i, positionControllerConfigDataPtr->d)); + pidPosController->maxIntegral = positionControllerConfigDataPtr->maxIntegral; + pidPosController->differentialFilter.reset(new rtfilters::AverageFilter(10)); } JointPWMPositionController::~JointPWMPositionController() noexcept(true) @@ -88,13 +99,16 @@ void JointPWMPositionController::rtRun(const IceUtil::Time& sensorValuesTimestam } else { - posController.currentPosition = currentPosition; - posController.currentV = lastTargetVelocity; - posController.dt = timeSinceLastIteration.toSecondsDouble(); - posController.targetPosition = target.position; - newVel = posController.run(); - } - + // posController.currentPosition = currentPosition; + posController.currentV = lastTargetVelocity; + posController.dt = timeSinceLastIteration.toSecondsDouble(); + posController.setTargetPosition(target.position); + // ARMARX_CHECK_EXPRESSION(posController.validParameters()); + auto r = posController.run(); + posController.currentPosition = r.position; + posController.currentAcc = r.acceleration; + newVel = r.velocity; + } // double newVel = posController.p * (posController.targetPosition - posController.currentPosition); // newVel = math::MathUtils::LimitTo(newVel, posController.maxV); // ARMARX_INFO << deactivateSpam(1) << VAROUT(newVel); @@ -102,20 +116,42 @@ void JointPWMPositionController::rtRun(const IceUtil::Time& sensorValuesTimestam { newVel = 0; } + pidPosController->update(timeSinceLastIteration.toSecondsDouble(), currentPosition, r.position); + auto targetPWM = pidPosController->getControlValue(); + // auto targetPWM = static_cast<int>(controller.run(timeSinceLastIteration, dataPtr->getVelocity(), newVel)); + newVel = math::MathUtils::LimitTo(newVel, posController.maxV); + + float torqueFF = config->feedforwardTorqueToPWMFactor * -sensorValue->gravityTorque; + targetPWM += torqueFF; + targetPWM += newVel * config->feedforwardVelocityToPWMFactor; - auto targetPWM = static_cast<int>(controller.run(timeSinceLastIteration, dataPtr->getVelocity(), newVel)); + // ARMARX_INFO << deactivateSpam(0.1) << VAROUT(pidPosController->previousError) << VAROUT(r.acceleration) + // << VAROUT(target.position) << VAROUT(targetPWM) << VAROUT(pidPosController->Kp) << VAROUT(pidPosController->Ki) + // << VAROUT(torqueFF); if (std::isnan(targetPWM)) { - throw LocalException() << "Target PWM of " << getParent().getDeviceName() << " is NaN!"; + ARMARX_ERROR << deactivateSpam(1) << "Target PWM of " << getParent().getDeviceName() << " is NaN!"; + targetPWM = 0.0f; + } + float updateRatio = 0.3; + this->targetPWM = (1.0 - updateRatio) * this->targetPWM + updateRatio * targetPWM; + float pwmDiff = std::abs(dataPtr->getTargetPWM() - targetPWM); + // ARMARX_INFO << deactivateSpam(0.3) << VAROUT(pwmDiff) << VAROUT(dataPtr->getTargetPWM()) << VAROUT(targetPWM) << VAROUT(dataPtr->getVelocity()); + if (pwmDiff > 5 || dataPtr->getVelocity() > 0.01) // avoid jittering when standing still + { + // ARMARX_INFO << deactivateSpam(0.0, std::to_string(targetPWM)) << "Setting new targetPWM to" << targetPWM << " diff: " << pwmDiff << " vel: " << dataPtr->getVelocity(); + dataPtr->setTargetPWM(targetPWM); } - dataPtr->setTargetPWM(targetPWM); + this->targetPWM = targetPWM; lastTargetVelocity = newVel; // auto name = getParent().getDeviceName().c_str(); // ARMARX_RT_LOGF_INFO("%s: position: %.f, target position: %.f, targetvelocity: %.f, target PWM: %d", name, - // currentPosition, target.position, newVel, targetPWM).deactivateSpam(1); - // ARMARX_INFO << deactivateSpam(1) << VAROUT(name) << VAROUT(currentPosition) << VAROUT(target.position) << VAROUT(newVel) << VAROUT(targetPWM); + // currentPosition, targetPosition, newVel, targetPWM).deactivateSpam(1); + // ARMARX_INFO << deactivateSpam(1) << VAROUT(name) << VAROUT(currentPosition) << VAROUT(targetPosition) << VAROUT(newVel) << VAROUT(targetPWM); + + } else { @@ -130,14 +166,19 @@ ControlTargetBase* JointPWMPositionController::getControlTarget() void JointPWMPositionController::rtPreActivateController() { + targetPWM = 0.0f; lastTargetVelocity = dataPtr->getVelocity(); - controller.reset(dataPtr->getVelocity()); + posController.currentAcc = dataPtr->getAcceleration(); + posController.currentPosition = dataPtr->getPosition(); + posController.currentV = dataPtr->getVelocity(); + pidPosController->reset(); + // controller.reset(dataPtr->getVelocity()); } void JointPWMPositionController::rtPostDeactivateController() { - ARMARX_RT_LOGF_INFO("Setting PWM to 0"); - dataPtr->setTargetPWM(0); + // ARMARX_RT_LOGF_INFO("Setting PWM to 0"); + // dataPtr->setTargetPWM(0); } StringVariantBaseMap JointPWMPositionController::publish(const DebugDrawerInterfacePrx& draw, const DebugObserverInterfacePrx& observer) const @@ -145,118 +186,132 @@ StringVariantBaseMap JointPWMPositionController::publish(const DebugDrawerInterf if (!remoteGui) { - // threadHandle = Application::getInstance()->getThreadPool()->runTask([this] - // { - // std::string guiTabName; - // while (!stopRequested) - // { - // ManagedIceObjectPtr object; - // ARMARX_IMPORTANT << deactivateSpam(1) << "Trying to get parent"; - // try - // { - // object = ManagedIceObjectPtr::dynamicCast(getParent().getOwner()); - // ARMARX_CHECK_EXPRESSION(object); - // remoteGui = object->getProxy<RemoteGuiInterfacePrx>("RemoteGuiProvider", false, "", false); - // if (!remoteGui) - // { - // continue; - // } - // ARMARX_IMPORTANT << deactivateSpam(1) << "Got Proxy"; - // guiTabName = getParent().getDeviceName() + getControlMode(); - // break; - // } - // catch (...) - // { - // sleep(1); - // } - - // } - // if (remoteGui) - // { - // ARMARX_IMPORTANT << "Creating GUI " << guiTabName; - // using namespace RemoteGui; - - - - // auto vLayout = makeVBoxLayout(); - - // { - // WidgetPtr KpLabel = makeTextLabel("Kp: "); - - // WidgetPtr KiSlider = makeFloatSlider("KpSlider") - // .min(0.0f).max(5000.0f) - // .value(config->p); - // WidgetPtr line = makeHBoxLayout() - // .children({KpLabel, KiSlider}); - - // vLayout.addChild(line); - - // } - - - // { - // WidgetPtr KiLabel = makeTextLabel("Ki: "); - // WidgetPtr KiSlider = makeFloatSlider("KiSlider") - // .min(0.0f).max(50000.0f) - // .value(config->i); - - // WidgetPtr line = makeHBoxLayout() - // .children({KiLabel, KiSlider}); - - // vLayout.addChild(line); - - // } - - // { - // WidgetPtr KdLabel = makeTextLabel("Kd: "); - // WidgetPtr KdSlider = makeFloatSlider("KdSlider") - // .min(0.0f).max(50.0f) - // .steps(100) - // .value(config->d); - - // WidgetPtr line = makeHBoxLayout() - // .children({KdLabel, KdSlider}); - - // vLayout.addChild(line); - // vLayout.addChild(new VSpacer); - // } - - // // WidgetPtr spin = makeFloatSpinBox("KpSpin") - // // .min(0.0f).max(2.0f) - // // .steps(20).decimals(2) - // // .value(0.4f); - - - - - // WidgetPtr groupBox = makeGroupBox("GroupBox") - // .label("Group") - // .child(vLayout); - - // remoteGui->createTab(guiTabName, groupBox); - - // while (!stopRequested) - // { - // RemoteGui::TabProxy tab(remoteGui, guiTabName); - // tab.receiveUpdates(); - // this->controller.pid->Kp = tab.getValue<float>("KpSlider").get(); - // this->controller.pid->Ki = tab.getValue<float>("KiSlider").get(); - // this->controller.pid->Kd = tab.getValue<float>("KdSlider").get(); - // usleep(100000); - // } - // } - - // }); + threadHandle = Application::getInstance()->getThreadPool()->runTask([this] + { + std::string guiTabName; + while (!stopRequested) + { + ManagedIceObjectPtr object; + ARMARX_IMPORTANT << deactivateSpam(1) << "Trying to get parent"; + try + { + object = ManagedIceObjectPtr::dynamicCast(getParent().getOwner()); + ARMARX_CHECK_EXPRESSION(object); + remoteGui = object->getProxy<RemoteGuiInterfacePrx>("RemoteGuiProvider", false, "", false); + if (!remoteGui) + { + continue; + } + ARMARX_IMPORTANT << deactivateSpam(1) << "Got Proxy"; + guiTabName = getParent().getDeviceName() + getControlMode(); + break; + } + catch (...) + { + sleep(1); + } + + } + if (remoteGui) + { + ARMARX_IMPORTANT << "Creating GUI " << guiTabName; + using namespace RemoteGui; + + + + auto vLayout = makeVBoxLayout(); + + { + WidgetPtr KpLabel = makeTextLabel("Kp: "); + + WidgetPtr KpSlider = makeFloatSlider("KpSlider") + .min(0.0f).max(pidPosController->Kp * 5) + .value(pidPosController->Kp); + WidgetPtr KpLabelValue = makeTextLabel(std::to_string(pidPosController->Kp * 5)); + WidgetPtr line = makeHBoxLayout() + .children({KpLabel, KpSlider, KpLabelValue}); + + vLayout.addChild(line); + + } + + + { + WidgetPtr KiLabel = makeTextLabel("Ki: "); + WidgetPtr KiSlider = makeFloatSlider("KiSlider") + .min(0.0f).max(pidPosController->Ki * 5) + .value(pidPosController->Ki); + WidgetPtr KiLabelValue = makeTextLabel(std::to_string(pidPosController->Ki * 5)); + + WidgetPtr line = makeHBoxLayout() + .children({KiLabel, KiSlider, KiLabelValue}); + + vLayout.addChild(line); + + } + + { + WidgetPtr KdLabel = makeTextLabel("Kd: "); + WidgetPtr KdSlider = makeFloatSlider("KdSlider") + .min(-10.0f * pidPosController->Kd).max(10.0f * pidPosController->Kd) + .steps(1000) + .value(pidPosController->Kd); + WidgetPtr KdLabelValue = makeTextLabel(std::to_string(pidPosController->Kd * 10)); + + WidgetPtr line = makeHBoxLayout() + .children({KdLabel, KdSlider, KdLabelValue}); + + vLayout.addChild(line); + vLayout.addChild(new VSpacer); + } + + // WidgetPtr spin = makeFloatSpinBox("KpSpin") + // .min(0.0f).max(2.0f) + // .steps(20).decimals(2) + // .value(0.4f); + + + + + WidgetPtr groupBox = makeGroupBox("GroupBox") + .label("Group") + .child(vLayout); + + remoteGui->createTab(guiTabName, groupBox); + + while (!stopRequested) + { + RemoteGui::TabProxy tab(remoteGui, guiTabName); + tab.receiveUpdates(); + // this->controller.pid->Kp = tab.getValue<float>("KpSlider").get(); + // this->controller.pid->Ki = tab.getValue<float>("KiSlider").get(); + // this->controller.pid->Kd = tab.getValue<float>("KdSlider").get(); + pidPosController->Kp = tab.getValue<float>("KpSlider").get(); + pidPosController->Ki = tab.getValue<float>("KiSlider").get(); + pidPosController->Kd = tab.getValue<float>("KdSlider").get(); + usleep(100000); + } + } + + }); } return {{"lastTargetVelocity", new Variant(lastTargetVelocity.load())}, - {"targetPosition", new Variant(posController.targetPosition)}, - {"posError", new Variant(posController.targetPosition - posController.currentPosition)}, - {"pidError", new Variant(controller.pid->previousError)}, - {"filteredVelocity", new Variant(controller.lastActualVelocity.load())}, - {"pidIntegralCV", new Variant(controller.pid->integral * controller.pid->Ki)}, - {"pidPropCV", new Variant(controller.pid->previousError * controller.pid->Kp)}, - {"pidDiffCV", new Variant(controller.pid->derivative * controller.pid->Kd)}, - {"pidUsed", new Variant(posController.getCurrentlyPIDActive())} + {"targetPosition", new Variant(posController.currentPosition)}, // position of profile generator is target position + {"posError", new Variant(posController.getTargetPosition() - posController.currentPosition)}, + {"pidError", new Variant(pidPosController->previousError)}, + // {"filteredVelocity", new Variant(controller.lastActualVelocity.load())}, + {"pidIntegralCV", new Variant(pidPosController->integral * pidPosController->Ki)}, + {"pidIntegral", new Variant(pidPosController->integral)}, + {"pidPropCV", new Variant(pidPosController->previousError * pidPosController->Kp)}, + {"pidDiffCV", new Variant(pidPosController->derivative * pidPosController->Kd)}, + // {"pospidIntegralCV", new Variant(posController.pid->integral * posController.pid->Ki)}, + // {"pospidIntegral", new Variant(posController.pid->integral)}, + // {"pospidPropCV", new Variant(posController.pid->previousError * posController.pid->Kp)}, + // {"pospidDiffCV", new Variant(posController.pid->derivative * posController.pid->Kd)}, + // {"pidUsed", new Variant(posController.getCurrentlyPIDActive())}, + {"desiredPWM", new Variant(targetPWM.load())} + + }; } @@ -264,3 +319,25 @@ StringVariantBaseMap JointPWMPositionController::publish(const DebugDrawerInterf + +PWMPositionControllerConfigurationCPtr PWMPositionControllerConfiguration::CreatePWMPositionControllerConfigDataFromXml(DefaultRapidXmlReaderNode node) +{ + PWMPositionControllerConfiguration configData; + + configData.maxVelocityRad = node.first_node("maxVelocityRad").value_as_float(); + configData.maxAccelerationRad = node.first_node("maxAccelerationRad").value_as_float(); + configData.maxDecelerationRad = node.first_node("maxDecelerationRad").value_as_float(); + configData.maxDt = node.first_node("maxDt").value_as_float(); + configData.p = node.first_node("p").value_as_float(); + configData.i = node.first_node("i").value_as_float(); + configData.d = node.first_node("d").value_as_float(); + configData.maxIntegral = node.first_node("maxIntegral").value_as_float(); + configData.feedforwardVelocityToPWMFactor = node.first_node("feedforwardVelocityToPWMFactor").value_as_float(); + configData.feedforwardTorqueToPWMFactor = node.first_node("feedforwardTorqueToPWMFactor").value_as_float(); + configData.PWMDeadzone = node.first_node("PWMDeadzone").value_as_float(); + configData.velocityUpdatePercent = node.first_node("velocityUpdatePercent").value_as_float(); + configData.conditionalIntegralErrorTreshold = node.first_node("conditionalIntegralErrorTreshold").value_as_float(); + configData.feedForwardMode = node.first_node("FeedForwardMode").value_as_bool("1", "0"); + + return std::make_shared<PWMPositionControllerConfiguration>(configData); +} diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h index 62818578f3ba3db58f0158fc3e330d2d31f67a36..d409d57690acf87aca4164892b02b4888a4ec2eb 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMPositionController.h @@ -19,6 +19,30 @@ namespace armarx { using KITGripperBasisBoardPtr = std::shared_ptr<class KITGripperBasisBoard>; + typedef std::shared_ptr<class PWMPositionControllerConfiguration> PWMPositionControllerConfigurationPtr; + typedef std::shared_ptr<const PWMPositionControllerConfiguration> PWMPositionControllerConfigurationCPtr; + + + class PWMPositionControllerConfiguration + { + public: + PWMPositionControllerConfiguration() {} + static PWMPositionControllerConfigurationCPtr CreatePWMPositionControllerConfigDataFromXml(DefaultRapidXmlReaderNode node); + float maxVelocityRad; + float maxAccelerationRad; + float maxDecelerationRad; + float maxDt; + float p; + float i; + float d; + float maxIntegral; + float feedforwardVelocityToPWMFactor; + float feedforwardTorqueToPWMFactor; + float PWMDeadzone; + float velocityUpdatePercent; + float conditionalIntegralErrorTreshold; + bool feedForwardMode; + }; class JointPWMPositionController; @@ -27,7 +51,7 @@ namespace armarx class JointPWMPositionController : public JointController { public: - JointPWMPositionController(const std::string deviceName, KITGripperBasisBoardPtr board, ActorDataPtr jointData, PWMVelocityControllerConfigurationCPtr velocityControllerConfigDataPtr); + JointPWMPositionController(const std::string deviceName, KITGripperBasisBoardPtr board, ActorDataPtr jointData, PWMPositionControllerConfigurationCPtr positionControllerConfigDataPtr); ~JointPWMPositionController() noexcept(true); void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override ; @@ -35,14 +59,15 @@ namespace armarx void rtPreActivateController() override; protected: - PWMVelocityControllerConfigurationCPtr config; - PWMVelocityController controller; - PositionThroughVelocityControllerWithAccelerationAndPositionBounds posController; + PWMPositionControllerConfigurationCPtr config; + // PWMVelocityController controller; + // PositionThroughVelocityControllerWithAccelerationAndPositionBounds posController; + MinJerkPositionController posController; + PIDControllerPtr pidPosController; PositionThroughVelocityControllerWithAccelerationBoundsAndPeriodicPosition limitlessController; - ControlTarget1DoFActuatorPosition target; - std::atomic<double> lastTargetVelocity; + std::atomic<double> lastTargetVelocity, targetPWM; bool isLimitless; ActorDataPtr dataPtr; @@ -52,6 +77,7 @@ namespace armarx mutable RemoteGuiInterfacePrx remoteGui; bool stopRequested = false; mutable ThreadPool::Handle threadHandle; + const SensorValue1DoFActuator* sensorValue; // JointController interface protected: void rtPostDeactivateController() override; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.cpp index 6f289e055d87d9a8894ac8e2ad9c945f16b0fba6..67da106534cb1255002b9d88197ea2f4854ace72 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.cpp @@ -24,10 +24,13 @@ JointPWMVelocityController::JointPWMVelocityController(const std::string deviceN target(), board(board), deviceName(deviceName) { actorIndex = board->getActorIndex(deviceName); + sensorValue = board->getDevices().at(actorIndex)->getSensorValue()->asA<SensorValue1DoFActuator>(); + ARMARX_CHECK_EXPRESSION_W_HINT(sensorValue, deviceName); dataPtr = jointData; - velController.acceleration = velocityControllerConfigDataPtr->maxAccelerationRad; + // velController.acceleration = velocityControllerConfigDataPtr->maxAccelerationRad; velController.deceleration = velocityControllerConfigDataPtr->maxDecelerationRad; + velController.jerk = 30; velController.maxDt = velocityControllerConfigDataPtr->maxDt; velController.maxV = velocityControllerConfigDataPtr->maxVelocityRad; velController.directSetVLimit = velocityControllerConfigDataPtr->directSetVLimit; @@ -69,27 +72,31 @@ void JointPWMVelocityController::rtRun(const IceUtil::Time& sensorValuesTimestam velController.currentPosition = currentPosition; } velController.currentV = lastTargetVelocity; + velController.currentAcc = lastTargetAcceleration; velController.dt = timeSinceLastIteration.toSecondsDouble(); velController.targetV = target.velocity; - double newVel = velController.run(); - ARMARX_DEBUG << deactivateSpam(1) << VAROUT(newVel) << VAROUT(target.velocity); + auto r = velController.run(); + double newVel = r.velocity; + double newAcc = r.acceleration; if (std::isnan(newVel)) { newVel = 0; + newAcc = 0; } if ((currentPosition > velController.positionLimitHiSoft && target.velocity > 0) || (currentPosition < velController.positionLimitLoSoft && target.velocity < 0)) { newVel = 0; + newAcc = 0; ARMARX_INFO << deactivateSpam(1) << "Breaking now at " << dataPtr->getPosition() << " pwm: " << dataPtr->getTargetPWM(); } - auto targetPWM = static_cast<int>(controller.run(timeSinceLastIteration, dataPtr->getVelocity(), newVel)); - ARMARX_DEBUG << deactivateSpam(1) << "Target PWM: " << targetPWM; + auto targetPWM = static_cast<int>(controller.run(timeSinceLastIteration, dataPtr->getVelocity(), newVel, sensorValue->gravityTorque)); dataPtr->setTargetPWM(targetPWM); lastTargetVelocity = newVel; + lastTargetAcceleration = newAcc; // ARMARX_RT_LOGF_INFO("target velocity: %.3f, current velocity: %.3f, target pwm: %d, kp: %.3f ki: %f, kd: %f, max acc: %.3f", // target.velocity, dataPtr->getVelocity(), targetPWM, pid->Kp, pid->Ki, pid->Kd, controller.acceleration).deactivateSpam(1); @@ -110,13 +117,14 @@ ControlTargetBase* JointPWMVelocityController::getControlTarget() void JointPWMVelocityController::rtPreActivateController() { lastTargetVelocity = dataPtr->getVelocity(); + lastTargetAcceleration = dataPtr->getAcceleration(); controller.reset(dataPtr->getVelocity()); } void JointPWMVelocityController::rtPostDeactivateController() { - ARMARX_RT_LOGF_INFO("Setting PWM to 0"); - dataPtr->setTargetPWM(0); + // ARMARX_RT_LOGF_INFO("Setting PWM to 0"); + // dataPtr->setTargetPWM(0); } StringVariantBaseMap JointPWMVelocityController::publish(const DebugDrawerInterfacePrx& draw, const DebugObserverInterfacePrx& observer) const @@ -124,111 +132,114 @@ StringVariantBaseMap JointPWMVelocityController::publish(const DebugDrawerInterf if (!remoteGui && !threadHandle.isValid()) { - // threadHandle = Application::getInstance()->getThreadPool()->runTask([this] - // { - // std::string guiTabName; - // while (!stopRequested) - // { - // ManagedIceObjectPtr object; - // ARMARX_IMPORTANT << deactivateSpam(1) << "Trying to get parent"; - // try - // { - // object = ManagedIceObjectPtr::dynamicCast(getParent().getOwner()); - // ARMARX_CHECK_EXPRESSION(object); - // remoteGui = object->getProxy<RemoteGuiInterfacePrx>("RemoteGuiProvider", false, "", false); - // if (!remoteGui) - // { - // return; - // } - // ARMARX_IMPORTANT << deactivateSpam(1) << "Got Proxy"; - // guiTabName = getParent().getDeviceName() + getControlMode(); - // break; - // } - // catch (...) - // { - // handleExceptions(); - // sleep(1); - // } - - // } - // if (remoteGui) - // { - // ARMARX_IMPORTANT << "Creating GUI " << guiTabName; - // using namespace RemoteGui; - - - - // auto vLayout = makeVBoxLayout(); - - // { - // WidgetPtr KpLabel = makeTextLabel("Kp: "); - - // WidgetPtr KiSlider = makeFloatSlider("KpSlider") - // .min(0.0f).max(5000.0f) - // .value(config->p); - // WidgetPtr line = makeHBoxLayout() - // .children({KpLabel, KiSlider}); - - // vLayout.addChild(line); - - // } - - - // { - // WidgetPtr KiLabel = makeTextLabel("Ki: "); - // WidgetPtr KiSlider = makeFloatSlider("KiSlider") - // .min(0.0f).max(50000.0f) - // .value(config->i); - - // WidgetPtr line = makeHBoxLayout() - // .children({KiLabel, KiSlider}); - - // vLayout.addChild(line); - - // } - - // { - // WidgetPtr KdLabel = makeTextLabel("Kd: "); - // WidgetPtr KdSlider = makeFloatSlider("KdSlider") - // .min(0.0f).max(50.0f) - // .steps(100) - // .value(config->d); - - // WidgetPtr line = makeHBoxLayout() - // .children({KdLabel, KdSlider}); - - // vLayout.addChild(line); - // vLayout.addChild(new VSpacer); - // } - - // // WidgetPtr spin = makeFloatSpinBox("KpSpin") - // // .min(0.0f).max(2.0f) - // // .steps(20).decimals(2) - // // .value(0.4f); - - - - - // WidgetPtr groupBox = makeGroupBox("GroupBox") - // .label("Group") - // .child(vLayout); - - // remoteGui->createTab(guiTabName, groupBox); - - // while (!stopRequested) - // { - // RemoteGui::TabProxy tab(remoteGui, guiTabName); - // tab.receiveUpdates(); - // this->controller.pid->Kp = tab.getValue<float>("KpSlider").get(); - // this->controller.pid->Ki = tab.getValue<float>("KiSlider").get(); - // this->controller.pid->Kd = tab.getValue<float>("KdSlider").get(); - // usleep(100000); - // } - // } - - // }); + threadHandle = Application::getInstance()->getThreadPool()->runTask([this] + { + std::string guiTabName; + while (!stopRequested) + { + ManagedIceObjectPtr object; + ARMARX_IMPORTANT << deactivateSpam(1) << "Trying to get parent"; + try + { + object = ManagedIceObjectPtr::dynamicCast(getParent().getOwner()); + ARMARX_CHECK_EXPRESSION(object); + remoteGui = object->getProxy<RemoteGuiInterfacePrx>("RemoteGuiProvider", false, "", false); + if (!remoteGui) + { + return; + } + ARMARX_IMPORTANT << deactivateSpam(1) << "Got Proxy"; + guiTabName = getParent().getDeviceName() + getControlMode(); + break; + } + catch (...) + { + handleExceptions(); + sleep(1); + } + + } + if (remoteGui) + { + ARMARX_IMPORTANT << "Creating GUI " << guiTabName; + using namespace RemoteGui; + + + + auto vLayout = makeVBoxLayout(); + + { + WidgetPtr KpLabel = makeTextLabel("Kp: "); + + WidgetPtr KiSlider = makeFloatSlider("KpSlider") + .min(0.0f).max(5000.0f) + .value(config->p); + WidgetPtr line = makeHBoxLayout() + .children({KpLabel, KiSlider}); + + vLayout.addChild(line); + + } + + + { + WidgetPtr KiLabel = makeTextLabel("Ki: "); + WidgetPtr KiSlider = makeFloatSlider("KiSlider") + .min(0.0f).max(50000.0f) + .value(config->i); + + WidgetPtr line = makeHBoxLayout() + .children({KiLabel, KiSlider}); + + vLayout.addChild(line); + + } + + { + WidgetPtr KdLabel = makeTextLabel("Kd: "); + WidgetPtr KdSlider = makeFloatSlider("KdSlider") + .min(0.0f).max(50.0f) + .steps(100) + .value(config->d); + + WidgetPtr line = makeHBoxLayout() + .children({KdLabel, KdSlider}); + + vLayout.addChild(line); + vLayout.addChild(new VSpacer); + } + + // WidgetPtr spin = makeFloatSpinBox("KpSpin") + // .min(0.0f).max(2.0f) + // .steps(20).decimals(2) + // .value(0.4f); + + + + + WidgetPtr groupBox = makeGroupBox("GroupBox") + .label("Group") + .child(vLayout); + + remoteGui->createTab(guiTabName, groupBox); + + while (!stopRequested) + { + RemoteGui::TabProxy tab(remoteGui, guiTabName); + tab.receiveUpdates(); + this->controller.pid->Kp = tab.getValue<float>("KpSlider").get(); + this->controller.pid->Ki = tab.getValue<float>("KiSlider").get(); + this->controller.pid->Kd = tab.getValue<float>("KdSlider").get(); + usleep(100000); + } + } + + }); } - return {{"lastTargetVelocity", new Variant(lastTargetVelocity.load())}, + return + { + {"lastTargetVelocity", new Variant(lastTargetVelocity.load())}, + {"lastTargetAcceleration", new Variant(lastTargetAcceleration.load())}, {"filteredVelocity", new Variant(controller.lastActualVelocity.load())}, {"pidIntegralCV", new Variant(controller.pid->integral * controller.pid->Ki)}, {"pidPropCV", new Variant(controller.pid->previousError * controller.pid->Kp)}, diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.h index 34312460d248e73648bd1d4f94b5f51ffe595932..d54e3b7dfa70b014c529c9e4b19b43b6c81e2ba2 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/JointPWMVelocityController.h @@ -37,11 +37,11 @@ namespace armarx protected: PWMVelocityControllerConfigurationCPtr config; PWMVelocityController controller; - VelocityControllerWithAccelerationAndPositionBounds velController; + VelocityControllerWithRampedAccelerationAndPositionBounds velController; ControlTarget1DoFActuatorVelocity target; - std::atomic<double> lastTargetVelocity; + std::atomic<double> lastTargetVelocity, lastTargetAcceleration; bool isLimitless; ActorDataPtr dataPtr; @@ -51,6 +51,7 @@ namespace armarx mutable RemoteGuiInterfacePrx remoteGui; bool stopRequested = false; mutable ThreadPool::Handle threadHandle; + const SensorValue1DoFActuator* sensorValue; // JointController interface protected: void rtPostDeactivateController() override; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp index d5451bbd65d0a5acd6a761d891304e364c895223..2b83bac93f9102bce75baec4b05850ee8c91da4f 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.cpp @@ -22,7 +22,9 @@ * GNU General Public License */ #include "PWMVelocityController.h" + #include <ArmarXCore/observers/filters/rtfilters/AverageFilter.h> + namespace armarx { @@ -35,13 +37,14 @@ namespace armarx pid.reset(new PIDController(velocityControllerConfigDataPtr->p, velocityControllerConfigDataPtr->i, velocityControllerConfigDataPtr->d)); + pid->pdOutputFilter.reset(new rtfilters::AverageFilter(10)); pid->maxIntegral = velocityControllerConfigDataPtr->maxIntegral; pid->differentialFilter.reset(new rtfilters::AverageFilter(130)); pid->conditionalIntegralErrorTreshold = velocityControllerConfigDataPtr->conditionalIntegralErrorTreshold; pid->threadSafe = false; } - double PWMVelocityController::run(IceUtil::Time const& deltaT, double currentVelocity, double targetVelocity) + double PWMVelocityController::run(IceUtil::Time const& deltaT, double currentVelocity, double targetVelocity, double gravityTorque) { double targetPWM = 0; if (!this->config->feedForwardMode) @@ -50,13 +53,15 @@ namespace armarx pid->update(deltaT.toSecondsDouble(), lastActualVelocity, targetVelocity); targetPWM = pid->getControlValue(); } - // TODO: add feedforward term based on gravity + float torqueFF = config->feedforwardTorqueToPWMFactor * -gravityTorque; + // ARMARX_INFO << deactivateSpam(1) << VAROUT(torqueFF); + targetPWM += torqueFF; //feed forward - if (std::abs(targetVelocity) > 0.001) + if (std::abs(targetVelocity) > 0.001 && std::abs(currentVelocity) < 0.0001f) { - // targetPWM += config->PWMDeadzone * math::MathUtils::Sign(targetVelocity); // deadzone + targetPWM += config->PWMDeadzone * math::MathUtils::Sign(targetVelocity); // deadzone } targetPWM += config->feedforwardVelocityToPWMFactor * targetVelocity; // approx. feedforward vel @@ -85,13 +90,13 @@ namespace armarx configData.maxAccelerationRad = node.first_node("maxAccelerationRad").value_as_float(); configData.maxDecelerationRad = node.first_node("maxDecelerationRad").value_as_float(); configData.maxDt = node.first_node("maxDt").value_as_float(); - configData.maxDt = node.first_node("directSetVLimit").value_as_float(); - configData.posControlP = node.first_node("posControlP").value_as_float(); + configData.directSetVLimit = node.first_node("directSetVLimit").value_as_float(); configData.p = node.first_node("p").value_as_float(); configData.i = node.first_node("i").value_as_float(); configData.d = node.first_node("d").value_as_float(); configData.maxIntegral = node.first_node("maxIntegral").value_as_float(); configData.feedforwardVelocityToPWMFactor = node.first_node("feedforwardVelocityToPWMFactor").value_as_float(); + configData.feedforwardTorqueToPWMFactor = node.first_node("feedforwardTorqueToPWMFactor").value_as_float(); configData.PWMDeadzone = node.first_node("PWMDeadzone").value_as_float(); configData.velocityUpdatePercent = node.first_node("velocityUpdatePercent").value_as_float(); configData.conditionalIntegralErrorTreshold = node.first_node("conditionalIntegralErrorTreshold").value_as_float(); diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h index 2dc6b7e80eb99a73be932434f6b7303532238c30..f85e19c84520e8647fabefc61cd23bc3652c3d13 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/PWMVelocityController.h @@ -54,6 +54,7 @@ namespace armarx float d; float maxIntegral; float feedforwardVelocityToPWMFactor; + float feedforwardTorqueToPWMFactor; float PWMDeadzone; float velocityUpdatePercent; float conditionalIntegralErrorTreshold; @@ -64,7 +65,7 @@ namespace armarx { public: PWMVelocityController(PWMVelocityControllerConfigurationCPtr velocityControllerConfigDataPtr); - double run(IceUtil::Time const& deltaT, double currentVelocity, double targetVelocity); + double run(IceUtil::Time const& deltaT, double currentVelocity, double targetVelocity, double gravityTorque); void reset(double currentVelocity); PWMVelocityControllerConfigurationCPtr config; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.cpp index 969260fe52c7fdf638867de452f3738dba5b159d..24acd829759500298faa531292bd6a917367e784 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.cpp @@ -20,8 +20,8 @@ using namespace armarx; ParallelGripperPositionController::ParallelGripperPositionController(const std::string deviceName, KITGripperBasisBoardPtr board, ActorDataPtr jointData, - PWMVelocityControllerConfigurationCPtr velocityControllerConfigDataPtr) : - JointPWMPositionController(deviceName, board, jointData, velocityControllerConfigDataPtr) + PWMPositionControllerConfigurationCPtr positionControllerConfigDataPtr) : + JointPWMPositionController(deviceName, board, jointData, positionControllerConfigDataPtr) { linkedJointConnectorIndex = jointData->getSiblingControlActorIndex(); } diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.h index ab776fa914b63e0212410c47aabe7f43e4e9606d..b645f126bc7c0e6815bcdf0c461dde2efdb70f16 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/JointController/ParallelGripperPositionController.h @@ -28,7 +28,7 @@ namespace armarx class ParallelGripperPositionController : public JointPWMPositionController { public: - ParallelGripperPositionController(const std::string deviceName, KITGripperBasisBoardPtr board, ActorDataPtr jointData, PWMVelocityControllerConfigurationCPtr velocityControllerConfigDataPtr); + ParallelGripperPositionController(const std::string deviceName, KITGripperBasisBoardPtr board, ActorDataPtr jointData, PWMPositionControllerConfigurationCPtr positionControllerConfigDataPtr); ~ParallelGripperPositionController() noexcept(true); void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override ; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.cpp index c40b666578f08e78283124a9193f653dd9ff62b9..834674e21c541026e61538d2ebb9be7a2b04119a 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.cpp @@ -31,6 +31,7 @@ #include "JointController/JointKITGripperEmergencyStopController.h" #include "JointController/JointKITGripperStopMovementController.h" #include "JointController/JointPWMVelocityController.h" +#include "JointController/JointKITGripperPWMPassThroughController.h" #include "JointController/ParallelGripperPositionController.h" #include "JointController/ParallelGripperVelocityController.h" namespace armarx @@ -117,6 +118,7 @@ namespace armarx dataPtr->rtReadSensorValues(sensorValuesTimestamp, timeSinceLastIteration); // TODO: read IMU + sensorValue.IMUTemperature = dataPtr->getIMUTemperature(); } // void KITGripperBasisBoard::rtWriteTargetValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) @@ -167,6 +169,10 @@ namespace armarx addJointController(emergencyController.get()); stopMovementController.reset(new JointKITGripperStopMovementController(actorDataPtr)); addJointController(stopMovementController.get()); + auto positionControllerCfg = PWMPositionControllerConfiguration::CreatePWMPositionControllerConfigDataFromXml( + defaultConfigurationNode.first_node("JointPWMPositionControllerDefaultConfiguration") + .add_node_at_end(configNode.first_node("JointPWMPositionControllerConfig"))); + auto velocityControllerCfg = PWMVelocityControllerConfiguration::CreatePWMVelocityControllerConfigDataFromXml( defaultConfigurationNode.first_node("JointPWMVelocityControllerDefaultConfiguration") .add_node_at_end(configNode.first_node("JointPWMVelocityControllerConfig"))); @@ -188,6 +194,16 @@ namespace armarx // add_node_at_end(configNode); // parallelGripperDecouplingFactor = tempConfigNode.first_node("ParallelGripperDecoupplingFactor").value_as_float(); + pwmController.reset(new JointKITGripperPWMPassThroughController(getDeviceName(), actorDataPtr)); + addJointController(pwmController.get()); + // ARMARX_CHECK_EQUAL_W_HINT( + // configNode.has_node("ParallelGripperDecoupplingFactor"), + // configNode.has_node("SiblingConnectorId"), + // "Either both or none have to be set."); + // auto tempConfigNode = defaultConfigurationNode.first_node("KITGripperActorDefaultConfiguration"). + // add_node_at_end(configNode); + // parallelGripperDecouplingFactor = tempConfigNode.first_node("ParallelGripperDecoupplingFactor").value_as_float(); + // if (configNode.has_node("ParallelGripperDecoupplingFactor")) // { // const int siblingConnectorId = configNode.first_node("SiblingConnectorId").value_as_int32(); @@ -221,7 +237,7 @@ namespace armarx } if (actorDataPtr->getPositionControlEnabled()) { - positionController.reset(new ParallelGripperPositionController(getDeviceName(), dev, actorDataPtr, velocityControllerCfg)); + positionController.reset(new ParallelGripperPositionController(getDeviceName(), dev, actorDataPtr, positionControllerCfg)); addJointController(positionController.get()); } else @@ -248,7 +264,7 @@ namespace armarx addJointController(zeroTorqueController.get()); if (actorDataPtr->getPositionControlEnabled()) { - positionController.reset(new JointPWMPositionController(getDeviceName(), dev, actorDataPtr, velocityControllerCfg)); + positionController.reset(new JointPWMPositionController(getDeviceName(), dev, actorDataPtr, positionControllerCfg)); addJointController(positionController.get()); } else @@ -282,7 +298,7 @@ namespace armarx sensorValue.position = actorDataPtr->getPosition(); sensorValue.relativePosition = actorDataPtr->getRelativePosition(); sensorValue.velocity = actorDataPtr->getVelocity(); - // TODO add new entry for acceleration + sensorValue.acceleration = actorDataPtr->getAcceleration(); sensorValue.absoluteEncoderVelocity = actorDataPtr->getAbsoluteEncoderVelocity(); sensorValue.targetPWM = actorDataPtr->getTargetPWM(); sensorValue.motorCurrent = actorDataPtr->getTargetPWM(); @@ -300,6 +316,7 @@ namespace armarx void KITGripperBasisBoard::ActorRobotUnitDevice::rtWriteTargetValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { + } } // namespace diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.h index a345164e85ff9b8c512ae766a0898a52764bc541..6ce753478c4b190efe3f05f4507bb89c3318f5c8 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoard.h @@ -43,6 +43,7 @@ namespace armarx using JointPWMPositionControllerPtr = std::shared_ptr<class JointPWMPositionController>; using JointPWMTorqueControllerPtr = std::shared_ptr<class JointPWMTorqueController>; using JointPWMZeroTorqueControllerPtr = std::shared_ptr<class JointPWMZeroTorqueController>; + using JointKITGripperPWMPassThroughControllerPtr = std::shared_ptr<class JointKITGripperPWMPassThroughController>; using ParallelGripperPositionControllerPtr = std::shared_ptr<class ParallelGripperPositionController>; using ParallelGripperVelocityControllerPtr = std::shared_ptr<class ParallelGripperVelocityController>; using PWMVelocityControllerConfigurationPtr = std::shared_ptr<class PWMVelocityControllerConfiguration>; @@ -84,6 +85,7 @@ namespace armarx JointPWMPositionControllerPtr positionController; JointPWMTorqueControllerPtr torqueController; JointPWMZeroTorqueControllerPtr zeroTorqueController; + JointKITGripperPWMPassThroughControllerPtr pwmController; /// The data object for copying to non-rt part KITGripperActorSensorData sensorValue; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp index 6b31205aa9679cec06ed5d5baa05837e788f9c96..8f85a09ffc983f917e0070220b3783257272dc6b 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.cpp @@ -115,6 +115,7 @@ namespace armarx void KITGripperBasisBoardData::rtReadSensorValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { int i = 0; + double dt = timeSinceLastIteration.toSecondsDouble(); for (auto& ptr : actorData) { if (!ptr) @@ -230,12 +231,14 @@ namespace armarx // d.torque.read(); d.currentMaxPWM = std::round(*d.velocityTicks * d.currentPWMBoundGradient + d.currentPWMBoundOffset); d.currentMinPWM = std::round(*d.velocityTicks * d.currentPWMBoundGradient - d.currentPWMBoundOffset); + d.acceleration = (d.velocity.value - oldVelocity) / dt; // d.acceleration.read(); // d.gravityTorque.read(); // d.motorCurrent.read(); // d.motorTemperature.read(); i++; } + } void KITGripperBasisBoardData::rtWriteTargetValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) @@ -253,6 +256,21 @@ namespace armarx // i++; // } + + for (auto& ptr : actorData) + { + if (!ptr) + { + + continue; + } + ActorData& d = *ptr; + if (this->getIMUTemperature() > 87) + { + ARMARX_RT_LOGF_WARNING("IMU Temperature of a gripper board is too high! %d degree celcius").deactivateSpam(5); + d.setTargetPWM(0); + } + } } ActorDataPtr& KITGripperBasisBoardData::getActorData(size_t actorIndex) @@ -263,7 +281,15 @@ namespace armarx } ActorData::ActorData(int32_t filterWindowSize): - velocityFilter(filterWindowSize) + velocityFilter(filterWindowSize){} + + int8_t KITGripperBasisBoardData::getIMUTemperature() const + { + return sensorOUT->IMUTemperature; + } + + ActorData::ActorData() : + velocityFilter(10) { } @@ -303,6 +329,11 @@ namespace armarx return rawVelocity; } + float ActorData::getAcceleration() const + { + return acceleration; + } + float ActorData::getTorque() const { return 0; //torque.value; diff --git a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h index e43e9fc38aa2fd9312577a6153363e8c8826cd10..31dd7c18f2b831582522553935c87151550c90ed 100644 --- a/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h +++ b/source/RobotAPI/libraries/KITGripperEtherCAT/KITGripperBasisBoard/KITGripperBasisBoardData.h @@ -38,10 +38,15 @@ namespace armarx { public: DETAIL_SensorValueBase_DEFAULT_METHOD_IMPLEMENTATION + + int IMUTemperature; + + static SensorValueInfo<SensorValueKITGripperBasisBoard> GetClassMemberInfo() { SensorValueInfo<SensorValueKITGripperBasisBoard> svi; // svi.addBaseClass<SensorValue1DoFActuatorMotorTemperature>(); + svi.addMemberVariable(&SensorValueKITGripperBasisBoard::IMUTemperature, "IMUTemperature"); svi.addBaseClass<SensorValueIMU>(); return svi; } @@ -96,11 +101,13 @@ namespace armarx class ActorData { public: - ActorData(int32_t filterWindowSize); - // position data function + ActorData(); + void setTargetPWM(int32_t targetPWM); float getPosition() const; float getRelativePosition() const; float getCompensatedRelativePosition() const; + float getVelocity() const; + float getAcceleration() const; int32_t getRelativePositionSensorOnly() const; bool getPositionControlEnabled() const; float getSoftLimitHi() const; @@ -124,6 +131,7 @@ namespace armarx int32_t getTargetPWM() const; int32_t getCurrentMinPWM() const; int32_t getCurrentMaxPWM() const; + float getCurrentPWMBoundGradient() const; int32_t getCurrentPWMBoundOffset() const; std::string getMotorModelTypeIndex() const; @@ -134,6 +142,10 @@ namespace armarx int32_t getSiblingControlActorIndex() const; float getParallelGripperDecouplingFactor() const; + float getParallelGripperDecouplingFactor() const; + + float getAbsoluteEncoderVelocity() const; + private: // parallel gripper controller configuration float parallelGripperDecouplingFactor = std::nanf(""); @@ -145,6 +157,7 @@ namespace armarx LinearConvertedValue<int32_t> relativePosition; float adjustedRelativePosition; float relativePositionOffset = std::nan(""); + float parallelGripperDecouplingFactor = std::nanf(""); LinearConvertedValue<u_int32_t> position; float sanitizedAbsolutePosition; float lastAbsolutePosition = std::nanf(""); @@ -157,6 +170,7 @@ namespace armarx // int32_t velocityFilterWindowSize = 10; rtfilters::AverageFilter velocityFilter; float absoluteEncoderVelocity = 0.0f; + float acceleration; int32_t* velocityTicks; int32_t useABSEncoderForVelocity = -1; @@ -194,6 +208,8 @@ namespace armarx void rtWriteTargetValues(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override; ActorDataPtr& getActorData(size_t actorIndex); + int8_t getIMUTemperature() const; + private: KITGripperBasisBoardOUT_t* sensorOUT; KITGripperBasisBoardIN_t* sensorIN; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt index b7a74584a68d152ab97b9e6cca4c67300d628873..a31bc7d02144f5b1f39730cadf15cc4431492162 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/CMakeLists.txt @@ -33,7 +33,10 @@ set(LIB_HEADERS DMPController/NJointBimanualCCDMPController.h DMPController/NJointTaskSpaceImpedanceDMPController.h DMPController/NJointBimanualForceMPController.h -# DMPController/NJointTSMultiMPController.h + DMPController/NJointPeriodicTSDMPForwardVelController.h + DMPController/NJointPeriodicTSDMPCompliantController.h + DMPController/NJointAdaptiveWipingController.h + ) list(APPEND LIB_FILES DMPController/NJointJointSpaceDMPController.cpp @@ -43,7 +46,10 @@ set(LIB_HEADERS DMPController/NJointBimanualCCDMPController.cpp DMPController/NJointTaskSpaceImpedanceDMPController.cpp DMPController/NJointBimanualForceMPController.cpp -# DMPController/NJointTSMultiMPController.cpp + DMPController/NJointPeriodicTSDMPForwardVelController.cpp + DMPController/NJointPeriodicTSDMPCompliantController.cpp + DMPController/NJointAdaptiveWipingController.cpp + ) list(APPEND LIBS ${DMP_LIBRARIES} DMPController) diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d19f4e5856fd718aae44d98650d787c8d7efead8 --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.cpp @@ -0,0 +1,704 @@ +#include "NJointAdaptiveWipingController.h" + +namespace armarx +{ + NJointControllerRegistration<NJointAdaptiveWipingController> registrationControllerNJointAdaptiveWipingController("NJointAdaptiveWipingController"); + + NJointAdaptiveWipingController::NJointAdaptiveWipingController(const RobotUnitPtr& robUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) + { + useSynchronizedRtRobot(); + cfg = NJointAdaptiveWipingControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION(!cfg->nodeSetName.empty()); + VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); + + ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName); + for (size_t i = 0; i < rns->getSize(); ++i) + { + std::string jointName = rns->getNode(i)->getName(); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Torque1DoF); + const SensorValueBase* sv = useSensorValue(jointName); + targets.push_back(ct->asA<ControlTarget1DoFActuatorTorque>()); + + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + + velocitySensors.push_back(velocitySensor); + positionSensors.push_back(positionSensor); + }; + + tcp = rns->getTCP(); + // set tcp controller + nodeSetName = cfg->nodeSetName; + ik.reset(new VirtualRobot::DifferentialIK(rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + + TaskSpaceDMPControllerConfig taskSpaceDMPConfig; + taskSpaceDMPConfig.motionTimeDuration = cfg->timeDuration; + taskSpaceDMPConfig.DMPKernelSize = cfg->kernelSize; + taskSpaceDMPConfig.DMPAmplitude = cfg->dmpAmplitude; + taskSpaceDMPConfig.DMPMode = "Linear"; + taskSpaceDMPConfig.DMPStyle = "Periodic"; + taskSpaceDMPConfig.phaseStopParas.goDist = cfg->phaseDist0; + taskSpaceDMPConfig.phaseStopParas.backDist = cfg->phaseDist1; + taskSpaceDMPConfig.phaseStopParas.Kpos = cfg->phaseKpPos; + taskSpaceDMPConfig.phaseStopParas.Kori = cfg->phaseKpOri; + taskSpaceDMPConfig.phaseStopParas.mm2radi = cfg->posToOriRatio; + taskSpaceDMPConfig.phaseStopParas.maxValue = cfg->phaseL; + taskSpaceDMPConfig.phaseStopParas.slop = cfg->phaseK; + taskSpaceDMPConfig.phaseStopParas.Dpos = 0; + taskSpaceDMPConfig.phaseStopParas.Dori = 0; + + + + dmpCtrl.reset(new TaskSpaceDMPController("periodicDMP", taskSpaceDMPConfig, false)); + + NJointAdaptiveWipingControllerControlData initData; + initData.targetTSVel.resize(6); + for (size_t i = 0; i < 6; ++i) + { + initData.targetTSVel(i) = 0; + } + reinitTripleBuffer(initData); + + firstRun = true; + dmpRunning = false; + + + ARMARX_CHECK_EQUAL(cfg->Kpos.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Dpos.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Kori.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Dori.size(), 3); + + kpos << cfg->Kpos[0], cfg->Kpos[1], cfg->Kpos[2]; + dpos << cfg->Dpos[0], cfg->Dpos[1], cfg->Dpos[2]; + kori << cfg->Kori[0], cfg->Kori[1], cfg->Kori[2]; + dori << cfg->Dori[0], cfg->Dori[1], cfg->Dori[2]; + + kpf = cfg->Kpf; + knull.setZero(targets.size()); + dnull.setZero(targets.size()); + + for (size_t i = 0; i < targets.size(); ++i) + { + knull(i) = cfg->Knull.at(i); + dnull(i) = cfg->Dnull.at(i); + + } + + nullSpaceJointsVec.resize(cfg->desiredNullSpaceJointValues.size()); + for (size_t i = 0; i < cfg->desiredNullSpaceJointValues.size(); ++i) + { + nullSpaceJointsVec(i) = cfg->desiredNullSpaceJointValues.at(i); + } + + + const SensorValueBase* svlf = robUnit->getSensorDevice(cfg->forceSensorName)->getSensorValue(); + forceSensor = svlf->asA<SensorValueForceTorque>(); + + + ARMARX_CHECK_EQUAL(cfg->ftOffset.size(), 6); + + currentForceOffset.setZero(); + forceOffset << cfg->ftOffset[0], cfg->ftOffset[1], cfg->ftOffset[2]; + torqueOffset << cfg->ftOffset[3], cfg->ftOffset[4], cfg->ftOffset[5]; + + handMass = cfg->handMass; + handCOM << cfg->handCOM[0], cfg->handCOM[1], cfg->handCOM[2]; + + + filteredForce.setZero(); + filteredTorque.setZero(); + + filteredForceInRoot.setZero(); + filteredTorqueInRoot.setZero(); + + UserToRTData initUserData; + initUserData.targetForce = 0; + user2rtData.reinitAllBuffers(initUserData); + + oriToolDir << 0, 0, 1; + gravityInRoot << 0, 0, -9.8; + + qvel_filtered.setZero(targets.size()); + + ARMARX_CHECK_EQUAL(cfg->ws_x.size(), 2); + ARMARX_CHECK_EQUAL(cfg->ws_y.size(), 2); + ARMARX_CHECK_EQUAL(cfg->ws_z.size(), 2); + // only for ARMAR-6 (safe-guard) + ARMARX_CHECK_LESS(cfg->ws_x[0], cfg->ws_x[1]); + ARMARX_CHECK_LESS(cfg->ws_x[0], 1000); + ARMARX_CHECK_LESS(-200, cfg->ws_x[1]); + + ARMARX_CHECK_LESS(cfg->ws_y[0], cfg->ws_y[1]); + ARMARX_CHECK_LESS(cfg->ws_y[0], 1200); + ARMARX_CHECK_LESS(0, cfg->ws_y[1]); + + ARMARX_CHECK_LESS(cfg->ws_z[0], cfg->ws_z[1]); + ARMARX_CHECK_LESS(cfg->ws_z[0], 1800); + ARMARX_CHECK_LESS(300, cfg->ws_z[1]); + + adaptK = kpos; + lastDiff = 0; + changeTimer = 0; + } + + void NJointAdaptiveWipingController::onInitNJointController() + { + ARMARX_INFO << "init ..."; + + + RTToControllerData initSensorData; + initSensorData.deltaT = 0; + initSensorData.currentTime = 0; + initSensorData.currentPose = tcp->getPoseInRootFrame(); + initSensorData.currentTwist.setZero(); + initSensorData.isPhaseStop = false; + rt2CtrlData.reinitAllBuffers(initSensorData); + + RTToUserData initInterfaceData; + initInterfaceData.currentTcpPose = tcp->getPoseInRootFrame(); + initInterfaceData.waitTimeForCalibration = 0; + rt2UserData.reinitAllBuffers(initInterfaceData); + + started = false; + + runTask("NJointAdaptiveWipingController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); + + } + + std::string NJointAdaptiveWipingController::getClassName(const Ice::Current&) const + { + return "NJointAdaptiveWipingController"; + } + + void NJointAdaptiveWipingController::controllerRun() + { + if (!started) + { + return; + } + + if (!dmpCtrl) + { + return; + } + + Eigen::VectorXf targetVels(6); + bool isPhaseStop = rt2CtrlData.getUpToDateReadBuffer().isPhaseStop; + if (isPhaseStop) + { + targetVels.setZero(); + } + else + { + + double deltaT = rt2CtrlData.getUpToDateReadBuffer().deltaT; + Eigen::Matrix4f currentPose = rt2CtrlData.getUpToDateReadBuffer().currentPose; + Eigen::VectorXf currentTwist = rt2CtrlData.getUpToDateReadBuffer().currentTwist; + + LockGuardType guard {controllerMutex}; + dmpCtrl->flow(deltaT, currentPose, currentTwist); + + targetVels = dmpCtrl->getTargetVelocity(); + + debugOutputData.getWriteBuffer().latestTargetVelocities["x_vel"] = targetVels(0); + debugOutputData.getWriteBuffer().latestTargetVelocities["y_vel"] = targetVels(1); + debugOutputData.getWriteBuffer().latestTargetVelocities["z_vel"] = targetVels(2); + debugOutputData.getWriteBuffer().latestTargetVelocities["roll_vel"] = targetVels(3); + debugOutputData.getWriteBuffer().latestTargetVelocities["pitch_vel"] = targetVels(4); + debugOutputData.getWriteBuffer().latestTargetVelocities["yaw_vel"] = targetVels(5); + debugOutputData.getWriteBuffer().currentPose["currentPose_x"] = currentPose(0, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_y"] = currentPose(1, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_z"] = currentPose(2, 3); + VirtualRobot::MathTools::Quaternion currentQ = VirtualRobot::MathTools::eigen4f2quat(currentPose); + debugOutputData.getWriteBuffer().currentPose["currentPose_qw"] = currentQ.w; + debugOutputData.getWriteBuffer().currentPose["currentPose_qx"] = currentQ.x; + debugOutputData.getWriteBuffer().currentPose["currentPose_qy"] = currentQ.y; + debugOutputData.getWriteBuffer().currentPose["currentPose_qz"] = currentQ.z; + debugOutputData.getWriteBuffer().currentCanVal = dmpCtrl->debugData.canVal; + debugOutputData.getWriteBuffer().mpcFactor = dmpCtrl->debugData.mpcFactor; + debugOutputData.getWriteBuffer().error = dmpCtrl->debugData.poseError; + debugOutputData.getWriteBuffer().posError = dmpCtrl->debugData.posiError; + debugOutputData.getWriteBuffer().oriError = dmpCtrl->debugData.oriError; + debugOutputData.getWriteBuffer().deltaT = deltaT; + debugOutputData.commitWrite(); + } + + getWriterControlStruct().targetTSVel = targetVels; + writeControlStruct(); + + dmpRunning = true; + } + + + void NJointAdaptiveWipingController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + double deltaT = timeSinceLastIteration.toSecondsDouble(); + + Eigen::Matrix4f currentPose = tcp->getPoseInRootFrame(); + rt2UserData.getWriteBuffer().currentTcpPose = currentPose; + rt2UserData.getWriteBuffer().waitTimeForCalibration += deltaT; + rt2UserData.commitWrite(); + + Eigen::Vector3f currentToolDir; + currentToolDir.setZero(); + Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All); + + Eigen::VectorXf qpos(positionSensors.size()); + Eigen::VectorXf qvel(velocitySensors.size()); + for (size_t i = 0; i < positionSensors.size(); ++i) + { + qpos(i) = positionSensors[i]->position; + qvel(i) = velocitySensors[i]->velocity; + } + + qvel_filtered = (1 - cfg->velFilter) * qvel_filtered + cfg->velFilter * qvel; + Eigen::VectorXf currentTwist = jacobi * qvel_filtered; + + Eigen::VectorXf targetVel(6); + targetVel.setZero(); + if (firstRun || !dmpRunning) + { + lastPosition = currentPose.block<2, 1>(0, 3); + targetPose = currentPose; + firstRun = false; + filteredForce.setZero(); + Eigen::Vector3f currentForce = forceSensor->force - forceOffset; + Eigen::Matrix3f forceFrameOri = rtGetRobot()->getRobotNode(cfg->forceFrameName)->getPoseInRootFrame().block<3, 3>(0, 0); + Eigen::Vector3f gravityInForceFrame = forceFrameOri.transpose() * gravityInRoot; + Eigen::Vector3f handGravity = handMass * gravityInForceFrame; + currentForce = currentForce - handGravity; + currentForceOffset = 0.1 * currentForceOffset + 0.9 * currentForce; + + origHandOri = currentPose.block<3, 3>(0, 0); + toolTransform = origHandOri.transpose(); + targetVel.setZero(); + } + else + { + // communicate with DMP controller + rtUpdateControlStruct(); + targetVel = rtGetControlStruct().targetTSVel; + targetVel(2) = 0; + + // calculate force + Eigen::Vector3f currentForce = forceSensor->force - forceOffset - currentForceOffset; + + Eigen::Matrix3f forceFrameOri = rtGetRobot()->getRobotNode(cfg->forceFrameName)->getPoseInRootFrame().block<3, 3>(0, 0); + Eigen::Vector3f gravityInForceFrame = forceFrameOri.transpose() * gravityInRoot; + Eigen::Vector3f handGravity = handMass * gravityInForceFrame; + + currentForce = currentForce - handGravity; + filteredForce = (1 - cfg->forceFilter) * filteredForce + cfg->forceFilter * currentForce; + + // Eigen::Vector3f currentTorque = forceSensor->torque - torqueOffset; + // Eigen::Vector3f handTorque = handCOM.cross(gravityInForceFrame); + // currentTorque = currentTorque - handTorque; + + for (size_t i = 0; i < 3; ++i) + { + if (fabs(filteredForce(i)) > cfg->forceDeadZone) + { + filteredForce(i) -= (filteredForce(i) / fabs(filteredForce(i))) * cfg->forceDeadZone; + } + else + { + filteredForce(i) = 0; + } + } + + filteredForceInRoot = forceFrameOri * filteredForce; + float targetForce = user2rtData.getUpToDateReadBuffer().targetForce; + + Eigen::Matrix3f currentHandOri = currentPose.block<3, 3>(0, 0); + Eigen::Matrix3f currentToolOri = currentHandOri * toolTransform; + + Eigen::Vector3f forceInToolFrame = currentToolOri.transpose() * filteredForceInRoot; + float desiredZVel = kpf * (targetForce - forceInToolFrame(2)); + targetVel(2) -= desiredZVel; + targetVel.block<3, 1>(0, 0) = currentToolOri * targetVel.block<3, 1>(0, 0); + + currentToolDir = currentToolOri * oriToolDir; + + for (int i = 3; i < 6; ++i) + { + targetVel(i) = 0; + } + + // rotation changes + + if (filteredForceInRoot.norm() > fabs(cfg->minimumReactForce)) + { + Eigen::Vector3f desiredToolDir = filteredForceInRoot.normalized();// / filteredForceInRoot.norm(); + currentToolDir.normalize(); + + Eigen::Vector3f axis = currentToolDir.cross(desiredToolDir); + float angle = acosf(currentToolDir.dot(desiredToolDir)); + + if (fabs(angle) < M_PI / 2) + { + Eigen::AngleAxisf desiredToolRot(angle, axis); + Eigen::Matrix3f desiredRotMat = desiredToolRot * Eigen::Matrix3f::Identity(); + + Eigen::Vector3f angularDiff = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); + + targetVel.tail(3) = cfg->angularKp * angularDiff; + + Eigen::Vector3f desiredRPY = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); + Eigen::Vector3f checkedToolDir = desiredRotMat * currentToolDir; + checkedToolDir.normalize(); + } + + } + + + // integrate for targetPose + } + + bool isPhaseStop = false; + + float diff = (targetPose.block<2, 1>(0, 3) - currentPose.block<2, 1>(0, 3)).norm(); + if (diff > cfg->phaseDist0) + { + isPhaseStop = true; + } + + float dTf = (float)deltaT; + + + if (filteredForceInRoot.block<2, 1>(0, 0).norm() > cfg->dragForceDeadZone) + { + Eigen::Vector2f dragForce = filteredForceInRoot.block<2, 1>(0, 0) - cfg->dragForceDeadZone * filteredForceInRoot.block<2, 1>(0, 0) / filteredForceInRoot.block<2, 1>(0, 0).norm(); + adaptK(0) = fmax(adaptK(0) - dTf * cfg->adaptForceCoeff * dragForce.norm(), 0); + adaptK(1) = fmax(adaptK(1) - dTf * cfg->adaptForceCoeff * dragForce.norm(), 0); + lastDiff = diff; + } + else + { + adaptK(0) = fmin(adaptK(0) + fabs(dTf * cfg->adaptCoeff), kpos(0)); + adaptK(1) = fmin(adaptK(1) + fabs(dTf * cfg->adaptCoeff), kpos(1)); + } + adaptK(2) = kpos(2); + + // adaptive control with distance + + + + + targetPose.block<3, 1>(0, 3) = targetPose.block<3, 1>(0, 3) + dTf * targetVel.block<3, 1>(0, 0); + Eigen::Matrix3f rotMat = VirtualRobot::MathTools::rpy2eigen3f(dTf * targetVel(3), dTf * targetVel(4), dTf * targetVel(5)); + targetPose.block<3, 3>(0, 0) = rotMat * targetPose.block<3, 3>(0, 0); + + if (isPhaseStop) + { + Eigen::Vector2f currentXY = currentPose.block<2, 1>(0, 3); + if ((lastPosition - currentXY).norm() < cfg->changePositionTolerance) + { + changeTimer += deltaT; + } + else + { + lastPosition = currentPose.block<2, 1>(0, 3); + changeTimer = 0; + } + + if (changeTimer > cfg->changeTimerThreshold) + { + targetPose(0, 3) = currentPose(0, 3); + targetPose(1, 3) = currentPose(1, 3); + isPhaseStop = false; + changeTimer = 0; + } + } + else + { + changeTimer = 0; + } + + + targetPose(0, 3) = targetPose(0, 3) > cfg->ws_x[0] ? targetPose(0, 3) : cfg->ws_x[0]; + targetPose(0, 3) = targetPose(0, 3) < cfg->ws_x[1] ? targetPose(0, 3) : cfg->ws_x[1]; + + targetPose(1, 3) = targetPose(1, 3) > cfg->ws_y[0] ? targetPose(1, 3) : cfg->ws_y[0]; + targetPose(1, 3) = targetPose(1, 3) < cfg->ws_y[1] ? targetPose(1, 3) : cfg->ws_y[1]; + + targetPose(2, 3) = targetPose(2, 3) > cfg->ws_z[0] ? targetPose(2, 3) : cfg->ws_z[0]; + targetPose(2, 3) = targetPose(2, 3) < cfg->ws_z[1] ? targetPose(2, 3) : cfg->ws_z[1]; + + + + debugRT.getWriteBuffer().currentToolDir = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystemVec(currentToolDir); + debugRT.getWriteBuffer().targetPose = targetPose; + debugRT.getWriteBuffer().globalPose = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystem(targetPose); + debugRT.getWriteBuffer().currentPose = currentPose; + debugRT.getWriteBuffer().filteredForceInRoot = filteredForceInRoot; + debugRT.getWriteBuffer().filteredForce = filteredForce; + debugRT.getWriteBuffer().globalFilteredForce = tcp->getRobot()->getRootNode()->toGlobalCoordinateSystemVec(filteredForceInRoot); + debugRT.getWriteBuffer().targetVel = targetVel; + debugRT.getWriteBuffer().adaptK = adaptK; + debugRT.getWriteBuffer().isPhaseStop = isPhaseStop; + debugRT.commitWrite(); + + rt2CtrlData.getWriteBuffer().currentPose = currentPose; + rt2CtrlData.getWriteBuffer().currentTwist = currentTwist; + rt2CtrlData.getWriteBuffer().deltaT = deltaT; + rt2CtrlData.getWriteBuffer().currentTime += deltaT; + rt2CtrlData.getWriteBuffer().isPhaseStop = isPhaseStop; + rt2CtrlData.commitWrite(); + + // Eigen::Matrix3f rotVel = VirtualRobot::MathTools::rpy2eigen3f(targetVel(3) * dTf, targetVel(4) * dTf, targetVel(5) * dTf); + // targetPose.block<3, 3>(0, 0) = rotVel * targetPose.block<3, 3>(0, 0); + + // inverse dynamic controller + jacobi.block<3, 8>(0, 0) = 0.001 * jacobi.block<3, 8>(0, 0); // convert mm to m + + + + + Eigen::Vector6f jointControlWrench; + { + Eigen::Vector3f targetTCPLinearVelocity; + targetTCPLinearVelocity << 0.001 * targetVel(0), 0.001 * targetVel(1), 0.001 * targetVel(2); + Eigen::Vector3f currentTCPLinearVelocity; + currentTCPLinearVelocity << 0.001 * currentTwist(0), 0.001 * currentTwist(1), 0.001 * currentTwist(2); + Eigen::Vector3f currentTCPPosition = currentPose.block<3, 1>(0, 3); + Eigen::Vector3f desiredPosition = targetPose.block<3, 1>(0, 3); + + Eigen::Vector3f linearVel = adaptK.cwiseProduct(desiredPosition - currentTCPPosition); + + // if (isPhaseStop) + // { + // linearVel = ((float)cfg->phaseKpPos) * (desiredPosition - currentTCPPosition); + // for (size_t i = 0; i < 3; ++i) + // { + // linearVel(i) = fmin(cfg->maxLinearVel, linearVel(i)); + // } + // } + // else + // { + // linearVel = kpos.cwiseProduct(desiredPosition - currentTCPPosition); + // } + Eigen::Vector3f tcpDesiredForce = 0.001 * linearVel + dpos.cwiseProduct(- currentTCPLinearVelocity); + + Eigen::Vector3f currentTCPAngularVelocity; + currentTCPAngularVelocity << currentTwist(3), currentTwist(4), currentTwist(5); + Eigen::Matrix3f currentRotMat = currentPose.block<3, 3>(0, 0); + Eigen::Matrix3f diffMat = targetPose.block<3, 3>(0, 0) * currentRotMat.inverse(); + Eigen::Vector3f rpy = VirtualRobot::MathTools::eigen3f2rpy(diffMat); + Eigen::Vector3f tcpDesiredTorque = kori.cwiseProduct(rpy) + dori.cwiseProduct(- currentTCPAngularVelocity); + jointControlWrench << tcpDesiredForce, tcpDesiredTorque; + } + + + + Eigen::MatrixXf I = Eigen::MatrixXf::Identity(targets.size(), targets.size()); + Eigen::VectorXf nullspaceTorque = knull.cwiseProduct(nullSpaceJointsVec - qpos) - dnull.cwiseProduct(qvel); + Eigen::MatrixXf jtpinv = ik->computePseudoInverseJacobianMatrix(jacobi.transpose(), 2.0); + Eigen::VectorXf jointDesiredTorques = jacobi.transpose() * jointControlWrench + (I - jacobi.transpose() * jtpinv) * nullspaceTorque; + + // torque filter (maybe) + for (size_t i = 0; i < targets.size(); ++i) + { + targets.at(i)->torque = jointDesiredTorques(i); + + if (!targets.at(i)->isValid()) + { + targets.at(i)->torque = 0.0f; + } + else + { + if (fabs(targets.at(i)->torque) > fabs(cfg->maxJointTorque)) + { + targets.at(i)->torque = fabs(cfg->maxJointTorque) * (targets.at(i)->torque / fabs(targets.at(i)->torque)); + } + } + } + + + } + + + void NJointAdaptiveWipingController::learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromFiles(fileNames); + + } + + void NJointAdaptiveWipingController::learnDMPFromTrajectory(const TrajectoryBasePtr& trajectory, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + ARMARX_CHECK_EXPRESSION(trajectory); + TrajectoryPtr dmpTraj = TrajectoryPtr::dynamicCast(trajectory); + ARMARX_CHECK_EXPRESSION(dmpTraj); + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromTrajectory(dmpTraj); + + } + + void NJointAdaptiveWipingController::setSpeed(Ice::Double times, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setSpeed(times); + } + + + void NJointAdaptiveWipingController::setGoals(const Ice::DoubleSeq& goals, const Ice::Current& ice) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setGoalPoseVec(goals); + } + + void NJointAdaptiveWipingController::setAmplitude(Ice::Double amp, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setAmplitude(amp); + } + + void NJointAdaptiveWipingController::setTargetForceInRootFrame(float targetForce, const Ice::Current&) + { + LockGuardType guard {controlDataMutex}; + user2rtData.getWriteBuffer().targetForce = targetForce; + user2rtData.commitWrite(); + } + + void NJointAdaptiveWipingController::runDMP(const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&) + { + firstRun = true; + while (firstRun || rt2UserData.getUpToDateReadBuffer().waitTimeForCalibration < cfg->waitTimeForCalibration) + { + usleep(100); + } + + + Eigen::Matrix4f pose = rt2UserData.getUpToDateReadBuffer().currentTcpPose; + + LockGuardType guard {controllerMutex}; + dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); + dmpCtrl->setSpeed(tau); + + ARMARX_IMPORTANT << "run DMP"; + started = true; + dmpRunning = false; + } + + + void NJointAdaptiveWipingController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx& debugDrawer, const DebugObserverInterfacePrx& debugObs) + { + std::string datafieldName; + std::string debugName = "Periodic"; + StringVariantBaseMap datafields; + + Eigen::Matrix4f targetPoseDebug = debugRT.getUpToDateReadBuffer().targetPose; + datafields["target_x"] = new Variant(targetPoseDebug(0, 3)); + datafields["target_y"] = new Variant(targetPoseDebug(1, 3)); + datafields["target_z"] = new Variant(targetPoseDebug(2, 3)); + + Eigen::Matrix4f currentPoseDebug = debugRT.getUpToDateReadBuffer().currentPose; + datafields["current_x"] = new Variant(currentPoseDebug(0, 3)); + datafields["current_y"] = new Variant(currentPoseDebug(1, 3)); + datafields["current_z"] = new Variant(currentPoseDebug(2, 3)); + + Eigen::Vector3f filteredForce = debugRT.getUpToDateReadBuffer().filteredForce; + datafields["filteredforce_x"] = new Variant(filteredForce(0)); + datafields["filteredforce_y"] = new Variant(filteredForce(1)); + datafields["filteredforce_z"] = new Variant(filteredForce(2)); + + Eigen::Vector3f filteredForceInRoot = debugRT.getUpToDateReadBuffer().filteredForceInRoot; + datafields["filteredForceInRoot_x"] = new Variant(filteredForceInRoot(0)); + datafields["filteredForceInRoot_y"] = new Variant(filteredForceInRoot(1)); + datafields["filteredForceInRoot_z"] = new Variant(filteredForceInRoot(2)); + + Eigen::Vector3f reactForce = debugRT.getUpToDateReadBuffer().reactForce; + datafields["reactForce_x"] = new Variant(reactForce(0)); + datafields["reactForce_y"] = new Variant(reactForce(1)); + datafields["reactForce_z"] = new Variant(reactForce(2)); + + Eigen::VectorXf targetVel = debugRT.getUpToDateReadBuffer().targetVel; + datafields["targetVel_x"] = new Variant(targetVel(0)); + datafields["targetVel_y"] = new Variant(targetVel(1)); + datafields["targetVel_z"] = new Variant(targetVel(2)); + + Eigen::Vector3f adaptK = debugRT.getUpToDateReadBuffer().adaptK; + datafields["adaptK_x"] = new Variant(adaptK(0)); + datafields["adaptK_y"] = new Variant(adaptK(1)); + + datafields["canVal"] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + datafields["deltaT"] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + + datafields["PhaseStop"] = new Variant(debugRT.getUpToDateReadBuffer().isPhaseStop); + + + // datafields["targetVel_rx"] = new Variant(targetVel(3)); + // datafields["targetVel_ry"] = new Variant(targetVel(4)); + // datafields["targetVel_rz"] = new Variant(targetVel(5)); + + // auto values = debugOutputData.getUpToDateReadBuffer().latestTargetVelocities; + // for (auto& pair : values) + // { + // datafieldName = pair.first + "_" + debugName; + // datafields[datafieldName] = new Variant(pair.second); + // } + + // auto currentPose = debugOutputData.getUpToDateReadBuffer().currentPose; + // for (auto& pair : currentPose) + // { + // datafieldName = pair.first + "_" + debugName; + // datafields[datafieldName] = new Variant(pair.second); + // } + + // datafieldName = "canVal_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + // datafieldName = "mpcFactor_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().mpcFactor); + // datafieldName = "error_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().error); + // datafieldName = "posError_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().posError); + // datafieldName = "oriError_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().oriError); + // datafieldName = "deltaT_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + + datafieldName = "PeriodicDMP"; + debugObs->setDebugChannel(datafieldName, datafields); + + + // draw force; + Eigen::Matrix4f globalPose = debugRT.getUpToDateReadBuffer().globalPose; + Eigen::Vector3f handPosition = globalPose.block<3, 1>(0, 3); + Eigen::Vector3f forceDir = debugRT.getUpToDateReadBuffer().globalFilteredForce; + + debugDrawer->setArrowVisu("Force", "currentForce", new Vector3(handPosition), new Vector3(forceDir), DrawColor {0, 0, 1, 1}, 10 * forceDir.norm(), 3); + + // draw direction of the tool + Eigen::Vector3f currentToolDir = debugRT.getUpToDateReadBuffer().currentToolDir; + debugDrawer->setArrowVisu("Tool", "Tool", new Vector3(handPosition), new Vector3(currentToolDir), DrawColor {1, 0, 0, 1}, 100, 3); + debugDrawer->setPoseVisu("target", "targetPose", new Pose(globalPose)); + + } + + + + void NJointAdaptiveWipingController::onDisconnectNJointController() + { + ARMARX_INFO << "stopped ..."; + } + + + +} diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h new file mode 100644 index 0000000000000000000000000000000000000000..9bfd26e64ad5551f5997634189034bd158e9b43e --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointAdaptiveWipingController.h @@ -0,0 +1,209 @@ + +#pragma once + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h> +#include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include <VirtualRobot/IK/DifferentialIK.h> +#include <RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.h> +#include <RobotAPI/libraries/core/CartesianVelocityController.h> + +#include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h> +#include <ArmarXCore/core/time/CycleUtil.h> + +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h> +#include <RobotAPI/libraries/core/Trajectory.h> + +namespace armarx +{ + + + TYPEDEF_PTRS_HANDLE(NJointAdaptiveWipingController); + TYPEDEF_PTRS_HANDLE(NJointAdaptiveWipingControllerControlData); + + class NJointAdaptiveWipingControllerControlData + { + public: + Eigen::VectorXf targetTSVel; + }; + + class pidController + { + public: + float Kp = 0, Kd = 0; + float lastError = 0; + float update(float dt, float error) + { + float derivative = (error - lastError) / dt; + float retVal = Kp * error + Kd * derivative; + lastError = error; + return retVal; + } + }; + + /** + * @brief The NJointAdaptiveWipingController class + * @ingroup Library-RobotUnit-NJointControllers + */ + class NJointAdaptiveWipingController : + public NJointControllerWithTripleBuffer<NJointAdaptiveWipingControllerControlData>, + public NJointAdaptiveWipingControllerInterface + { + public: + using ConfigPtrT = NJointAdaptiveWipingControllerConfigPtr; + NJointAdaptiveWipingController(const RobotUnitPtr&, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + // NJointControllerInterface interface + std::string getClassName(const Ice::Current&) const; + + // NJointController interface + + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + // NJointAdaptiveWipingControllerInterface interface + void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&); + void learnDMPFromTrajectory(const TrajectoryBasePtr& trajectory, const Ice::Current&); + bool isFinished(const Ice::Current&) + { + return false; + } + + void setSpeed(Ice::Double times, const Ice::Current&); + void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&); + void setAmplitude(Ice::Double amp, const Ice::Current&); + void runDMP(const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&); + void setTargetForceInRootFrame(Ice::Float force, const Ice::Current&); + double getCanVal(const Ice::Current&) + { + return dmpCtrl->canVal; + } + + protected: + virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); + + void onInitNJointController(); + void onDisconnectNJointController(); + void controllerRun(); + + private: + struct DebugBufferData + { + StringFloatDictionary latestTargetVelocities; + StringFloatDictionary currentPose; + double currentCanVal; + double mpcFactor; + double error; + double phaseStop; + double posError; + double oriError; + double deltaT; + }; + + TripleBuffer<DebugBufferData> debugOutputData; + + + struct DebugRTData + { + Eigen::Matrix4f targetPose; + Eigen::Vector3f filteredForce; + Eigen::Vector3f filteredForceInRoot; + + Eigen::Vector3f reactForce; + Eigen::Vector3f adaptK; + Eigen::VectorXf targetVel; + Eigen::Matrix4f currentPose; + bool isPhaseStop; + + Eigen::Matrix4f globalPose; + Eigen::Vector3f globalFilteredForce; + Eigen::Vector3f currentToolDir; + }; + TripleBuffer<DebugRTData> debugRT; + + + + + struct RTToControllerData + { + double currentTime; + double deltaT; + Eigen::Matrix4f currentPose; + Eigen::VectorXf currentTwist; + bool isPhaseStop; + }; + TripleBuffer<RTToControllerData> rt2CtrlData; + + struct RTToUserData + { + Eigen::Matrix4f currentTcpPose; + float waitTimeForCalibration; + }; + TripleBuffer<RTToUserData> rt2UserData; + + struct UserToRTData + { + float targetForce; + }; + TripleBuffer<UserToRTData> user2rtData; + + + TaskSpaceDMPControllerPtr dmpCtrl; + + std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> positionSensors; + std::vector<ControlTarget1DoFActuatorTorque*> targets; + + // velocity ik controller parameters + std::string nodeSetName; + + bool started; + bool firstRun; + bool dmpRunning; + + VirtualRobot::DifferentialIKPtr ik; + VirtualRobot::RobotNodePtr tcp; + + NJointAdaptiveWipingControllerConfigPtr cfg; + mutable MutexType controllerMutex; + PeriodicTask<NJointAdaptiveWipingController>::pointer_type controllerTask; + Eigen::Matrix4f targetPose; + + Eigen::Vector3f kpos; + Eigen::Vector3f dpos; + Eigen::Vector3f kori; + Eigen::Vector3f dori; + Eigen::VectorXf knull; + Eigen::VectorXf dnull; + float kpf; + + Eigen::VectorXf nullSpaceJointsVec; + const SensorValueForceTorque* forceSensor; + + Eigen::Vector3f filteredForce; + Eigen::Vector3f filteredTorque; + Eigen::Vector3f forceOffset; + Eigen::Vector3f currentForceOffset; + Eigen::Vector3f torqueOffset; + float handMass; + Eigen::Vector3f handCOM; + Eigen::Vector3f gravityInRoot; + + Eigen::Vector3f filteredForceInRoot; + Eigen::Vector3f filteredTorqueInRoot; + + Eigen::Matrix3f toolTransform; + Eigen::Vector3f oriToolDir; + Eigen::Matrix3f origHandOri; + Eigen::VectorXf qvel_filtered; + + Eigen::Vector3f adaptK; + float lastDiff; + Eigen::Vector2f lastPosition; + double changeTimer; + + }; + +} // namespace armarx + diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.cpp index c0d0f4c5011b1c5432a22f59f06700cb4d9b6d77..d61b8fa3f02fb55eb0311332c289c6df6febcae3 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.cpp @@ -833,13 +833,13 @@ namespace armarx debugObs->setDebugChannel("DMPController", datafields); } - void NJointBimanualCCDMPController::onInitComponent() + void NJointBimanualCCDMPController::onInitNJointController() { ARMARX_INFO << "init ..."; controllerTask = new PeriodicTask<NJointBimanualCCDMPController>(this, &NJointBimanualCCDMPController::controllerRun, 0.3); } - void NJointBimanualCCDMPController::onDisconnectComponent() + void NJointBimanualCCDMPController::onDisconnectNJointController() { controllerTask->stop(); ARMARX_INFO << "stopped ..."; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.h index 4f4f8bf0f5d4fadaf22434a56934759e95791fb2..2bc75223f0578d3607704e65d0fbe5691d9104b7 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualCCDMPController.h @@ -87,8 +87,8 @@ namespace armarx virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; - void onInitComponent() override; - void onDisconnectComponent() override; + void onInitNJointController() override; + void onDisconnectNJointController() override; void controllerRun(); private: diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.cpp index eeb9ebe8f80df2f0775ad22d8b1156e913dc9d3e..7d1a8f2c012aa7da744dd39281b6327c376e8b41 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.cpp @@ -479,13 +479,13 @@ namespace armarx debugObs->setDebugChannel(datafieldName, datafields); } - void NJointBimanualForceMPController::onInitComponent() + void NJointBimanualForceMPController::onInitNJointController() { ARMARX_INFO << "init ..."; controllerTask = new PeriodicTask<NJointBimanualForceMPController>(this, &NJointBimanualForceMPController::controllerRun, 0.3); } - void NJointBimanualForceMPController::onDisconnectComponent() + void NJointBimanualForceMPController::onDisconnectNJointController() { ARMARX_INFO << "stopped ..."; controllerTask->stop(); diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.h index db73f4f8c74b281f2e10dd0e6676101185e5deeb..29f765ded0010d57043dafbb876c4771d5c904ae 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointBimanualForceMPController.h @@ -68,8 +68,8 @@ namespace armarx protected: virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; - void onInitComponent() override; - void onDisconnectComponent() override; + void onInitNJointController() override; + void onDisconnectNJointController() override; void controllerRun(); private: diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.cpp index 6b38de60d12a6bce7ea37f9f8cfcef81aa7242cf..c9fa7a03af5c54636240a9374efce3ed78fee0e1 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.cpp @@ -189,14 +189,14 @@ namespace armarx double timeDuration = timeDurations[i]; std::string dmpType = dmpTypes[i]; - // double amplitude = 1.0; + //double amplitude = 1.0; if (dmpType == "Periodic") { if (canVals[i] <= 1e-8) { canVals[i] = timeDuration; } - // amplitude = amplitudes[i]; + //amplitude = amplitudes[i]; } if (canVals[i] > 1e-8) @@ -570,13 +570,13 @@ namespace armarx debugObs->setDebugChannel("DMPController", datafields); } - void NJointCCDMPController::onInitComponent() + void NJointCCDMPController::onInitNJointController() { ARMARX_INFO << "init ..."; controllerTask = new PeriodicTask<NJointCCDMPController>(this, &NJointCCDMPController::controllerRun, 0.3); } - void NJointCCDMPController::onDisconnectComponent() + void NJointCCDMPController::onDisconnectNJointController() { controllerTask->stop(); ARMARX_INFO << "stopped ..."; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.h index cd0c4b0dbcd57285433dceda0a16f3e2f87b2021..2fd838d91be90490ad57d3f50f6e6586048caaba 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointCCDMPController.h @@ -90,8 +90,8 @@ namespace armarx VirtualRobot::IKSolver::CartesianSelection ModeFromIce(const NJointTaskSpaceDMPControllerMode::CartesianSelection mode); virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; - void onInitComponent() override; - void onDisconnectComponent() override; + void onInitNJointController() override; + void onDisconnectNJointController() override; void controllerRun(); private: diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp index fd33f782b3f3e90d99d10c639a8237d93b485928..0a8ba174454a5f6c0b80f40aea0fc4caf75967f1 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.cpp @@ -261,13 +261,13 @@ namespace armarx } - void NJointJSDMPController::onInitComponent() + void NJointJSDMPController::onInitNJointController() { ARMARX_INFO << "init ..."; controllerTask = new PeriodicTask<NJointJSDMPController>(this, &NJointJSDMPController::controllerRun, 0.2); } - void NJointJSDMPController::onDisconnectComponent() + void NJointJSDMPController::onDisconnectNJointController() { controllerTask->stop(); ARMARX_INFO << "stopped ..."; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h index 210dd98621f43aa2820c30b1d0388c6107f50921..f593d4943bcaae6286a654d8dcfb46823abe3915 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointJSDMPController.h @@ -123,8 +123,8 @@ namespace armarx // ManagedIceObject interface protected: void controllerRun(); - void onInitComponent() override; - void onDisconnectComponent() override; + void onInitNJointController() override; + void onDisconnectNJointController() override; }; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPCompliantController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPCompliantController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6b58637cb064bd03c43befb53ddceac9e7ee97a --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPCompliantController.cpp @@ -0,0 +1,667 @@ +#include "NJointPeriodicTSDMPCompliantController.h" + +namespace armarx +{ + NJointControllerRegistration<NJointPeriodicTSDMPCompliantController> registrationControllerNJointPeriodicTSDMPCompliantController("NJointPeriodicTSDMPCompliantController"); + + NJointPeriodicTSDMPCompliantController::NJointPeriodicTSDMPCompliantController(const RobotUnitPtr& robUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) + { + useSynchronizedRtRobot(); + cfg = NJointPeriodicTSDMPCompliantControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION(!cfg->nodeSetName.empty()); + VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); + + ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName); + for (size_t i = 0; i < rns->getSize(); ++i) + { + std::string jointName = rns->getNode(i)->getName(); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Torque1DoF); + const SensorValueBase* sv = useSensorValue(jointName); + targets.push_back(ct->asA<ControlTarget1DoFActuatorTorque>()); + + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + + velocitySensors.push_back(velocitySensor); + positionSensors.push_back(positionSensor); + }; + + tcp = rns->getTCP(); + // set tcp controller + nodeSetName = cfg->nodeSetName; + ik.reset(new VirtualRobot::DifferentialIK(rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + + TaskSpaceDMPControllerConfig taskSpaceDMPConfig; + taskSpaceDMPConfig.motionTimeDuration = cfg->timeDuration; + taskSpaceDMPConfig.DMPKernelSize = cfg->kernelSize; + taskSpaceDMPConfig.DMPAmplitude = cfg->dmpAmplitude; + taskSpaceDMPConfig.DMPMode = "Linear"; + taskSpaceDMPConfig.DMPStyle = "Periodic"; + taskSpaceDMPConfig.phaseStopParas.goDist = cfg->phaseDist0; + taskSpaceDMPConfig.phaseStopParas.backDist = cfg->phaseDist1; + taskSpaceDMPConfig.phaseStopParas.Kpos = cfg->phaseKpPos; + taskSpaceDMPConfig.phaseStopParas.Kori = cfg->phaseKpOri; + taskSpaceDMPConfig.phaseStopParas.mm2radi = cfg->posToOriRatio; + taskSpaceDMPConfig.phaseStopParas.maxValue = cfg->phaseL; + taskSpaceDMPConfig.phaseStopParas.slop = cfg->phaseK; + taskSpaceDMPConfig.phaseStopParas.Dpos = 0; + taskSpaceDMPConfig.phaseStopParas.Dori = 0; + + + + dmpCtrl.reset(new TaskSpaceDMPController("periodicDMP", taskSpaceDMPConfig, false)); + + NJointPeriodicTSDMPCompliantControllerControlData initData; + initData.targetTSVel.resize(6); + for (size_t i = 0; i < 6; ++i) + { + initData.targetTSVel(i) = 0; + } + reinitTripleBuffer(initData); + + firstRun = true; + dmpRunning = false; + + + ARMARX_CHECK_EQUAL(cfg->Kpos.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Dpos.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Kori.size(), 3); + ARMARX_CHECK_EQUAL(cfg->Dori.size(), 3); + + kpos << cfg->Kpos[0], cfg->Kpos[1], cfg->Kpos[2]; + dpos << cfg->Dpos[0], cfg->Dpos[1], cfg->Dpos[2]; + kori << cfg->Kori[0], cfg->Kori[1], cfg->Kori[2]; + dori << cfg->Dori[0], cfg->Dori[1], cfg->Dori[2]; + + kpf = cfg->Kpf; + knull = cfg->Knull; + dnull = cfg->Dnull; + + + + nullSpaceJointsVec.resize(cfg->desiredNullSpaceJointValues.size()); + for (size_t i = 0; i < cfg->desiredNullSpaceJointValues.size(); ++i) + { + nullSpaceJointsVec(i) = cfg->desiredNullSpaceJointValues.at(i); + } + + + const SensorValueBase* svlf = robUnit->getSensorDevice(cfg->forceSensorName)->getSensorValue(); + forceSensor = svlf->asA<SensorValueForceTorque>(); + + forceOffset.setZero(); + filteredForce.setZero(); + filteredForceInRoot.setZero(); + + + UserToRTData initUserData; + initUserData.targetForce = 0; + user2rtData.reinitAllBuffers(initUserData); + + oriToolDir << 0, 0, 1; + + qvel_filtered.setZero(targets.size()); + + ARMARX_CHECK_EQUAL(cfg->ws_x.size(), 2); + ARMARX_CHECK_EQUAL(cfg->ws_y.size(), 2); + ARMARX_CHECK_EQUAL(cfg->ws_z.size(), 2); + // only for ARMAR-6 (safe-guard) + ARMARX_CHECK_LESS(cfg->ws_x[0], cfg->ws_x[1]); + ARMARX_CHECK_LESS(cfg->ws_x[0], 1000); + ARMARX_CHECK_LESS(-200, cfg->ws_x[1]); + + ARMARX_CHECK_LESS(cfg->ws_y[0], cfg->ws_y[1]); + ARMARX_CHECK_LESS(cfg->ws_y[0], 1200); + ARMARX_CHECK_LESS(0, cfg->ws_y[1]); + + ARMARX_CHECK_LESS(cfg->ws_z[0], cfg->ws_z[1]); + ARMARX_CHECK_LESS(cfg->ws_z[0], 1800); + ARMARX_CHECK_LESS(300, cfg->ws_z[1]); + + adaptK = kpos; + lastDiff = 0; + changeTimer = 0; + } + + void NJointPeriodicTSDMPCompliantController::onInitNJointController() + { + ARMARX_INFO << "init ..."; + + + RTToControllerData initSensorData; + initSensorData.deltaT = 0; + initSensorData.currentTime = 0; + initSensorData.currentPose = tcp->getPoseInRootFrame(); + initSensorData.currentTwist.setZero(); + initSensorData.isPhaseStop = false; + rt2CtrlData.reinitAllBuffers(initSensorData); + + RTToUserData initInterfaceData; + initInterfaceData.currentTcpPose = tcp->getPoseInRootFrame(); + initInterfaceData.waitTimeForCalibration = 0; + rt2UserData.reinitAllBuffers(initInterfaceData); + + + ARMARX_IMPORTANT << "read force sensor ..."; + + forceOffset = forceSensor->force; + + ARMARX_IMPORTANT << "force offset: " << forceOffset; + + started = false; + + runTask("NJointPeriodicTSDMPCompliantController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); + + ARMARX_IMPORTANT << "started controller "; + + } + + std::string NJointPeriodicTSDMPCompliantController::getClassName(const Ice::Current&) const + { + return "NJointPeriodicTSDMPCompliantController"; + } + + void NJointPeriodicTSDMPCompliantController::controllerRun() + { + if (!started) + { + return; + } + + if (!dmpCtrl) + { + return; + } + + Eigen::VectorXf targetVels(6); + bool isPhaseStop = rt2CtrlData.getUpToDateReadBuffer().isPhaseStop; + if (isPhaseStop) + { + targetVels.setZero(); + } + else + { + + double deltaT = rt2CtrlData.getUpToDateReadBuffer().deltaT; + Eigen::Matrix4f currentPose = rt2CtrlData.getUpToDateReadBuffer().currentPose; + Eigen::VectorXf currentTwist = rt2CtrlData.getUpToDateReadBuffer().currentTwist; + + LockGuardType guard {controllerMutex}; + dmpCtrl->flow(deltaT, currentPose, currentTwist); + + targetVels = dmpCtrl->getTargetVelocity(); + + debugOutputData.getWriteBuffer().latestTargetVelocities["x_vel"] = targetVels(0); + debugOutputData.getWriteBuffer().latestTargetVelocities["y_vel"] = targetVels(1); + debugOutputData.getWriteBuffer().latestTargetVelocities["z_vel"] = targetVels(2); + debugOutputData.getWriteBuffer().latestTargetVelocities["roll_vel"] = targetVels(3); + debugOutputData.getWriteBuffer().latestTargetVelocities["pitch_vel"] = targetVels(4); + debugOutputData.getWriteBuffer().latestTargetVelocities["yaw_vel"] = targetVels(5); + debugOutputData.getWriteBuffer().currentPose["currentPose_x"] = currentPose(0, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_y"] = currentPose(1, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_z"] = currentPose(2, 3); + VirtualRobot::MathTools::Quaternion currentQ = VirtualRobot::MathTools::eigen4f2quat(currentPose); + debugOutputData.getWriteBuffer().currentPose["currentPose_qw"] = currentQ.w; + debugOutputData.getWriteBuffer().currentPose["currentPose_qx"] = currentQ.x; + debugOutputData.getWriteBuffer().currentPose["currentPose_qy"] = currentQ.y; + debugOutputData.getWriteBuffer().currentPose["currentPose_qz"] = currentQ.z; + debugOutputData.getWriteBuffer().currentCanVal = dmpCtrl->debugData.canVal; + debugOutputData.getWriteBuffer().mpcFactor = dmpCtrl->debugData.mpcFactor; + debugOutputData.getWriteBuffer().error = dmpCtrl->debugData.poseError; + debugOutputData.getWriteBuffer().posError = dmpCtrl->debugData.posiError; + debugOutputData.getWriteBuffer().oriError = dmpCtrl->debugData.oriError; + debugOutputData.getWriteBuffer().deltaT = deltaT; + debugOutputData.commitWrite(); + } + + getWriterControlStruct().targetTSVel = targetVels; + writeControlStruct(); + + dmpRunning = true; + } + + + void NJointPeriodicTSDMPCompliantController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + double deltaT = timeSinceLastIteration.toSecondsDouble(); + + Eigen::Matrix4f currentPose = tcp->getPoseInRootFrame(); + rt2UserData.getWriteBuffer().currentTcpPose = currentPose; + rt2UserData.getWriteBuffer().waitTimeForCalibration += deltaT; + rt2UserData.commitWrite(); + + + Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All); + + Eigen::VectorXf qpos(positionSensors.size()); + Eigen::VectorXf qvel(velocitySensors.size()); + for (size_t i = 0; i < positionSensors.size(); ++i) + { + qpos(i) = positionSensors[i]->position; + qvel(i) = velocitySensors[i]->velocity; + } + + qvel_filtered = (1 - cfg->velFilter) * qvel_filtered + cfg->velFilter * qvel; + Eigen::VectorXf currentTwist = jacobi * qvel_filtered; + + Eigen::VectorXf targetVel(6); + targetVel.setZero(); + if (firstRun || !dmpRunning) + { + lastPosition = currentPose.block<2, 1>(0, 3); + targetPose = currentPose; + firstRun = false; + filteredForce.setZero(); + + origHandOri = currentPose.block<3, 3>(0, 0); + toolTransform = origHandOri.transpose(); + // force calibration + if (!dmpRunning) + { + forceOffset = (1 - cfg->forceFilter) * forceOffset + cfg->forceFilter * forceSensor->force; + } + + targetVel.setZero(); + } + else + { + // communicate with DMP controller + rtUpdateControlStruct(); + targetVel = rtGetControlStruct().targetTSVel; + targetVel(2) = 0; + + // force detection + filteredForce = (1 - cfg->forceFilter) * filteredForce + cfg->forceFilter * (forceSensor->force - forceOffset); + + for (size_t i = 0; i < 3; ++i) + { + if (fabs(filteredForce(i)) > cfg->forceDeadZone) + { + filteredForce(i) -= (filteredForce(i) / fabs(filteredForce(i))) * cfg->forceDeadZone; + } + else + { + filteredForce(i) = 0; + } + } + Eigen::Matrix4f forceFrameInRoot = rtGetRobot()->getRobotNode(cfg->forceFrameName)->getPoseInRootFrame(); + filteredForceInRoot = forceFrameInRoot.block<3, 3>(0, 0) * filteredForce; + float targetForce = user2rtData.getUpToDateReadBuffer().targetForce; + + // Eigen::Matrix3f currentHandOri = currentPose.block<3, 3>(0, 0); + // Eigen::Matrix3f currentToolOri = toolTransform * currentHandOri; + float desiredZVel = kpf * (targetForce - filteredForceInRoot(2)); + targetVel(2) -= desiredZVel; + // targetVel.block<3, 1>(0, 0) = currentToolOri * targetVel.block<3, 1>(0, 0); + + + // Eigen::Vector3f currentToolDir = currentToolOri * oriToolDir; + + for (int i = 3; i < 6; ++i) + { + targetVel(i) = 0; + } + + // rotation changes + + // if (filteredForceInRoot.norm() > fabs(cfg->minimumReactForce)) + // { + // Eigen::Vector3f desiredToolDir = filteredForceInRoot / filteredForceInRoot.norm(); + // currentToolDir = currentToolDir / currentToolDir.norm(); + // Eigen::Vector3f axis = currentToolDir.cross(desiredToolDir); + // float angle = acosf(currentToolDir.dot(desiredToolDir)); + + // if (fabs(angle) < M_PI / 2) + // { + // Eigen::AngleAxisf desiredToolRot(angle, axis); + // Eigen::Matrix3f desiredRotMat = desiredToolRot * Eigen::Matrix3f::Identity(); + + // targetPose.block<3, 3>(0, 0) = desiredRotMat * currentHandOri; + + // Eigen::Vector3f desiredRPY = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); + // Eigen::Vector3f checkedToolDir = desiredRotMat * currentToolDir; + // ARMARX_IMPORTANT << "axis: " << axis; + // ARMARX_IMPORTANT << "angle: " << angle * 180 / 3.1415; + // ARMARX_IMPORTANT << "desiredRotMat: " << desiredRotMat; + + // ARMARX_IMPORTANT << "desiredToolDir: " << desiredToolDir; + // ARMARX_IMPORTANT << "currentToolDir: " << currentToolDir; + // ARMARX_IMPORTANT << "checkedToolDir: " << checkedToolDir; + // } + + // } + + + // integrate for targetPose + + + + + + } + + bool isPhaseStop = false; + + float diff = (targetPose.block<2, 1>(0, 3) - currentPose.block<2, 1>(0, 3)).norm(); + if (diff > cfg->phaseDist0) + { + isPhaseStop = true; + } + + float dTf = (float)deltaT; + + + if (filteredForceInRoot.block<2, 1>(0, 0).norm() > cfg->dragForceDeadZone) + { + Eigen::Vector2f dragForce = filteredForceInRoot.block<2, 1>(0, 0) - cfg->dragForceDeadZone * filteredForceInRoot.block<2, 1>(0, 0) / filteredForceInRoot.block<2, 1>(0, 0).norm(); + adaptK(0) = fmax(adaptK(0) - dTf * cfg->adaptForceCoeff * dragForce.norm(), 0); + adaptK(1) = fmax(adaptK(1) - dTf * cfg->adaptForceCoeff * dragForce.norm(), 0); + lastDiff = diff; + } + else + { + adaptK(0) = fmin(adaptK(0) + fabs(dTf * cfg->adaptCoeff), kpos(0)); + adaptK(1) = fmin(adaptK(1) + fabs(dTf * cfg->adaptCoeff), kpos(1)); + } + adaptK(2) = kpos(2); + + // adaptive control with distance + + + + + targetPose.block<3, 1>(0, 3) = targetPose.block<3, 1>(0, 3) + dTf * targetVel.block<3, 1>(0, 0); + + if (isPhaseStop) + { + Eigen::Vector2f currentXY = currentPose.block<2, 1>(0, 3); + if ((lastPosition - currentXY).norm() < cfg->changePositionTolerance) + { + changeTimer += deltaT; + } + else + { + lastPosition = currentPose.block<2, 1>(0, 3); + changeTimer = 0; + } + + if (changeTimer > cfg->changeTimerThreshold) + { + targetPose(0, 3) = currentPose(0, 3); + targetPose(1, 3) = currentPose(1, 3); + isPhaseStop = false; + changeTimer = 0; + } + } + else + { + changeTimer = 0; + } + + + targetPose(0, 3) = targetPose(0, 3) > cfg->ws_x[0] ? targetPose(0, 3) : cfg->ws_x[0]; + targetPose(0, 3) = targetPose(0, 3) < cfg->ws_x[1] ? targetPose(0, 3) : cfg->ws_x[1]; + + targetPose(1, 3) = targetPose(1, 3) > cfg->ws_y[0] ? targetPose(1, 3) : cfg->ws_y[0]; + targetPose(1, 3) = targetPose(1, 3) < cfg->ws_y[1] ? targetPose(1, 3) : cfg->ws_y[1]; + + targetPose(2, 3) = targetPose(2, 3) > cfg->ws_z[0] ? targetPose(2, 3) : cfg->ws_z[0]; + targetPose(2, 3) = targetPose(2, 3) < cfg->ws_z[1] ? targetPose(2, 3) : cfg->ws_z[1]; + + + + + debugRT.getWriteBuffer().targetPose = targetPose; + debugRT.getWriteBuffer().currentPose = currentPose; + debugRT.getWriteBuffer().filteredForce = filteredForceInRoot; + debugRT.getWriteBuffer().targetVel = targetVel; + debugRT.getWriteBuffer().adaptK = adaptK; + debugRT.getWriteBuffer().isPhaseStop = isPhaseStop; + debugRT.commitWrite(); + + rt2CtrlData.getWriteBuffer().currentPose = currentPose; + rt2CtrlData.getWriteBuffer().currentTwist = currentTwist; + rt2CtrlData.getWriteBuffer().deltaT = deltaT; + rt2CtrlData.getWriteBuffer().currentTime += deltaT; + rt2CtrlData.getWriteBuffer().isPhaseStop = isPhaseStop; + rt2CtrlData.commitWrite(); + + // Eigen::Matrix3f rotVel = VirtualRobot::MathTools::rpy2eigen3f(targetVel(3) * dTf, targetVel(4) * dTf, targetVel(5) * dTf); + // targetPose.block<3, 3>(0, 0) = rotVel * targetPose.block<3, 3>(0, 0); + + // inverse dynamic controller + jacobi.block<3, 8>(0, 0) = 0.001 * jacobi.block<3, 8>(0, 0); // convert mm to m + + + + + Eigen::Vector6f jointControlWrench; + { + Eigen::Vector3f targetTCPLinearVelocity; + targetTCPLinearVelocity << 0.001 * targetVel(0), 0.001 * targetVel(1), 0.001 * targetVel(2); + Eigen::Vector3f currentTCPLinearVelocity; + currentTCPLinearVelocity << 0.001 * currentTwist(0), 0.001 * currentTwist(1), 0.001 * currentTwist(2); + Eigen::Vector3f currentTCPPosition = currentPose.block<3, 1>(0, 3); + Eigen::Vector3f desiredPosition = targetPose.block<3, 1>(0, 3); + + Eigen::Vector3f linearVel = adaptK.cwiseProduct(desiredPosition - currentTCPPosition); + + // if (isPhaseStop) + // { + // linearVel = ((float)cfg->phaseKpPos) * (desiredPosition - currentTCPPosition); + // for (size_t i = 0; i < 3; ++i) + // { + // linearVel(i) = fmin(cfg->maxLinearVel, linearVel(i)); + // } + // } + // else + // { + // linearVel = kpos.cwiseProduct(desiredPosition - currentTCPPosition); + // } + Eigen::Vector3f tcpDesiredForce = 0.001 * linearVel + dpos.cwiseProduct(- currentTCPLinearVelocity); + + Eigen::Vector3f currentTCPAngularVelocity; + currentTCPAngularVelocity << currentTwist(3), currentTwist(4), currentTwist(5); + Eigen::Matrix3f currentRotMat = currentPose.block<3, 3>(0, 0); + Eigen::Matrix3f diffMat = targetPose.block<3, 3>(0, 0) * currentRotMat.inverse(); + Eigen::Vector3f rpy = VirtualRobot::MathTools::eigen3f2rpy(diffMat); + Eigen::Vector3f tcpDesiredTorque = kori.cwiseProduct(rpy) + dori.cwiseProduct(- currentTCPAngularVelocity); + jointControlWrench << tcpDesiredForce, tcpDesiredTorque; + } + + + + Eigen::MatrixXf I = Eigen::MatrixXf::Identity(targets.size(), targets.size()); + Eigen::VectorXf nullspaceTorque = knull * (nullSpaceJointsVec - qpos) - dnull * qvel; + Eigen::MatrixXf jtpinv = ik->computePseudoInverseJacobianMatrix(jacobi.transpose(), 2.0); + Eigen::VectorXf jointDesiredTorques = jacobi.transpose() * jointControlWrench + (I - jacobi.transpose() * jtpinv) * nullspaceTorque; + + // torque filter (maybe) + for (size_t i = 0; i < targets.size(); ++i) + { + targets.at(i)->torque = jointDesiredTorques(i); + + if (!targets.at(i)->isValid()) + { + targets.at(i)->torque = 0.0f; + } + else + { + if (fabs(targets.at(i)->torque) > fabs(cfg->maxJointTorque)) + { + targets.at(i)->torque = fabs(cfg->maxJointTorque) * (targets.at(i)->torque / fabs(targets.at(i)->torque)); + } + } + } + + + } + + + void NJointPeriodicTSDMPCompliantController::learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromFiles(fileNames); + + } + + void NJointPeriodicTSDMPCompliantController::learnDMPFromTrajectory(const TrajectoryBasePtr& trajectory, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + ARMARX_CHECK_EXPRESSION(trajectory); + TrajectoryPtr dmpTraj = TrajectoryPtr::dynamicCast(trajectory); + ARMARX_CHECK_EXPRESSION(dmpTraj); + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromTrajectory(dmpTraj); + + } + + void NJointPeriodicTSDMPCompliantController::setSpeed(Ice::Double times, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setSpeed(times); + } + + + void NJointPeriodicTSDMPCompliantController::setGoals(const Ice::DoubleSeq& goals, const Ice::Current& ice) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setGoalPoseVec(goals); + } + + void NJointPeriodicTSDMPCompliantController::setAmplitude(Ice::Double amp, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setAmplitude(amp); + } + + void NJointPeriodicTSDMPCompliantController::setTargetForceInRootFrame(float targetForce, const Ice::Current&) + { + LockGuardType guard {controlDataMutex}; + user2rtData.getWriteBuffer().targetForce = targetForce; + user2rtData.commitWrite(); + } + + void NJointPeriodicTSDMPCompliantController::runDMP(const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&) + { + firstRun = true; + while (firstRun || rt2UserData.getUpToDateReadBuffer().waitTimeForCalibration < cfg->waitTimeForCalibration) + { + usleep(100); + } + + + Eigen::Matrix4f pose = rt2UserData.getUpToDateReadBuffer().currentTcpPose; + + LockGuardType guard {controllerMutex}; + dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); + dmpCtrl->setSpeed(tau); + + ARMARX_IMPORTANT << "run DMP"; + started = true; + dmpRunning = false; + } + + + void NJointPeriodicTSDMPCompliantController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx& debugObs) + { + std::string datafieldName; + std::string debugName = "Periodic"; + StringVariantBaseMap datafields; + + Eigen::Matrix4f targetPoseDebug = debugRT.getUpToDateReadBuffer().targetPose; + datafields["target_x"] = new Variant(targetPoseDebug(0, 3)); + datafields["target_y"] = new Variant(targetPoseDebug(1, 3)); + datafields["target_z"] = new Variant(targetPoseDebug(2, 3)); + + Eigen::Matrix4f currentPoseDebug = debugRT.getUpToDateReadBuffer().currentPose; + datafields["current_x"] = new Variant(currentPoseDebug(0, 3)); + datafields["current_y"] = new Variant(currentPoseDebug(1, 3)); + datafields["current_z"] = new Variant(currentPoseDebug(2, 3)); + + Eigen::Vector3f filteredForce = debugRT.getUpToDateReadBuffer().filteredForce; + datafields["filteredforce_x"] = new Variant(filteredForce(0)); + datafields["filteredforce_y"] = new Variant(filteredForce(1)); + datafields["filteredforce_z"] = new Variant(filteredForce(2)); + + + Eigen::Vector3f reactForce = debugRT.getUpToDateReadBuffer().reactForce; + datafields["reactForce_x"] = new Variant(reactForce(0)); + datafields["reactForce_y"] = new Variant(reactForce(1)); + datafields["reactForce_z"] = new Variant(reactForce(2)); + + Eigen::VectorXf targetVel = debugRT.getUpToDateReadBuffer().targetVel; + datafields["targetVel_x"] = new Variant(targetVel(0)); + datafields["targetVel_y"] = new Variant(targetVel(1)); + datafields["targetVel_z"] = new Variant(targetVel(2)); + + Eigen::Vector3f adaptK = debugRT.getUpToDateReadBuffer().adaptK; + datafields["adaptK_x"] = new Variant(adaptK(0)); + datafields["adaptK_y"] = new Variant(adaptK(1)); + + datafields["canVal"] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + datafields["deltaT"] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + + datafields["PhaseStop"] = new Variant(debugRT.getUpToDateReadBuffer().isPhaseStop); + + + // datafields["targetVel_rx"] = new Variant(targetVel(3)); + // datafields["targetVel_ry"] = new Variant(targetVel(4)); + // datafields["targetVel_rz"] = new Variant(targetVel(5)); + + // auto values = debugOutputData.getUpToDateReadBuffer().latestTargetVelocities; + // for (auto& pair : values) + // { + // datafieldName = pair.first + "_" + debugName; + // datafields[datafieldName] = new Variant(pair.second); + // } + + // auto currentPose = debugOutputData.getUpToDateReadBuffer().currentPose; + // for (auto& pair : currentPose) + // { + // datafieldName = pair.first + "_" + debugName; + // datafields[datafieldName] = new Variant(pair.second); + // } + + // datafieldName = "canVal_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + // datafieldName = "mpcFactor_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().mpcFactor); + // datafieldName = "error_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().error); + // datafieldName = "posError_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().posError); + // datafieldName = "oriError_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().oriError); + // datafieldName = "deltaT_" + debugName; + // datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + + datafieldName = "PeriodicDMP"; + debugObs->setDebugChannel(datafieldName, datafields); + } + + + + void NJointPeriodicTSDMPCompliantController::onDisconnectNJointController() + { + ARMARX_INFO << "stopped ..."; + } + + + +} diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPCompliantController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPCompliantController.h new file mode 100644 index 0000000000000000000000000000000000000000..8b59a968731f1404f917d1a3f68e6a81dadd43ed --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPCompliantController.h @@ -0,0 +1,192 @@ + +#pragma once + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h> +#include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include <VirtualRobot/IK/DifferentialIK.h> +#include <RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.h> +#include <RobotAPI/libraries/core/CartesianVelocityController.h> + +#include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h> +#include <ArmarXCore/core/time/CycleUtil.h> + +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h> +#include <RobotAPI/libraries/core/Trajectory.h> + +namespace armarx +{ + + + TYPEDEF_PTRS_HANDLE(NJointPeriodicTSDMPCompliantController); + TYPEDEF_PTRS_HANDLE(NJointPeriodicTSDMPCompliantControllerControlData); + + class NJointPeriodicTSDMPCompliantControllerControlData + { + public: + Eigen::VectorXf targetTSVel; + }; + + class pidController + { + public: + float Kp = 0, Kd = 0; + float lastError = 0; + float update(float dt, float error) + { + float derivative = (error - lastError) / dt; + float retVal = Kp * error + Kd * derivative; + lastError = error; + return retVal; + } + }; + + /** + * @brief The NJointPeriodicTSDMPCompliantController class + * @ingroup Library-RobotUnit-NJointControllers + */ + class NJointPeriodicTSDMPCompliantController : + public NJointControllerWithTripleBuffer<NJointPeriodicTSDMPCompliantControllerControlData>, + public NJointPeriodicTSDMPCompliantControllerInterface + { + public: + using ConfigPtrT = NJointPeriodicTSDMPCompliantControllerConfigPtr; + NJointPeriodicTSDMPCompliantController(const RobotUnitPtr&, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + // NJointControllerInterface interface + std::string getClassName(const Ice::Current&) const; + + // NJointController interface + + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + // NJointPeriodicTSDMPCompliantControllerInterface interface + void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&); + void learnDMPFromTrajectory(const TrajectoryBasePtr& trajectory, const Ice::Current&); + bool isFinished(const Ice::Current&) + { + return false; + } + + void setSpeed(Ice::Double times, const Ice::Current&); + void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&); + void setAmplitude(Ice::Double amp, const Ice::Current&); + void runDMP(const Ice::DoubleSeq& goals, Ice::Double tau, const Ice::Current&); + void setTargetForceInRootFrame(Ice::Float force, const Ice::Current&); + double getCanVal(const Ice::Current&) + { + return dmpCtrl->canVal; + } + + protected: + virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); + + void onInitNJointController(); + void onDisconnectNJointController(); + void controllerRun(); + + private: + struct DebugBufferData + { + StringFloatDictionary latestTargetVelocities; + StringFloatDictionary currentPose; + double currentCanVal; + double mpcFactor; + double error; + double phaseStop; + double posError; + double oriError; + double deltaT; + }; + + TripleBuffer<DebugBufferData> debugOutputData; + + + struct DebugRTData + { + Eigen::Matrix4f targetPose; + Eigen::Vector3f filteredForce; + Eigen::Vector3f reactForce; + Eigen::Vector3f adaptK; + Eigen::VectorXf targetVel; + Eigen::Matrix4f currentPose; + bool isPhaseStop; + }; + TripleBuffer<DebugRTData> debugRT; + + struct RTToControllerData + { + double currentTime; + double deltaT; + Eigen::Matrix4f currentPose; + Eigen::VectorXf currentTwist; + bool isPhaseStop; + }; + TripleBuffer<RTToControllerData> rt2CtrlData; + + struct RTToUserData + { + Eigen::Matrix4f currentTcpPose; + float waitTimeForCalibration; + }; + TripleBuffer<RTToUserData> rt2UserData; + + struct UserToRTData + { + float targetForce; + }; + TripleBuffer<UserToRTData> user2rtData; + + + TaskSpaceDMPControllerPtr dmpCtrl; + + std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> positionSensors; + std::vector<ControlTarget1DoFActuatorTorque*> targets; + + // velocity ik controller parameters + std::string nodeSetName; + + bool started; + bool firstRun; + bool dmpRunning; + + VirtualRobot::DifferentialIKPtr ik; + VirtualRobot::RobotNodePtr tcp; + + NJointPeriodicTSDMPCompliantControllerConfigPtr cfg; + mutable MutexType controllerMutex; + PeriodicTask<NJointPeriodicTSDMPCompliantController>::pointer_type controllerTask; + Eigen::Matrix4f targetPose; + + Eigen::Vector3f kpos; + Eigen::Vector3f dpos; + Eigen::Vector3f kori; + Eigen::Vector3f dori; + float knull; + float dnull; + float kpf; + + Eigen::VectorXf nullSpaceJointsVec; + const SensorValueForceTorque* forceSensor; + + Eigen::Vector3f filteredForce; + Eigen::Vector3f forceOffset; + Eigen::Vector3f filteredForceInRoot; + + Eigen::Matrix3f toolTransform; + Eigen::Vector3f oriToolDir; + Eigen::Matrix3f origHandOri; + Eigen::VectorXf qvel_filtered; + + Eigen::Vector3f adaptK; + float lastDiff; + Eigen::Vector2f lastPosition; + double changeTimer; + + }; + +} // namespace armarx + diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d892d2c7ddf40dbef0b3bace0835d6890b848b7 --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.cpp @@ -0,0 +1,427 @@ +#include "NJointPeriodicTSDMPForwardVelController.h" + +namespace armarx +{ + NJointControllerRegistration<NJointPeriodicTSDMPForwardVelController> registrationControllerNJointPeriodicTSDMPForwardVelController("NJointPeriodicTSDMPForwardVelController"); + + NJointPeriodicTSDMPForwardVelController::NJointPeriodicTSDMPForwardVelController(const RobotUnitPtr& robUnit, const armarx::NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&) + { + useSynchronizedRtRobot(); + cfg = NJointPeriodicTSDMPControllerConfigPtr::dynamicCast(config); + ARMARX_CHECK_EXPRESSION(!cfg->nodeSetName.empty()); + VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); + + ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName); + for (size_t i = 0; i < rns->getSize(); ++i) + { + std::string jointName = rns->getNode(i)->getName(); + ControlTargetBase* ct = useControlTarget(jointName, ControlModes::Velocity1DoF); + const SensorValueBase* sv = useSensorValue(jointName); + targets.push_back(ct->asA<ControlTarget1DoFActuatorVelocity>()); + + const SensorValue1DoFActuatorVelocity* velocitySensor = sv->asA<SensorValue1DoFActuatorVelocity>(); + const SensorValue1DoFActuatorPosition* positionSensor = sv->asA<SensorValue1DoFActuatorPosition>(); + + velocitySensors.push_back(velocitySensor); + positionSensors.push_back(positionSensor); + }; + + tcp = rns->getTCP(); + // set tcp controller + tcpController.reset(new CartesianVelocityController(rns, tcp)); + nodeSetName = cfg->nodeSetName; + ik.reset(new VirtualRobot::DifferentialIK(rns, rns->getRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); + + + + TaskSpaceDMPControllerConfig taskSpaceDMPConfig; + taskSpaceDMPConfig.motionTimeDuration = cfg->timeDuration; + taskSpaceDMPConfig.DMPKernelSize = cfg->kernelSize; + taskSpaceDMPConfig.DMPAmplitude = cfg->dmpAmplitude; + + taskSpaceDMPConfig.DMPMode = "Linear"; + taskSpaceDMPConfig.DMPStyle = "Periodic"; + taskSpaceDMPConfig.phaseStopParas.goDist = cfg->phaseDist0; + taskSpaceDMPConfig.phaseStopParas.backDist = cfg->phaseDist1; + taskSpaceDMPConfig.phaseStopParas.Kpos = cfg->phaseKpPos; + taskSpaceDMPConfig.phaseStopParas.Kori = cfg->phaseKpOri; + taskSpaceDMPConfig.phaseStopParas.mm2radi = cfg->posToOriRatio; + taskSpaceDMPConfig.phaseStopParas.maxValue = cfg->phaseL; + taskSpaceDMPConfig.phaseStopParas.slop = cfg->phaseK; + taskSpaceDMPConfig.phaseStopParas.Dpos = 0; + taskSpaceDMPConfig.phaseStopParas.Dori = 0; + + + dmpCtrl.reset(new TaskSpaceDMPController("periodicDMP", taskSpaceDMPConfig, false)); + + NJointPeriodicTSDMPForwardVelControllerControlData initData; + initData.targetPose = tcp->getPoseInRootFrame(); + initData.targetTSVel.resize(6); + for (size_t i = 0; i < 6; ++i) + { + initData.targetTSVel(i) = 0; + } + reinitTripleBuffer(initData); + + finished = false; + firstRun = true; + + + const SensorValueBase* svlf = robUnit->getSensorDevice(cfg->forceSensorName)->getSensorValue(); + forceSensor = svlf->asA<SensorValueForceTorque>(); + + forceOffset.setZero(); + filteredForce.setZero(); + + UserToRTData initUserData; + initUserData.targetForce = 0; + user2rtData.reinitAllBuffers(initUserData); + + oriToolDir << 0, 0, 1; + + kpf = cfg->Kpf; + + } + + std::string NJointPeriodicTSDMPForwardVelController::getClassName(const Ice::Current&) const + { + return "NJointPeriodicTSDMPForwardVelController"; + } + + void NJointPeriodicTSDMPForwardVelController::controllerRun() + { + if (!started) + { + return; + } + + if (!rt2CtrlData.updateReadBuffer() || !dmpCtrl) + { + return; + } + + double deltaT = rt2CtrlData.getReadBuffer().deltaT; + Eigen::Matrix4f currentPose = rt2CtrlData.getReadBuffer().currentPose; + Eigen::VectorXf currentTwist = rt2CtrlData.getReadBuffer().currentTwist; + + LockGuardType guard {controllerMutex}; + dmpCtrl->flow(deltaT, currentPose, currentTwist); + + Eigen::VectorXf targetVels = dmpCtrl->getTargetVelocity(); + Eigen::Matrix4f targetPose = dmpCtrl->getIntegratedPoseMat(); + + debugOutputData.getWriteBuffer().latestTargetVelocities["x_vel"] = targetVels(0); + debugOutputData.getWriteBuffer().latestTargetVelocities["y_vel"] = targetVels(1); + debugOutputData.getWriteBuffer().latestTargetVelocities["z_vel"] = targetVels(2); + debugOutputData.getWriteBuffer().latestTargetVelocities["roll_vel"] = targetVels(3); + debugOutputData.getWriteBuffer().latestTargetVelocities["pitch_vel"] = targetVels(4); + debugOutputData.getWriteBuffer().latestTargetVelocities["yaw_vel"] = targetVels(5); + debugOutputData.getWriteBuffer().currentPose["currentPose_x"] = currentPose(0, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_y"] = currentPose(1, 3); + debugOutputData.getWriteBuffer().currentPose["currentPose_z"] = currentPose(2, 3); + VirtualRobot::MathTools::Quaternion currentQ = VirtualRobot::MathTools::eigen4f2quat(currentPose); + debugOutputData.getWriteBuffer().currentPose["currentPose_qw"] = currentQ.w; + debugOutputData.getWriteBuffer().currentPose["currentPose_qx"] = currentQ.x; + debugOutputData.getWriteBuffer().currentPose["currentPose_qy"] = currentQ.y; + debugOutputData.getWriteBuffer().currentPose["currentPose_qz"] = currentQ.z; + debugOutputData.getWriteBuffer().currentCanVal = dmpCtrl->debugData.canVal; + debugOutputData.getWriteBuffer().mpcFactor = dmpCtrl->debugData.mpcFactor; + debugOutputData.getWriteBuffer().error = dmpCtrl->debugData.poseError; + debugOutputData.getWriteBuffer().posError = dmpCtrl->debugData.posiError; + debugOutputData.getWriteBuffer().oriError = dmpCtrl->debugData.oriError; + debugOutputData.getWriteBuffer().deltaT = deltaT; + debugOutputData.commitWrite(); + + getWriterControlStruct().targetTSVel = targetVels; + getWriterControlStruct().targetPose = targetPose; + writeControlStruct(); + + dmpRunning = true; + } + + + void NJointPeriodicTSDMPForwardVelController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) + { + double deltaT = timeSinceLastIteration.toSecondsDouble(); + + Eigen::Matrix4f currentPose = tcp->getPoseInRootFrame(); + rt2UserData.getWriteBuffer().currentTcpPose = currentPose; + rt2UserData.getWriteBuffer().waitTimeForCalibration += deltaT; + rt2UserData.commitWrite(); + + if (firstRun || !dmpRunning) + { + targetPose = currentPose; + for (size_t i = 0; i < targets.size(); ++i) + { + targets.at(i)->velocity = 0.0f; + } + firstRun = false; + filteredForce.setZero(); + + Eigen::Matrix3f currentHandOri = currentPose.block<3, 3>(0, 0); + toolTransform = currentHandOri.transpose(); + // force calibration + if (!dmpRunning) + { + forceOffset = (1 - cfg->forceFilter) * forceOffset + cfg->forceFilter * forceSensor->force; + } + } + else + { + + Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All); + + Eigen::VectorXf qvel; + qvel.resize(velocitySensors.size()); + for (size_t i = 0; i < velocitySensors.size(); ++i) + { + qvel(i) = velocitySensors[i]->velocity; + } + + Eigen::VectorXf tcptwist = jacobi * qvel; + + rt2CtrlData.getWriteBuffer().currentPose = currentPose; + rt2CtrlData.getWriteBuffer().currentTwist = tcptwist; + rt2CtrlData.getWriteBuffer().deltaT = deltaT; + rt2CtrlData.getWriteBuffer().currentTime += deltaT; + rt2CtrlData.commitWrite(); + + + // forward controller + rtUpdateControlStruct(); + Eigen::VectorXf targetVel = rtGetControlStruct().targetTSVel; + // Eigen::Matrix4f targetPose = rtGetControlStruct().targetPose; + + // force detection + // filteredForce = (1 - cfg->forceFilter) * filteredForce + cfg->forceFilter * (forceSensor->force - forceOffset); + // Eigen::Vector3f filteredForceInRoot = rtGetRobot()->getRobotNode(cfg->forceFrameName)->toGlobalCoordinateSystemVec(filteredForce); + // filteredForceInRoot = rtGetRobot()->getRootNode()->toLocalCoordinateSystemVec(filteredForceInRoot); + // float targetForce = user2rtData.getUpToDateReadBuffer().targetForce; + + // Eigen::Matrix3f currentHandOri = currentPose.block<3, 3>(0, 0); + // Eigen::Matrix3f currentToolOri = toolTransform * currentHandOri; + // targetVel.block<3, 1>(0, 0) = currentToolOri * targetVel.block<3, 1>(0, 0); + // Eigen::Vector3f currentToolDir = currentToolOri * oriToolDir; + + // ARMARX_IMPORTANT << "original force: " << forceSensor->force; + // ARMARX_IMPORTANT << "filteredForce: " << filteredForce; + // ARMARX_IMPORTANT << "filteredForceInRoot: " << filteredForceInRoot; + // ARMARX_IMPORTANT << "forceOffset: " << forceOffset; + // ARMARX_IMPORTANT << "currentToolOri: " << currentToolOri; + + for (size_t i = 3; i < 6; ++i) + { + targetVel(i) = 0; + } + + // float forceCtrl = kpf * (targetForce - filteredForceInRoot.norm()); + // Eigen::Vector3f desiredZVel = - forceCtrl * (currentToolDir / currentToolDir.norm()); + // targetVel.block<3, 1>(0, 0) += desiredZVel; + + // dead zone for force + // if (filteredForceInRoot.norm() > cfg->minimumReactForce) + // { + // // rotation changes + // Eigen::Vector3f axis = oriToolDir.cross(filteredForceInRoot); + // float angle = oriToolDir.dot(filteredForceInRoot); + // Eigen::AngleAxisf desiredToolOri(angle, axis); + // Eigen::Matrix3f desiredHandOri = toolTransform.transpose() * desiredToolOri; + // Eigen::Matrix3f desiredRotMat = desiredHandOri * currentHandOri.transpose(); + // Eigen::Vector3f desiredRPY = VirtualRobot::MathTools::eigen3f2rpy(desiredRotMat); + // for (size_t i = 3; i < 6; ++i) + // { + // targetVel(i) = desiredRPY(i - 3); + // } + // }} + + // ARMARX_IMPORTANT << "targetVel: " << targetVel; + // ARMARX_IMPORTANT << "targetPose: " << targetPose; + + // targetPose.block<3, 1>(0, 3) += deltaT * targetVel.block<3, 1>(0, 0); + // Eigen::Matrix3f rotVel = VirtualRobot::MathTools::rpy2eigen3f(targetVel(3) * deltaT, targetVel(4) * deltaT, targetVel(5) * deltaT); + // targetPose.block<3, 3>(0, 0) = rotVel * targetPose.block<3, 3>(0, 0); + + float dTf = (float)deltaT; + targetPose.block<3, 1>(0, 3) = targetPose.block<3, 1>(0, 3) + dTf * targetVel.block<3, 1>(0, 0); + Eigen::Matrix3f rotVel = VirtualRobot::MathTools::rpy2eigen3f(targetVel(3) * dTf, targetVel(4) * dTf, targetVel(5) * dTf); + targetPose.block<3, 3>(0, 0) = rotVel * targetPose.block<3, 3>(0, 0); + + ARMARX_IMPORTANT << "targetVel: " << targetVel.block<3, 1>(0, 0); + ARMARX_IMPORTANT << "targetPose: " << targetPose; + ARMARX_IMPORTANT << "deltaT: " << deltaT; + + Eigen::Matrix3f diffMat = targetPose.block<3, 3>(0, 0) * currentPose.block<3, 3>(0, 0).inverse(); + Eigen::Vector3f errorRPY = VirtualRobot::MathTools::eigen3f2rpy(diffMat); + + Eigen::VectorXf rtTargetVel = targetVel; + rtTargetVel.block<3, 1>(0, 0) += cfg->Kpos * (targetPose.block<3, 1>(0, 3) - currentPose.block<3, 1>(0, 3)); + rtTargetVel.block<3, 1>(3, 0) += cfg->Kori * errorRPY; + + float normLinearVelocity = rtTargetVel.block<3, 1>(0, 0).norm(); + if (normLinearVelocity > fabs(cfg->maxLinearVel)) + { + rtTargetVel.block<3, 1>(0, 0) = fabs(cfg->maxLinearVel) * rtTargetVel.block<3, 1>(0, 0) / normLinearVelocity; + } + + float normAngularVelocity = rtTargetVel.block<3, 1>(3, 0).norm(); + if (normAngularVelocity > fabs(cfg->maxAngularVel)) + { + rtTargetVel.block<3, 1>(3, 0) = fabs(cfg->maxAngularVel) * rtTargetVel.block<3, 1>(3, 0) / normAngularVelocity; + } + + + // cartesian vel controller + + Eigen::VectorXf jnv = Eigen::VectorXf::Zero(tcpController->rns->getSize()); + if (cfg->avoidJointLimitsKp > 0) + { + jnv += cfg->avoidJointLimitsKp * tcpController->calculateJointLimitAvoidance(); + } + + Eigen::VectorXf jointTargetVelocities = tcpController->calculate(rtTargetVel, jnv, VirtualRobot::IKSolver::CartesianSelection::All); + for (size_t i = 0; i < targets.size(); ++i) + { + targets.at(i)->velocity = jointTargetVelocities(i); + if (!targets.at(i)->isValid()) + { + targets.at(i)->velocity = 0.0f; + } + else + { + if (fabs(targets.at(i)->velocity) > fabs(cfg->maxJointVel)) + { + targets.at(i)->velocity = fabs(cfg->maxJointVel) * (targets.at(i)->velocity / fabs(targets.at(i)->velocity)); + } + } + } + } + + } + + + void NJointPeriodicTSDMPForwardVelController::learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) + { + ARMARX_INFO << "Learning DMP ... "; + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromFiles(fileNames); + + } + + void NJointPeriodicTSDMPForwardVelController::setSpeed(Ice::Double times, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setSpeed(times); + } + + + void NJointPeriodicTSDMPForwardVelController::setGoals(const Ice::DoubleSeq& goals, const Ice::Current& ice) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setGoalPoseVec(goals); + } + + void NJointPeriodicTSDMPForwardVelController::setAmplitude(Ice::Double amp, const Ice::Current&) + { + LockGuardType guard {controllerMutex}; + dmpCtrl->setAmplitude(amp); + } + + void NJointPeriodicTSDMPForwardVelController::runDMP(const Ice::DoubleSeq& goals, double tau, const Ice::Current&) + { + firstRun = true; + while (firstRun || rt2UserData.getUpToDateReadBuffer().waitTimeForCalibration < cfg->waitTimeForCalibration) + { + usleep(100); + } + + Eigen::Matrix4f pose = rt2UserData.getUpToDateReadBuffer().currentTcpPose; + LockGuardType guard {controllerMutex}; + dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); + dmpCtrl->setSpeed(tau); + finished = false; + + ARMARX_INFO << "run DMP"; + started = true; + dmpRunning = false; + } + + + void NJointPeriodicTSDMPForwardVelController::onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx& debugObs) + { + std::string datafieldName; + std::string debugName = "Periodic"; + StringVariantBaseMap datafields; + auto values = debugOutputData.getUpToDateReadBuffer().latestTargetVelocities; + for (auto& pair : values) + { + datafieldName = pair.first + "_" + debugName; + datafields[datafieldName] = new Variant(pair.second); + } + + auto currentPose = debugOutputData.getUpToDateReadBuffer().currentPose; + for (auto& pair : currentPose) + { + datafieldName = pair.first + "_" + debugName; + datafields[datafieldName] = new Variant(pair.second); + } + + datafieldName = "canVal_" + debugName; + datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().currentCanVal); + datafieldName = "mpcFactor_" + debugName; + datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().mpcFactor); + datafieldName = "error_" + debugName; + datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().error); + datafieldName = "posError_" + debugName; + datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().posError); + datafieldName = "oriError_" + debugName; + datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().oriError); + datafieldName = "deltaT_" + debugName; + datafields[datafieldName] = new Variant(debugOutputData.getUpToDateReadBuffer().deltaT); + datafieldName = "DMPController_" + debugName; + + debugObs->setDebugChannel(datafieldName, datafields); + } + + void NJointPeriodicTSDMPForwardVelController::onInitNJointController() + { + ARMARX_INFO << "init ..."; + + RTToControllerData initSensorData; + initSensorData.deltaT = 0; + initSensorData.currentTime = 0; + initSensorData.currentPose = tcp->getPoseInRootFrame(); + initSensorData.currentTwist.setZero(); + rt2CtrlData.reinitAllBuffers(initSensorData); + + RTToUserData initInterfaceData; + initInterfaceData.currentTcpPose = tcp->getPoseInRootFrame(); + rt2UserData.reinitAllBuffers(initInterfaceData); + + + started = false; + runTask("NJointPeriodicTSDMPForwardVelController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); + + } + + void NJointPeriodicTSDMPForwardVelController::onDisconnectNJointController() + { + ARMARX_INFO << "stopped ..."; + } + + + +} diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.cpp.F24458 b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.cpp.F24458 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.h new file mode 100644 index 0000000000000000000000000000000000000000..351a9451c601ca2c61fc0416d016f41ecd4a3b73 --- /dev/null +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointPeriodicTSDMPForwardVelController.h @@ -0,0 +1,166 @@ + +#pragma once + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointController.h> +#include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/RobotUnit.h> +#include <RobotAPI/components/units/RobotUnit/ControlTargets/ControlTarget1DoFActuator.h> +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValue1DoFActuator.h> +#include <VirtualRobot/IK/DifferentialIK.h> +#include <RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.h> +#include <RobotAPI/libraries/core/CartesianVelocityController.h> + +#include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h> +#include <ArmarXCore/core/time/CycleUtil.h> + +#include <RobotAPI/components/units/RobotUnit/SensorValues/SensorValueForceTorque.h> + +namespace armarx +{ + + + TYPEDEF_PTRS_HANDLE(NJointPeriodicTSDMPForwardVelController); + TYPEDEF_PTRS_HANDLE(NJointPeriodicTSDMPForwardVelControllerControlData); + + class NJointPeriodicTSDMPForwardVelControllerControlData + { + public: + Eigen::VectorXf targetTSVel; + Eigen::Matrix4f targetPose; + }; + + class pidController + { + public: + float Kp = 0, Kd = 0; + float lastError = 0; + float update(float dt, float error) + { + float derivative = (error - lastError) / dt; + float retVal = Kp * error + Kd * derivative; + lastError = error; + return retVal; + } + }; + + /** + * @brief The NJointPeriodicTSDMPForwardVelController class + * @ingroup Library-RobotUnit-NJointControllers + */ + class NJointPeriodicTSDMPForwardVelController : + public NJointControllerWithTripleBuffer<NJointPeriodicTSDMPForwardVelControllerControlData>, + public NJointPeriodicTSDMPControllerInterface + { + public: + using ConfigPtrT = NJointPeriodicTSDMPControllerConfigPtr; + NJointPeriodicTSDMPForwardVelController(const RobotUnitPtr&, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); + + // NJointControllerInterface interface + std::string getClassName(const Ice::Current&) const; + + // NJointController interface + + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); + + // NJointPeriodicTSDMPForwardVelControllerInterface interface + void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&); + bool isFinished(const Ice::Current&) + { + return finished; + } + + void runDMP(const Ice::DoubleSeq& goals, double tau, const Ice::Current&); + void setSpeed(Ice::Double times, const Ice::Current&); + void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&); + void setAmplitude(Ice::Double amp, const Ice::Current&); + + + double getCanVal(const Ice::Current&) + { + return dmpCtrl->canVal; + } + + protected: + virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); + + void onInitNJointController(); + void onDisconnectNJointController(); + void controllerRun(); + + private: + struct DebugBufferData + { + StringFloatDictionary latestTargetVelocities; + StringFloatDictionary currentPose; + double currentCanVal; + double mpcFactor; + double error; + double phaseStop; + double posError; + double oriError; + double deltaT; + }; + + TripleBuffer<DebugBufferData> debugOutputData; + + struct RTToControllerData + { + double currentTime; + double deltaT; + Eigen::Matrix4f currentPose; + Eigen::VectorXf currentTwist; + }; + TripleBuffer<RTToControllerData> rt2CtrlData; + + struct RTToUserData + { + Eigen::Matrix4f currentTcpPose; + float waitTimeForCalibration; + }; + TripleBuffer<RTToUserData> rt2UserData; + + + TaskSpaceDMPControllerPtr dmpCtrl; + + std::vector<const SensorValue1DoFActuatorVelocity*> velocitySensors; + std::vector<const SensorValue1DoFActuatorPosition*> positionSensors; + std::vector<ControlTarget1DoFActuatorVelocity*> targets; + + // velocity ik controller parameters + CartesianVelocityControllerPtr tcpController; + std::string nodeSetName; + + // dmp parameters + bool finished; + bool started; + bool dmpRunning; + bool firstRun; + + VirtualRobot::DifferentialIKPtr ik; + VirtualRobot::RobotNodePtr tcp; + + NJointPeriodicTSDMPControllerConfigPtr cfg; + mutable MutexType controllerMutex; + PeriodicTask<NJointPeriodicTSDMPForwardVelController>::pointer_type controllerTask; + + Eigen::Matrix4f targetPose; + + + const SensorValueForceTorque* forceSensor; + Eigen::Vector3f filteredForce; + Eigen::Vector3f forceOffset; + Eigen::Matrix3f toolTransform; + Eigen::Vector3f oriToolDir; + struct UserToRTData + { + float targetForce; + }; + TripleBuffer<UserToRTData> user2rtData; + float kpf; + + // NJointPeriodicTSDMPControllerInterface interface + + }; + +} // namespace armarx + diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp index b24cb76fb61f8be9726f42bb58e9094bf68e4842..25e5943508f959ae1b505e248e833c51e0c7ad1a 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.cpp @@ -59,19 +59,29 @@ namespace armarx taskSpaceDMPConfig.phaseStopParas.maxValue = cfg->phaseL; taskSpaceDMPConfig.phaseStopParas.slop = cfg->phaseK; - taskSpaceDMPController.reset(new TaskSpaceDMPController("default", taskSpaceDMPConfig, false)); + dmpCtrl.reset(new TaskSpaceDMPController("default", taskSpaceDMPConfig, false)); // initialize tcp position and orientation - NJointTSDMPControllerSensorData initSensorData; + RTToControllerData initSensorData; initSensorData.deltaT = 0; initSensorData.currentTime = 0; initSensorData.currentPose.setZero(); initSensorData.currentTwist.setZero(); - controllerSensorData.reinitAllBuffers(initSensorData); + rt2CtrlData.reinitAllBuffers(initSensorData); + targetVels.resize(6); + NJointTSDMPControllerControlData initData; + initData.targetTSVel.resize(6); + for (size_t i = 0; i < 6; ++i) + { + initData.targetTSVel(i) = 0; + targetVels(i) = 0; + } + initData.nullspaceJointVelocities.resize(tcpController->rns->getSize(), 0); + reinitTripleBuffer(initData); debugName = cfg->debugName; @@ -105,6 +115,9 @@ namespace armarx NJointTSDMPControllerInterfaceData initInterfaceData; initInterfaceData.currentTcpPose = tcp->getPoseInRootFrame(); interfaceData.reinitAllBuffers(initInterfaceData); + RTToUserData initInterfaceData2; + initInterfaceData2.currentTcpPose = Eigen::Matrix4f::Identity(); + rt2UserData.reinitAllBuffers(initInterfaceData2); } std::string NJointTSDMPController::getClassName(const Ice::Current&) const @@ -119,31 +132,25 @@ namespace armarx return; } - if (!controllerSensorData.updateReadBuffer() || !taskSpaceDMPController) + if (!rt2CtrlData.updateReadBuffer() || !dmpCtrl) { return; } - double deltaT = controllerSensorData.getReadBuffer().deltaT; - Eigen::Matrix4f currentPose = controllerSensorData.getReadBuffer().currentPose; - Eigen::VectorXf currentTwist = controllerSensorData.getReadBuffer().currentTwist; + double deltaT = rt2CtrlData.getReadBuffer().deltaT; + Eigen::Matrix4f currentPose = rt2CtrlData.getReadBuffer().currentPose; + Eigen::VectorXf currentTwist = rt2CtrlData.getReadBuffer().currentTwist; LockGuardType guard {controllerMutex}; - taskSpaceDMPController->flow(deltaT, currentPose, currentTwist); + dmpCtrl->flow(deltaT, currentPose, currentTwist); - - if (taskSpaceDMPController->canVal < 1e-8) + if (dmpCtrl->canVal < 1e-8) { finished = true; } - targetVels = taskSpaceDMPController->getTargetVelocity(); - targetPose = taskSpaceDMPController->getTargetPoseMat(); - std::vector<double> targetState = taskSpaceDMPController->getTargetPose(); - - // ARMARX_IMPORTANT << "CanVal: " << taskSpaceDMPController->canVal; - // ARMARX_IMPORTANT << "currentPose: " << currentPose; - // ARMARX_IMPORTANT << "targetVels: " << targetVels; - // ARMARX_IMPORTANT << "targetPose: " << targetPose; + targetVels = dmpCtrl->getTargetVelocity(); + targetPose = dmpCtrl->getTargetPoseMat(); + std::vector<double> targetState = dmpCtrl->getTargetPose(); debugOutputData.getWriteBuffer().latestTargetVelocities["x_vel"] = targetVels(0); debugOutputData.getWriteBuffer().latestTargetVelocities["y_vel"] = targetVels(1); @@ -167,20 +174,21 @@ namespace armarx debugOutputData.getWriteBuffer().currentPose["currentPose_qx"] = currentQ.x; debugOutputData.getWriteBuffer().currentPose["currentPose_qy"] = currentQ.y; debugOutputData.getWriteBuffer().currentPose["currentPose_qz"] = currentQ.z; - debugOutputData.getWriteBuffer().currentCanVal = taskSpaceDMPController->debugData.canVal; - debugOutputData.getWriteBuffer().mpcFactor = taskSpaceDMPController->debugData.mpcFactor; - debugOutputData.getWriteBuffer().error = taskSpaceDMPController->debugData.poseError; - debugOutputData.getWriteBuffer().posError = taskSpaceDMPController->debugData.posiError; - debugOutputData.getWriteBuffer().oriError = taskSpaceDMPController->debugData.oriError; + debugOutputData.getWriteBuffer().currentCanVal = dmpCtrl->debugData.canVal; + debugOutputData.getWriteBuffer().mpcFactor = dmpCtrl->debugData.mpcFactor; + debugOutputData.getWriteBuffer().error = dmpCtrl->debugData.poseError; + debugOutputData.getWriteBuffer().posError = dmpCtrl->debugData.posiError; + debugOutputData.getWriteBuffer().oriError = dmpCtrl->debugData.oriError; debugOutputData.getWriteBuffer().deltaT = deltaT; debugOutputData.commitWrite(); + getWriterControlStruct().targetTSVel = targetVels; getWriterControlStruct().targetPose = targetPose; - writeControlStruct(); + } @@ -223,13 +231,14 @@ namespace armarx filtered_qvel = (1 - vel_filter_factor) * filtered_qvel + vel_filter_factor * qvel; Eigen::VectorXf tcptwist = jacobi * filtered_qvel; - controllerSensorData.getWriteBuffer().currentPose = currentPose; - controllerSensorData.getWriteBuffer().currentTwist = tcptwist; - controllerSensorData.getWriteBuffer().deltaT = deltaT; - controllerSensorData.getWriteBuffer().currentTime += deltaT; - controllerSensorData.commitWrite(); - + rt2CtrlData.getWriteBuffer().currentPose = currentPose; + rt2CtrlData.getWriteBuffer().currentTwist = tcptwist; + rt2CtrlData.getWriteBuffer().deltaT = deltaT; + rt2CtrlData.getWriteBuffer().currentTime += deltaT; + rt2CtrlData.commitWrite(); + rt2UserData.getWriteBuffer().currentTcpPose = currentPose; + rt2UserData.commitWrite(); Eigen::VectorXf targetVel = rtGetControlStruct().targetTSVel; Eigen::Matrix4f targetPose = rtGetControlStruct().targetPose; @@ -273,38 +282,10 @@ namespace armarx // cartesian vel controller Eigen::VectorXf x; - auto mode = rtGetControlStruct().mode; - - if (mode == VirtualRobot::IKSolver::All) + x.resize(6); + for (size_t i = 0; i < 6; i++) { - x.resize(6); - for (size_t i = 0; i < 6; i++) - { - x(i) = rtTargetVel(i); - } - } - else if (mode == VirtualRobot::IKSolver::Position) - { - x.resize(3); - for (size_t i = 0; i < 3; i++) - { - x(i) = rtTargetVel(i); - } - - } - else if (mode == VirtualRobot::IKSolver::Orientation) - { - x.resize(3); - for (size_t i = 0; i < 3; i++) - { - x(i) = rtTargetVel(i + 3); - } - } - else - { - - // No mode has been set - return; + x(i) = rtTargetVel(i); } Eigen::VectorXf jnv = Eigen::VectorXf::Zero(tcpController->rns->getSize()); @@ -318,7 +299,7 @@ namespace armarx jnv(i) += rtGetControlStruct().nullspaceJointVelocities.at(i); } - Eigen::VectorXf jointTargetVelocities = tcpController->calculate(x, jnv, mode); + Eigen::VectorXf jointTargetVelocities = tcpController->calculate(x, jnv, VirtualRobot::IKSolver::CartesianSelection::All); ARMARX_CHECK_EXPRESSION(!targets.empty()); ARMARX_CHECK_LESS(targets.size(), 1000); for (size_t i = 0; i < targets.size(); ++i) @@ -327,35 +308,36 @@ namespace armarx if (!targets.at(i)->isValid() || fabs(targets.at(i)->velocity) > cfg->maxJointVelocity) { - ARMARX_IMPORTANT << deactivateSpam(1, std::to_string(i)) - << "Velocity controller target is invalid - setting to zero! set value: " << targets.at(i)->velocity; + //ARMARX_IMPORTANT << deactivateSpam(1, std::to_string(i)) + // << "Velocity controller target is invalid - setting to zero! set value: " << targets.at(i)->velocity; targets.at(i)->velocity = 0.0f; + } } - } void NJointTSDMPController::learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) { - taskSpaceDMPController->learnDMPFromFiles(fileNames); + ARMARX_INFO << "Learning DMP ... "; + + LockGuardType guard {controllerMutex}; + dmpCtrl->learnDMPFromFiles(fileNames); - ARMARX_INFO << "Learned DMP ... "; } void NJointTSDMPController::setSpeed(Ice::Double times, const Ice::Current&) { LockGuardType guard {controllerMutex}; - taskSpaceDMPController->setSpeed(times); + dmpCtrl->setSpeed(times); } void NJointTSDMPController::setViaPoints(Ice::Double u, const Ice::DoubleSeq& viapoint, const Ice::Current&) { LockGuardType guard {controllerMutex}; - ARMARX_INFO << "setting via points "; - taskSpaceDMPController->setViaPose(u, viapoint); + dmpCtrl->setViaPose(u, viapoint); } void NJointTSDMPController::removeAllViaPoints(const Ice::Current&) @@ -368,54 +350,8 @@ namespace armarx void NJointTSDMPController::setGoals(const Ice::DoubleSeq& goals, const Ice::Current& ice) { - taskSpaceDMPController->setGoalPoseVec(goals); - } - - void NJointTSDMPController::setControllerTarget(Ice::Float avoidJointLimitsKp, NJointTaskSpaceDMPControllerMode::CartesianSelection mode, const Ice::Current&) - { - LockGuardType guard {controlDataMutex}; - getWriterControlStruct().avoidJointLimitsKp = avoidJointLimitsKp; - getWriterControlStruct().mode = ModeFromIce(mode); - writeControlStruct(); - } - - VirtualRobot::IKSolver::CartesianSelection NJointTSDMPController::ModeFromIce(const NJointTaskSpaceDMPControllerMode::CartesianSelection mode) - { - if (mode == NJointTaskSpaceDMPControllerMode::CartesianSelection::ePosition) - { - return VirtualRobot::IKSolver::CartesianSelection::Position; - } - if (mode == NJointTaskSpaceDMPControllerMode::CartesianSelection::eOrientation) - { - return VirtualRobot::IKSolver::CartesianSelection::Orientation; - } - if (mode == NJointTaskSpaceDMPControllerMode::CartesianSelection::eAll) - { - return VirtualRobot::IKSolver::CartesianSelection::All; - } - ARMARX_ERROR_S << "invalid mode " << mode; - return (VirtualRobot::IKSolver::CartesianSelection)0; - } - - - void NJointTSDMPController::setTorqueKp(const StringFloatDictionary& torqueKp, const Ice::Current&) - { - LockGuardType guard {controlDataMutex}; - for (size_t i = 0; i < tcpController->rns->getSize(); i++) - { - getWriterControlStruct().torqueKp.at(i) = torqueKp.at(tcpController->rns->getNode(i)->getName()); - } - writeControlStruct(); - } - - void NJointTSDMPController::setNullspaceJointVelocities(const StringFloatDictionary& nullspaceJointVelocities, const Ice::Current&) - { - LockGuardType guard {controlDataMutex}; - for (size_t i = 0; i < tcpController->rns->getSize(); i++) - { - getWriterControlStruct().nullspaceJointVelocities.at(i) = nullspaceJointVelocities.at(tcpController->rns->getNode(i)->getName()); - } - writeControlStruct(); + LockGuardType guard {controllerMutex}; + dmpCtrl->setGoalPoseVec(goals); } void NJointTSDMPController::pauseDMP(const Ice::Current&) @@ -450,16 +386,16 @@ namespace armarx { usleep(100); } - while (!interfaceData.updateReadBuffer()) + while (!rt2UserData.updateReadBuffer()) { usleep(100); } - Eigen::Matrix4f pose = interfaceData.getReadBuffer().currentTcpPose; - ARMARX_INFO << "current pose: " << pose; - // Eigen::Matrix4f pose = tcp->getPoseInRootFrame(); + Eigen::Matrix4f pose = rt2UserData.getReadBuffer().currentTcpPose; + LockGuardType guard {controllerMutex}; - taskSpaceDMPController->prepareExecution(taskSpaceDMPController->eigen4f2vec(pose), goals, tau); + // Eigen::Matrix4f pose = tcp->getPoseInRootFrame(); + dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); finished = false; ARMARX_INFO << "run DMP"; @@ -520,9 +456,10 @@ namespace armarx debugObs->setDebugChannel(datafieldName, datafields); } - void NJointTSDMPController::onInitComponent() + void NJointTSDMPController::onInitNJointController() { ARMARX_INFO << "init ..."; +/* targetVels.resize(6); NJointTSDMPControllerControlData initData; initData.targetTSVel.resize(6); @@ -537,19 +474,28 @@ namespace armarx initData.torqueKd.resize(tcpController->rns->getSize(), 0); initData.mode = ModeFromIce(cfg->mode); reinitTripleBuffer(initData); +*/ - - - controllerTask = new PeriodicTask<NJointTSDMPController>(this, &NJointTSDMPController::controllerRun, 0.3); - controllerTask->start(); - + started = false; + runTask("NJointTSDMPController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); } - void NJointTSDMPController::onDisconnectComponent() + void NJointTSDMPController::onDisconnectNJointController() { ARMARX_INFO << "stopped ..."; - controllerTask->stop(); } diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h index 9eea8c9bbe2f40204b22feced8784250527d1602..75cf0f9067823c869b61e4ec18585c7f83464cac 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTSDMPController.h @@ -12,6 +12,8 @@ #include <RobotAPI/libraries/core/CartesianVelocityController.h> #include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h> +#include <ArmarXCore/core/time/CycleUtil.h> + using namespace DMP; namespace armarx @@ -31,9 +33,6 @@ namespace armarx // cartesian velocity control data std::vector<float> nullspaceJointVelocities; float avoidJointLimitsKp = 0; - std::vector<float> torqueKp; - std::vector<float> torqueKd; - VirtualRobot::IKSolver::CartesianSelection mode = VirtualRobot::IKSolver::All; }; class pidController @@ -91,9 +90,10 @@ namespace armarx void resetDMP(const Ice::Current&) override; void stopDMP(const Ice::Current&) override; + double getCanVal(const Ice::Current&) override { - return taskSpaceDMPController->canVal / cfg->timeDuration; + return dmpCtrl->canVal; } protected: @@ -102,8 +102,8 @@ namespace armarx VirtualRobot::IKSolver::CartesianSelection ModeFromIce(const NJointTaskSpaceDMPControllerMode::CartesianSelection mode); virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; - void onInitComponent() override; - void onDisconnectComponent() override; + void onInitNJointController() override; + void onDisconnectNJointController() override; void controllerRun(); private: @@ -123,21 +123,26 @@ namespace armarx TripleBuffer<DebugBufferData> debugOutputData; - struct NJointTSDMPControllerSensorData + struct RTToControllerData { double currentTime; double deltaT; Eigen::Matrix4f currentPose; Eigen::VectorXf currentTwist; }; - TripleBuffer<NJointTSDMPControllerSensorData> controllerSensorData; + TripleBuffer<RTToControllerData> rt2CtrlData; - struct NJointTSDMPControllerInterfaceData + struct RTToUserData { Eigen::Matrix4f currentTcpPose; + }; + TripleBuffer<RTToUserData> rt2UserData; + + + TaskSpaceDMPControllerPtr dmpCtrl; + - TripleBuffer<NJointTSDMPControllerInterfaceData> interfaceData; std::vector<const SensorValue1DoFActuatorTorque*> torqueSensors; std::vector<const SensorValue1DoFGravityTorque*> gravityTorqueSensors; @@ -146,7 +151,6 @@ namespace armarx std::vector<const SensorValue1DoFActuatorPosition*> positionSensors; - TaskSpaceDMPControllerPtr taskSpaceDMPController; // velocity ik controller parameters std::vector<pidController> torquePIDs; diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp index dfb40405adbc4a8613233833a252145fbc8d2c57..5675efd9521c6092afb4e3e84f1cbb784f58584a 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp @@ -8,7 +8,7 @@ namespace armarx { cfg = NJointTaskSpaceImpedanceDMPControllerConfigPtr::dynamicCast(config); useSynchronizedRtRobot(); - VirtualRobot::RobotNodeSetPtr rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); + rns = rtGetRobot()->getRobotNodeSet(cfg->nodeSetName); ARMARX_CHECK_EXPRESSION_W_HINT(rns, cfg->nodeSetName); for (size_t i = 0; i < rns->getSize(); ++i) @@ -36,7 +36,7 @@ namespace armarx tcp = rns->getTCP(); ik.reset(new VirtualRobot::DifferentialIK(rns, rtGetRobot()->getRootNode(), VirtualRobot::JacobiProvider::eSVDDamped)); - + numOfJoints = targets.size(); // set DMP TaskSpaceDMPControllerConfig taskSpaceDMPConfig; taskSpaceDMPConfig.motionTimeDuration = cfg->timeDuration; @@ -75,8 +75,19 @@ namespace armarx dpos << cfg->Dpos[0], cfg->Dpos[1], cfg->Dpos[2]; kori << cfg->Kori[0], cfg->Kori[1], cfg->Kori[2]; dori << cfg->Dori[0], cfg->Dori[1], cfg->Dori[2]; - knull = cfg->Knull; - dnull = cfg->Dnull; + + + ARMARX_CHECK_EQUAL(cfg->Knull.size(), targets.size()); + ARMARX_CHECK_EQUAL(cfg->Dnull.size(), targets.size()); + + knull.setZero(targets.size()); + dnull.setZero(targets.size()); + + for (size_t i = 0; i < targets.size(); ++i) + { + knull(i) = cfg->Knull.at(i); + dnull(i) = cfg->Dnull.at(i); + } torqueLimit = cfg->torqueLimit; timeDuration = cfg->timeDuration; @@ -91,6 +102,8 @@ namespace armarx initControllerSensorData.deltaT = 0; initControllerSensorData.currentTwist.setZero(); controllerSensorData.reinitAllBuffers(initControllerSensorData); + + firstRun = true; } std::string NJointTaskSpaceImpedanceDMPController::getClassName(const Ice::Current&) const @@ -115,63 +128,99 @@ namespace armarx void NJointTaskSpaceImpedanceDMPController::controllerRun() { - if (!controllerSensorData.updateReadBuffer()) + + + if (!dmpCtrl) { return; } - if (dmpCtrl->canVal < 1e-8) + if (!controllerSensorData.updateReadBuffer()) { - finished = true; - LockGuardType guard {controlDataMutex}; - getWriterControlStruct().targetVel.setZero(); - writeControlStruct(); return; } + double deltaT = 0.001; //controllerSensorData.getReadBuffer().deltaT; Eigen::Matrix4f currentPose = controllerSensorData.getReadBuffer().currentPose; Eigen::VectorXf currentTwist = controllerSensorData.getReadBuffer().currentTwist; - dmpCtrl->flow(deltaT, currentPose, currentTwist); - - Eigen::VectorXf desiredNullSpaceJointValues(jointNames.size()); - if (useNullSpaceJointDMP && isNullSpaceJointDMPLearned) + if (!started) { - DMP::DVec targetJointState; - currentJointState = nullSpaceJointDMPPtr->calculateDirectlyVelocity(currentJointState, dmpCtrl->canVal / timeDuration, deltaT / timeDuration, targetJointState); - - if (targetJointState.size() == jointNames.size()) + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().desiredNullSpaceJointValues = defaultNullSpaceJointValues; + getWriterControlStruct().targetVel.setZero(6); + getWriterControlStruct().targetPose = currentPose; + getWriterControlStruct().canVal = 1.0; + getWriterControlStruct().mpcFactor = 0.0; + writeControlStruct(); + } + else + { + if (stopped) { - for (size_t i = 0; i < targetJointState.size(); ++i) - { - desiredNullSpaceJointValues(i) = targetJointState[i]; - } + + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().desiredNullSpaceJointValues = defaultNullSpaceJointValues; + getWriterControlStruct().targetVel.setZero(6); + getWriterControlStruct().targetPose = oldPose; + getWriterControlStruct().canVal = dmpCtrl->canVal; + getWriterControlStruct().mpcFactor = dmpCtrl->debugData.mpcFactor; + writeControlStruct(); } else { - desiredNullSpaceJointValues = defaultNullSpaceJointValues; - } - } - else - { - desiredNullSpaceJointValues = defaultNullSpaceJointValues; - } + if (dmpCtrl->canVal < 1e-8) + { + finished = true; + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().targetVel.setZero(); + writeControlStruct(); + return; + } + + dmpCtrl->flow(deltaT, currentPose, currentTwist); + + Eigen::VectorXf desiredNullSpaceJointValues(jointNames.size()); + if (useNullSpaceJointDMP && isNullSpaceJointDMPLearned) + { + DMP::DVec targetJointState; + currentJointState = nullSpaceJointDMPPtr->calculateDirectlyVelocity(currentJointState, dmpCtrl->canVal / timeDuration, deltaT / timeDuration, targetJointState); + + if (targetJointState.size() == jointNames.size()) + { + for (size_t i = 0; i < targetJointState.size(); ++i) + { + desiredNullSpaceJointValues(i) = targetJointState[i]; + } + } + else + { + desiredNullSpaceJointValues = defaultNullSpaceJointValues; + } + } + else + { + desiredNullSpaceJointValues = defaultNullSpaceJointValues; + } - LockGuardType guard {controlDataMutex}; - getWriterControlStruct().desiredNullSpaceJointValues = desiredNullSpaceJointValues; - getWriterControlStruct().targetVel = dmpCtrl->getTargetVelocity(); - getWriterControlStruct().targetPose = dmpCtrl->getTargetPoseMat(); - getWriterControlStruct().canVal = dmpCtrl->canVal; - getWriterControlStruct().mpcFactor = dmpCtrl->debugData.mpcFactor; + LockGuardType guard {controlDataMutex}; + getWriterControlStruct().desiredNullSpaceJointValues = desiredNullSpaceJointValues; + getWriterControlStruct().targetVel = dmpCtrl->getTargetVelocity(); + getWriterControlStruct().targetPose = dmpCtrl->getTargetPoseMat(); + getWriterControlStruct().canVal = dmpCtrl->canVal; + getWriterControlStruct().mpcFactor = dmpCtrl->debugData.mpcFactor; - writeControlStruct(); + writeControlStruct(); + } + } } void NJointTaskSpaceImpedanceDMPController::rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) { double deltaT = timeSinceLastIteration.toSecondsDouble(); + Eigen::MatrixXf jacobi = ik->getJacobianMatrix(tcp, VirtualRobot::IKSolver::CartesianSelection::All); Eigen::VectorXf qpos(positionSensors.size()); @@ -194,11 +243,25 @@ namespace armarx interfaceData.getWriteBuffer().currentTcpPose = currentPose; interfaceData.commitWrite(); - jacobi.block<3, 8>(0, 0) = 0.001 * jacobi.block<3, 8>(0, 0); // convert mm to m + jacobi.block(0, 0, 3, numOfJoints) = 0.001 * jacobi.block(0, 0, 3, numOfJoints); // convert mm to m - Eigen::Matrix4f targetPose = rtGetControlStruct().targetPose; - Eigen::VectorXf targetVel = rtGetControlStruct().targetVel; + Eigen::Matrix4f targetPose; + Eigen::VectorXf targetVel; + Eigen::VectorXf desiredNullSpaceJointValues; + if (firstRun) + { + firstRun = false; + targetPose = currentPose; + targetVel.setZero(6); + desiredNullSpaceJointValues = defaultNullSpaceJointValues; + } + else + { + targetPose = rtGetControlStruct().targetPose; + targetVel = rtGetControlStruct().targetVel; + desiredNullSpaceJointValues = rtGetControlStruct().desiredNullSpaceJointValues; + } Eigen::Vector6f jointControlWrench; { Eigen::Vector3f targetTCPLinearVelocity; @@ -207,7 +270,7 @@ namespace armarx currentTCPLinearVelocity << 0.001 * currentTwist(0), 0.001 * currentTwist(1), 0.001 * currentTwist(2); Eigen::Vector3f currentTCPPosition = currentPose.block<3, 1>(0, 3); Eigen::Vector3f desiredPosition = targetPose.block<3, 1>(0, 3); - Eigen::Vector3f tcpDesiredForce = 0.001 * kpos.cwiseProduct(desiredPosition - currentTCPPosition) + dpos.cwiseProduct(- currentTCPLinearVelocity); + Eigen::Vector3f tcpDesiredForce = 0.001 * kpos.cwiseProduct(desiredPosition - currentTCPPosition) + dpos.cwiseProduct(targetTCPLinearVelocity - currentTCPLinearVelocity); Eigen::Vector3f currentTCPAngularVelocity; currentTCPAngularVelocity << currentTwist(3), currentTwist(4), currentTwist(5); @@ -220,8 +283,7 @@ namespace armarx Eigen::MatrixXf I = Eigen::MatrixXf::Identity(targets.size(), targets.size()); - Eigen::VectorXf desiredNullSpaceJointValues = rtGetControlStruct().desiredNullSpaceJointValues; - Eigen::VectorXf nullspaceTorque = knull * (desiredNullSpaceJointValues - qpos) - dnull * qvel; + Eigen::VectorXf nullspaceTorque = knull.cwiseProduct(desiredNullSpaceJointValues - qpos) - dnull.cwiseProduct(qvel); Eigen::MatrixXf jtpinv = ik->computePseudoInverseJacobianMatrix(jacobi.transpose(), 2.0); Eigen::VectorXf jointDesiredTorques = jacobi.transpose() * jointControlWrench + (I - jacobi.transpose() * jtpinv) * nullspaceTorque; @@ -263,8 +325,8 @@ namespace armarx debugOutputData.getWriteBuffer().forceDesired_ry = jointControlWrench(4); debugOutputData.getWriteBuffer().forceDesired_rz = jointControlWrench(5); - debugOutputData.getWriteBuffer().currentCanVal = rtGetControlStruct().canVal; - debugOutputData.getWriteBuffer().mpcfactor = rtGetControlStruct().mpcFactor; + // debugOutputData.getWriteBuffer().currentCanVal = rtGetControlStruct().canVal; + // debugOutputData.getWriteBuffer().mpcfactor = rtGetControlStruct().mpcFactor; debugOutputData.getWriteBuffer().targetPose_x = targetPose(0, 3); debugOutputData.getWriteBuffer().targetPose_y = targetPose(1, 3); @@ -298,7 +360,7 @@ namespace armarx void NJointTaskSpaceImpedanceDMPController::setViaPoints(Ice::Double u, const Ice::DoubleSeq& viapoint, const Ice::Current&) { - // LockGuardType guard(controllerMutex); + LockGuardType guard(controllerMutex); ARMARX_INFO << "setting via points "; dmpCtrl->setViaPose(u, viapoint); @@ -310,7 +372,7 @@ namespace armarx } - void NJointTaskSpaceImpedanceDMPController::learnJointDMPFromFiles(const std::string& fileName, const Ice::Current&) + void NJointTaskSpaceImpedanceDMPController::learnJointDMPFromFiles(const std::string& fileName, const Ice::FloatSeq& currentJVS, const Ice::Current&) { DMP::Vec<DMP::SampledTrajectoryV2 > trajs; DMP::DVec ratios; @@ -330,7 +392,7 @@ namespace armarx for (size_t i = 0; i < goal.size(); ++i) { goal.at(i) = traj.rbegin()->getPosition(i); - currentJointState.at(i).pos = traj.begin()->getPosition(i); + currentJointState.at(i).pos = currentJVS.at(i); currentJointState.at(i).vel = 0; } @@ -343,15 +405,61 @@ namespace armarx isNullSpaceJointDMPLearned = true; } - void NJointTaskSpaceImpedanceDMPController::runDMP(const Ice::DoubleSeq& goals, const Ice::Current&) + + void NJointTaskSpaceImpedanceDMPController::resetDMP(const Ice::Current&) + { + if (started) + { + ARMARX_INFO << "Cannot reset running DMP"; + } + firstRun = true; + } + + void NJointTaskSpaceImpedanceDMPController::stopDMP(const Ice::Current&) + { + oldPose = interfaceData.getUpToDateReadBuffer().currentTcpPose; + stopped = true; + } + + void NJointTaskSpaceImpedanceDMPController::resumeDMP(const Ice::Current&) + { + stopped = false; + } + + + void NJointTaskSpaceImpedanceDMPController::runDMPWithTime(const Ice::DoubleSeq& goals, Ice::Double timeDuration, const Ice::Current&) { + firstRun = true; + while (firstRun) + { + usleep(100); + } - while (!interfaceData.updateReadBuffer()) + Eigen::Matrix4f pose = interfaceData.getUpToDateReadBuffer().currentTcpPose; + dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); + + dmpCtrl->canVal = timeDuration; + finished = false; + + if (isNullSpaceJointDMPLearned && useNullSpaceJointDMP) + { + ARMARX_INFO << "Using Null Space Joint DMP"; + } + + started = true; + stopped = false; + // controllerTask->start(); + } + + void NJointTaskSpaceImpedanceDMPController::runDMP(const Ice::DoubleSeq& goals, const Ice::Current&) + { + firstRun = true; + while (firstRun) { usleep(100); } - Eigen::Matrix4f pose = interfaceData.getReadBuffer().currentTcpPose; + Eigen::Matrix4f pose = interfaceData.getUpToDateReadBuffer().currentTcpPose; dmpCtrl->prepareExecution(dmpCtrl->eigen4f2vec(pose), goals); finished = false; @@ -361,7 +469,9 @@ namespace armarx ARMARX_INFO << "Using Null Space Joint DMP"; } - controllerTask->start(); + started = true; + stopped = false; + // controllerTask->start(); } @@ -412,16 +522,28 @@ namespace armarx debugObs->setDebugChannel(channelName, datafields); } - void NJointTaskSpaceImpedanceDMPController::onInitComponent() + void NJointTaskSpaceImpedanceDMPController::onInitNJointController() { ARMARX_INFO << "init ..."; - controllerTask = new PeriodicTask<NJointTaskSpaceImpedanceDMPController>(this, &NJointTaskSpaceImpedanceDMPController::controllerRun, 1); + // controllerTask = new PeriodicTask<NJointTaskSpaceImpedanceDMPController>(this, &NJointTaskSpaceImpedanceDMPController::controllerRun, 1); + runTask("NJointTaskSpaceImpedanceDMPController", [&] + { + CycleUtil c(1); + getObjectScheduler()->waitForObjectStateMinimum(eManagedIceObjectStarted); + while (getState() == eManagedIceObjectStarted) + { + if (isControllerActive()) + { + controllerRun(); + } + c.waitForCycleDuration(); + } + }); } - void NJointTaskSpaceImpedanceDMPController::onDisconnectComponent() + void NJointTaskSpaceImpedanceDMPController::onDisconnectNJointController() { - controllerTask->stop(); - ARMARX_INFO << "stopped ..."; + // controllerTask->stop(); } diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.h b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.h index 143d752d358edf14a5abd5497649679b4aef8589..49a104b540537279487adb1ad50b86bc7da9ccb5 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.h +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.h @@ -10,6 +10,7 @@ #include <RobotAPI/interface/units/RobotUnit/NJointTaskSpaceDMPController.h> #include <RobotAPI/libraries/DMPController/TaskSpaceDMPController.h> #include <dmp/representation/dmp/umidmp.h> +#include <ArmarXCore/core/time/CycleUtil.h> using namespace DMP; @@ -46,34 +47,39 @@ namespace armarx NJointTaskSpaceImpedanceDMPController(const RobotUnitPtr& robotUnit, const NJointControllerConfigPtr& config, const VirtualRobot::RobotPtr&); // NJointControllerInterface interface - std::string getClassName(const Ice::Current&) const override; + std::string getClassName(const Ice::Current&) const; // NJointController interface - void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration) override; + void rtRun(const IceUtil::Time& sensorValuesTimestamp, const IceUtil::Time& timeSinceLastIteration); // NJointTaskSpaceImpedanceDMPControllerInterface interface - void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&) override; - bool isFinished(const Ice::Current&) override + void learnDMPFromFiles(const Ice::StringSeq& fileNames, const Ice::Current&); + bool isFinished(const Ice::Current&) { return finished; } - void setViaPoints(Ice::Double u, const Ice::DoubleSeq& viapoint, const Ice::Current&) override; - void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&) override; + void setViaPoints(Ice::Double u, const Ice::DoubleSeq& viapoint, const Ice::Current&); + void setGoals(const Ice::DoubleSeq& goals, const Ice::Current&); - void learnJointDMPFromFiles(const std::string& fileName, const Ice::Current&) override; - void runDMP(const Ice::DoubleSeq& goals, const Ice::Current&) override; - Ice::Double getVirtualTime(const Ice::Current&) override + void learnJointDMPFromFiles(const std::string& fileName, const Ice::FloatSeq& currentJVS, const Ice::Current&); + void runDMP(const Ice::DoubleSeq& goals, const Ice::Current&); + void runDMPWithTime(const Ice::DoubleSeq& goals, Ice::Double timeDuration, const Ice::Current&); + + Ice::Double getVirtualTime(const Ice::Current&) { return dmpCtrl->canVal; } + void stopDMP(const Ice::Current&); + void resumeDMP(const Ice::Current&); + void resetDMP(const Ice::Current&); protected: - virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&) override; + virtual void onPublish(const SensorAndControl&, const DebugDrawerInterfacePrx&, const DebugObserverInterfacePrx&); - void onInitComponent() override; - void onDisconnectComponent() override; + void onInitNJointController(); + void onDisconnectNJointController(); void controllerRun(); private: @@ -142,6 +148,7 @@ namespace armarx // dmp parameters double timeDuration; bool finished; + VirtualRobot::RobotNodeSetPtr rns; // phaseStop parameters double phaseL; @@ -161,9 +168,9 @@ namespace armarx Eigen::Vector3f kori; Eigen::Vector3f dpos; Eigen::Vector3f dori; - float knull; - float dnull; - + Eigen::VectorXf knull; + Eigen::VectorXf dnull; + int numOfJoints; bool useNullSpaceJointDMP; bool isNullSpaceJointDMPLearned; @@ -173,11 +180,14 @@ namespace armarx std::vector<std::string> jointNames; mutable MutexType controllerMutex; PeriodicTask<NJointTaskSpaceImpedanceDMPController>::pointer_type controllerTask; + bool firstRun; + bool started = false; + bool stopped = false; - + Eigen::Matrix4f oldPose; // NJointController interface protected: - void rtPreActivateController() override; + void rtPreActivateController(); }; } // namespace armarx diff --git a/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.cpp b/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.cpp index a54cf2fe86393f94a679b1d09d45cb5a7d48f292..a54284c3a03b39a3406b74d83cebae127856a815 100644 --- a/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.cpp +++ b/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.cpp @@ -51,12 +51,34 @@ PositionControllerHelper::PositionControllerHelper(const VirtualRobot::RobotNode } } +void PositionControllerHelper::readConfig(const CartesianPositionControllerConfigBasePtr& config) +{ + thresholdOrientationNear = config->thresholdOrientationNear; + thresholdOrientationReached = config->thresholdOrientationReached; + thresholdPositionNear = config->thresholdPositionNear; + thresholdPositionReached = config->thresholdPositionReached; + posController.KpOri = config->KpOri; + posController.KpPos = config->KpPos; + posController.maxOriVel = config->maxOriVel; + posController.maxPosVel = config->maxPosVel; +} + void PositionControllerHelper::update() +{ + updateRead(); + updateWrite(); +} + +void PositionControllerHelper::updateRead() { if (!isLastWaypoint() && isCurrentTargetNear()) { currentWaypointIndex++; } +} + +void PositionControllerHelper::updateWrite() +{ Eigen::VectorXf cv = posController.calculate(getCurrentTarget(), VirtualRobot::IKSolver::All); velocityControllerHelper->setTargetVelocity(cv + feedForwardVelocity); if (autoClearFeedForward) @@ -109,6 +131,15 @@ void PositionControllerHelper::clearFeedForwardVelocity() feedForwardVelocity = Eigen::Vector6f::Zero(); } +void PositionControllerHelper::immediateHardStop(bool clearTargets) +{ + velocityControllerHelper->controller->immediateHardStop(); + if (clearTargets) + { + setTarget(posController.getTcp()->getPoseInRootFrame()); + } +} + float PositionControllerHelper::getPositionError() const { return posController.getPositionError(getCurrentTarget()); diff --git a/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.h b/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.h index c38c2be4452781a2b53667d538f4199725836abc..4f380eeb34eaebe736b7fa8281baa7a2a4bb732d 100644 --- a/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.h +++ b/source/RobotAPI/libraries/RobotStatechartHelpers/PositionControllerHelper.h @@ -51,7 +51,15 @@ namespace armarx PositionControllerHelper(const VirtualRobot::RobotNodePtr& tcp, const VelocityControllerHelperPtr& velocityControllerHelper, const std::vector<Eigen::Matrix4f>& waypoints); PositionControllerHelper(const VirtualRobot::RobotNodePtr& tcp, const VelocityControllerHelperPtr& velocityControllerHelper, const std::vector<PosePtr>& waypoints); + void readConfig(const CartesianPositionControllerConfigBasePtr& config); + + // read data and write targets, call this if you are unsure, anywhere in your control loop. + // use updateRead and updateWrite for better performance void update(); + // read data, call this after robot sync + void updateRead(); + // write targets, call this at the end of your control loop, before the sleep + void updateWrite(); void setNewWaypoints(const std::vector<Eigen::Matrix4f>& waypoints); void setNewWaypoints(const std::vector<PosePtr>& waypoints); @@ -60,6 +68,7 @@ namespace armarx void setFeedForwardVelocity(const Eigen::Vector6f& feedForwardVelocity); void setFeedForwardVelocity(const Eigen::Vector3f& feedForwardVelocityPos, const Eigen::Vector3f& feedForwardVelocityOri); void clearFeedForwardVelocity(); + void immediateHardStop(bool clearTargets = true); float getPositionError() const; diff --git a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp index 5bf8b9eaa04605ebb19483a147b675a7e1b337d1..9d0551330671517b37dc0872135e302cd268d00b 100644 --- a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp +++ b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.cpp @@ -121,6 +121,11 @@ void RobotNameHelper::Node::checkValid() } +std::string RobotNameHelper::Arm::getSide() const +{ + return side; +} + std::string RobotNameHelper::Arm::getKinematicChain() const { return select("KinematicChain"); @@ -156,6 +161,21 @@ std::string RobotNameHelper::Arm::getHandControllerName() const return select("HandControllerName"); } +std::string RobotNameHelper::Arm::getHandRootNode() const +{ + return select("HandRootNode"); +} + +std::string RobotNameHelper::Arm::getHandModelPath() const +{ + return select("HandModelPath"); +} + +std::string RobotNameHelper::Arm::getHandModelPackage() const +{ + return select("HandModelPackage"); +} + RobotNameHelper::RobotArm RobotNameHelper::Arm::addRobot(const VirtualRobot::RobotPtr& robot) const { return RobotArm(*this, robot); @@ -172,6 +192,11 @@ std::string RobotNameHelper::Arm::select(const std::string& path) const return rnh->select(side + "Arm/" + path); } +std::string RobotNameHelper::RobotArm::getSide() const +{ + return arm.getSide(); +} + VirtualRobot::RobotNodeSetPtr RobotNameHelper::RobotArm::getKinematicChain() const { return robot->getRobotNodeSet(arm.getKinematicChain()); @@ -187,6 +212,21 @@ VirtualRobot::RobotNodePtr RobotNameHelper::RobotArm::getTCP() const return robot->getRobotNode(arm.getTCP()); } +VirtualRobot::RobotNodePtr RobotNameHelper::RobotArm::getHandRootNode() const +{ + return robot->getRobotNode(arm.getHandRootNode()); +} + +Eigen::Matrix4f RobotNameHelper::RobotArm::getTcp2HandRootTransform() const +{ + return getTCP()->getPoseInRootFrame().inverse() * getHandRootNode()->getPoseInRootFrame(); +} + +RobotNameHelper::Arm RobotNameHelper::RobotArm::getArm() const +{ + return arm; +} + RobotNameHelper::RobotArm::RobotArm(const Arm& arm, const VirtualRobot::RobotPtr& robot) : arm(arm), robot(robot) { diff --git a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h index c4e34fd0f60852c242db1d6d1424396065ca5cb7..bd95565df108c2b30086bb83aae8a2588cb9cc86 100644 --- a/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h +++ b/source/RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h @@ -61,6 +61,7 @@ namespace armarx friend class RobotNameHelper; public: + std::string getSide() const; std::string getKinematicChain() const; std::string getTorsoKinematicChain() const; std::string getTCP() const; @@ -68,6 +69,9 @@ namespace armarx std::string getEndEffector() const; std::string getMemoryHandName() const; std::string getHandControllerName() const; + std::string getHandRootNode() const; + std::string getHandModelPath() const; + std::string getHandModelPackage() const; RobotArm addRobot(const VirtualRobot::RobotPtr& robot) const; private: @@ -83,9 +87,13 @@ namespace armarx friend class RobotNameHelper; friend class Arm; public: + std::string getSide() const; VirtualRobot::RobotNodeSetPtr getKinematicChain() const; VirtualRobot::RobotNodeSetPtr getTorsoKinematicChain() const; VirtualRobot::RobotNodePtr getTCP() const; + VirtualRobot::RobotNodePtr getHandRootNode() const; + Eigen::Matrix4f getTcp2HandRootTransform() const; + Arm getArm() const; private: RobotArm(const Arm& arm, const VirtualRobot::RobotPtr& robot); diff --git a/source/RobotAPI/libraries/core/CMakeLists.txt b/source/RobotAPI/libraries/core/CMakeLists.txt index 8ecf0ea00336d937225cb97c95603a62374441c5..249994acdaa5b4e1dd955f3619adec22879c8232 100644 --- a/source/RobotAPI/libraries/core/CMakeLists.txt +++ b/source/RobotAPI/libraries/core/CMakeLists.txt @@ -8,7 +8,10 @@ armarx_build_if(Simox_FOUND "Simox-VirtualRobot not available") set(LIB_NAME RobotAPICore) set(LIBS RobotAPIInterfaces ArmarXCoreObservers ArmarXCoreStatechart ArmarXCoreEigen3Variants - VirtualRobot Saba) + VirtualRobot + Saba + SimDynamics + ) set(LIB_FILES PIDController.cpp @@ -33,10 +36,15 @@ set(LIB_FILES math/TimeSeriesUtils.cpp CartesianVelocityController.cpp CartesianPositionController.cpp + CartesianWaypointController.cpp CartesianFeedForwardPositionController.cpp CartesianVelocityRamp.cpp JointVelocityRamp.cpp CartesianVelocityControllerWithRamp.cpp + SimpleDiffIK.cpp + + visualization/DebugDrawerTopic.cpp + visualization/GlasbeyLUT.cpp ) set(LIB_HEADERS @@ -76,11 +84,16 @@ set(LIB_HEADERS math/TimeSeriesUtils.h CartesianVelocityController.h CartesianPositionController.h + CartesianWaypointController.h CartesianFeedForwardPositionController.h CartesianVelocityRamp.h JointVelocityRamp.h CartesianVelocityControllerWithRamp.h + SimpleDiffIK.h EigenHelpers.h + + visualization/DebugDrawerTopic.h + visualization/GlasbeyLUT.h ) add_subdirectory(test) diff --git a/source/RobotAPI/libraries/core/CartesianPositionController.cpp b/source/RobotAPI/libraries/core/CartesianPositionController.cpp index 4559e139c7d93e803f1ff7d070a77698396863a0..60c62092f4e34302e825e530436e150204f91fe5 100644 --- a/source/RobotAPI/libraries/core/CartesianPositionController.cpp +++ b/source/RobotAPI/libraries/core/CartesianPositionController.cpp @@ -100,3 +100,64 @@ Eigen::Vector3f CartesianPositionController::getOrientationDiff(const Eigen::Mat Eigen::AngleAxisf aa(oriDir); return aa.axis() * aa.angle(); } + +VirtualRobot::RobotNodePtr CartesianPositionController::getTcp() const +{ + return tcp; +} + + + +#define SS_OUT(x) ss << #x << " = " << x << "\n" +#define SET_FLT(x) obj->setFloat(#x, x) +#define GET_FLT(x) x = obj->getFloat(#x); + +CartesianPositionControllerConfig::CartesianPositionControllerConfig() +{ +} + +std::string CartesianPositionControllerConfig::output(const Ice::Current& c) const +{ + std::stringstream ss; + + SS_OUT(KpPos); + SS_OUT(KpOri); + SS_OUT(maxPosVel); + SS_OUT(maxOriVel); + SS_OUT(thresholdOrientationNear); + SS_OUT(thresholdOrientationReached); + SS_OUT(thresholdPositionNear); + SS_OUT(thresholdPositionReached); + + return ss.str(); +} + +void CartesianPositionControllerConfig::serialize(const ObjectSerializerBasePtr& serializer, const Ice::Current&) const +{ + AbstractObjectSerializerPtr obj = AbstractObjectSerializerPtr::dynamicCast(serializer); + + SET_FLT(KpPos); + SET_FLT(KpOri); + SET_FLT(maxPosVel); + SET_FLT(maxOriVel); + SET_FLT(thresholdOrientationNear); + SET_FLT(thresholdOrientationReached); + SET_FLT(thresholdPositionNear); + SET_FLT(thresholdPositionReached); + +} + +void CartesianPositionControllerConfig::deserialize(const ObjectSerializerBasePtr& serializer, const Ice::Current&) +{ + AbstractObjectSerializerPtr obj = AbstractObjectSerializerPtr::dynamicCast(serializer); + + GET_FLT(KpPos); + GET_FLT(KpOri); + GET_FLT(maxPosVel); + GET_FLT(maxOriVel); + GET_FLT(thresholdOrientationNear); + GET_FLT(thresholdOrientationReached); + GET_FLT(thresholdPositionNear); + GET_FLT(thresholdPositionReached); + +} diff --git a/source/RobotAPI/libraries/core/CartesianPositionController.h b/source/RobotAPI/libraries/core/CartesianPositionController.h index 392ba95c03b8dd8e3c5242ae23bcbf26a2a5f6b7..e5f2c832edb3640ef649e3d8550fa94c7daa1654 100644 --- a/source/RobotAPI/libraries/core/CartesianPositionController.h +++ b/source/RobotAPI/libraries/core/CartesianPositionController.h @@ -28,6 +28,10 @@ #include <VirtualRobot/Robot.h> #include <Eigen/Dense> +#include <RobotAPI/interface/core/CartesianPositionControllerConfig.h> +#include <ArmarXCore/observers/variant/Variant.h> +#include <ArmarXCore/observers/AbstractObjectSerializer.h> + namespace armarx { class CartesianPositionController; @@ -39,12 +43,16 @@ namespace armarx public: CartesianPositionController(const VirtualRobot::RobotNodePtr& tcp); + CartesianPositionController(CartesianPositionController&&) = default; + CartesianPositionController& operator=(CartesianPositionController&&) = default; + Eigen::VectorXf calculate(const Eigen::Matrix4f& targetPose, VirtualRobot::IKSolver::CartesianSelection mode) const; float getPositionError(const Eigen::Matrix4f& targetPose) const; float getOrientationError(const Eigen::Matrix4f& targetPose) const; Eigen::Vector3f getPositionDiff(const Eigen::Matrix4f& targetPose) const; Eigen::Vector3f getOrientationDiff(const Eigen::Matrix4f& targetPose) const; + VirtualRobot::RobotNodePtr getTcp() const; float KpPos = 1; float KpOri = 1; @@ -55,5 +63,50 @@ namespace armarx private: VirtualRobot::RobotNodePtr tcp; }; + + namespace VariantType + { + // variant types + const VariantTypeId CartesianPositionControllerConfig = Variant::addTypeName("::armarx::CartesianPositionControllerConfig"); + } + + class CartesianPositionControllerConfig : virtual public CartesianPositionControllerConfigBase + { + public: + CartesianPositionControllerConfig(); + + + // inherited from VariantDataClass + Ice::ObjectPtr ice_clone() const override + { + return this->clone(); + } + VariantDataClassPtr clone(const Ice::Current& = ::Ice::Current()) const override + { + return new CartesianPositionControllerConfig(*this); + } + std::string output(const Ice::Current& c = ::Ice::Current()) const override; + VariantTypeId getType(const Ice::Current& = ::Ice::Current()) const override + { + return VariantType::CartesianPositionControllerConfig; + } + bool validate(const Ice::Current& = ::Ice::Current()) override + { + return true; + } + + friend std::ostream& operator<<(std::ostream& stream, const CartesianPositionControllerConfig& rhs) + { + stream << "CartesianPositionControllerConfig: " << std::endl << rhs.output() << std::endl; + return stream; + } + + public: // serialization + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + + }; + + typedef IceInternal::Handle<CartesianPositionControllerConfig> CartesianPositionControllerConfigPtr; } diff --git a/source/RobotAPI/libraries/core/CartesianVelocityController.cpp b/source/RobotAPI/libraries/core/CartesianVelocityController.cpp index 3ec8849c620e0abbc05a548f625a771b76839199..0f84e0a0a2e54981497c4723009f297800e2bc02 100644 --- a/source/RobotAPI/libraries/core/CartesianVelocityController.cpp +++ b/source/RobotAPI/libraries/core/CartesianVelocityController.cpp @@ -119,6 +119,7 @@ Eigen::VectorXf CartesianVelocityController::calculateJointLimitAvoidance() { float f = math::MathUtils::ILerp(rn->getJointLimitLo(), rn->getJointLimitHi(), rn->getJointValue()); r(i) = cos(f * M_PI); + //r(i) = math::MathUtils::Lerp(1.f, -1.f, f); } } return r; diff --git a/source/RobotAPI/libraries/core/CartesianVelocityController.h b/source/RobotAPI/libraries/core/CartesianVelocityController.h index c0bc793a75603caf9becceffb8a13c0d75aebf16..9447e901e4bf6c927c78b56984bc22c6dc27324f 100644 --- a/source/RobotAPI/libraries/core/CartesianVelocityController.h +++ b/source/RobotAPI/libraries/core/CartesianVelocityController.h @@ -41,6 +41,9 @@ namespace armarx const VirtualRobot::JacobiProvider::InverseJacobiMethod invJacMethod = VirtualRobot::JacobiProvider::eSVDDamped, bool considerJointLimits = true); + CartesianVelocityController(CartesianVelocityController&&) = default; + CartesianVelocityController& operator=(CartesianVelocityController&&) = default; + Eigen::VectorXf calculate(const Eigen::VectorXf& cartesianVel, float KpJointLimitAvoidanceScale, VirtualRobot::IKSolver::CartesianSelection mode); Eigen::VectorXf calculate(const Eigen::VectorXf& cartesianVel, const Eigen::VectorXf& nullspaceVel, VirtualRobot::IKSolver::CartesianSelection mode); Eigen::VectorXf calculateJointLimitAvoidance(); diff --git a/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.cpp b/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.cpp index e1d5154f4e51f7b959b1c891c63cdccbaca4f23b..bd336320b84558e883136633cfa90876f5c0379c 100644 --- a/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.cpp +++ b/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.cpp @@ -35,7 +35,7 @@ CartesianVelocityControllerWithRamp::CartesianVelocityControllerWithRamp(const V setCurrentJointVelocity(currentJointVelocity); } -void CartesianVelocityControllerWithRamp::setCurrentJointVelocity(const Eigen::VectorXf& currentJointVelocity) +void CartesianVelocityControllerWithRamp::setCurrentJointVelocity(const Eigen::Ref<const Eigen::VectorXf>& currentJointVelocity) { Eigen::MatrixXf jacobi = controller.ik->getJacobianMatrix(controller.tcp, mode); Eigen::MatrixXf inv = controller.ik->computePseudoInverseJacobianMatrix(jacobi, controller.ik->getJacobiRegularization(mode)); diff --git a/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.h b/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.h index cca4d79339de53704b8d2a7a19aec6babb568c5d..3f0932ab997767a6ce03dba72e49a5417946a75c 100644 --- a/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.h +++ b/source/RobotAPI/libraries/core/CartesianVelocityControllerWithRamp.h @@ -46,7 +46,10 @@ namespace armarx float maxPositionAcceleration, float maxOrientationAcceleration, float maxNullspaceAcceleration, const VirtualRobot::RobotNodePtr& tcp = nullptr); - void setCurrentJointVelocity(const Eigen::VectorXf& currentJointVelocity); + CartesianVelocityControllerWithRamp(CartesianVelocityControllerWithRamp&&) = default; + CartesianVelocityControllerWithRamp& operator=(CartesianVelocityControllerWithRamp&&) = default; + + void setCurrentJointVelocity(const Eigen::Ref<const Eigen::VectorXf>& currentJointVelocity); Eigen::VectorXf calculate(const Eigen::VectorXf& cartesianVel, float jointLimitAvoidanceScale, float dt); Eigen::VectorXf calculate(const Eigen::VectorXf& cartesianVel, const Eigen::VectorXf& nullspaceVel, float dt); diff --git a/source/RobotAPI/libraries/core/CartesianWaypointController.cpp b/source/RobotAPI/libraries/core/CartesianWaypointController.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de563c4f306b4cc1245ec978a33f345efbd3212e --- /dev/null +++ b/source/RobotAPI/libraries/core/CartesianWaypointController.cpp @@ -0,0 +1,221 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), + * Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Raphael Grimm (raphael dot grimm at kit dot edu) + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include <VirtualRobot/math/Helpers.h> + +#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <RobotAPI/libraries/core/CartesianVelocityController.h> + +#include "CartesianWaypointController.h" + +namespace armarx +{ + CartesianWaypointController::CartesianWaypointController( + const VirtualRobot::RobotNodeSetPtr& rns, + const Eigen::VectorXf& currentJointVelocity, + float maxPositionAcceleration, + float maxOrientationAcceleration, + float maxNullspaceAcceleration, + const VirtualRobot::RobotNodePtr& tcp + ) : + ctrlCartesianVelWithRamps + { + rns, + currentJointVelocity, + VirtualRobot::IKSolver::CartesianSelection::All, + maxPositionAcceleration, + maxOrientationAcceleration, + maxNullspaceAcceleration, + tcp ? tcp : rns->getTCP() + }, + ctrlCartesianPos2Vel(tcp ? tcp : rns->getTCP()), + currentWaypointIndex(0) + { + ARMARX_CHECK_NOT_NULL(rns); + _out = Eigen::VectorXf::Zero(rns->getSize()); + _jnv = Eigen::VectorXf::Zero(rns->getSize()); + } + + const Eigen::VectorXf& CartesianWaypointController::calculate(float dt) + { + //calculate cartesian velocity + some management stuff + if (!isLastWaypoint() && isCurrentTargetNear()) + { + currentWaypointIndex++; + } + cartesianVelocity = ctrlCartesianPos2Vel.calculate(getCurrentTarget(), VirtualRobot::IKSolver::All) + feedForwardVelocity; + + if (autoClearFeedForward) + { + clearFeedForwardVelocity(); + } + + //calculate joint velocity + if (nullSpaceControlEnabled) + { + //avoid joint limits + _jnv = KpJointLimitAvoidance * ctrlCartesianVelWithRamps.controller.calculateJointLimitAvoidance() + + ctrlCartesianVelWithRamps.controller.calculateNullspaceVelocity( + cartesianVelocity, + jointLimitAvoidanceScale, + VirtualRobot::IKSolver::All + ); + } + else + { + //don't avoid joint limits + _jnv *= 0; + } + _out = ctrlCartesianVelWithRamps.calculate(cartesianVelocity, _jnv, dt); + return _out; + } + + void CartesianWaypointController::setNewWaypoints(const std::vector<Eigen::Matrix4f>& waypoints) + { + this->waypoints = waypoints; + currentWaypointIndex = 0; + } + + void CartesianWaypointController::addWaypoint(const Eigen::Matrix4f& waypoint) + { + this->waypoints.push_back(waypoint); + } + + void CartesianWaypointController::setTarget(const Eigen::Matrix4f& target) + { + waypoints.clear(); + waypoints.push_back(target); + currentWaypointIndex = 0; + } + + void CartesianWaypointController::setFeedForwardVelocity(const Eigen::Vector6f& feedForwardVelocity) + { + this->feedForwardVelocity = feedForwardVelocity; + } + + void CartesianWaypointController::setFeedForwardVelocity(const Eigen::Vector3f& feedForwardVelocityPos, const Eigen::Vector3f& feedForwardVelocityOri) + { + feedForwardVelocity.block<3, 1>(0, 0) = feedForwardVelocityPos; + feedForwardVelocity.block<3, 1>(3, 0) = feedForwardVelocityOri; + } + + void CartesianWaypointController::clearFeedForwardVelocity() + { + feedForwardVelocity = Eigen::Vector6f::Zero(); + } + + float CartesianWaypointController::getPositionError() const + { + return ctrlCartesianPos2Vel.getPositionError(getCurrentTarget()); + } + + float CartesianWaypointController::getOrientationError() const + { + return ctrlCartesianPos2Vel.getOrientationError(getCurrentTarget()); + } + + bool CartesianWaypointController::isCurrentTargetReached() const + { + return getPositionError() < thresholdPositionReached && getOrientationError() < thresholdOrientationReached; + } + + bool CartesianWaypointController::isCurrentTargetNear() const + { + return getPositionError() < thresholdPositionNear && getOrientationError() < thresholdOrientationNear; + } + + bool CartesianWaypointController::isFinalTargetReached() const + { + return isLastWaypoint() && isCurrentTargetReached(); + } + + bool CartesianWaypointController::isFinalTargetNear() const + { + return isLastWaypoint() && isCurrentTargetNear(); + } + + bool CartesianWaypointController::isLastWaypoint() const + { + return currentWaypointIndex + 1 >= waypoints.size(); + } + + const Eigen::Matrix4f& CartesianWaypointController::getCurrentTarget() const + { + return waypoints.at(currentWaypointIndex); + } + + const Eigen::Vector3f CartesianWaypointController::getCurrentTargetPosition() const + { + return math::Helpers::GetPosition(waypoints.at(currentWaypointIndex)); + } + + size_t CartesianWaypointController::skipToClosestWaypoint(float rad2mmFactor) + { + float dist = FLT_MAX; + size_t minIndex = 0; + for (size_t i = 0; i < waypoints.size(); i++) + { + float posErr = ctrlCartesianPos2Vel.getPositionError(waypoints.at(i)); + float oriErr = ctrlCartesianPos2Vel.getOrientationError(waypoints.at(i)); + float d = posErr + oriErr * rad2mmFactor; + if (d < dist) + { + minIndex = i; + dist = d; + } + } + currentWaypointIndex = minIndex; + return currentWaypointIndex; + } + + void CartesianWaypointController::setNullSpaceControl(bool enabled) + { + nullSpaceControlEnabled = enabled; + } + + std::string CartesianWaypointController::getStatusText() + { + std::stringstream ss; + ss.precision(2); + ss << std::fixed << "Waypoint: " << (currentWaypointIndex + 1) << "/" << waypoints.size() << " distance: " << getPositionError() << " mm " << VirtualRobot::MathTools::rad2deg(getOrientationError()) << " deg"; + return ss.str(); + } + + void CartesianWaypointController::setConfig(const CartesianWaypointControllerConfig& cfg) + { + KpJointLimitAvoidance = cfg.kpJointLimitAvoidance; + jointLimitAvoidanceScale = cfg.jointLimitAvoidanceScale; + + thresholdOrientationNear = cfg.thresholdOrientationNear; + thresholdOrientationReached = cfg.thresholdOrientationReached; + thresholdPositionNear = cfg.thresholdPositionNear; + thresholdPositionReached = cfg.thresholdPositionReached; + + ctrlCartesianPos2Vel.maxOriVel = cfg.maxOriVel; + ctrlCartesianPos2Vel.maxPosVel = cfg.maxPosVel; + ctrlCartesianPos2Vel.KpOri = cfg.kpOri; + ctrlCartesianPos2Vel.KpPos = cfg.kpPos; + } +} diff --git a/source/RobotAPI/libraries/core/CartesianWaypointController.h b/source/RobotAPI/libraries/core/CartesianWaypointController.h new file mode 100644 index 0000000000000000000000000000000000000000..95bef4948f192eebbeb526bec591640499eda419 --- /dev/null +++ b/source/RobotAPI/libraries/core/CartesianWaypointController.h @@ -0,0 +1,121 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), + * Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Raphael Grimm (raphael dot grimm at kit dot edu) + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <memory> + +#include <Eigen/Dense> + +#include <VirtualRobot/Robot.h> + +#include <RobotAPI/libraries/core/CartesianPositionController.h> +#include <RobotAPI/libraries/core/Pose.h> +#include <RobotAPI/interface/core/CartesianWaypointControllerConfig.h> + +#include "CartesianVelocityControllerWithRamp.h" + +namespace Eigen +{ + using Vector6f = Matrix<float, 6, 1>; +} + + +namespace armarx +{ + class CartesianWaypointController; + using CartesianWaypointControllerPtr = std::shared_ptr<CartesianWaypointController>; + + class CartesianWaypointController + { + public: + CartesianWaypointController(const VirtualRobot::RobotNodeSetPtr& rns, + const Eigen::VectorXf& currentJointVelocity, + float maxPositionAcceleration, + float maxOrientationAcceleration, + float maxNullspaceAcceleration, + const VirtualRobot::RobotNodePtr& tcp = nullptr + ); + + + void setCurrentJointVelocity(const Eigen::Ref<const Eigen::VectorXf>& currentJointVelocity) + { + ctrlCartesianVelWithRamps.setCurrentJointVelocity(currentJointVelocity); + } + + const Eigen::VectorXf& calculate(float dt); + + void setNewWaypoints(const std::vector<Eigen::Matrix4f>& waypoints); + void addWaypoint(const Eigen::Matrix4f& waypoint); + void setTarget(const Eigen::Matrix4f& target); + void setFeedForwardVelocity(const Eigen::Vector6f& feedForwardVelocity); + void setFeedForwardVelocity(const Eigen::Vector3f& feedForwardVelocityPos, const Eigen::Vector3f& feedForwardVelocityOri); + void clearFeedForwardVelocity(); + + float getPositionError() const; + + float getOrientationError() const; + + bool isCurrentTargetReached() const; + bool isCurrentTargetNear() const; + bool isFinalTargetReached() const; + bool isFinalTargetNear() const; + + bool isLastWaypoint() const; + + const Eigen::Matrix4f& getCurrentTarget() const; + const Eigen::Vector3f getCurrentTargetPosition() const; + + size_t skipToClosestWaypoint(float rad2mmFactor); + + void setNullSpaceControl(bool enabled); + + std::string getStatusText(); + + void setConfig(const CartesianWaypointControllerConfig& cfg); + + CartesianVelocityControllerWithRamp ctrlCartesianVelWithRamps; + CartesianPositionController ctrlCartesianPos2Vel; + + std::vector<Eigen::Matrix4f> waypoints; + size_t currentWaypointIndex = 0.f; + + float thresholdPositionReached = 0.f; + float thresholdOrientationReached = 0.f; + float thresholdPositionNear = 0.f; + float thresholdOrientationNear = 0.f; + + Eigen::Vector6f feedForwardVelocity = Eigen::Vector6f::Zero(); + Eigen::Vector6f cartesianVelocity = Eigen::Vector6f::Zero(); + bool autoClearFeedForward = true; + + + bool nullSpaceControlEnabled = true; + float jointLimitAvoidanceScale = 0.f; + float KpJointLimitAvoidance = 0.f; + + private: + Eigen::VectorXf _out; + Eigen::VectorXf _jnv; + }; +} diff --git a/source/RobotAPI/libraries/core/FramedOrientedPoint.h b/source/RobotAPI/libraries/core/FramedOrientedPoint.h index 387ab6f4ce53f697362cc880da5e299dda3dcf47..7f2231c17bc80c39acb745a15ebdb672393a9a72 100644 --- a/source/RobotAPI/libraries/core/FramedOrientedPoint.h +++ b/source/RobotAPI/libraries/core/FramedOrientedPoint.h @@ -79,16 +79,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new FramedOrientedPoint(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::FramedOrientedPoint; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -100,8 +100,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; } diff --git a/source/RobotAPI/libraries/core/FramedPose.h b/source/RobotAPI/libraries/core/FramedPose.h index e847e63497dfa5b2929649c77e1a6da2af5a1436..390396d228ebe51250dd0941019f30c661478cb3 100644 --- a/source/RobotAPI/libraries/core/FramedPose.h +++ b/source/RobotAPI/libraries/core/FramedPose.h @@ -110,16 +110,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new FramedDirection(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::FramedDirection; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -131,8 +131,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; private: static Eigen::Matrix4f __GetRotationBetweenFrames(const std::string& oldFrame, const std::string& newFrame, VirtualRobot::RobotPtr robotState); @@ -194,16 +194,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new FramedPosition(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::FramedPosition; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -215,8 +215,8 @@ namespace armarx }; public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; class FramedOrientation; @@ -246,16 +246,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new FramedOrientation(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::FramedOrientation; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -280,8 +280,8 @@ namespace armarx }; public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; @@ -314,19 +314,19 @@ namespace armarx return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new FramedPose(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::FramedPose; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -355,8 +355,8 @@ namespace armarx static VirtualRobot::LinkedCoordinate createLinkedCoordinate(const VirtualRobot::RobotPtr& virtualRobot, const FramedPositionPtr& position, const FramedOrientationPtr& orientation); public: - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; diff --git a/source/RobotAPI/libraries/core/LinkedPose.h b/source/RobotAPI/libraries/core/LinkedPose.h index d6cb56ea4cbdac46b288629fb350da4cfb3e7e22..414309200bb28472dc03cbee856c42c01e8243b0 100644 --- a/source/RobotAPI/libraries/core/LinkedPose.h +++ b/source/RobotAPI/libraries/core/LinkedPose.h @@ -81,15 +81,15 @@ namespace armarx // inherited from VariantDataClass Ice::ObjectPtr ice_clone() const override; - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override; + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override; - std::string output(const Ice::Current& c = ::Ice::Current()) const override; + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override; + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override; - bool validate(const Ice::Current& c = ::Ice::Current()) override; + bool validate(const Ice::Current& c = Ice::emptyCurrent) override; - void changeFrame(const std::string& newFrame, const Ice::Current& c = ::Ice::Current()) override; + void changeFrame(const std::string& newFrame, const Ice::Current& c = Ice::emptyCurrent) override; void changeToGlobal(); LinkedPosePtr toGlobal() const; @@ -99,8 +99,8 @@ namespace armarx return stream; }; - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; protected: void ice_postUnmarshal() override; @@ -126,7 +126,7 @@ namespace armarx ~LinkedDirection() override; - void changeFrame(const std::string& newFrame, const Ice::Current& c = Ice::Current()) override; + void changeFrame(const std::string& newFrame, const Ice::Current& c = Ice::emptyCurrent) override; // inherited from VariantDataClass Ice::ObjectPtr ice_clone() const override @@ -134,24 +134,24 @@ namespace armarx return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new LinkedDirection(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override { std::stringstream s; s << FramedDirection::toEigen() << std::endl << "reference robot: " << referenceRobot; return s.str(); } - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::LinkedDirection; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -163,8 +163,8 @@ namespace armarx return stream; }; - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; protected: diff --git a/source/RobotAPI/libraries/core/OrientedPoint.h b/source/RobotAPI/libraries/core/OrientedPoint.h index 6d1ed992b1de2adfbe4050c9d6a294a1b16de451..4bb8955e8cf9ddb85933c498c5861655fd453ab3 100644 --- a/source/RobotAPI/libraries/core/OrientedPoint.h +++ b/source/RobotAPI/libraries/core/OrientedPoint.h @@ -55,16 +55,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new OrientedPoint(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& c = Ice::emptyCurrent) const override { return VariantType::OrientedPoint; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& c = Ice::emptyCurrent) override { return true; } @@ -76,8 +76,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; using OrientedPointPtr = IceInternal::Handle<OrientedPoint>; diff --git a/source/RobotAPI/libraries/core/PIDController.cpp b/source/RobotAPI/libraries/core/PIDController.cpp index c9f70d3db8b12fe00a364d484990e13387d59219..5a4700dc5ee7b1a618227e03f97b0260b213bd00 100644 --- a/source/RobotAPI/libraries/core/PIDController.cpp +++ b/source/RobotAPI/libraries/core/PIDController.cpp @@ -59,6 +59,14 @@ void PIDController::reset() previousError = 0; integral = 0; lastUpdateTime = TimeUtil::GetTime(); + if (pdOutputFilter) + { + pdOutputFilter->reset(); + } + if (differentialFilter) + { + differentialFilter->reset(); + } } ScopedRecursiveLockPtr PIDController::getLock() const @@ -146,7 +154,12 @@ void PIDController::update(double deltaSec, double measuredValue, double targetV firstRun = false; double oldControlValue = controlValue; - controlValue = Kp * error + Ki * integral + Kd * derivative; + double pdControlValue = Kp * error + Kd * derivative; + if (pdOutputFilter) + { + pdControlValue = pdOutputFilter->update(deltaSec, pdControlValue); + } + controlValue = pdControlValue + Ki * integral; if (deltaSec > 0.0) { double deriv = (controlValue - oldControlValue) / deltaSec; diff --git a/source/RobotAPI/libraries/core/PIDController.h b/source/RobotAPI/libraries/core/PIDController.h index 87bca749ae5f1ab06c9d04b425b2a2a37e653151..a1c6b006029187f32a71b56d7a2fb41cd58edbb1 100644 --- a/source/RobotAPI/libraries/core/PIDController.h +++ b/source/RobotAPI/libraries/core/PIDController.h @@ -68,6 +68,7 @@ namespace armarx bool limitless; bool threadSafe = true; rtfilters::RTFilterBasePtr differentialFilter; + rtfilters::RTFilterBasePtr pdOutputFilter; private: ScopedRecursiveLockPtr getLock() const; mutable RecursiveMutex mutex; diff --git a/source/RobotAPI/libraries/core/Pose.cpp b/source/RobotAPI/libraries/core/Pose.cpp index 7a99d4b6b7682be748d9d8cc2a63f764fda2318e..400b2839c8c90d726a8c780720b08190a20da1f5 100644 --- a/source/RobotAPI/libraries/core/Pose.cpp +++ b/source/RobotAPI/libraries/core/Pose.cpp @@ -29,6 +29,9 @@ #include <VirtualRobot/LinkedCoordinate.h> #include <VirtualRobot/VirtualRobot.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + + using namespace Eigen; template class ::IceInternal::Handle<::armarx::Pose>; @@ -198,13 +201,7 @@ namespace armarx Matrix3f Quaternion::toEigen() const { - Matrix3f rot; - rot = Quaternionf( - this->qw, - this->qx, - this->qy, - this->qz); - return rot; + return toEigenQuaternion().toRotationMatrix(); } Eigen::Quaternionf Quaternion::toEigenQuaternion() const diff --git a/source/RobotAPI/libraries/core/Pose.h b/source/RobotAPI/libraries/core/Pose.h index 848eaa099195dc63a0546e697d088a29dc3d4f7e..acd23ad090153f7b7a93a69992e0e0b405ceeb51 100644 --- a/source/RobotAPI/libraries/core/Pose.h +++ b/source/RobotAPI/libraries/core/Pose.h @@ -28,7 +28,6 @@ #include <ArmarXCore/observers/variant/Variant.h> #include <ArmarXCore/observers/AbstractObjectSerializer.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <Eigen/Core> #include <Eigen/Geometry> @@ -70,16 +69,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& = Ice::emptyCurrent) const override { return new Vector2(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& = Ice::emptyCurrent) const override { return VariantType::Vector2; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& = Ice::emptyCurrent) override { return true; } @@ -91,8 +90,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; @@ -122,16 +121,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override { return new Vector3(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& = Ice::emptyCurrent) const override { return VariantType::Vector3; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& = Ice::emptyCurrent) override { return true; } @@ -144,8 +143,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; }; using Vector3Ptr = IceInternal::Handle<Vector3>; @@ -161,6 +160,8 @@ namespace armarx virtual public QuaternionBase { public: + + /// Construct an identity quaternion. Quaternion(); Quaternion(const Eigen::Matrix4f&); Quaternion(const Eigen::Matrix3f&); @@ -178,16 +179,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& = Ice::emptyCurrent) const override { return new Quaternion(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& = Ice::emptyCurrent) const override { return VariantType::Quaternion; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& = Ice::emptyCurrent) override { return true; } @@ -200,8 +201,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; private: void init(const Eigen::Matrix3f&); @@ -235,16 +236,16 @@ namespace armarx { return this->clone(); } - VariantDataClassPtr clone(const Ice::Current& c = ::Ice::Current()) const override + VariantDataClassPtr clone(const Ice::Current& = Ice::emptyCurrent) const override { return new Pose(*this); } - std::string output(const Ice::Current& c = ::Ice::Current()) const override; - VariantTypeId getType(const Ice::Current& c = ::Ice::Current()) const override + std::string output(const Ice::Current& = Ice::emptyCurrent) const override; + VariantTypeId getType(const Ice::Current& = Ice::emptyCurrent) const override { return VariantType::Pose; } - bool validate(const Ice::Current& c = ::Ice::Current()) override + bool validate(const Ice::Current& = Ice::emptyCurrent) override { return true; } @@ -256,8 +257,8 @@ namespace armarx } public: // serialization - void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) const override; - void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = ::Ice::Current()) override; + void serialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const armarx::ObjectSerializerBasePtr& serializer, const ::Ice::Current& = Ice::emptyCurrent) override; protected: void init(); diff --git a/source/RobotAPI/libraries/core/RobotAPIObjectFactories.cpp b/source/RobotAPI/libraries/core/RobotAPIObjectFactories.cpp index e2d22fa0e321fc0ce6c3699466fc24b5002755c0..4305b29ceb6166b81d238e2309c799db83ce1e66 100644 --- a/source/RobotAPI/libraries/core/RobotAPIObjectFactories.cpp +++ b/source/RobotAPI/libraries/core/RobotAPIObjectFactories.cpp @@ -36,6 +36,7 @@ #include <RobotAPI/libraries/core/observerfilters/MedianDerivativeFilterV3.h> #include <RobotAPI/libraries/core/OrientedPoint.h> #include <RobotAPI/libraries/core/FramedOrientedPoint.h> +#include <RobotAPI/libraries/core/CartesianPositionController.h> using namespace armarx; @@ -58,6 +59,7 @@ ObjectFactoryMap RobotAPIObjectFactories::getFactories() add<armarx::OrientedPointBase, armarx::OrientedPoint>(map); add<armarx::FramedOrientedPointBase, armarx::FramedOrientedPoint>(map); + add<armarx::CartesianPositionControllerConfigBase, armarx::CartesianPositionControllerConfig>(map); add<armarx::PoseMedianFilterBase, armarx::filters::PoseMedianFilter>(map); add<armarx::PoseAverageFilterBase, armarx::filters::PoseAverageFilter>(map); diff --git a/source/RobotAPI/libraries/core/SimpleDiffIK.cpp b/source/RobotAPI/libraries/core/SimpleDiffIK.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2eb0226c18833da6139d8b236defadaef3f0bbf7 --- /dev/null +++ b/source/RobotAPI/libraries/core/SimpleDiffIK.cpp @@ -0,0 +1,101 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), + * Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author SecondHands Demo (shdemo at armar6) + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "CartesianPositionController.h" +#include "CartesianVelocityController.h" +#include "SimpleDiffIK.h" + +using namespace armarx; + + + +SimpleDiffIK::Result SimpleDiffIK::CalculateDiffIK(const Eigen::Matrix4f targetPose, VirtualRobot::RobotNodeSetPtr rns, VirtualRobot::RobotNodePtr tcp, Parameters params) +{ + tcp = tcp ? tcp : rns->getTCP(); + CartesianVelocityController velocityController(rns); + CartesianPositionController positionController(tcp); + + for (size_t i = 0; i <= params.stepsInitial + params.stepsFineTune; i++) + { + Eigen::Vector3f posDiff = positionController.getPositionDiff(targetPose); + Eigen::Vector3f oriDiff = positionController.getOrientationDiff(targetPose); + + //ARMARX_IMPORTANT << VAROUT(posDiff) << VAROUT(oriDiff); + + Eigen::VectorXf cartesialVel(6); + cartesialVel << posDiff(0), posDiff(1), posDiff(2), oriDiff(0), oriDiff(1), oriDiff(2); + Eigen::VectorXf jnv = params.jointLimitAvoidanceKp * velocityController.calculateJointLimitAvoidance(); + Eigen::VectorXf jv = velocityController.calculate(cartesialVel, jnv, VirtualRobot::IKSolver::All); + + + float stepLength = i < params.stepsInitial ? params.ikStepLengthInitial : params.ikStepLengthFineTune; + jv = jv * stepLength; + + for (size_t n = 0; n < rns->getSize(); n++) + { + rns->getNode(n)->setJointValue(rns->getNode(n)->getJointValue() + jv(n)); + } + } + + Result result; + result.jointValues = rns->getJointValuesEigen(); + result.posDiff = positionController.getPositionDiff(targetPose); + result.oriDiff = positionController.getOrientationDiff(targetPose); + result.posError = positionController.getPositionError(targetPose); + result.oriError = positionController.getOrientationError(targetPose); + result.reached = result.posError < params.maxPosError && result.oriError < params.maxOriError; + + result.jointLimitMargins = Eigen::VectorXf::Zero(rns->getSize()); + result.minimumJointLimitMargin = FLT_MAX; + for (size_t i = 0; i < rns->getSize(); i++) + { + VirtualRobot::RobotNodePtr rn = rns->getNode(i); + if (rn->isLimitless()) + { + result.jointLimitMargins(i) = M_PI; + } + else + { + result.jointLimitMargins(i) = std::min(rn->getJointValue() - rn->getJointLimitLo(), rn->getJointLimitHi() - rn->getJointValue()); + result.minimumJointLimitMargin = std::min(result.minimumJointLimitMargin, result.jointLimitMargins(i)); + } + } + + return result; +} + +SimpleDiffIK::Reachability SimpleDiffIK::CalculateReachability(const std::vector<Eigen::Matrix4f> targets, const Eigen::VectorXf& initialJV, VirtualRobot::RobotNodeSetPtr rns, VirtualRobot::RobotNodePtr tcp, SimpleDiffIK::Parameters params) +{ + Reachability r; + rns->setJointValues(initialJV); + for (const Eigen::Matrix4f& target : targets) + { + Result res = CalculateDiffIK(target, rns, tcp, params); + r.aggregate(res); + } + if (!r.reachable) + { + r.minimumJointLimitMargin = -1; + } + return r; +} diff --git a/source/RobotAPI/libraries/core/SimpleDiffIK.h b/source/RobotAPI/libraries/core/SimpleDiffIK.h new file mode 100644 index 0000000000000000000000000000000000000000..23f6e9ae0c81676fa449c48ee086b244e61e64ae --- /dev/null +++ b/source/RobotAPI/libraries/core/SimpleDiffIK.h @@ -0,0 +1,92 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2012-2016, High Performance Humanoid Technologies (H2T), + * Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Simon Ottenhaus (simon dot ottenhaus at kit dot edu) + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <boost/shared_ptr.hpp> + +#include <VirtualRobot/Nodes/RobotNode.h> +#include <VirtualRobot/RobotNodeSet.h> + +namespace armarx +{ + typedef boost::shared_ptr<class SimpleDiffIK> SimpleDiffIKPtr; + + class SimpleDiffIK + { + public: + struct Parameters + { + Parameters() {} + // IK params + float ikStepLengthInitial = 0.2f; + float ikStepLengthFineTune = 0.5f; + size_t stepsInitial = 25; + size_t stepsFineTune = 5; + float maxPosError = 10.f; + float maxOriError = 0.05f; + float jointLimitAvoidanceKp = 2.0f; + }; + struct Result + { + Eigen::VectorXf jointValues; + Eigen::Vector3f posDiff; + Eigen::Vector3f oriDiff; + float posError; + float oriError; + bool reached; + Eigen::VectorXf jointLimitMargins; + float minimumJointLimitMargin; + }; + struct Reachability + { + + bool reachable = true; + float minimumJointLimitMargin = M_PI; + Eigen::VectorXf jointLimitMargins; + float maxPosError = 0; + float maxOriError = 0; + + void aggregate(const Result& result) + { + reachable = reachable && result.reached; + minimumJointLimitMargin = std::min(minimumJointLimitMargin, result.minimumJointLimitMargin); + if (jointLimitMargins.rows() == 0) + { + jointLimitMargins = result.jointLimitMargins; + } + else + { + jointLimitMargins = jointLimitMargins.cwiseMin(result.jointLimitMargins); + } + maxPosError = std::max(maxPosError, result.posError); + maxOriError = std::max(maxOriError, result.oriError); + } + }; + + static Result CalculateDiffIK(const Eigen::Matrix4f targetPose, VirtualRobot::RobotNodeSetPtr rns, VirtualRobot::RobotNodePtr tcp = VirtualRobot::RobotNodePtr(), Parameters params = Parameters()); + + static Reachability CalculateReachability(const std::vector<Eigen::Matrix4f> targets, const Eigen::VectorXf& initialJV, VirtualRobot::RobotNodeSetPtr rns, VirtualRobot::RobotNodePtr tcp = VirtualRobot::RobotNodePtr(), Parameters params = Parameters()); + + }; +} diff --git a/source/RobotAPI/libraries/core/SimpleGridReachability.cpp b/source/RobotAPI/libraries/core/SimpleGridReachability.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c13ea798af6433f002e02832de70ac8093834d4f --- /dev/null +++ b/source/RobotAPI/libraries/core/SimpleGridReachability.cpp @@ -0,0 +1,44 @@ +#include "CartesianVelocityController.h" +#include "SimpleGridReachability.h" + +using namespace armarx; + + +SimpleGridReachability::Result SimpleGridReachability::calculateDiffIK(const Eigen::Matrix4f targetPose, const Parameters& params) +{ + VirtualRobot::RobotNodePtr rns = params.nodeSet; + VirtualRobot::RobotNodeSetPtr tcp = params.tcp ? params.tcp : rns->getTCP(); + CartesianVelocityController velocityController(rns); + CartesianPositionController positionController(tcp); + + Eigen::VectorXf initialJV = rns->getJointValuesEigen(); + for (size_t i = 0; i <= params.stepsInitial + params.stepsFineTune; i++) + { + Eigen::Vector3f posDiff = positionController.getPositionDiff(targetPose); + Eigen::Vector3f oriDiff = positionController.getOrientationDiff(targetPose); + + //ARMARX_IMPORTANT << VAROUT(posDiff) << VAROUT(oriDiff); + + Eigen::VectorXf cartesialVel(6); + cartesialVel << posDiff(0), posDiff(1), posDiff(2), oriDiff(0), oriDiff(1), oriDiff(2); + Eigen::VectorXf jnv = params.jointLimitAvoidanceKp * velocityController.calculateJointLimitAvoidance(); + Eigen::VectorXf jv = velocityController.calculate(cartesialVel, jnv, VirtualRobot::IKSolver::All); + + + float stepLength = i < params.stepsInitial ? params.ikStepLengthInitial : params.ikStepLengthFineTune; + jv = jv * stepLength; + + for (size_t n = 0; n < rns->getSize(); n++) + { + rns->getNode(n)->setJointValue(rns->getNode(n)->getJointValue() + jv(n)); + } + } + + Result result; + result.jointValues = rns->getJointValuesEigen(); + result.posError = positionController.getPositionDiff(targetPose); + result.oriError = positionController.getOrientationError(targetPose); + result.reached = result.posError < params.maxPosError && result.oriError < params.maxOriError; + + return result; +} diff --git a/source/RobotAPI/libraries/core/SimpleGridReachability.h b/source/RobotAPI/libraries/core/SimpleGridReachability.h new file mode 100644 index 0000000000000000000000000000000000000000..4ac9bc70f16524bbf86cbba53efc5f034a5c4f96 --- /dev/null +++ b/source/RobotAPI/libraries/core/SimpleGridReachability.h @@ -0,0 +1,37 @@ +#pragma once +#include "CartesianPositionController.h" + +namespace armarx +{ + class SimpleGridReachability + { + public: + struct Parameters + { + VirtualRobot::RobotNodePtr tcp; + VirtualRobot::RobotNodeSetPtr nodeSet; + + // IK params + float ikStepLengthInitial = 0.2f; + float ikStepLengthFineTune = 0.5f; + size_t stepsInitial = 25; + size_t stepsFineTune = 5; + float maxPosError = 10.f; + float maxOriError = 0.05f; + float jointLimitAvoidanceKp = 2.0f; + }; + struct Result + { + Eigen::VectorXf jointValues; + float posError; + float oriError; + bool reached; + }; + + static Result CalculateDiffIK(const Eigen::Matrix4f targetPose, const Parameters& params); + + + }; + +} + diff --git a/source/RobotAPI/libraries/core/Trajectory.h b/source/RobotAPI/libraries/core/Trajectory.h index 10099dac87eca2e7bf35145871fdba58f82eaf3a..f4e02ebe1bdb449c9baf0f0e9677f3d2164064a1 100644 --- a/source/RobotAPI/libraries/core/Trajectory.h +++ b/source/RobotAPI/libraries/core/Trajectory.h @@ -411,15 +411,15 @@ namespace armarx void ice_postUnmarshal() override; // Serializable interface - void serialize(const ObjectSerializerBasePtr& obj, const Ice::Current& = Ice::Current()) const override; - void deserialize(const ObjectSerializerBasePtr&, const Ice::Current& = Ice::Current()) override; + void serialize(const ObjectSerializerBasePtr& obj, const Ice::Current& = Ice::emptyCurrent) const override; + void deserialize(const ObjectSerializerBasePtr&, const Ice::Current& = Ice::emptyCurrent) override; // VariantDataClass interface - VariantDataClassPtr clone(const Ice::Current& c = Ice::Current()) const override; + VariantDataClassPtr clone(const Ice::Current& c = Ice::emptyCurrent) const override; TrajectoryPtr cloneMetaData() const; - std::string output(const Ice::Current& c = Ice::Current()) const override; - Ice::Int getType(const Ice::Current& c = Ice::Current()) const override; - bool validate(const Ice::Current& c = Ice::Current()) override; + std::string output(const Ice::Current& c = Ice::emptyCurrent) const override; + Ice::Int getType(const Ice::Current& c = Ice::emptyCurrent) const override; + bool validate(const Ice::Current& c = Ice::emptyCurrent) override; Ice::ObjectPtr ice_clone() const override; diff --git a/source/RobotAPI/libraries/core/observerfilters/OffsetFilter.h b/source/RobotAPI/libraries/core/observerfilters/OffsetFilter.h index de3c5eea8dcf7789e9f78eedb732bf2b7fa9553a..b5f668bae403f8e9d3dd48839fd3163115f05ab0 100644 --- a/source/RobotAPI/libraries/core/observerfilters/OffsetFilter.h +++ b/source/RobotAPI/libraries/core/observerfilters/OffsetFilter.h @@ -54,7 +54,7 @@ namespace armarx // DatafieldFilterBase interface public: - VariantBasePtr calculate(const Ice::Current& c = Ice::Current()) const override + VariantBasePtr calculate(const Ice::Current& c = Ice::emptyCurrent) const override { ScopedLock lock(historyMutex); diff --git a/source/RobotAPI/libraries/core/remoterobot/RobotStateObserver.cpp b/source/RobotAPI/libraries/core/remoterobot/RobotStateObserver.cpp index 2e4aa3efc6f3ae9d17c6012f3962f80b97411b66..9a2dbb98e6026f602741b6020b50e0d1fd1a9ad4 100644 --- a/source/RobotAPI/libraries/core/remoterobot/RobotStateObserver.cpp +++ b/source/RobotAPI/libraries/core/remoterobot/RobotStateObserver.cpp @@ -172,7 +172,10 @@ DatafieldRefBasePtr RobotStateObserver::getPoseDatafield(const std::string& node void RobotStateObserver::updateNodeVelocities(const NameValueMap& jointVel, long timestampMicroSeconds) { - + if (jointVel.empty()) + { + return; + } if (getState() < eManagedIceObjectStarting) { return; diff --git a/source/RobotAPI/libraries/core/test/CMakeLists.txt b/source/RobotAPI/libraries/core/test/CMakeLists.txt index 37fd064ee6553f8324c2fd2916c89dbb801f29e7..3f70ca0b8d1b201a90bc46e0d5bdf11d3241baf4 100644 --- a/source/RobotAPI/libraries/core/test/CMakeLists.txt +++ b/source/RobotAPI/libraries/core/test/CMakeLists.txt @@ -9,3 +9,5 @@ armarx_add_test(CartesianVelocityControllerTest CartesianVelocityControllerTest. armarx_add_test(CartesianVelocityRampTest CartesianVelocityRampTest.cpp "${LIBS}") armarx_add_test(CartesianVelocityControllerWithRampTest CartesianVelocityControllerWithRampTest.cpp "${LIBS}") + +armarx_add_test(DebugDrawerTopicTest DebugDrawerTopicTest.cpp "${LIBS}") diff --git a/source/RobotAPI/libraries/core/test/DebugDrawerTopicTest.cpp b/source/RobotAPI/libraries/core/test/DebugDrawerTopicTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b6a2310df6ec38de4a5bb4f413636765212b5c0e --- /dev/null +++ b/source/RobotAPI/libraries/core/test/DebugDrawerTopicTest.cpp @@ -0,0 +1,143 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2017, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package ArmarX + * @author Mirko Waechter( mirko.waechter at kit dot edu) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#define BOOST_TEST_MODULE RobotAPI::DebugDrawerTopicTest::Test + +#define ARMARX_BOOST_TEST + +#include <RobotAPI/Test.h> + +#include <ArmarXCore/core/test/IceTestHelper.h> + +#include <RobotAPI/libraries/core/visualization/DebugDrawerTopic.h> + + +using namespace armarx; + + +// PCL-like dummy types. + +struct PointXYZ +{ + float x, y, z; +}; + +struct PointXYZRGBA : public PointXYZ +{ + uint8_t r, g, b, a; +}; + +struct PointXYZRGBL : public PointXYZRGBA +{ + uint32_t label; +}; + +template <class PointT> +struct PointCloud +{ +private: + /// The point container type. + using VectorT = std::vector<PointT>; + +public: + + PointCloud() {} + PointCloud(const VectorT& points) : points(points) {} + + // Container methods. + std::size_t size() const { return points.size(); } + + PointT& operator[](std::size_t i) { return points[i]; } + const PointT& operator[](std::size_t i) const { return points[i]; } + + // Iterators. + typename VectorT::iterator begin() { return points.begin(); } + typename VectorT::const_iterator begin() const { return points.begin(); } + typename VectorT::iterator end() { return points.end(); } + typename VectorT::const_iterator end() const { return points.end(); } + + + /// The points. + VectorT points; +}; + + +/* These test do not actually check any behaviour, + * but check whether this code compiles. + */ + +template <class PointT> +struct Fixture +{ + Fixture() + { + } + + const DebugDrawerTopic::VisuID id {"name", "layer"}; + const int pointSize = 10; + + DebugDrawerTopic drawer; + + PointCloud<PointT> pointCloudMutable; + const PointCloud<PointT>& pointCloud = pointCloudMutable; +}; + + +BOOST_FIXTURE_TEST_CASE(test_drawPointCloud_PointXYZ, Fixture<PointXYZ>) +{ + pointCloudMutable.points = { {1, 2, 3}, {2, 3, 4}, {3, 4, 5} }; + + drawer.drawPointCloud(id, pointCloud); + drawer.drawPointCloud(id, pointCloud.points, DrawColor {0, 0.5, 1, 1}); + + drawer.drawPointCloud(id, pointCloud, + [](const PointXYZ&) { return DrawColor{0, 0.5, 1, 1}; }, pointSize); +} + + +BOOST_FIXTURE_TEST_CASE(test_drawPointCloud_PointXYZRGBA, Fixture<PointXYZRGBA>) +{ + drawer.drawPointCloud(id, pointCloud); + drawer.drawPointCloud(id, pointCloud.points, DrawColor {0, 0.5, 1, 1}); + + drawer.drawPointCloud(id, pointCloud, + [](const PointXYZRGBA&) { return DrawColor{0, 0.5, 1, 1}; }, pointSize); + + drawer.drawPointCloudRGBA(id, pointCloud, pointSize); +} + + +BOOST_FIXTURE_TEST_CASE(test_drawPointCloud_PointXYZRGBL, Fixture<PointXYZRGBL>) +{ + drawer.drawPointCloud(id, pointCloud); + drawer.drawPointCloud(id, pointCloud.points, DrawColor {0, 0.5, 1, 1}); + + drawer.drawPointCloud(id, pointCloud, + [](const PointXYZRGBL&) { return DrawColor{0, 0.5, 1, 1}; }, pointSize); + + drawer.drawPointCloudRGBA(id, pointCloud, pointSize); +} + + + diff --git a/source/RobotAPI/libraries/core/visualization/DebugDrawerTopic.cpp b/source/RobotAPI/libraries/core/visualization/DebugDrawerTopic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef4c6d5a64a8a0dcc49ba960babc9c0c74848348 --- /dev/null +++ b/source/RobotAPI/libraries/core/visualization/DebugDrawerTopic.cpp @@ -0,0 +1,933 @@ +#include "DebugDrawerTopic.h" + +#include <VirtualRobot/math/Helpers.h> +#include <VirtualRobot/Visualization/TriMeshModel.h> + +#include <ArmarXCore/core/ManagedIceObject.h> + +#include "../Pose.h" + +#include "GlasbeyLUT.h" + + +namespace armarx +{ + + DebugDrawerTopic::VisuID::VisuID() : VisuID("", "") + {} + + DebugDrawerTopic::VisuID::VisuID(const std::string& name, const std::string& layer) : + name(name), layer(layer) + {} + + std::ostream& operator<<(std::ostream& os, const DebugDrawerTopic::VisuID& rhs) + { + os << "Visu '" << rhs.name << "' at layer '" << rhs.layer << "'"; + return os; + } + + + const std::string DebugDrawerTopic::TOPIC_NAME = "DebugDrawerUpdates"; + const std::string DebugDrawerTopic::DEFAULT_LAYER = "debug"; + const DebugDrawerTopic::Defaults DebugDrawerTopic::DEFAULTS = {}; + + DebugDrawerTopic::DebugDrawerTopic(const std::string& layer) : + _layer(layer) + {} + + DebugDrawerTopic::DebugDrawerTopic( + const DebugDrawerInterfacePrx& topic, const std::string& layer) : + topic(topic), _layer(layer) + {} + + void DebugDrawerTopic::setTopic(const DebugDrawerInterfacePrx& topic) + { + this->topic = topic; + } + + DebugDrawerInterfacePrx DebugDrawerTopic::getTopic() const + { + return topic; + } + + void DebugDrawerTopic::offeringTopic(ManagedIceObject& component, const std::string& topicNameOverride) const + { + component.offeringTopic(topicNameOverride.empty() ? TOPIC_NAME : topicNameOverride); + } + + void DebugDrawerTopic::getTopic(ManagedIceObject& component, const std::string& topicNameOverride) + { + setTopic(component.getTopic<DebugDrawerInterfacePrx>(topicNameOverride.empty() ? TOPIC_NAME : topicNameOverride)); + } + + const std::string& DebugDrawerTopic::getLayer() const + { + return _layer; + } + + void DebugDrawerTopic::setLayer(const std::string& layer) + { + this->_layer = layer; + } + + float DebugDrawerTopic::getLengthScale() const + { + return _lengthScale; + } + + void DebugDrawerTopic::setLengthScale(float scale) + { + this->_lengthScale = scale; + } + + void DebugDrawerTopic::setLengthScaleMetersToMillimeters() + { + setLengthScale(1000); + } + + void DebugDrawerTopic::setLengthScaleMillimetersToMeters() + { + setLengthScale(0.001f); + } + + float DebugDrawerTopic::getPoseScale() const + { + return _poseScale; + } + + void DebugDrawerTopic::setPoseScale(float scale) + { + this->_poseScale = scale; + } + + void DebugDrawerTopic::setPoseScaleMeters() + { + setPoseScale(0.001f); + } + + void DebugDrawerTopic::setPoseScaleMillimeters() + { + setPoseScale(1); + } + + void DebugDrawerTopic::shortSleep() + { + this->sleepFor(_shortSleepDuration); + } + + void DebugDrawerTopic::clearAll(bool sleep) + { + if (enabled()) + { + topic->clearAll(); + if (sleep) + { + shortSleep(); + } + } + } + + void DebugDrawerTopic::clearLayer(bool sleep) + { + clearLayer(_layer, sleep); + } + + void DebugDrawerTopic::clearLayer(const std::string& layer, bool sleep) + { + if (enabled()) + { + topic->clearLayer(layer); + if (sleep) + { + shortSleep(); + } + } + } + + void DebugDrawerTopic::drawText( + const VisuID& id, const Eigen::Vector3f& position, const std::string& text, + int size, const DrawColor color, + bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + topic->setTextVisu(layer(id), id.name, text, scaled(scale, position), color, size); + } + } + + void DebugDrawerTopic::drawBox( + const VisuID& id, const Eigen::Vector3f& position, const Eigen::Quaternionf& orientation, + const Eigen::Vector3f& extents, const DrawColor& color, + bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + topic->setBoxVisu(layer(id), id.name, + new Pose(scaled(scale, position), new Quaternion(orientation)), + scaled(scale, extents), color); + } + } + + void DebugDrawerTopic::drawBox(const VisuID& id, const Eigen::Matrix4f& pose, const Eigen::Vector3f& extents, + const DrawColor& color, bool ignoreLengthScale) + { + drawBox(id, math::Helpers::Position(pose), Eigen::Quaternionf(math::Helpers::Orientation(pose)), + extents, color, ignoreLengthScale); + } + + void DebugDrawerTopic::drawBox(const DebugDrawerTopic::VisuID& id, const VirtualRobot::BoundingBox& boundingBox, + const DrawColor& color, bool ignoreLengthScale) + { + drawBox(id, boundingBox, Eigen::Matrix4f::Identity(), color, ignoreLengthScale); + } + + void DebugDrawerTopic::drawBox( + const DebugDrawerTopic::VisuID& id, + const VirtualRobot::BoundingBox& boundingBox, const Eigen::Matrix4f& pose, + const DrawColor& color, bool ignoreLengthScale) + { + const Eigen::Vector3f center = .5 * (boundingBox.getMin() + boundingBox.getMax()); + drawBox(id, math::Helpers::TransformPosition(pose, center), + Eigen::Quaternionf(math::Helpers::Orientation(pose)), + boundingBox.getMax() - boundingBox.getMin(), color, ignoreLengthScale); + } + + void DebugDrawerTopic::drawCylinder(const DebugDrawerTopic::VisuID& id, + const Eigen::Vector3f& center, const Eigen::Vector3f& direction, float radius, float length, + const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + topic->setCylinderVisu(layer(id), id.name, scaled(scale, center), new Vector3(direction), + scaled(scale, length), scaled(scale, radius), color); + } + } + + void DebugDrawerTopic::drawCylinder( + const DebugDrawerTopic::VisuID& id, + const Eigen::Vector3f& center, const Eigen::Quaternionf& orientation, float radius, float length, + const DrawColor& color, bool ignoreLengthScale) + { + drawCylinder(id, center, orientation * Eigen::Vector3f::UnitY(), radius, length, color, + ignoreLengthScale); + } + + void DebugDrawerTopic::drawCylinderFromTo( + const DebugDrawerTopic::VisuID& id, + const Eigen::Vector3f& from, const Eigen::Vector3f& to, float radius, + const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const Eigen::Vector3f dir = (to - from); // no need for scaling at this point + const float length = dir.norm(); + drawCylinder(id, .5 * (from + to), dir / length, radius, length, color, ignoreLengthScale); + } + } + + void DebugDrawerTopic::drawSphere( + const DebugDrawerTopic::VisuID& id, + const Eigen::Vector3f& center, float radius, + const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + topic->setSphereVisu(layer(id), id.name, scaled(scale, center), color, + scaled(scale, radius)); + } + } + + + void DebugDrawerTopic::drawArrow( + const VisuID& id, + const Eigen::Vector3f& position, const Eigen::Vector3f& direction, float length, + float width, const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + topic->setArrowVisu(layer(id), id.name, scaled(scale, position), new Vector3(direction), + color, scaled(scale, length), scaled(scale, width)); + } + } + + void DebugDrawerTopic::drawArrowFromTo( + const VisuID& id, + const Eigen::Vector3f& from, const Eigen::Vector3f& to, + float width, const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const Eigen::Vector3f dir = (to - from); // no need for scaling at this point + const float length = dir.norm(); + drawArrow(id, from, dir / length, length, width, color, ignoreLengthScale); + } + } + + void DebugDrawerTopic::removeArrow(const DebugDrawerTopic::VisuID& id) + { + if (enabled()) + { + topic->removeArrowVisu(layer(id), id.name); + } + } + + + void DebugDrawerTopic::drawPolygon(const VisuID& id, + const std::vector<Eigen::Vector3f>& points, + const DrawColor& colorFace, float lineWidth, const DrawColor& colorEdge, + bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + + PolygonPointList polyPoints; + polyPoints.reserve(points.size()); + + for (const auto& point : points) + { + polyPoints.push_back(scaled(scale, point)); + } + + topic->setPolygonVisu(layer(id), id.name, polyPoints, + colorFace, colorEdge, lineWidth); + } + } + + void DebugDrawerTopic::drawLine( + const VisuID& id, const Eigen::Vector3f& from, const Eigen::Vector3f& to, + float width, const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + + topic->setLineVisu(layer(id), id.name, scaled(scale, from), scaled(scale, to), + width, color); + } + } + + void DebugDrawerTopic::drawLineSet( + const VisuID& id, const DebugDrawerLineSet& lineSet, bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + + if (1.f <= scale && scale <= 1.f) + { + // Can use lineSet directly. + topic->setLineSetVisu(layer(id), id.name, lineSet); + } + else + { + // Need to scale line set, hence, reconstruct it. + DebugDrawerLineSet scaledLineSet = lineSet; + for (auto& point : scaledLineSet.points) + { + scaleXYZ(scale, point); + } + topic->setLineSetVisu(layer(id), id.name, scaledLineSet); + } + } + } + + void DebugDrawerTopic::drawLineSet( + const VisuID& id, const std::vector<Eigen::Vector3f>& points, + float width, const DrawColor& color, bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + + DebugDrawerLineSet lineSet; + for (const auto& point : points) + { + lineSet.points.push_back(scaledT<DebugDrawerPointCloudElement>(scale, point)); + } + + lineSet.lineWidth = width; + lineSet.colorNoIntensity = lineSet.colorFullIntensity = color; + lineSet.intensities.assign(points.size() / 2, 0.); + + topic->setLineSetVisu(layer(id), id.name, lineSet); + } + } + + + void DebugDrawerTopic::drawLineSet( + const VisuID& id, const std::vector<Eigen::Vector3f>& points, float width, + const DrawColor& colorA, const DrawColor& colorB, const std::vector<float>& intensitiesB, + bool ignoreLengthScale) + { + if (enabled()) + { + const float scale = lengthScale(ignoreLengthScale); + + DebugDrawerLineSet lineSet; + for (const auto& point : points) + { + lineSet.points.push_back(scaledT<DebugDrawerPointCloudElement>(scale, point)); + } + + lineSet.lineWidth = width; + lineSet.colorNoIntensity = colorA; + lineSet.colorFullIntensity = colorB; + lineSet.intensities = intensitiesB; + + topic->setLineSetVisu(layer(id), id.name, lineSet); + } + } + + + void DebugDrawerTopic::drawPose( + const VisuID& id, const Eigen::Matrix4f& pose, bool ignoreLengthScale) + { + drawPose(id, pose, _poseScale, ignoreLengthScale); + } + + void DebugDrawerTopic::drawPose( + const VisuID& id, + const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, + bool ignoreLengthScale) + { + drawPose(id, math::Helpers::Pose(pos, ori), _poseScale, ignoreLengthScale); + } + + void DebugDrawerTopic::drawPose( + const VisuID& id, const Eigen::Matrix4f& pose, float scale, + bool ignoreLengthScale) + { + if (enabled()) + { + const float lenghtScale = lengthScale(ignoreLengthScale); + + if (scale >= 1 && scale <= 1) // squelch compiler warning that == is unsafe + { + topic->setPoseVisu(layer(id), id.name, scaled(lenghtScale, pose)); + } + else + { + topic->setScaledPoseVisu(layer(id), id.name, scaled(lenghtScale, pose), scale); + } + } + + } + + void DebugDrawerTopic::drawPose( + const VisuID& id, const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, + float scale, bool ignoreLengthScale) + { + drawPose(id, math::Helpers::Pose(pos, ori), scale, ignoreLengthScale); + } + + void DebugDrawerTopic::removePose(const DebugDrawerTopic::VisuID& id) + { + if (enabled()) + { + topic->removePoseVisu(layer(id), id.name); + } + } + + + void DebugDrawerTopic::drawRobot(const DebugDrawerTopic::VisuID& id, + const std::string& robotFile, const std::string& armarxProject, + DrawStyle drawStyle) + { + if (enabled()) + { + topic->setRobotVisu(layer(id), id.name, robotFile, armarxProject, drawStyle); + } + } + + void DebugDrawerTopic::updateRobotPose( + const DebugDrawerTopic::VisuID& id, + const Eigen::Matrix4f& pose, bool ignoreScale) + { + if (enabled()) + { + topic->updateRobotPose(layer(id), id.name, scaled(lengthScale(ignoreScale), pose)); + } + } + + void DebugDrawerTopic::updateRobotPose( + const DebugDrawerTopic::VisuID& id, + const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, bool ignoreScale) + { + updateRobotPose(id, math::Helpers::Pose(pos, ori), ignoreScale); + } + + void DebugDrawerTopic::updateRobotConfig( + const DebugDrawerTopic::VisuID& id, const std::map<std::string, float>& config) + { + if (enabled()) + { + topic->updateRobotConfig(layer(id), id.name, config); + } + } + + void DebugDrawerTopic::updateRobotColor( + const DebugDrawerTopic::VisuID& id, const DrawColor& color) + { + if (enabled()) + { + topic->updateRobotColor(layer(id), id.name, color); + } + } + + void DebugDrawerTopic::updateRobotNodeColor( + const DebugDrawerTopic::VisuID& id, + const std::string& nodeName, const DrawColor& color) + { + if (enabled()) + { + topic->updateRobotNodeColor(layer(id), id.name, nodeName, color); + } + } + + void DebugDrawerTopic::removeRobot(const DebugDrawerTopic::VisuID& id) + { + if (enabled()) + { + topic->removeRobotVisu(layer(id), id.name); + } + } + + + void DebugDrawerTopic::drawTriMesh( + const VisuID& id, const VirtualRobot::TriMeshModel& triMesh, + const DrawColor& color, bool ignoreLengthScale) + { + if (!enabled()) + { + return; + } + + const float scale = lengthScale(ignoreLengthScale); + + DebugDrawerTriMesh dd; + dd.colors.push_back(color); + + for (const auto& vertex : triMesh.vertices) + { + auto scaled = vertex * scale; + dd.vertices.push_back({ scaled.x(), scaled.y(), scaled.z() }); + } + + const std::size_t normalBase = dd.vertices.size(); + for (const auto& normal : triMesh.normals) + { + dd.vertices.push_back({ normal.x(), normal.y(), normal.z() }); + } + + for (const auto& face : triMesh.faces) + { + DebugDrawerFace ddf; + ddf.vertex1.vertexID = static_cast<Ice::Int>(face.id1); + ddf.vertex2.vertexID = static_cast<Ice::Int>(face.id2); + ddf.vertex3.vertexID = static_cast<Ice::Int>(face.id3); + + ddf.vertex1.colorID = ddf.vertex2.colorID = ddf.vertex3.colorID = 0; + ddf.vertex1.normalID = ddf.vertex2.normalID = ddf.vertex3.normalID = -1; + + bool validNormalIDs = true; + for (const auto& id : + { + face.idNormal1, face.idNormal2, face.idNormal3 + }) + { + validNormalIDs &= id < triMesh.normals.size(); + } + + if (validNormalIDs) + { + ddf.vertex1.normalID = static_cast<Ice::Int>(normalBase + face.idNormal1); + ddf.vertex2.normalID = static_cast<Ice::Int>(normalBase + face.idNormal2); + ddf.vertex3.normalID = static_cast<Ice::Int>(normalBase + face.idNormal3); + } + else + { + const Eigen::Vector3f& normal = face.normal; + ddf.normal = { normal.x(), normal.y(), normal.z() }; + } + + dd.faces.push_back(ddf); + } + + topic->setTriMeshVisu(layer(id), id.name, dd); + } + + void DebugDrawerTopic::drawTriMeshAsPolygons(const VisuID& id, + const VirtualRobot::TriMeshModel& trimesh, + const DrawColor& colorFace, float lineWidth, const DrawColor& colorEdge, + bool ignoreLengthScale) + { + drawTriMeshAsPolygons(id, trimesh, Eigen::Matrix4f::Identity(), + colorFace, lineWidth, colorEdge, ignoreLengthScale); + } + + void DebugDrawerTopic::drawTriMeshAsPolygons(const VisuID& id, + const VirtualRobot::TriMeshModel& trimesh, const Eigen::Matrix4f& pose, + const DrawColor& colorFace, float lineWidth, const DrawColor& colorEdge, + bool ignoreLengthScale) + { + if (!enabled()) + { + return; + } + + const float scale = lengthScale(ignoreLengthScale); + bool isIdentity = pose.isIdentity(); + + auto toVector3 = [&scale, &isIdentity, &pose](const Eigen::Vector3f & v) + { + return scaled(scale, isIdentity ? v : math::Helpers::TransformPosition(pose, v)); + }; + + ARMARX_INFO << "Drawing trimesh as polygons"; + + int counter = 0; + for (std::size_t i = 0; i < trimesh.faces.size(); ++i) + { + const auto& face = trimesh.faces[i]; + PolygonPointList points + { + toVector3(trimesh.vertices.at(face.id1)), + toVector3(trimesh.vertices.at(face.id2)), + toVector3(trimesh.vertices.at(face.id3)) + }; + + topic->setPolygonVisu(layer(id), id.name + "_" + std::to_string(counter), points, + colorFace, colorEdge, lineWidth); + ++counter; + } + } + + void DebugDrawerTopic::drawTriMeshAsPolygons( + const VisuID& id, + const VirtualRobot::TriMeshModel& trimesh, + const std::vector<DrawColor>& faceColorsInner, float lineWidth, + const DrawColor& colorEdge, bool ignoreLengthScale) + { + if (!enabled()) + { + return; + } + + //ARMARX_INFO << "Drawing trimesh as polygons colored by area"; + const float scale = lengthScale(ignoreLengthScale); + + for (std::size_t i = 0; i < trimesh.faces.size(); ++i) + { + const auto& face = trimesh.faces[i]; + PolygonPointList points + { + scaled(scale, trimesh.vertices[face.id1]), + scaled(scale, trimesh.vertices[face.id2]), + scaled(scale, trimesh.vertices[face.id3]) + }; + + topic->setPolygonVisu(layer(id), id.name + "_" + std::to_string(i), points, + faceColorsInner.at(i), colorEdge, lineWidth); + } + } + + void DebugDrawerTopic::drawPointCloud( + const DebugDrawerTopic::VisuID& id, + const DebugDrawerPointCloud& pointCloud) + { + if (enabled()) + { + topic->setPointCloudVisu(id.layer, id.name, pointCloud); + } + } + + void DebugDrawerTopic::drawPointCloud( + const DebugDrawerTopic::VisuID& id, + const DebugDrawerColoredPointCloud& pointCloud) + { + if (enabled()) + { + topic->setColoredPointCloudVisu(id.layer, id.name, pointCloud); + } + } + + void DebugDrawerTopic::drawPointCloud( + const DebugDrawerTopic::VisuID& id, + const DebugDrawer24BitColoredPointCloud& pointCloud) + { + if (enabled()) + { + topic->set24BitColoredPointCloudVisu(id.layer, id.name, pointCloud); + } + } + + void DebugDrawerTopic::clearColoredPointCloud(const DebugDrawerTopic::VisuID& id) + { + // Draw an empty point cloud. + drawPointCloud(id, DebugDrawerColoredPointCloud{}); + } + + void DebugDrawerTopic::drawFloor( + const VisuID& id, + const Eigen::Vector3f& at, const Eigen::Vector3f& up, float size, + const DrawColor& color, bool ignoreLengthScale) + { + if (!enabled()) + { + return; + } + + Eigen::Vector3f seed = seed.UnitX(); + if (std::abs(up.dot(seed)) <= 1e-6f) + { + seed = seed.UnitY(); + } + + /* ^ b + * | 3---0 + * | | + | + * | 2---1 + * +--------> a + */ + + const float halfSize = size / 2; + + const Eigen::Vector3f a = halfSize * up.cross(seed).normalized(); + const Eigen::Vector3f b = halfSize * up.cross(a).normalized(); + + std::vector<Eigen::Vector3f> points; + points.push_back(at + a + b); + points.push_back(at + a - b); + points.push_back(at - a - b); + points.push_back(at - a + b); + + drawPolygon(id, points, color, ignoreLengthScale); + } + + + const std::string& DebugDrawerTopic::layer(const std::string& passedLayer) const + { + return passedLayer.empty() ? _layer : passedLayer; + } + + const std::string& DebugDrawerTopic::layer(const VisuID& id) const + { + return layer(id.layer); + } + + float DebugDrawerTopic::lengthScale(bool ignore) const + { + return ignore ? 1 : _lengthScale; + } + + float DebugDrawerTopic::scaled(float scale, float value) + { + return scale * value; + } + + Vector3BasePtr DebugDrawerTopic::scaled(float scale, const Eigen::Vector3f& vector) + { + if (scale >= 1 && scale <= 1) + { + return new Vector3(vector); + } + else + { + return new Vector3((vector * scale).eval()); + } + } + + PoseBasePtr DebugDrawerTopic::scaled(float scale, const Eigen::Matrix4f& pose) + { + if (scale >= 1 && scale <= 1) + { + return new Pose(pose); + } + else + { + Eigen::Matrix4f out = pose; + math::Helpers::Position(out) *= scale; + return new Pose(out); + } + } + + bool DebugDrawerTopic::enabled() const + { + return topic; + } + + + DebugDrawerTopic::operator bool() const + { + return topic; + } + + armarx::DebugDrawerTopic::operator DebugDrawerInterfacePrx& () + { + return topic; + } + + armarx::DebugDrawerTopic::operator const DebugDrawerInterfacePrx& () const + { + return topic; + } + + DebugDrawerInterfacePrx& DebugDrawerTopic::operator->() + { + return topic; + } + + const DebugDrawerInterfacePrx& DebugDrawerTopic::operator->() const + { + return topic; + } + + Eigen::Vector3f DebugDrawerTopic::rgb2hsv(const Eigen::Vector3f& rgb) + { + // source: https://stackoverflow.com/a/6930407 + + Eigen::Vector3f hsv; + float min, max, delta; + + min = rgb(0) < rgb(1) ? rgb(0) : rgb(1); + min = min < rgb(2) ? min : rgb(2); + + max = rgb(0) > rgb(1) ? rgb(0) : rgb(1); + max = max > rgb(2) ? max : rgb(2); + + hsv(2) = max; // v + delta = max - min; + if (delta < 1e-5f) + { + hsv(1) = 0; + hsv(0) = 0; // undefined, maybe nan? + return hsv; + } + if (max > 0.0f) + { + // NOTE: if Max is == 0, this divide would cause a crash + hsv(1) = (delta / max); // s + } + else + { + // if max is 0, then r = g = b = 0 + // s = 0, h is undefined + hsv(1) = 0.0; + hsv(0) = std::nanf(""); // its now undefined + return hsv; + } + if (rgb(0) >= max) // > is bogus, just keeps compilor happy + { + hsv(0) = (rgb(1) - rgb(2)) / delta; // between yellow & magenta + } + else + { + if (rgb(1) >= max) + { + hsv(0) = 2.f + (rgb(2) - rgb(0)) / delta; // between cyan & yellow + } + else + { + hsv(0) = 4.f + (rgb(0) - rgb(1)) / delta; // between magenta & cyan + } + } + + hsv(0) *= 60.0f; // degrees + + if (hsv(0) < 0.f) + { + hsv(0) += 360.0f; + } + + return hsv; + } + + Eigen::Vector3f DebugDrawerTopic::hsv2rgb(const Eigen::Vector3f& hsv) + { + // source: https://stackoverflow.com/a/6930407 + + float hh, p, q, t, ff; + long i; + Eigen::Vector3f rgb; + + if (hsv(1) <= 0.f) // < is bogus, just shuts up warnings + { + rgb(0) = hsv(2); + rgb(1) = hsv(2); + rgb(2) = hsv(2); + return rgb; + } + hh = hsv(0); + if (hh >= 360.f) + { + hh = 0.0; + } + hh /= 60.0f; + i = static_cast<long>(hh); + ff = hh - i; + p = hsv(2) * (1.f - hsv(1)); + q = hsv(2) * (1.f - (hsv(1) * ff)); + t = hsv(2) * (1.f - (hsv(1) * (1.f - ff))); + + switch (i) + { + case 0: + rgb(0) = hsv(2); + rgb(1) = t; + rgb(2) = p; + break; + case 1: + rgb(0) = q; + rgb(1) = hsv(2); + rgb(2) = p; + break; + case 2: + rgb(0) = p; + rgb(1) = hsv(2); + rgb(2) = t; + break; + + case 3: + rgb(0) = p; + rgb(1) = q; + rgb(2) = hsv(2); + break; + case 4: + rgb(0) = t; + rgb(1) = p; + rgb(2) = hsv(2); + break; + case 5: + default: + rgb(0) = hsv(2); + rgb(1) = p; + rgb(2) = q; + break; + } + return rgb; + } + + + DrawColor DebugDrawerTopic::getGlasbeyLUTColor(int id, float alpha) + { + return GlasbeyLUT::at(id, alpha); + } + + DrawColor DebugDrawerTopic::getGlasbeyLUTColor(uint32_t id, float alpha) + { + return GlasbeyLUT::at(id, alpha); + } + + DrawColor DebugDrawerTopic::getGlasbeyLUTColor(std::size_t id, float alpha) + { + return GlasbeyLUT::at(id, alpha); + } + + +} diff --git a/source/RobotAPI/libraries/core/visualization/DebugDrawerTopic.h b/source/RobotAPI/libraries/core/visualization/DebugDrawerTopic.h new file mode 100644 index 0000000000000000000000000000000000000000..b95de6d43258d5a34246a639f6735ea4ee1ee1b4 --- /dev/null +++ b/source/RobotAPI/libraries/core/visualization/DebugDrawerTopic.h @@ -0,0 +1,842 @@ +#pragma once + +#include <chrono> +#include <functional> +#include <thread> + +#include <Eigen/Geometry> + +#include <RobotAPI/interface/visualization/DebugDrawerInterface.h> + + +namespace VirtualRobot +{ + class TriMeshModel; + class BoundingBox; +} + + +namespace armarx +{ + // forward declaration + class ManagedIceObject; + + + /** + * @brief The `DebugDrawerTopic` wraps a `DebugDrawerInterfacePrx` and + * provides a more useful interface than the Ice interface. + * + * The methods by `DebugDrawerTopic` take "raw" types, such as Eigen or PCL + * types, and take care of conversion to Ice variants or data structures. + * In addition, this class provides useful overloads for different use cases. + * + * All methods check whether the internal topic proxy is set, and do + * nothing if it is not set. To disable visualization by this classs + * completely, just do not set the topic. To check whether visualization + * is enabled (i.e. a topic proxy is set), use `enabled()` or just convert + * `*this` to bool: + * @code + * DebugDrawerTopic debugDrawer; + * if (debugDrawer) // equivalent: if (debugDrawer.enabled()) + * { + * // do stuff if visualization is enabled + * } + * @endcode + * + * The `DebugDrawerTopic` allows to set a layer on constructor or via + * `setLayer()`. This layer will be used if none is passed to a drawing + * method. If no layer is passed or set, `DebugDrawerTopic::DEFAULT_LAYER` + * is used. + * + * + * @par Initialisation by Offering and Getting Topic + * + * A `DebugDrawerTopic` needs an underlying `DebugDrawerInterfacePrx` topic proxy. + * This proxy can be passed on construction or set via `setTopic()`. + * In a component (or any other `ManagedIceObject`), `DebugDrawerTopic` + * provides convenience functions to register and fetch the topics. + * + * In `onInitComponent()` (or equivalent method), call: + * @code + * debugDrawer.offeringTopic(*this); + * @endcode + * In `onConnectComponent()` (or equivalent), call: + * @code + * debugDrawer.getTopic(*this); + * @endcode + * where `*this` is a `ManagedIceObject`. + * + * This will call `this->offeringTopic("...")` and `this->getTopic("...")` + * with the correct topic name (`DebugDrawerTopic::TOPIC_NAME`) and + * enable the `DebugDrawerTopic`. + * + * + * @par Scaling + * + * `DebugDrawerTopic` supports length scaling and pose scaling. + * + * If a length scale is set, all visualizations will be scaled up or down + * by this value. This scaling affects positions, sizes / extents, and + * distances / lengths. This is useful when drawing quantities of + * different sources using different scalings (such as meters vs + * millimeters). + * Length scale can be set via `setPoseScale()` or the short hands + * `setPoseScaleMetersToMillimeters()` and `setPoseScaleMillimetersToMeters()`. + * + * All applicable methods offer a final argument called + * `ignoreLengthScaling` (false by default, expect for robots, see below), + * which can be set to true to ignore the set length scale for this + * method call. + * + * @note Robots are always drawn in their native size (the + * `DebugDrawerInterface` offers no size scaling for robots). + * (That is, robots are drawn in millimeters most of the time.) + * + * + * In addition, this class allows to set a pose scale, which will be used + * for all drawn poses (if no other value is passed to drawPose()). + * This is useful when working with geometries scaled in meters, in which + * case a pose scale of 0.001 can be used. + * + * + * @par Argument Pattern + * + * All drawing methods take a `VisuID` as first argument, which specifies + * the name of the visualization and the layer to draw on. + * There are different ways to specify the first argument. + * For example, to specify a only the name for a pose, pass just the name: + * + * @code + * Eigen::Matrix4f pose = Eigen::Matrix4f::Identity(); + * std::string name = "pose"; + * debugDrawer.drawPose(name, pose); + * debugDrawer.drawPose({name}, pose); // equivalent to the line above + * @endcode + * + * This will draw a pose on the preset layer (i.e. the layer passed to the + * constructor or set via `setLayer()`, or "debug" by default). + * To specify both name and layer of a single visualization, pass both in + * an initializer list: + * + * @code + * Eigen::Matrix4f pose = Eigen::Matrix4f::Identity(); + * std::string layer = "layer"; + * std::string name = "pose"; + * debugDrawer.drawPose({name, layer}, pose); + * @endcode + * + * + * After the VisuID, usually the essential geometric parameters follow, + * (e.g. position, size, length, point list, ...), depending on the type + * of visualization. + * Finally, decorative parameters like colors and width can be passed. + * Most of the time, they have sensible default values and can be omitted + * for quick-and-dirty drawing. + * + * (Added methods should adhere to this pattern.) + * + * @see `DebugDrawerTopic::VisuID` + */ + class DebugDrawerTopic + { + public: + + struct VisuID + { + /// Empty constructor. + VisuID(); + + /** + * @brief Construct a VisuID. + * + * This constructor can be called in the following ways + * (with `draw(const VisuID& id, ...)` being any drawing method): + * + * @code + * std::string name = "pose"; + * std::string layer = "layer"; + * draw(name, ...); // just the name, implicit call + * draw({name}, ...); // just the name, call with initializer list + * draw({name, layer}, ...); // name and layer, with initializer list + * @endcode + * + * (And of course by an explicit call if you want to be really verbose.) + * Not passing a layer will cause DebugDrawerTopic to use the + * preset layer. + * + * @param name the name + * @param layer the layer name + */ + VisuID(const std::string& name, const std::string& layer = ""); + + /// Construct a VisuID from a non-std::string source (e.g. char[]). + template <typename Source> + VisuID(const Source& name) : VisuID(std::string(name)) + {} + + std::string name = ""; ///< The visu name (empty by default). + std::string layer = ""; ///< The layer name (empty by default). + + friend std::ostream& operator<<(std::ostream& os, const VisuID& rhs); + }; + + struct Defaults + { + DrawColor colorText { 0, 0, 0, 1 }; + + DrawColor colorArrow { 1, .5, 0, 1 }; + DrawColor colorBox { 1, 0, 0, 1 }; + DrawColor colorCylinder { 0, 1, 0, 1 }; + DrawColor colorLine { .5, 0, 0, 1 }; + DrawColor colorSphere { 0, 0, 1, 1 }; + + DrawColor colorPolygonFace { 0, 1, 1, 1 }; + DrawColor colorPolygonEdge { .75, .75, .75, 1 }; + + DrawColor colorFloor { .1f, .1f, .1f, 1 }; + + DrawColor colorPointCloud { .5, .5, .5, 1. }; + + // Default value of DebugDrawerColoredPointCloud etc. + float pointCloudPointSize = 3.0f; + }; + static const Defaults DEFAULTS; + + + public: + + // CONSTRUCTION & SETUP + + /// Construct without topic, and optional layer. + DebugDrawerTopic(const std::string& layer = DEFAULT_LAYER); + /// Construct with given topic and optional layer. + DebugDrawerTopic(const DebugDrawerInterfacePrx& topic, const std::string& layer = DEFAULT_LAYER); + + + /// Set the topic. + void setTopic(const DebugDrawerInterfacePrx& topic); + /// Get the topic. + DebugDrawerInterfacePrx getTopic() const; + + /** + * @brief Call offeringTopic([topicName]) on the given component. + * @param component The component (`*this` when called from a component). + * @param topicNameOverride Optional override for the topic name. If left empty (default), + * uses the standard topic name (see `TOPIC_NAME`). + */ + void offeringTopic(ManagedIceObject& component, const std::string& topicNameOverride = "") const; + /** + * @brief Get the topic by calling getTopic([topicName]) on the given component. + * @param component The component (`*this` when called from a component). + * @param topicNameOverride Optional override for the topic name. If left empty (default), + * uses the standard topic name (see `TOPIC_NAME`). + */ + void getTopic(ManagedIceObject& component, const std::string& topicNameOverride = ""); + + /// Get the default layer (used if no layer is passed to a method). + const std::string& getLayer() const; + /// Set the default layer (used if no layer is passed to a method). + void setLayer(const std::string& layer); + + /// Get the scaling for positions, lengths and distances. + float getLengthScale() const; + /// Set the scale for positions, lengths and distances. + void setLengthScale(float scale); + /// Set the scale for positions, lengths and distances to 1000. + void setLengthScaleMetersToMillimeters(); + /// Set the scale for positions, lengths and distances to 0.001. + void setLengthScaleMillimetersToMeters(); + + /// Get the scale for pose visualization. + float getPoseScale() const; + /// Set the scale for pose visualization. + /// This value will be used for all successive calls to drawPose(). + void setPoseScale(float scale); + /// Set the pose scale to 0.001 (good when drawing in meters). + void setPoseScaleMeters(); + /// Set the pose scale to 1 (good when drawing in millimeters). + void setPoseScaleMillimeters(); + + + + // SLEEP + + /// Sleep for the `shortSleepDuration`. Useful after clearing. + void shortSleep(); + + /// If enabled, sleep for the given duration (e.g. a chrono duration). + template <typename DurationT> + void sleepFor(const DurationT& duration); + + /// Set the duration for "short sleeps". + template <typename DurationT> + void setShortSleepDuration(const DurationT& duration); + + + // CLEAR + + /// Clear all layers. + /// @param sleep If true, do a short sleep clearing. + void clearAll(bool sleep = false); + + /// Clear the (set default) layer. + /// @param sleep If true, do a short sleep clearing. + void clearLayer(bool sleep = false); + + /// Clear the given layer. + /// @param sleep If true, do a short sleep clearing. + void clearLayer(const std::string& layer, bool sleep = false); + + + // PRIMITIVES + + /** + * @brief Draw text at the specified position. + * @param size the text size (in pixels, not affected by length scaling) + */ + void drawText(const VisuID& id, const Eigen::Vector3f& position, const std::string& text, + int size = 10, const DrawColor color = DEFAULTS.colorText, + bool ignoreLengthScale = false); + + + /// Draw a box. + void drawBox(const VisuID& id, const Eigen::Vector3f& position, const Eigen::Quaternionf& orientation, + const Eigen::Vector3f& extents, const DrawColor& color = DEFAULTS.colorBox, + bool ignoreLengthScale = false); + /// Draw a box. + void drawBox(const VisuID& id, const Eigen::Matrix4f& pose, const Eigen::Vector3f& extents, + const DrawColor& color = DEFAULTS.colorBox, + bool ignoreLengthScale = false); + + /// Draw an axis aligned bounding box. + void drawBox(const VisuID& id, + const VirtualRobot::BoundingBox& boundingBox, + const DrawColor& color = DEFAULTS.colorBox, + bool ignoreLengthScale = false); + + /// Draw a locally axis aligned bounding box, transformed by the given pose. + void drawBox(const VisuID& id, + const VirtualRobot::BoundingBox& boundingBox, const Eigen::Matrix4f& pose, + const DrawColor& color = DEFAULTS.colorBox, + bool ignoreLengthScale = false); + + + /** + * @brief Draw a cylinder with center and direction. + * @param length the full length (not half-length) + */ + void drawCylinder( + const VisuID& id, + const Eigen::Vector3f& center, const Eigen::Vector3f& direction, float radius, float length, + const DrawColor& color = DEFAULTS.colorCylinder, + bool ignoreLengthScale = false); + + /** + * @brief Draw a cylinder with center and orientation. + * An identity orientation represents a cylinder with an axis aligned to the Y-axis. + * @param length the full length (not half-length) + */ + void drawCylinder( + const VisuID& id, + const Eigen::Vector3f& center, const Eigen::Quaternionf& orientation, float radius, float length, + const DrawColor& color = DEFAULTS.colorCylinder, + bool ignoreLengthScale = false); + + /// Draw a cylinder from start to end. + void drawCylinderFromTo( + const VisuID& id, + const Eigen::Vector3f& from, const Eigen::Vector3f& to, float radius, + const DrawColor& color = DEFAULTS.colorCylinder, + bool ignoreLengthScale = false); + + + /// Draw a sphere. + void drawSphere( + const VisuID& id, + const Eigen::Vector3f& center, float radius, + const DrawColor& color = DEFAULTS.colorSphere, + bool ignoreLengthScale = false); + + + /// Draw an arrow with position (start) and direction. + void drawArrow( + const VisuID& id, + const Eigen::Vector3f& position, const Eigen::Vector3f& direction, float length, + float width, const DrawColor& color = DEFAULTS.colorArrow, + bool ignoreLengthScale = false); + + /// Draw an arrow with start and end. + void drawArrowFromTo( + const VisuID& id, + const Eigen::Vector3f& from, const Eigen::Vector3f& to, + float width, const DrawColor& color = DEFAULTS.colorArrow, + bool ignoreLengthScale = false); + + /// Remove an arrow. + void removeArrow(const VisuID& id); + + + /// Draw a polygon. + void drawPolygon( + const VisuID& id, + const std::vector<Eigen::Vector3f>& points, + const DrawColor& colorFace, + float lineWidth = 0, const DrawColor& colorEdge = DEFAULTS.colorPolygonEdge, + bool ignoreLengthScale = false); + + + /// Draw a line from start to end. + void drawLine( + const VisuID& id, + const Eigen::Vector3f& from, const Eigen::Vector3f& to, + float width, const DrawColor& color = DEFAULTS.colorLine, + bool ignoreLengthScale = false); + + + /// Draw a line set. + void drawLineSet( + const VisuID& id, + const DebugDrawerLineSet& lineSet, + bool ignoreLengthScale = false); + + /** + * @brief Draw a set of lines with constant color. + * @param points List of start and end points [ s1, e1, s2, e2, ..., sn, en ]. + */ + void drawLineSet( + const VisuID& id, + const std::vector<Eigen::Vector3f>& points, + float width, const DrawColor& color = DEFAULTS.colorLine, + bool ignoreLengthScale = false); + + /** + * @brief Draw a set of lines with constant color. + * @param points List of start and end points [ s1, e1, s2, e2, ..., sn, en ]. + * @param colors List of line colors [ c1, c2, ..., cn ]. + */ + void drawLineSet( + const VisuID& id, + const std::vector<Eigen::Vector3f>& points, + float width, const DrawColor& colorA, const DrawColor& colorB, + const std::vector<float>& intensitiesB, + bool ignoreLengthScale = false); + + + // POSE + + /// Draw a pose (with the preset scale). + void drawPose(const VisuID& id, const Eigen::Matrix4f& pose, + bool ignoreLengthScale = false); + /// Draw a pose (with the preset scale). + void drawPose(const VisuID& id, const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, + bool ignoreLengthScale = false); + + /// Draw a pose with the given scale. + void drawPose(const VisuID& id, const Eigen::Matrix4f& pose, float scale, + bool ignoreLengthScale = false); + /// Draw a pose with the given scale. + void drawPose(const VisuID& id, const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, + float scale, + bool ignoreLengthScale = false); + + /// Remove a pose visualization. + void removePose(const VisuID& id); + + + // ROBOT + + /** + * @brief Draw a robot. + * Afterwards, `updateRobot*()` methods can be used with the same VisuID. + * + * @param robotFile The robot file. Either an absolute path, or a path + * relative to the data directory of the ArmarX project specified by `armarxProject`. + * @param armarxProject The name of the ArmarX project keeping the robot model. + * @param drawStyle The draw style (full or collision model). + */ + void drawRobot( + const VisuID& id, + const std::string& robotFile, const std::string& armarxProject, + armarx::DrawStyle drawStyle = armarx::DrawStyle::FullModel); + + /// Update / set the robot pose. + void updateRobotPose( + const VisuID& id, + const Eigen::Matrix4f& pose, + bool ignoreScale = false); + + /// Update / set the robot pose. + void updateRobotPose( + const VisuID& id, + const Eigen::Vector3f& pos, const Eigen::Quaternionf& ori, + bool ignoreScale = false); + + /// Update / set the robot configuration (joint angles). + void updateRobotConfig( + const VisuID& id, + const std::map<std::string, float>& config); + + /// Update / set the robot color. + void updateRobotColor(const VisuID& id, const DrawColor& color); + /// Update / set the color of a robot node. + void updateRobotNodeColor( + const VisuID& id, const std::string& nodeName, const DrawColor& color); + + /// Remove a robot visualization. + void removeRobot(const VisuID& id); + + + // TRI MESH + + /// Draw a TriMeshModel as DebugDrawerTriMesh. + void drawTriMesh( + const VisuID& id, + const VirtualRobot::TriMeshModel& triMesh, + const DrawColor& color = {.5, .5, .5, 1}, + bool ignoreLengthScale = false); + + /// Draw a TriMeshModel as individual polygons. + void drawTriMeshAsPolygons( + const VisuID& id, + const VirtualRobot::TriMeshModel& trimesh, + const DrawColor& colorFace = DEFAULTS.colorPolygonFace, + float lineWidth = 0, const DrawColor& colorEdge = DEFAULTS.colorPolygonEdge, + bool ignoreLengthScale = false); + + /// Draw a TriMeshModel as individual polygons, transformed by the given pose. + void drawTriMeshAsPolygons( + const VisuID& id, + const VirtualRobot::TriMeshModel& trimesh, const Eigen::Matrix4f& pose, + const DrawColor& colorFace = DEFAULTS.colorPolygonFace, + float lineWidth = 0, const DrawColor& colorEdge = DEFAULTS.colorPolygonEdge, + bool ignoreLengthScale = false); + + /// Draw a TriMeshModel as individual polygons with individual face colors. + void drawTriMeshAsPolygons( + const VisuID& id, + const VirtualRobot::TriMeshModel& trimesh, + const std::vector<DrawColor>& faceColorsInner, + float lineWidth = 0, const DrawColor& colorEdge = DEFAULTS.colorPolygonEdge, + bool ignoreLengthScale = false); + + + // POINT CLOUD + /* (By templating these functions, we can make them usable for PCL + * point clouds without a dependency on PCL.) + */ + + /** + * @brief Draw a unicolored point cloud. + * + * `pointCloud` must be iterable and its elements must provide members `x, y, z`. + */ + template <class PointCloudT> + void drawPointCloud( + const VisuID& id, + const PointCloudT& pointCloud, + const DrawColor& color = DEFAULTS.colorPointCloud, + float pointSize = DEFAULTS.pointCloudPointSize, + bool ignoreLengthScale = false); + + /** + * @brief Draw a colored point cloud with RGBA information. + * + * `pointCloud` must be iterable and its elements must provide + * members `x, y, z, r, g, b, a`. + */ + template <class PointCloudT> + void drawPointCloudRGBA( + const VisuID& id, + const PointCloudT& pointCloud, + float pointSize = DEFAULTS.pointCloudPointSize, + bool ignoreLengthScale = false); + + /** + * @brief Draw a colored point cloud with colors according to labels. + * + * `pointCloud` must be iterable and its elements must provide + * members `x, y, z, label`. + */ + template <class PointCloudT> + void drawPointCloudLabeled( + const VisuID& id, + const PointCloudT& pointCloud, + float pointSize = DEFAULTS.pointCloudPointSize, + bool ignoreLengthScale = false); + + /** + * @brief Draw a colored point cloud with custom colors. + * + * `pointCloud` must be iterable and its elements must provide + * members `x, y, z`. + * The color of a point is specified by `colorFunc`, which must be + * a callable taking an element of `pointCloud` and returning its + * color as `armarx::DrawColor`. + */ + template <class PointCloudT, class ColorFuncT> + void drawPointCloud( + const VisuID& id, + const PointCloudT& pointCloud, + const ColorFuncT& colorFunc, + float pointSize = DEFAULTS.pointCloudPointSize, + bool ignoreLengthScale = false); + + + // Debug Drawer Point Cloud Types + + /// Draw a non-colored point cloud. + void drawPointCloud(const VisuID& id, const DebugDrawerPointCloud& pointCloud); + + /// Draw a colored point cloud. + void drawPointCloud(const VisuID& id, const DebugDrawerColoredPointCloud& pointCloud); + + /// Draw a 24 bit colored point cloud. + void drawPointCloud(const VisuID& id, const DebugDrawer24BitColoredPointCloud& pointCloud); + + + /// Forces the "deletion" of a point cloud by drawing an empty one. + void clearColoredPointCloud(const VisuID& id); + + + // CUSTOM + + /** + * @brief Draw a quad representing the floor. + * @param at the quad'S center + * @param up the up direction (normal of the floor plane) + * @param size the quad's edge length + */ + void drawFloor( + const VisuID& id = { "floor" }, + const Eigen::Vector3f& at = Eigen::Vector3f::Zero(), + const Eigen::Vector3f& up = Eigen::Vector3f::UnitZ(), + float size = 5, const DrawColor& color = DEFAULTS.colorFloor, + bool ignoreLengthScale = false); + + + // STATUS + + /// Indicate whether a topic is set, i.e. visualization is enabled. + bool enabled() const; + /// Indicate whether a topic is set, i.e. visualization is enabled. + operator bool() const; + + + // OPERATORS + + /// Conversion operator to DebugDrawerInterfacePrx. + operator DebugDrawerInterfacePrx& (); + operator const DebugDrawerInterfacePrx& () const; + + /// Pointer member access operator to access the raw debug drawer proxy. + DebugDrawerInterfacePrx& operator->(); + const DebugDrawerInterfacePrx& operator->() const; + + + public: // STATIC + + /** + * @brief Convert a RGB color to HSV. + * @param rgb RGB color as [r, g, b] with r, g, b in [0, 1]. + * @return HSV color as [h, s, v] with h in [0 deg, 360 deg] and s, v in [0, 1]. + */ + static Eigen::Vector3f rgb2hsv(const Eigen::Vector3f& rgb); + + /** + * @brief Convert a HSV color RGB. + * @param HSV color as [h, s, v] with h in [0 deg, 360 deg] and s, v in [0, 1]. + * @return RGB color as [r, g, b] with r, g, b in [0, 1]. + */ + static Eigen::Vector3f hsv2rgb(const Eigen::Vector3f& hsv); + + + /** + * @brief Construct a DrawColor from the given color type. + * + * The used color type must have members named `r`, `g` and `b`. + * Applicable types include: + * - pcl::RGB (byteToFloat = true) + * - armarx::DrawColor (useful to get a color with a different alpha) + * + * @param alpha the alpha (default: 1) + * @param byteToFloat If true, scale from range [0, 255] to [0, 1] + */ + template <class ColorT> + static DrawColor toDrawColor(const ColorT& color, float alpha = 1, bool byteToFloat = false); + + + /// Get a color from the Glasbey look-up-table. + static DrawColor getGlasbeyLUTColor(int id, float alpha = 1.f); + static DrawColor getGlasbeyLUTColor(uint32_t id, float alpha = 1.f); + static DrawColor getGlasbeyLUTColor(std::size_t id, float alpha = 1.f); + + + private: + + /// Get the layer to draw on. (passedLayer if not empty, _layer otherwise). + const std::string& layer(const std::string& passedLayer) const; + + /// Get the layer to draw on. (id.layer if not empty, _layer otherwise). + const std::string& layer(const VisuID& id) const; + + + /// Return _lengthScale if ignore is false (default), 1 otherwise. + float lengthScale(bool ignore = false) const; + + /// Scale the value. + static float scaled(float scale, float value); + /// Scale the given vector and return it as Vector3 pointer. + static Vector3BasePtr scaled(float scale, const Eigen::Vector3f& vector); + /// Scale the translation of the given pose and return it as Pose pointer. + static PoseBasePtr scaled(float scale, const Eigen::Matrix4f& pose); + + /// Scale the given vector and return it as `ScaledT`. + /// The constructor of `ScaledT` must take the x, y and z coordinates. + template <class ScaledT> + static ScaledT scaledT(float scale, const Eigen::Vector3f& vector); + + /// Scale a value with .x, .y and .z attributes in-place. + template <class XYZT> + static void scaleXYZ(float scale, XYZT& xyz); + + + private: + + /// The name of the debug drawer topic. + static const std::string TOPIC_NAME; + /// The default layer ("debug"). + static const std::string DEFAULT_LAYER; + + + /// The debug drawer topic. + DebugDrawerInterfacePrx topic = nullptr; + + /// The default layer (used if none is passed to the method). + std::string _layer = DEFAULT_LAYER; + + /// Scaling for positions, lengths and distances. + float _lengthScale = 1; + + /// Scaling for pose visualization (1 is good when drawing in millimeters). + float _poseScale = 1; + + /// The duration for shortSleep(). + std::chrono::milliseconds _shortSleepDuration { 100 }; + + }; + + + template <typename DurationT> + void DebugDrawerTopic::sleepFor(const DurationT& duration) + { + if (enabled()) + { + std::this_thread::sleep_for(duration); + } + } + + template <typename DurationT> + void DebugDrawerTopic::setShortSleepDuration(const DurationT& duration) + { + this->_shortSleepDuration = std::chrono::duration_cast<std::chrono::milliseconds>(duration); + } + + template <class ColorT> + DrawColor DebugDrawerTopic::toDrawColor(const ColorT& color, float alpha, bool byteToFloat) + { + const float scale = byteToFloat ? (1 / 255.f) : 1; + return { color.r * scale, color.g * scale, color.b * scale, alpha }; + } + + + template <class PointCloudT> + void DebugDrawerTopic::drawPointCloud( + const VisuID& id, + const PointCloudT& pointCloud, + const DrawColor& color, + float pointSize, + bool ignoreLengthScale) + { + drawPointCloud(id, pointCloud, [&color](const auto&) + { + return color; + }, + pointSize, ignoreLengthScale); + } + + template<class PointCloudT> + void DebugDrawerTopic::drawPointCloudRGBA( + const VisuID& id, + const PointCloudT& pointCloud, + float pointSize, + bool ignoreLengthScale) + { + drawPointCloud(id, pointCloud, [](const auto & p) + { + return toDrawColor(p, p.a); + }, + pointSize, ignoreLengthScale); + } + + template <class PointCloudT> + void DebugDrawerTopic::drawPointCloudLabeled( + const VisuID& id, + const PointCloudT& pointCloud, + float pointSize, + bool ignoreLengthScale) + { + drawPointCloud(id, pointCloud, [](const auto & p) + { + return getGlasbeyLUTColor(p.label); + }, + pointSize, ignoreLengthScale); + } + + template <class PointCloudT, class ColorFuncT> + void DebugDrawerTopic::drawPointCloud( + const VisuID& id, + const PointCloudT& pointCloud, + const ColorFuncT& colorFn, + float pointSize, + bool ignoreLengthScale) + { + if (!enabled()) + { + return; + } + + const float lf = ignoreLengthScale ? 1.0 : _lengthScale; + + DebugDrawerColoredPointCloud dd; + dd.points.reserve(pointCloud.size()); + + dd.pointSize = pointSize; + + for (const auto& p : pointCloud) + { + dd.points.push_back(DebugDrawerColoredPointCloudElement + { + lf * p.x, lf * p.y, lf * p.z, colorFn(p) + }); + } + + drawPointCloud(id, dd); + } + + + template <class ScaledT> + ScaledT DebugDrawerTopic::scaledT(float scale, const Eigen::Vector3f& vector) + { + auto scaled = vector * scale; + return { scaled.x(), scaled.y(), scaled.z() }; + } + + template <class XYZT> + void DebugDrawerTopic::scaleXYZ(float scale, XYZT& xyz) + { + xyz.x *= scale; + xyz.y *= scale; + xyz.z *= scale; + } + +} diff --git a/source/RobotAPI/libraries/core/visualization/GlasbeyLUT.cpp b/source/RobotAPI/libraries/core/visualization/GlasbeyLUT.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59d11b5ddb95fb399e361254d74cf8a3694d8135 --- /dev/null +++ b/source/RobotAPI/libraries/core/visualization/GlasbeyLUT.cpp @@ -0,0 +1,579 @@ +#include "GlasbeyLUT.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +// taken from: +// https://github.com/PointCloudLibrary/pcl/blob/master/common/src/colors.cpp + + +namespace armarx +{ + + /// Glasbey lookup table + static const std::vector<unsigned char> GLASBEY_LUT = + { + 77, 175, 74, + 228, 26, 28, + 55, 126, 184, + 152, 78, 163, + 255, 127, 0, + 255, 255, 51, + 166, 86, 40, + 247, 129, 191, + 153, 153, 153, + 0, 0, 255, + 255, 0, 255, + 0, 255, 248, + 0, 255, 0, + 0, 101, 255, + 255, 255, 180, + 52, 68, 1, + 0, 0, 68, + 96, 0, 41, + 158, 147, 0, + 116, 0, 185, + 255, 0, 114, + 0, 149, 125, + 209, 186, 255, + 255, 183, 156, + 240, 0, 174, + 208, 255, 245, + 0, 255, 176, + 170, 255, 93, + 0, 207, 255, + 255, 190, 1, + 241, 117, 255, + 52, 74, 167, + 150, 166, 103, + 255, 114, 114, + 171, 100, 109, + 161, 0, 41, + 160, 135, 255, + 105, 86, 121, + 178, 21, 105, + 0, 3, 123, + 255, 221, 236, + 160, 238, 173, + 237, 161, 77, + 0, 141, 255, + 0, 97, 109, + 1, 238, 98, + 81, 0, 78, + 128, 103, 66, + 0, 108, 44, + 209, 224, 94, + 155, 0, 255, + 0, 45, 223, + 88, 28, 0, + 166, 2, 162, + 165, 205, 239, + 84, 121, 0, + 76, 109, 80, + 122, 180, 0, + 104, 204, 204, + 145, 95, 255, + 214, 208, 177, + 185, 130, 176, + 130, 120, 194, + 128, 96, 0, + 247, 161, 255, + 10, 65, 119, + 232, 102, 54, + 7, 191, 131, + 105, 54, 171, + 10, 177, 0, + 215, 191, 105, + 198, 66, 249, + 140, 255, 145, + 135, 60, 105, + 254, 170, 191, + 130, 173, 255, + 161, 35, 0, + 197, 255, 0, + 40, 153, 180, + 215, 83, 185, + 255, 77, 161, + 128, 175, 147, + 216, 91, 124, + 193, 144, 91, + 210, 196, 0, + 232, 39, 73, + 76, 52, 116, + 159, 206, 110, + 138, 147, 187, + 140, 5, 114, + 0, 56, 183, + 191, 105, 0, + 83, 58, 0, + 94, 224, 0, + 121, 99, 99, + 212, 123, 104, + 89, 160, 99, + 146, 58, 54, + 231, 46, 215, + 93, 245, 200, + 191, 147, 133, + 255, 211, 89, + 171, 77, 214, + 0, 121, 0, + 60, 14, 107, + 255, 82, 1, + 112, 115, 43, + 0, 172, 245, + 255, 184, 240, + 1, 210, 111, + 203, 151, 0, + 95, 114, 129, + 183, 215, 17, + 80, 110, 231, + 201, 25, 87, + 218, 250, 203, + 255, 148, 103, + 255, 217, 163, + 198, 172, 199, + 78, 139, 135, + 197, 255, 134, + 38, 0, 165, + 197, 208, 211, + 193, 117, 225, + 111, 0, 128, + 147, 255, 238, + 125, 62, 254, + 112, 213, 78, + 198, 76, 61, + 155, 48, 82, + 0, 199, 176, + 118, 6, 0, + 2, 106, 192, + 140, 167, 49, + 189, 81, 145, + 254, 162, 38, + 134, 138, 106, + 0, 68, 17, + 122, 73, 61, + 255, 251, 239, + 127, 94, 193, + 181, 140, 48, + 66, 235, 255, + 189, 140, 218, + 190, 0, 138, + 132, 177, 185, + 90, 54, 202, + 0, 35, 131, + 251, 139, 149, + 74, 0, 225, + 0, 106, 90, + 106, 199, 155, + 104, 169, 217, + 255, 239, 134, + 44, 94, 130, + 126, 0, 78, + 196, 214, 145, + 160, 238, 255, + 222, 215, 255, + 255, 87, 126, + 170, 161, 255, + 81, 120, 60, + 255, 242, 91, + 168, 130, 145, + 158, 153, 64, + 211, 123, 156, + 255, 0, 3, + 210, 118, 197, + 0, 41, 111, + 198, 178, 125, + 211, 255, 169, + 109, 215, 130, + 41, 90, 0, + 235, 193, 183, + 114, 58, 0, + 140, 96, 155, + 163, 223, 193, + 255, 142, 63, + 66, 155, 1, + 83, 96, 152, + 106, 133, 230, + 255, 85, 72, + 141, 216, 0, + 162, 102, 73, + 79, 0, 146, + 190, 0, 30, + 165, 0, 193, + 81, 84, 255, + 0, 148, 74, + 203, 0, 255, + 121, 54, 71, + 215, 255, 97, + 163, 178, 0, + 111, 154, 68, + 120, 93, 222, + 254, 187, 126, + 112, 0, 27, + 204, 59, 0, + 0, 165, 167, + 151, 255, 0, + 104, 41, 124, + 138, 89, 113, + 255, 94, 224, + 86, 91, 48, + 75, 255, 76, + 204, 190, 67, + 255, 224, 0, + 49, 126, 85, + 145, 196, 135, + 187, 64, 79, + 255, 130, 233, + 205, 127, 68, + 105, 195, 223, + 161, 213, 81, + 165, 183, 240, + 125, 255, 180, + 126, 255, 111, + 67, 255, 145, + 154, 138, 83, + 46, 145, 231, + 118, 121, 0, + 154, 2, 222, + 185, 119, 255, + 255, 0, 62, + 131, 28, 170, + 177, 70, 183, + 217, 0, 115, + 186, 196, 95, + 97, 46, 97, + 84, 134, 167, + 81, 54, 145, + 107, 117, 107, + 51, 15, 80, + 215, 139, 143, + 255, 247, 203, + 255, 86, 192, + 153, 91, 0, + 255, 1, 156, + 183, 79, 19, + 235, 255, 146, + 211, 1, 224, + 178, 167, 144, + 217, 97, 0, + 134, 91, 38, + 175, 151, 206, + 0, 182, 63, + 210, 40, 179, + 2, 213, 42, + 94, 84, 160, + 136, 48, 0, + 255, 110, 163, + 144, 121, 157, + 153, 61, 225, + 237, 87, 255, + 87, 24, 206, + 117, 143, 207, + }; + + + DrawColor GlasbeyLUT::at(std::size_t id, float alpha) + { + const float scale = 1.f / 255.f; // Scale byte to float. + + id = id % size(); + + DrawColor color; + color.r = scale * GLASBEY_LUT[id * 3 + 0]; + color.g = scale * GLASBEY_LUT[id * 3 + 1]; + color.b = scale * GLASBEY_LUT[id * 3 + 2]; + color.a = alpha; + return color; + } + + + DrawColor24Bit GlasbeyLUT::atByte(std::size_t id) + { + id = id % size(); + + DrawColor24Bit color; + color.r = GLASBEY_LUT[id * 3 + 0]; + color.g = GLASBEY_LUT[id * 3 + 1]; + color.b = GLASBEY_LUT[id * 3 + 2]; + return color; + } + + + + std::size_t GlasbeyLUT::size() + { + ARMARX_CHECK_EQUAL(GLASBEY_LUT.size() % 3, 0); + return GLASBEY_LUT.size() / 3; + } + + const std::vector<unsigned char>& GlasbeyLUT::data() + { + return GLASBEY_LUT; + } + + +#if 0 + + /// Viridis lookup table + static const unsigned char VIRIDIS_LUT[] = + { + 68, 1, 84, + 68, 2, 85, + 69, 3, 87, + 69, 5, 88, + 69, 6, 90, + 70, 8, 91, + 70, 9, 93, + 70, 11, 94, + 70, 12, 96, + 71, 14, 97, + 71, 15, 98, + 71, 17, 100, + 71, 18, 101, + 71, 20, 102, + 72, 21, 104, + 72, 22, 105, + 72, 24, 106, + 72, 25, 108, + 72, 26, 109, + 72, 28, 110, + 72, 29, 111, + 72, 30, 112, + 72, 32, 113, + 72, 33, 115, + 72, 34, 116, + 72, 36, 117, + 72, 37, 118, + 72, 38, 119, + 72, 39, 120, + 71, 41, 121, + 71, 42, 121, + 71, 43, 122, + 71, 44, 123, + 71, 46, 124, + 70, 47, 125, + 70, 48, 126, + 70, 49, 126, + 70, 51, 127, + 69, 52, 128, + 69, 53, 129, + 69, 54, 129, + 68, 56, 130, + 68, 57, 131, + 68, 58, 131, + 67, 59, 132, + 67, 60, 132, + 67, 62, 133, + 66, 63, 133, + 66, 64, 134, + 65, 65, 134, + 65, 66, 135, + 65, 67, 135, + 64, 69, 136, + 64, 70, 136, + 63, 71, 136, + 63, 72, 137, + 62, 73, 137, + 62, 74, 137, + 61, 75, 138, + 61, 77, 138, + 60, 78, 138, + 60, 79, 138, + 59, 80, 139, + 59, 81, 139, + 58, 82, 139, + 58, 83, 139, + 57, 84, 140, + 57, 85, 140, + 56, 86, 140, + 56, 87, 140, + 55, 88, 140, + 55, 89, 140, + 54, 91, 141, + 54, 92, 141, + 53, 93, 141, + 53, 94, 141, + 52, 95, 141, + 52, 96, 141, + 51, 97, 141, + 51, 98, 141, + 51, 99, 141, + 50, 100, 142, + 50, 101, 142, + 49, 102, 142, + 49, 103, 142, + 48, 104, 142, + 48, 105, 142, + 47, 106, 142, + 47, 107, 142, + 47, 108, 142, + 46, 109, 142, + 46, 110, 142, + 45, 111, 142, + 45, 112, 142, + 45, 112, 142, + 44, 113, 142, + 44, 114, 142, + 43, 115, 142, + 43, 116, 142, + 43, 117, 142, + 42, 118, 142, + 42, 119, 142, + 41, 120, 142, + 41, 121, 142, + 41, 122, 142, + 40, 123, 142, + 40, 124, 142, + 40, 125, 142, + 39, 126, 142, + 39, 127, 142, + 38, 128, 142, + 38, 129, 142, + 38, 130, 142, + 37, 131, 142, + 37, 131, 142, + 37, 132, 142, + 36, 133, 142, + 36, 134, 142, + 35, 135, 142, + 35, 136, 142, + 35, 137, 142, + 34, 138, 141, + 34, 139, 141, + 34, 140, 141, + 33, 141, 141, + 33, 142, 141, + 33, 143, 141, + 32, 144, 141, + 32, 145, 140, + 32, 146, 140, + 32, 147, 140, + 31, 147, 140, + 31, 148, 140, + 31, 149, 139, + 31, 150, 139, + 31, 151, 139, + 30, 152, 139, + 30, 153, 138, + 30, 154, 138, + 30, 155, 138, + 30, 156, 137, + 30, 157, 137, + 30, 158, 137, + 30, 159, 136, + 30, 160, 136, + 31, 161, 136, + 31, 162, 135, + 31, 163, 135, + 31, 163, 134, + 32, 164, 134, + 32, 165, 134, + 33, 166, 133, + 33, 167, 133, + 34, 168, 132, + 35, 169, 131, + 35, 170, 131, + 36, 171, 130, + 37, 172, 130, + 38, 173, 129, + 39, 174, 129, + 40, 175, 128, + 41, 175, 127, + 42, 176, 127, + 43, 177, 126, + 44, 178, 125, + 46, 179, 124, + 47, 180, 124, + 48, 181, 123, + 50, 182, 122, + 51, 183, 121, + 53, 183, 121, + 54, 184, 120, + 56, 185, 119, + 57, 186, 118, + 59, 187, 117, + 61, 188, 116, + 62, 189, 115, + 64, 190, 114, + 66, 190, 113, + 68, 191, 112, + 70, 192, 111, + 72, 193, 110, + 73, 194, 109, + 75, 194, 108, + 77, 195, 107, + 79, 196, 106, + 81, 197, 105, + 83, 198, 104, + 85, 198, 102, + 88, 199, 101, + 90, 200, 100, + 92, 201, 99, + 94, 201, 98, + 96, 202, 96, + 98, 203, 95, + 101, 204, 94, + 103, 204, 92, + 105, 205, 91, + 108, 206, 90, + 110, 206, 88, + 112, 207, 87, + 115, 208, 85, + 117, 208, 84, + 119, 209, 82, + 122, 210, 81, + 124, 210, 79, + 127, 211, 78, + 129, 212, 76, + 132, 212, 75, + 134, 213, 73, + 137, 213, 72, + 139, 214, 70, + 142, 215, 68, + 144, 215, 67, + 147, 216, 65, + 149, 216, 63, + 152, 217, 62, + 155, 217, 60, + 157, 218, 58, + 160, 218, 57, + 163, 219, 55, + 165, 219, 53, + 168, 220, 51, + 171, 220, 50, + 173, 221, 48, + 176, 221, 46, + 179, 221, 45, + 181, 222, 43, + 184, 222, 41, + 187, 223, 39, + 189, 223, 38, + 192, 223, 36, + 195, 224, 35, + 197, 224, 33, + 200, 225, 32, + 203, 225, 30, + 205, 225, 29, + 208, 226, 28, + 211, 226, 27, + 213, 226, 26, + 216, 227, 25, + 219, 227, 24, + 221, 227, 24, + 224, 228, 24, + 226, 228, 24, + 229, 228, 24, + 232, 229, 25, + 234, 229, 25, + 237, 229, 26, + 239, 230, 27, + 242, 230, 28, + 244, 230, 30, + 247, 230, 31, + 249, 231, 33, + 251, 231, 35, + 254, 231, 36, + }; + +#endif + +} diff --git a/source/RobotAPI/libraries/core/visualization/GlasbeyLUT.h b/source/RobotAPI/libraries/core/visualization/GlasbeyLUT.h new file mode 100644 index 0000000000000000000000000000000000000000..770a7fca64107c6c9446e0b634722ec0a0b696b2 --- /dev/null +++ b/source/RobotAPI/libraries/core/visualization/GlasbeyLUT.h @@ -0,0 +1,91 @@ +#pragma once + +#include <cmath> +#include <type_traits> +#include <vector> + +#include <RobotAPI/interface/visualization/DebugDrawerInterface.h> + + +namespace armarx +{ + + struct DrawColor; + struct DrawColor24Bit; + + + /** + * "Color lookup table consisting of 256 colors structured in a maximally + * discontinuous manner. Generated using the method of Glasbey et al. + * (see https://github.com/taketwo/glasbey)" [1] + * + * + * [1](https://github.com/PointCloudLibrary/pcl/blob/master/common/include/pcl/common/colors.h) + */ + class GlasbeyLUT + { + public: + + /** + * @brief Get a color from the lookup table with given ID (with float values). + * The ID is automaticall wrapped if greater than `size()`. + * If `id` is negative, its absolute value is used. + */ + template <typename UIntT, std::enable_if_t<std::is_unsigned<UIntT>::value, int> = 0> + static DrawColor at(UIntT id, float alpha = 1.f); + // If `id` is negative, its absolute value is used. + template <typename IntT, std::enable_if_t<std::is_signed<IntT>::value, int> = 0> + static DrawColor at(IntT id, float alpha = 1.f); + static DrawColor at(std::size_t id, float alpha = 1.f); + + /** + * @brief Get a color from the lookup table with given ID (with integer values). + * The ID is automaticall wrapped if greater than `size()`. + * If `id` is negative, its absolute value is used. + */ + template <typename UIntT, std::enable_if_t<std::is_unsigned<UIntT>::value, int> = 0> + static DrawColor24Bit atByte(UIntT id); + template <typename IntT, std::enable_if_t<std::is_signed<IntT>::value, int> = 0> + static DrawColor24Bit atByte(IntT id); + static DrawColor24Bit atByte(std::size_t id); + + /// Get the number of colors in the lookup table.; + static std::size_t size(); + + /// Get the raw lookup table (flat). + static const std::vector<unsigned char>& data(); + + + private: + + /// Private constructor. + GlasbeyLUT() = default; + + }; + + + template <typename UIntT, std::enable_if_t<std::is_unsigned<UIntT>::value, int>> + DrawColor GlasbeyLUT::at(UIntT id, float alpha) + { + return at(static_cast<std::size_t>(id), alpha); + } + + template <typename IntT, std::enable_if_t<std::is_signed<IntT>::value, int>> + DrawColor GlasbeyLUT::at(IntT id, float alpha) + { + return at(static_cast<std::size_t>(id >= 0 ? id : std::abs(id)), alpha); + } + + template <typename UIntT, std::enable_if_t<std::is_unsigned<UIntT>::value, int>> + DrawColor24Bit GlasbeyLUT::atByte(UIntT id) + { + return atByte(static_cast<std::size_t>(id)); + } + + template <typename IntT, std::enable_if_t<std::is_signed<IntT>::value, int>> + DrawColor24Bit GlasbeyLUT::atByte(IntT id) + { + return atByte(static_cast<std::size_t>(id >= 0 ? id : std::abs(id))); + } + +} diff --git a/source/RobotAPI/libraries/widgets/CMakeLists.txt b/source/RobotAPI/libraries/widgets/CMakeLists.txt index bd78d50f3f8867cd5a0225362ca61a259cce44b7..8321b0aba4842080cc31585fee4dce13697c4a6c 100644 --- a/source/RobotAPI/libraries/widgets/CMakeLists.txt +++ b/source/RobotAPI/libraries/widgets/CMakeLists.txt @@ -10,8 +10,14 @@ armarx_build_if(jsoncpp_FOUND "jsoncpp not available") set(LIB_NAME RobotAPIWidgets) set(LIBS RobotAPICore) -set(LIB_FILES DebugLayerControlWidget.cpp) -set(LIB_HEADERS DebugLayerControlWidget.h) +set(LIB_FILES + DebugLayerControlWidget.cpp + JSONTreeModel.cpp + ) +set(LIB_HEADERS + DebugLayerControlWidget.h + JSONTreeModel.h + ) set(GUI_UIS DebugLayerControlWidget.ui) list(APPEND LIB_HEADERS ${GUI_UIS}) diff --git a/source/RobotAPI/libraries/widgets/JSONTreeModel.cpp b/source/RobotAPI/libraries/widgets/JSONTreeModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..74681c074bc599aa8148e3a8b5738da5d9fc37c0 --- /dev/null +++ b/source/RobotAPI/libraries/widgets/JSONTreeModel.cpp @@ -0,0 +1,168 @@ +#include "JSONTreeModel.h" + +namespace armarx +{ + + void JSONTreeModel::setRoot(ValuePtr root) + { + this->root = root; + rows.clear(); + rows[root] = 0; + parents.clear(); + parents[root] = nullptr; + names.clear(); + names[root] = ""; + } + + QModelIndex JSONTreeModel::index(int row, int column, const QModelIndex& parentIndex) const + { + ValuePtr parent = refFrom(parentIndex); + if (row >= (int)parent->size()) + { + return QModelIndex(); + } + + ValuePtr child = nullptr; + if (parent->is_array()) + { + child = &parent->at(row); + names[child] = "[" + std::to_string(row) + "]"; + } + else if (parent->is_object()) + { + auto it = parent->begin(); + std::advance(it, row); + child = &it.value(); + names[child] = it.key(); + } + + if (child) + { + parents[child] = parent; + rows[child] = row; + + return createIndex(row, column, child); + } + else + { + return QModelIndex(); + } + + } + + QModelIndex JSONTreeModel::parent(const QModelIndex& index) const + { + if (!index.isValid()) + { + return QModelIndex(); + } + + ValuePtr child = refFrom(index); + ValuePtr parent = parents.at(child); + if (parent == nullptr) + { + return QModelIndex(); + } + int row = rows.at(parent); + + return createIndex(row, 0, parent); + } + + int JSONTreeModel::rowCount(const QModelIndex& parentIndex) const + { + if (parentIndex.column() > 1) + { + return 0; + } + + ValuePtr parent = refFrom(parentIndex); + if (parent->is_array() || parent->is_object()) + { + return parent->size(); + } + else + { + return 0; + } + } + + int JSONTreeModel::columnCount(const QModelIndex& parent) const + { + return 2; + } + + QVariant JSONTreeModel::data(const QModelIndex& index, int role) const + { + if (!index.isValid()) + { + return QVariant(); + } + + if (role != Qt::DisplayRole) + { + return QVariant(); + } + + nlohmann::json* ref = refFrom(index); + + if (index.column() == 0) + { + std::string const& name = names.at(ref); + return QVariant(name.c_str()); + } + + switch (ref->type()) + { + case nlohmann::json::value_t::null: + return QVariant("null"); + case nlohmann::json::value_t::object: + return QVariant("object"); + case nlohmann::json::value_t::array: + return QVariant("array"); + case nlohmann::json::value_t::string: + return QVariant(ref->get<std::string>().c_str()); + case nlohmann::json::value_t::boolean: + return QVariant(ref->get<bool>()); + case nlohmann::json::value_t::number_integer: + return QVariant(ref->get<int>()); + case nlohmann::json::value_t::number_unsigned: + return QVariant(ref->get<unsigned int>()); + case nlohmann::json::value_t::number_float: + return QVariant(ref->get<float>()); + + default: + return QVariant("n/a"); + } + } + + QVariant JSONTreeModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (role != Qt::DisplayRole) + { + return QVariant(); + } + + switch (section) + { + case 0: + return QVariant("Name"); + case 1: + return QVariant("Value"); + default: + return QVariant(); + } + } + + auto JSONTreeModel::refFrom(const QModelIndex& index) const -> ValuePtr + { + if (index.isValid()) + { + return static_cast<ValuePtr>(index.internalPointer()); + } + else + { + return root; + } + } + +} diff --git a/source/RobotAPI/libraries/widgets/JSONTreeModel.h b/source/RobotAPI/libraries/widgets/JSONTreeModel.h new file mode 100644 index 0000000000000000000000000000000000000000..83de308bd4a6cea7bccf9a1c0b21b414a633df31 --- /dev/null +++ b/source/RobotAPI/libraries/widgets/JSONTreeModel.h @@ -0,0 +1,34 @@ +#pragma once + +#include <VirtualRobot/Util/json/json.hpp> +#include <QAbstractItemModel> + +namespace armarx +{ + + class JSONTreeModel : public QAbstractItemModel + { + public: + using ValuePtr = nlohmann::json*; + + void setRoot(ValuePtr root); + + // QAbstractItemModel interface + QModelIndex index(int row, int column, const QModelIndex& parent) const override; + QModelIndex parent(const QModelIndex& index) const override; + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + QVariant data(const QModelIndex& index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + private: + ValuePtr refFrom(QModelIndex const& index) const; + + private: + mutable std::map<ValuePtr, int> rows; + mutable std::map<ValuePtr, ValuePtr> parents; + mutable std::map<ValuePtr, std::string> names; + ValuePtr root; + }; + +} diff --git a/source/RobotAPI/statecharts/operations/RobotControl.h b/source/RobotAPI/statecharts/operations/RobotControl.h index 78f9a394c4eb907a257bee597766db35d3623ee9..7aca952c718ff55f39188ad2ef246cbf36e85ba7 100644 --- a/source/RobotAPI/statecharts/operations/RobotControl.h +++ b/source/RobotAPI/statecharts/operations/RobotControl.h @@ -76,7 +76,7 @@ namespace armarx void onExitRemoteStateOfferer() override; void startRobotStatechart(); - void hardReset(const Ice::Current& = ::Ice::Current()) override; + void hardReset(const Ice::Current& = Ice::emptyCurrent) override; PropertyDefinitionsPtr createPropertyDefinitions() override;