diff --git a/VirtualRobot/Robot.cpp b/VirtualRobot/Robot.cpp index bc5f88c866919ba58f167387bd89cea2b3b679da..9f54a572433e1ec67af34aa7c7d938d1acd47eb9 100644 --- a/VirtualRobot/Robot.cpp +++ b/VirtualRobot/Robot.cpp @@ -1242,5 +1242,27 @@ namespace VirtualRobot propagatingJointValuesEnabled = enabled; } + void Robot::validateNodeMapping(const NodeMapping& nodeMapping) const + { + for(const auto& nodeMap : nodeMapping) + { + THROW_VR_EXCEPTION_IF(not hasRobotNode(nodeMap.first), "Node '" + nodeMap.first + " not found in the node set!"); + THROW_VR_EXCEPTION_IF(not hasRobotNode(nodeMap.second.node), "Node '" + nodeMap.second.node + " not found in the node set!"); + } + } + + void Robot::registerNodeMapping(const NodeMapping& nodeMapping){ + + validateNodeMapping(nodeMapping); + + this->nodeMapping = nodeMapping; + } + + const NodeMapping& Robot::getNodeMapping() const + { + return nodeMapping; + } + + } // namespace VirtualRobot diff --git a/VirtualRobot/Robot.h b/VirtualRobot/Robot.h index d9e35d52bc4569228c5069a4a3f8e7e3d1313205..b75e3e9fe4b1497b9f3e9a36b15c855b4edf420e 100644 --- a/VirtualRobot/Robot.h +++ b/VirtualRobot/Robot.h @@ -40,6 +40,16 @@ namespace VirtualRobot class Visualization; class RobotNode; + + struct NodeMappingElement + { + std::string node; + + float sign; + }; + + using NodeMapping = std::unordered_map<std::string, NodeMappingElement>; + /*! This is the main object defining the kinematic structure of a robot. @@ -167,6 +177,11 @@ namespace VirtualRobot virtual std::vector<std::string> getRobotNodeSetNames() const; virtual void getRobotNodeSets(std::vector<RobotNodeSetPtr>& storeNodeSet) const = 0; + /*! + * Node mapping + */ + const NodeMapping& getNodeMapping() const; + /** * */ @@ -430,8 +445,15 @@ namespace VirtualRobot return passive; } + void registerNodeMapping(const NodeMapping& nodeMapping); + + protected: Robot(); + + void validateNodeMapping(const NodeMapping& nodeMapping) const; + + /*! Goes through all RobotNodes and if no visualization is present: * the collision model is checked and in case it owns a visualization @@ -456,6 +478,9 @@ namespace VirtualRobot //float radianToMMfactor = 10; + private: + NodeMapping nodeMapping; + }; /** diff --git a/VirtualRobot/RobotNodeSet.cpp b/VirtualRobot/RobotNodeSet.cpp index 1ef28241d6e9348ed29ef34be2abebaea9dce348..5ec6e8ce65d9d36b3a5806832357268093b42191 100644 --- a/VirtualRobot/RobotNodeSet.cpp +++ b/VirtualRobot/RobotNodeSet.cpp @@ -3,6 +3,7 @@ #include "SceneObjectSet.h" #include "Robot.h" #include "RobotConfig.h" +#include "VirtualRobot.h" #include "VirtualRobotException.h" #include "CollisionDetection/CollisionChecker.h" #include <set> @@ -551,9 +552,15 @@ namespace VirtualRobot { THROW_VR_EXCEPTION_IF((i >= (int)robotNodes.size() || i < 0), "Index out of bounds:" << i << ", (should be between 0 and " << (robotNodes.size() - 1)); - return robotNodes[i]; + return robotNodes.at(i); + } + + RobotNodePtr& RobotNodeSet::getNode(const std::string& nodeName) + { + return getNode(getRobotNodeIndex(nodeName)); } + RobotNodePtr& RobotNodeSet::operator[](int i) { THROW_VR_EXCEPTION_IF((i >= (int)robotNodes.size() || i < 0), "Index out of bounds:" << i << ", (should be between 0 and " << (robotNodes.size() - 1)); @@ -803,6 +810,41 @@ namespace VirtualRobot return false; } + bool RobotNodeSet::mirror(const RobotNodeSet& targetNodeSet) + { + const NodeMapping nodeMapping = getRobot()->getNodeMapping(); + const auto nodeNames = getNodeNames(); + + for(const auto& targetNode : targetNodeSet.getAllRobotNodes()) + { + // if node exists in both node set, just copy the joint value + if(hasRobotNode(targetNode->getName())) + { + targetNode->setJointValue(getNode(targetNode->getName())->getJointValue()); + continue; + } + + // otherwise, check if mirroring is possible + const auto nodeIt = nodeMapping.find(targetNode->getName()); + if(nodeIt == nodeMapping.end()) + { + return false; + } + + const NodeMappingElement& mapping = nodeIt->second; + if(not hasRobotNode(mapping.node)) + { + return false; + } + + // mirror it + const auto& sourceNode = getNode(mapping.node); + targetNode->setJointValue(mapping.sign * sourceNode->getJointValue()); + } + + return true; + } + bool RobotNodeSet::isKinematicRoot(RobotNodePtr robotNode) { RobotNodePtr node; diff --git a/VirtualRobot/RobotNodeSet.h b/VirtualRobot/RobotNodeSet.h index f80eaeb4964d18d3116290b6ddd7d06e949b6bd6..950a8548d2eec2d11601822133c013c86bb4f743 100644 --- a/VirtualRobot/RobotNodeSet.h +++ b/VirtualRobot/RobotNodeSet.h @@ -141,6 +141,7 @@ namespace VirtualRobot RobotNodePtr& operator[](int i); RobotNodePtr& getNode(int i); + RobotNodePtr& getNode(const std::string& nodeName); // implement container interface for easy access inline auto begin() @@ -222,6 +223,8 @@ namespace VirtualRobot //! this is forbidden for RobotNodeSets, a call will throw an exception bool removeSceneObject(SceneObjectPtr sceneObject) override; + [[nodiscard]] bool mirror(const RobotNodeSet& targetNodeSet); + protected: /*! * Tests if the given robot node is a valid kinematic root diff --git a/VirtualRobot/XML/BaseIO.cpp b/VirtualRobot/XML/BaseIO.cpp index 07f09e22fe3b2e1717c800b9fa0f1788b31f027f..aa8c33c3cb303d17eb1807a276947d22716486de 100644 --- a/VirtualRobot/XML/BaseIO.cpp +++ b/VirtualRobot/XML/BaseIO.cpp @@ -627,6 +627,43 @@ namespace VirtualRobot } } + + NodeMapping BaseIO::processNodeMapping(rapidxml::xml_node<char>* XMLNode, RobotPtr robot) + { + std::string parentName = processNameAttribute(XMLNode, true); + rapidxml::xml_node<>* node = XMLNode->first_node(); + + NodeMapping nodeMapping; + while (node != nullptr) + { + std::string nodeName = getLowerCase(node->name()); + + if (nodeName == "mapping") + { + const std::string from = processStringAttribute("from", node, true); + const std::string to = processStringAttribute("to", node, true); + const float sign = processFloatAttribute("sign", node, true); + + THROW_VR_EXCEPTION_IF(not(sign == 1 or sign == -1), "'sign' attribute has to be either '1' or '-1'"); + THROW_VR_EXCEPTION_IF(from.empty(), "'from' attribute is empty!"); + THROW_VR_EXCEPTION_IF(to.empty(), "'to' attribute is empty!"); + + // allow bidirectional lookup + nodeMapping.emplace(from, NodeMappingElement{.node = from, .sign = sign}); + nodeMapping.emplace(to, NodeMappingElement{.node = to, .sign = sign}); + } + else + { + THROW_VR_EXCEPTION("XML definition <" << nodeName << "> not supported in <NodeMapping> with name " << parentName); + } + + node = node->next_sibling(); + } + + return nodeMapping; + } + + /** * This method takes a rapidxml::xml_node and returns the value of the * first tag it finds with name \p attributeName. @@ -1680,6 +1717,7 @@ namespace VirtualRobot return rns; } + bool BaseIO::processFloatValueTags(rapidxml::xml_node<char>* XMLNode, int dim, Eigen::VectorXf& stroreResult) { if (!XMLNode || dim <= 0) diff --git a/VirtualRobot/XML/BaseIO.h b/VirtualRobot/XML/BaseIO.h index 601eed2239da8a7ece7de1a53d5523e1b17ddad6..be962a3d0899ac08f3559dca5bf91fa5ea686694 100644 --- a/VirtualRobot/XML/BaseIO.h +++ b/VirtualRobot/XML/BaseIO.h @@ -43,6 +43,7 @@ namespace rapidxml namespace VirtualRobot { + /*! Several basic XML IO methods. \see RobotIO, SceneIO, ObjectIO @@ -97,6 +98,8 @@ namespace VirtualRobot static void getAllAttributes(rapidxml::xml_node<char>* node, const std::string& attrString, std::vector<std::string>& storeValues); static void processDHNode(rapidxml::xml_node<char>* dhXMLNode, DHParameter& dh); + static NodeMapping processNodeMapping(rapidxml::xml_node<char>* XMLNode, RobotPtr robot); + static std::string toXML(const Eigen::Matrix4f& m, std::string ident = "\t"); static std::vector<VisualizationNodePtr> processVisuFiles(rapidxml::xml_node<char>* visualizationXMLNode, const std::string& basePath, std::string& fileType); diff --git a/VirtualRobot/XML/RobotIO.cpp b/VirtualRobot/XML/RobotIO.cpp index 19ba0f9617485ab554890b9b190675f03850858d..d7a2d7dcede9e49c4c48e53bb01f716df0560352 100644 --- a/VirtualRobot/XML/RobotIO.cpp +++ b/VirtualRobot/XML/RobotIO.cpp @@ -16,6 +16,7 @@ #include "../Visualization/TriMeshModel.h" #include "../RobotConfig.h" #include "../RuntimeEnvironment.h" +#include "VirtualRobot.h" #include "rapidxml.hpp" #include "mujoco/RobotMjcf.h" #include <VirtualRobot/Import/URDF/SimoxURDFFactory.h> @@ -820,7 +821,9 @@ namespace VirtualRobot std::vector<rapidxml::xml_node<char>* > robotNodeSetNodes; std::vector<rapidxml::xml_node<char>* > endeffectorNodes; - processRobotChildNodes(robotXMLNode, robo, robotRoot, basePath, childrenFromRobotFilesMap, robotNodeSetNodes, endeffectorNodes, loadMode); + NodeMapping nodeMapping; + + processRobotChildNodes(robotXMLNode, robo, robotRoot, basePath, childrenFromRobotFilesMap, robotNodeSetNodes, endeffectorNodes, nodeMapping, loadMode); // process childfromrobot tags std::map< RobotNodePtr, std::vector< ChildFromRobotDef > >::iterator iter = childrenFromRobotFilesMap.begin(); @@ -906,6 +909,8 @@ namespace VirtualRobot } } + robo->registerNodeMapping(nodeMapping); + return robo; } @@ -1023,6 +1028,7 @@ namespace VirtualRobot std::vector<ChildFromRobotDef> >& childrenFromRobotFilesMap, std::vector<rapidxml::xml_node<char>* >& robotNodeSetNodes, std::vector<rapidxml::xml_node<char>* >& endeffectorNodes, + NodeMapping& nodeMapping, RobotDescription loadMode) { std::vector<RobotNodePtr> robotNodes; @@ -1105,6 +1111,11 @@ namespace VirtualRobot { //skip } + else if ("nodemapping" == nodeName) + { + VR_INFO << "Found robot node mapping"; + nodeMapping = processNodeMapping(XMLNode, robo); + } else { THROW_VR_EXCEPTION("XML node of type <" << nodeName_ << "> is not supported. Ignoring contents..." << endl); @@ -1120,6 +1131,7 @@ namespace VirtualRobot { THROW_VR_EXCEPTION("Error while initializing Robot" << endl); } + } diff --git a/VirtualRobot/XML/RobotIO.h b/VirtualRobot/XML/RobotIO.h index f4d2cd0b84c8e01c756e90127f7aebecdd29bda2..b1e9d11a1be94af79f3a62b2c6019111d296176c 100644 --- a/VirtualRobot/XML/RobotIO.h +++ b/VirtualRobot/XML/RobotIO.h @@ -116,6 +116,7 @@ namespace VirtualRobot std::vector<ChildFromRobotDef> > & childrenFromRobotFilesMap, std::vector<rapidxml::xml_node<char>* >& robotNodeSetNodes, std::vector<rapidxml::xml_node<char>* >& endeffectorNodes, + NodeMapping& nodeMapping, RobotDescription loadMode = eFull); static RobotNodePtr processRobotNode(rapidxml::xml_node<char>* robotNodeXMLNode, RobotPtr robo,