diff --git a/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp b/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp index 03be2d119bcc476d97ffdafd6fe1ae148dcf40bd..4f5c7e35ad1876414344f595e114ef1533bc08b2 100644 --- a/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp +++ b/VirtualRobot/XML/mjcf/MasslessBodySanitizer.cpp @@ -19,7 +19,7 @@ void MasslessBodySanitizer::sanitize() { // merge body leaf nodes with parent if they do not have a mass (inertial or geom) - XMLElement* root = document->worldbody()->FirstChildElement("body"); + XMLElement* root = document->robotRootBody(); for (XMLElement* body = root->FirstChildElement("body"); body; @@ -58,7 +58,9 @@ void MasslessBodySanitizer::sanitizeRecursion(XMLElement* body) } else { - std::cout << "[WARN] Massless body with >1 child bodies." << std::endl; + std::cout << "Adding dummy inertial to massless body with >1 child bodies." << std::endl; + // add a small mass + document->addDummyInertial(body, true); break; } } @@ -198,7 +200,7 @@ void MasslessBodySanitizer::sanitizeLeafBody(XMLElement* body) else { // add a small mass - std::cout << "Adding dummy inertial." << std::endl; + std::cout << "Adding dummy inertial to massless leaf body with children." << std::endl; document->addDummyInertial(body); } } diff --git a/VirtualRobot/XML/mjcf/MjcfDocument.cpp b/VirtualRobot/XML/mjcf/MjcfDocument.cpp index 8c62d514659616eded949921f982fbd9ba9cf247..0224841e54cda8a1c3c464a1d0d36c0dc1027a54 100644 --- a/VirtualRobot/XML/mjcf/MjcfDocument.cpp +++ b/VirtualRobot/XML/mjcf/MjcfDocument.cpp @@ -40,6 +40,26 @@ XMLElement* Document::addSkyboxTexture(const Eigen::Vector3f& rgb1, const Eigen: return texSkybox; } +XMLElement* Document::addMocapBody(const std::string& name, float geomSize) +{ + /* + <body name="hand-ctrl" mocap="true" > + <geom type="box" size="0.01 0.01 0.01" contype="0" conaffinity="0" rgba="0.9 0.5 0.5 0.5" /> + </body> + */ + XMLElement* mocap = addNewElement(worldbody(), "body"); + mocap->SetAttribute("name", name.c_str()); + mocap->SetAttribute("mocap", "true"); + + // add geom for visualization + + XMLElement* geom = addNewElement(mocap, "geom"); + geom->SetAttribute("type", "box"); + setAttr(geom, "size", Eigen::Vector3f{geomSize, geomSize, geomSize}); + + return mocap; +} + XMLElement* Document::addDefaultsClass(const std::string& className) @@ -130,11 +150,11 @@ XMLElement* Document::addInertialElement(XMLElement* body, RobotNodePtr node) return inertial; } -XMLElement* Document::addDummyInertial(XMLElement* body) +XMLElement*Document::addDummyInertial(XMLElement* body, bool first) { assertElementIsBody(body); - XMLElement* inertial = addNewElement(body, "inertial"); + XMLElement* inertial = addNewElement(body, "inertial", "", first); inertial->SetAttribute("pos", toAttr(Eigen::Vector3f(0, 0, 0)).c_str()); inertial->SetAttribute("mass", dummyMass); @@ -180,6 +200,12 @@ XMLElement* Document::addJointElement(XMLElement* body, RobotNodePtr node) return joint; } +XMLElement* Document::addFreeJointElement(XMLElement* body) +{ + assertElementIsBody(body); + return addNewElement(body, "freejoint"); +} + XMLElement* Document::addMeshElement(const std::string& name, const std::string& file, const std::string& className) { XMLElement* mesh = addNewElement(asset(), "mesh", className); @@ -214,6 +240,17 @@ XMLElement*Document::addActuatorVelocityElement(const std::string& jointName, fl return addActuatorShortcut("velocity", jointName, jointName, "kv", kv); } +XMLElement* Document::addEqualityWeld(const std::string& name, const std::string& body1, const std::string& body2, const std::string& className) +{ + XMLElement* weld = addNewElement(equality(), "weld", className); + + weld->SetAttribute("name", name.c_str()); + weld->SetAttribute("body1", body1.c_str()); + weld->SetAttribute("body2", body2.c_str()); + + return weld; +} + XMLElement* Document::addActuatorShortcut( const std::string& type, const std::string& name, const std::string& jointName, diff --git a/VirtualRobot/XML/mjcf/MjcfDocument.h b/VirtualRobot/XML/mjcf/MjcfDocument.h index 4b9239d6c81a48c0a43153d6369f0fbe82585dd8..6f89d5c7c98c7737c2b6b64d27d0dc015612e86f 100644 --- a/VirtualRobot/XML/mjcf/MjcfDocument.h +++ b/VirtualRobot/XML/mjcf/MjcfDocument.h @@ -82,6 +82,8 @@ namespace mjcf /// Add a skybox texture asset (only one allowed). XMLElement* addSkyboxTexture(const Eigen::Vector3f& rgb1, const Eigen::Vector3f& rgb2); + /// Add a mocap body with the given name to the worldbody. + XMLElement* addMocapBody(const std::string& name, float geomSize); /// Add a body element to a parent from a RobotNode. /// Adds inertial and joint element, if necessary. @@ -90,9 +92,11 @@ namespace mjcf /// Add an inertial element to a body from a RobotNode. XMLElement* addInertialElement(XMLElement* body, RobotNodePtr node); /// Add a dummy inertial element with small mass and identity inertia matrix. - XMLElement* addDummyInertial(XMLElement* body); + XMLElement* addDummyInertial(XMLElement* body, bool first=false); /// Add a joint element to a body from a RobotNode. XMLElement* addJointElement(XMLElement* body, RobotNodePtr node); + /// Add a free joint element to a body. + XMLElement* addFreeJointElement(XMLElement* body); /// Add a geom to a body, referencing a mesh. XMLElement* addGeomElement(XMLElement* body, const std::string& meshName); @@ -111,7 +115,8 @@ namespace mjcf const std::string& jointName, bool ctrlLimited, const Eigen::Vector2f& ctrlRange, float kp = 0); XMLElement* addActuatorVelocityElement(const std::string& jointName, float kv = 0); - + XMLElement* addEqualityWeld(const std::string& name, const std::string& body1, const std::string& body2, + const std::string& className=""); /// Set the pos and quat attribute of an element. void setElementPose(XMLElement* elem, const Eigen::Matrix4f& pose); diff --git a/VirtualRobot/XML/mjcf/MujocoIO.cpp b/VirtualRobot/XML/mjcf/MujocoIO.cpp index 38c7027554e69339cdef30101ec502fe9364fd3d..43d3fbde20b5032920dc55ca74623f1bd8528d01 100644 --- a/VirtualRobot/XML/mjcf/MujocoIO.cpp +++ b/VirtualRobot/XML/mjcf/MujocoIO.cpp @@ -36,6 +36,12 @@ void MujocoIO::saveMJCF(const std::string& filename, const std::string& basePath addSkybox(); + if (withMocapBody) + { + std::cout << "Adding mocap body ..." << std::endl; + addMocapBody(); + } + std::cout << "Creating bodies structure ..." << std::endl; makeNodeBodies(); @@ -135,6 +141,38 @@ void MujocoIO::addSkybox() } +void MujocoIO::addMocapBody() +{ + std::string className = "mocap"; + float geomSize = 0.01f; + + std::string bodyName; + { + std::stringstream ss; + ss << robot->getName() << "_Mocap"; + bodyName = ss.str(); + } + + // add defaults class + XMLElement* defClass = document->addDefaultsClass(className); + + document->addDefaultAttr(defClass, "geom", "rgba", + toAttr(Eigen::Vector4f(.9f, .5f, .5f, .5f)).c_str()); + + document->addDefaultAttr(defClass, "equality", "solimp", + toAttr(Eigen::Vector3f{.95f, .99f, .001f}).c_str()); + document->addDefaultAttr(defClass, "equality", "solref", + toAttr(Eigen::Vector2f{ .02f, 1.f}).c_str()); + + // add body + XMLElement* mocap = document->addMocapBody(bodyName, geomSize); + mocap->SetAttribute("childclass", className.c_str()); + + // add equality weld constraint + document->addEqualityWeld(bodyName, robot->getName(), bodyName, className); +} + + void MujocoIO::makeNodeBodies() { nodeBodies.clear(); @@ -145,6 +183,12 @@ void MujocoIO::makeNodeBodies() // add root XMLElement* robotRootBody = document->addRobotRootBodyElement(robot->getName()); + if (withMocapBody) + { + document->addDummyInertial(robotRootBody); + document->addFreeJointElement(robotRootBody); + } + XMLElement* root = document->addBodyElement(robotRootBody, rootNode); nodeBodies[rootNode->getName()] = root; assert(root); @@ -449,12 +493,18 @@ std::vector<const mjcf::XMLElement*> MujocoIO::getAllElements(const std::string& return visitor.foundElements; } +void MujocoIO::setLengthScaling(float value) +{ + lengthScaling = value; +} + void MujocoIO::setActuatorType(ActuatorType value) { actuatorType = value; } -void MujocoIO::setLengthScaling(float value) +void MujocoIO::setWithMocapBody(bool enabled) { - lengthScaling = value; + withMocapBody = enabled; } + diff --git a/VirtualRobot/XML/mjcf/MujocoIO.h b/VirtualRobot/XML/mjcf/MujocoIO.h index db3d6d2ab44d3f946506084400b3f496aacd2db3..7ab57bb0f49ab5ea2264b4d8e2b3f7efba786fb5 100644 --- a/VirtualRobot/XML/mjcf/MujocoIO.h +++ b/VirtualRobot/XML/mjcf/MujocoIO.h @@ -38,6 +38,16 @@ namespace mjcf /// Set the actuator type. void setActuatorType(ActuatorType value); + /** + * @brief Enable or disable adding of a mocap body controlling the robot pose. + * + * @param enabled If true: + * - Add a free joint to the robot root body mocap body + * - Add a mocap body in the world body (called <RobotName>_Mocap) + * - Add a weld constraint welding them together. + */ + void setWithMocapBody(bool enabled); + private: @@ -54,6 +64,9 @@ namespace mjcf /// Add a skybox texture asset. void addSkybox(); + /// Add a mocap body with defaults group. + void addMocapBody(); + /// Construct the body structure corresponding to the robot nodes. void makeNodeBodies(); /// Add a body element for a robot node. If its parent does not exist, @@ -82,6 +95,9 @@ namespace mjcf /// The actuator type. ActuatorType actuatorType = ActuatorType::MOTOR; + /// Add a mocap + bool withMocapBody = false; + // Paths @@ -90,6 +106,7 @@ namespace mjcf boost::filesystem::path outputMeshRelDirectory; boost::filesystem::path outputMeshDirectory() { return outputDirectory / outputMeshRelDirectory; } + // Input /// The input robot. @@ -100,7 +117,7 @@ namespace mjcf /// The built MJCF document. DocumentPtr document = nullptr; - + // Processing diff --git a/VirtualRobot/examples/Simox2Mjcf/main.cpp b/VirtualRobot/examples/Simox2Mjcf/main.cpp index a86cd3ca2c60abd9439558709ff9acc163cd8e2a..e3fa1d1e819829a4890ff95a56e7ab1c489dddb5 100644 --- a/VirtualRobot/examples/Simox2Mjcf/main.cpp +++ b/VirtualRobot/examples/Simox2Mjcf/main.cpp @@ -19,6 +19,7 @@ void printUsage(const char* argv0) << "[--outdir <output directory>] " << "[--meshRelDir <relative mesh directory>] " << "[--actuator {motor|position|velocity}] " + << "[--mocap {y|n}]" << std::endl; } @@ -33,6 +34,8 @@ int main(int argc, char* argv[]) RuntimeEnvironment::considerKey("outdir"); RuntimeEnvironment::considerKey("meshRelDir"); RuntimeEnvironment::considerKey("actuator"); + RuntimeEnvironment::considerKey("mocap"); + RuntimeEnvironment::processCommandLine(argc, argv); //RuntimeEnvironment::print(); @@ -77,12 +80,14 @@ int main(int argc, char* argv[]) return -1; } + bool mocap = RuntimeEnvironment::checkParameter("mocap", "n") == "y"; std::cout << "Input file: " << inputFilename << std::endl; std::cout << "Output dir: " << outputDir << std::endl; std::cout << "Output mesh dir: " << outputDir / meshRelDir << std::endl; //std::cout << "Actuator type: " << actuatorType << std::endl; + std::cout << "With mocap body: " << (mocap ? "yes" : "no "); std::cout << "Loading robot ..." << std::endl; @@ -107,6 +112,7 @@ int main(int argc, char* argv[]) // direct API mjcf::MujocoIO mujocoIO(robot); mujocoIO.setActuatorType(actuatorType); + mujocoIO.setWithMocapBody(mocap); mujocoIO.saveMJCF(inputFilename.filename().string(), outputDir.string(), meshRelDir); } }