32 #include <boost/filesystem.hpp>
33 #include <string_view>
53 std::unique_ptr<tesseract_scene_graph::SceneGraph>
parseURDFString(
const std::string& urdf_xml_string,
56 tinyxml2::XMLDocument xml_doc;
57 if (xml_doc.Parse(urdf_xml_string.c_str()) != tinyxml2::XML_SUCCESS)
58 std::throw_with_nested(std::runtime_error(
"URDF: Failed to parse urdf string!"));
62 std::throw_with_nested(std::runtime_error(
"URDF: Missing element 'robot'!"));
64 std::string robot_name;
66 std::throw_with_nested(std::runtime_error(
"URDF: Missing or failed parsing attribute 'name'!"));
69 auto version_status = robot->QueryIntAttribute(
"version", &urdf_version);
70 if (version_status != tinyxml2::XML_NO_ATTRIBUTE && version_status != tinyxml2::XML_SUCCESS)
71 std::throw_with_nested(
72 std::runtime_error(
"URDF: Failed parsing attribute 'version' for robot '" + robot_name +
"'!"));
74 if (urdf_version != 1)
75 std::throw_with_nested(std::runtime_error(
"URDF: 'version' for robot '" + robot_name +
"' is set to `" +
76 std::to_string(urdf_version) +
77 "', this is not supported, please set it to 1.0."));
80 bool make_convex =
false;
81 auto make_convex_status = robot->QueryBoolAttribute(
"tesseract:make_convex", &make_convex);
82 switch (make_convex_status)
84 case tinyxml2::XML_SUCCESS:
86 case tinyxml2::XML_NO_ATTRIBUTE:
88 const std::string message =
"URDF: missing boolean attribute 'tesseract:make_convex'. This attribute indicates "
89 "whether Tesseract should globally convert all collision mesh geometries into convex "
90 "hulls. Previous versions of Tesseract performed this conversion automatically "
91 "(i.e., 'tesseract:make_convex=\"true\"'. If you want to perform collision checking "
92 "with detailed meshes instead of convex hulls, set "
93 "'tesseract:make_convex=\"false\"'. This global attribute can be overriden on a "
94 "per-mesh basis by specifying the 'tesseract:make_convex' attribute in the 'mesh' "
95 "element (e.g., <mesh filename=\"...\" tesseract:make_convex=\"true/false\"> .";
96 std::throw_with_nested(std::runtime_error(message));
99 std::throw_with_nested(std::runtime_error(
"URDF: Failed to parse boolean attribute 'tesseract:make_convex' for "
104 auto sg = std::make_unique<tesseract_scene_graph::SceneGraph>();
105 sg->setName(robot_name);
107 std::unordered_map<std::string, tesseract_scene_graph::Material::Ptr> available_materials;
108 for (tinyxml2::XMLElement* material = robot->FirstChildElement(
MATERIAL_ELEMENT_NAME.data()); material !=
nullptr;
112 std::unordered_map<std::string, tesseract_scene_graph::Material::Ptr> empty_material;
119 std::throw_with_nested(
120 std::runtime_error(
"URDF: Error parsing urdf global 'material' element for robot '" + robot_name +
"'!"));
123 available_materials[m->getName()] = m;
126 for (tinyxml2::XMLElement* link = robot->FirstChildElement(
LINK_ELEMENT_NAME.data()); link !=
nullptr;
132 l =
parseLink(link, locator, make_convex, available_materials);
136 std::throw_with_nested(std::runtime_error(
"URDF: Error parsing 'link' element for robot '" + robot_name +
"'!"));
140 if (sg->getLink(l->getName()) !=
nullptr)
141 std::throw_with_nested(std::runtime_error(
"URDF: Error link name '" + l->getName() +
142 "' is not unique for robot '" + robot_name +
"'!"));
145 if (!sg->addLink(*l))
146 std::throw_with_nested(std::runtime_error(
"URDF: Error adding link '" + l->getName() +
147 "' to scene graph for robot '" + robot_name +
"'!"));
150 if (sg->getLinks().empty())
151 std::throw_with_nested(std::runtime_error(
"URDF: Error no links were found for robot '" + robot_name +
"'!"));
153 for (tinyxml2::XMLElement* joint = robot->FirstChildElement(
JOINT_ELEMENT_NAME.data()); joint !=
nullptr;
163 std::throw_with_nested(std::runtime_error(
"URDF: Error parsing 'joint' element for robot '" + robot_name +
"'!"));
167 if (sg->getJoint(j->getName()) !=
nullptr)
168 std::throw_with_nested(std::runtime_error(
"URDF: Error joint name '" + j->getName() +
169 "' is not unique for robot '" + robot_name +
"'!"));
172 if (!sg->addJoint(*j))
173 std::throw_with_nested(std::runtime_error(
"URDF: Error adding joint '" + j->getName() +
174 "' to scene graph for robot '" + robot_name +
"'!"));
177 if (sg->getJoints().empty())
178 std::throw_with_nested(std::runtime_error(
"URDF: Error no joints were found for robot '" + robot_name +
"'!"));
182 if (!sg->isAcyclic())
183 std::throw_with_nested(std::runtime_error(
"URDF: Error, is not a tree structure and contains cycles for robot '" +
186 std::throw_with_nested(std::runtime_error(
"URDF: Error, is not a tree structure for robot '" + robot_name +
"'!"));
190 for (
const auto& l : sg->getLinks())
191 if (sg->getInboundJoints(l->getName()).empty())
192 sg->setRoot(l->getName());
197 std::unique_ptr<tesseract_scene_graph::SceneGraph>
parseURDFFile(
const std::string& path,
200 std::ifstream ifs(path);
202 std::throw_with_nested(std::runtime_error(
"URDF: Error opening file '" + path +
"'!"));
204 std::string urdf_xml_string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
212 std::throw_with_nested(std::runtime_error(
"URDF: Error parsing file '" + path +
"'!"));
218 void writeURDFFile(
const std::shared_ptr<const tesseract_scene_graph::SceneGraph>& sg,
219 const std::string& package_path,
220 const std::string& urdf_name)
224 std::throw_with_nested(std::runtime_error(
"Scene Graph is nullptr and cannot be converted to URDF"));
227 if (package_path.empty())
228 std::throw_with_nested(std::runtime_error(
"Package path cannot be empty"));
229 std::filesystem::create_directory(std::filesystem::path(package_path));
236 tinyxml2::XMLDocument doc;
239 tinyxml2::XMLDeclaration* xml_declaration = doc.NewDeclaration(R
"(xml version="1.0")");
240 doc.InsertFirstChild(xml_declaration);
244 xml_robot->SetAttribute(
"name", sg->getName().c_str());
245 xml_robot->SetAttribute(
"tesseract:make_convex",
false);
247 doc.InsertEndChild(xml_robot);
252 std::vector<std::string> link_names;
256 std::throw_with_nested(std::runtime_error(
"Link is nullptr, cannot get name"));
257 link_names.push_back(l->getName());
263 link_names.begin(), link_names.end(), [](
const std::string& a,
const std::string& b) ->
bool { return a < b; });
266 for (
const std::string& s : link_names)
271 tinyxml2::XMLElement* xml_link =
writeLink(l, doc, package_path);
272 xml_robot->InsertEndChild(xml_link);
276 std::throw_with_nested(std::runtime_error(
"Could not write out urdf link"));
281 std::vector<std::string> joint_names;
285 std::throw_with_nested(std::runtime_error(
"Joint is nullptr, cannot get name!"));
286 joint_names.push_back(j->getName());
292 joint_names.begin(), joint_names.end(), [](
const std::string& a,
const std::string& b) ->
bool { return a < b; });
295 for (
const std::string& s : joint_names)
300 tinyxml2::XMLElement* xml_joint =
writeJoint(j, doc);
301 xml_robot->InsertEndChild(xml_joint);
305 std::throw_with_nested(std::runtime_error(
"Could not write out urdf joint"));
312 std::filesystem::create_directory(std::filesystem::path(
trailingSlash(package_path) +
"urdf/"));
315 std::string full_filepath;
316 if (!urdf_name.empty())
319 full_filepath =
trailingSlash(package_path) +
"urdf/" + sg->getName() +
".urdf";
320 doc.SaveFile(full_filepath.c_str());