1#ifndef TR_URDFPARSER_HPP
2#define TR_URDFPARSER_HPP
12namespace tinyrobotics {
19 template <
typename Scalar>
21 Eigen::Matrix<Scalar, 3, 1> vec;
22 std::vector<Scalar> values;
23 std::istringstream ss(vector_str);
24 std::string vector_element;
25 while (ss >> vector_element) {
27 values.push_back(std::stod(vector_element));
29 catch (std::invalid_argument& e) {
30 throw std::runtime_error(
"Error not able to parse component (" + vector_element
31 +
") to a Scalar (while parsing a vector value)");
35 if (values.size() != 3) {
36 std::ostringstream error_msg;
37 error_msg <<
"Parser found " << values.size() <<
" elements but 3 expected while parsing vector ["
39 throw std::runtime_error(error_msg.str());
54 template <
typename Scalar>
56 Eigen::Matrix<Scalar, Eigen::Dynamic, 1> rpy = vec_from_string<Scalar>(rotation_str);
57 Eigen::Matrix<Scalar, 3, 3> R =
58 Eigen::AngleAxis<Scalar>(rpy.z(), Eigen::Matrix<Scalar, 3, 1>::UnitZ())
59 * Eigen::AngleAxis<Scalar>(rpy.y(), Eigen::Matrix<Scalar, 3, 1>::UnitY())
60 * Eigen::AngleAxis<Scalar>(rpy.x(), Eigen::Matrix<Scalar, 3, 1>::UnitX()).toRotationMatrix();
69 template <
typename Scalar>
71 Eigen::Transform<Scalar, 3, Eigen::Isometry> t;
74 const char* rpy_str = xml->Attribute(
"rpy");
75 if (rpy_str !=
nullptr) {
76 t.linear() = rot_from_string<Scalar>(rpy_str);
79 const char* xyz_str = xml->Attribute(
"xyz");
80 if (xyz_str !=
nullptr) {
81 t.translation() = vec_from_string<Scalar>(xyz_str);
92 template <
typename Scalar>
94 Eigen::Matrix<Scalar, 6, 6> X = Eigen::Matrix<Scalar, 6, 6>::Identity();
95 Eigen::Matrix<Scalar, 6, 6> R = Eigen::Matrix<Scalar, 6, 6>::Identity();
96 Eigen::Matrix<Scalar, 6, 6> T = Eigen::Matrix<Scalar, 6, 6>::Identity();
98 const char* rpy_str = xml->Attribute(
"rpy");
99 if (rpy_str !=
nullptr) {
100 Eigen::Matrix<Scalar, 3, 1> rpy = vec_from_string<Scalar>(rpy_str);
104 const char* xyz_str = xml->Attribute(
"xyz");
105 if (xyz_str !=
nullptr) {
106 Eigen::Matrix<Scalar, 3, 1> translation = vec_from_string<Scalar>(xyz_str);
120 tinyxml2::XMLElement* e = c->Parent()->ToElement();
121 while (e->Parent() !=
nullptr) {
122 if (std::strcmp(e->Value(),
"link") == 0) {
125 e = e->Parent()->ToElement();
127 return e->Attribute(
"name");
135 template <
typename Scalar>
140 const char* name_char = xml->Attribute(
"name");
141 if (name_char !=
nullptr) {
142 link.
name = std::string(name_char);
145 throw std::runtime_error(
"Error! Link without a name attribute detected!");
148 tinyxml2::XMLElement* i = xml->FirstChildElement(
"inertial");
151 tinyxml2::XMLElement* o = i->FirstChildElement(
"origin");
157 tinyxml2::XMLElement* mass_xml = i->FirstChildElement(
"mass");
158 if (mass_xml !=
nullptr) {
159 if (mass_xml->Attribute(
"value") !=
nullptr) {
161 link.
mass = std::stod(mass_xml->Attribute(
"value"));
163 catch (std::invalid_argument& e) {
165 +
"': inertial mass [" + mass_xml->Attribute(
"value")
166 +
"] is not a valid double: " + e.what() +
"!");
171 +
"' <mass> element must have a value attribute!");
176 +
"' inertial element must have a <mass> element!");
181 tinyxml2::XMLElement* inertia_xml = i->FirstChildElement(
"inertia");
182 if (inertia_xml !=
nullptr) {
183 Eigen::Matrix<Scalar, 3, 3> inertia;
184 if (inertia_xml->Attribute(
"ixx") && inertia_xml->Attribute(
"ixy") && inertia_xml->Attribute(
"ixz")
185 && inertia_xml->Attribute(
"iyy") && inertia_xml->Attribute(
"iyz") && inertia_xml->Attribute(
"izz")) {
187 inertia(0, 0) = std::stod(inertia_xml->Attribute(
"ixx"));
188 inertia(0, 1) = std::stod(inertia_xml->Attribute(
"ixy"));
189 inertia(0, 2) = std::stod(inertia_xml->Attribute(
"ixz"));
190 inertia(1, 0) = std::stod(inertia_xml->Attribute(
"ixy"));
191 inertia(1, 1) = std::stod(inertia_xml->Attribute(
"iyy"));
192 inertia(1, 2) = std::stod(inertia_xml->Attribute(
"iyz"));
193 inertia(2, 0) = std::stod(inertia_xml->Attribute(
"ixz"));
194 inertia(2, 1) = std::stod(inertia_xml->Attribute(
"iyz"));
195 inertia(2, 2) = std::stod(inertia_xml->Attribute(
"izz"));
198 catch (std::invalid_argument& e) {
199 std::ostringstream error_msg;
201 <<
"Inertial: one of the inertia elements is not a valid double:"
202 <<
" ixx [" << inertia_xml->Attribute(
"ixx") <<
"]"
203 <<
" ixy [" << inertia_xml->Attribute(
"ixy") <<
"]"
204 <<
" ixz [" << inertia_xml->Attribute(
"ixz") <<
"]"
205 <<
" iyy [" << inertia_xml->Attribute(
"iyy") <<
"]"
206 <<
" iyz [" << inertia_xml->Attribute(
"iyz") <<
"]"
207 <<
" izz [" << inertia_xml->Attribute(
"izz") <<
"]\n\n"
209 throw std::runtime_error(error_msg.str());
214 +
"' <inertia> element must have ixx,ixy,ixz,iyy,iyz,izz attributes!");
219 +
"' inertial element must have a <inertia> element!");
233 template <
typename Scalar>
237 const char* name = xml->Attribute(
"name");
238 if (name !=
nullptr) {
239 joint.
name = std::string(name);
242 std::ostringstream error_msg;
243 error_msg <<
"Error while parsing model: unnamed joint found!";
244 throw std::runtime_error(error_msg.str());
247 tinyxml2::XMLElement* origin_xml = xml->FirstChildElement(
"origin");
248 if (origin_xml !=
nullptr) {
252 tinyxml2::XMLElement* parent_xml = xml->FirstChildElement(
"parent");
253 if (parent_xml !=
nullptr) {
254 const char* pname = parent_xml->Attribute(
"link");
255 if (pname !=
nullptr) {
261 tinyxml2::XMLElement* child_xml = xml->FirstChildElement(
"child");
263 const char* pname = child_xml->Attribute(
"link");
264 if (pname !=
nullptr) {
269 const char* type_char = xml->Attribute(
"type");
270 if (type_char ==
nullptr) {
271 std::ostringstream error_msg;
272 error_msg <<
"Error! Joint " << joint.
name <<
" has no type, check to see if it's a reference.";
273 throw std::runtime_error(error_msg.str());
276 std::string type_str = type_char;
277 if (type_str ==
"planar") {
278 joint.
type = JointType::PLANAR;
280 else if (type_str ==
"floating") {
281 joint.
type = JointType::FLOATING;
283 else if (type_str ==
"revolute") {
284 joint.
type = JointType::REVOLUTE;
286 else if (type_str ==
"continuous") {
287 joint.
type = JointType::CONTINUOUS;
289 else if (type_str ==
"prismatic") {
290 joint.
type = JointType::PRISMATIC;
292 else if (type_str ==
"fixed") {
293 joint.
type = JointType::FIXED;
296 std::ostringstream error_msg;
297 error_msg <<
"Error! Joint '" << joint.
name <<
"' has unknown type (" << type_str <<
")!";
298 throw std::runtime_error(error_msg.str());
301 if (joint.
type != JointType::FLOATING && joint.
type != JointType::FIXED) {
302 tinyxml2::XMLElement* axis_xml = xml->FirstChildElement(
"axis");
303 if (axis_xml ==
nullptr) {
304 joint.
axis << 1, 0, 0;
305 joint.
S << 1, 0, 0, 0, 0, 0;
308 const char* xyz_char = axis_xml->Attribute(
"xyz");
309 if (xyz_char !=
nullptr) {
310 joint.
axis = vec_from_string<Scalar>(std::string(xyz_char));
314 if (joint.
type == JointType::REVOLUTE) {
315 joint.
S << joint.
axis, 0, 0, 0;
317 else if (joint.
type == JointType::PRISMATIC) {
318 joint.
S << 0, 0, 0, joint.
axis;
356 template <
typename Scalar,
int nq>
362 for (
auto joint : joints) {
364 std::string parent_link_name = joint.parent_link_name;
365 if (parent_link_name.empty()) {
366 throw std::runtime_error(
"Error while constructing model! Joint [" + joint.name
367 +
"] is missing a parent link specification.");
369 std::string child_link_name = joint.child_link_name;
370 if (child_link_name.empty()) {
371 throw std::runtime_error(
"Error while constructing model! Joint [" + joint.name
372 +
"] is missing a child link specification.");
376 auto child_link = model.
get_link(child_link_name);
377 if (child_link.idx == -1) {
378 throw std::runtime_error(
"Error while constructing model! Child link [" + child_link_name
379 +
"] of joint [" + joint.name +
"] not found");
382 auto parent_link = model.
get_link(parent_link_name);
383 if (parent_link.idx == -1) {
384 throw std::runtime_error(
"Error while constructing model! Parent link [" + parent_link_name
385 +
"] of joint [" + joint.name +
"] not found");
388 child_link.parent = parent_link.idx;
389 parent_link.add_child_link_idx(child_link.idx);
392 if (joint.type == JointType::REVOLUTE || joint.type == JointType::PRISMATIC) {
393 joint.idx = model.
n_q;
398 child_link.joint = joint;
399 model.
links[child_link.idx] = child_link;
402 model.
links[parent_link.idx] = parent_link;
406 for (
auto link : model.
links) {
408 if (link.parent == -1) {
410 throw std::runtime_error(
411 "Error! Multiple base links found. The urdf does not contain a valid link tree.");
426 template <
typename Scalar,
int nq>
428 for (
auto& link : model.
links) {
430 if (link.joint.type == JointType::FIXED && link.idx != model.
base_link_idx) {
432 for (
auto child_link_idx : link.child_links) {
433 auto child_link = model.
links[child_link_idx];
434 for (
int j = 0; j < model.
links.size(); j++) {
435 if (model.
links[j].name == child_link.name) {
436 model.
links[j].joint.X = model.
links[j].joint.X * link.joint.X;
442 auto parent_link = model.
links[link.parent];
444 for (
int j = 0; j < model.
links.size(); j++) {
445 if (model.
links[j].name == parent_link.name) {
446 Eigen::Matrix<Scalar, 6, 6> X_T = model.
links[j].joint.X;
447 model.
links[j].I += X_T.transpose() * link.I * X_T;
454 model.
q_map.push_back(link.idx);
458 for (
int i = 0; i < model.
q_map.size(); i++) {
462 auto parent_link = model.
links[link.parent];
463 while (parent_link.joint.type == JointType::FIXED) {
464 parent_link = model.
links[parent_link.parent];
469 model.
parent.push_back(-1);
474 for (
int j = 0; j < model.
q_map.size(); j++) {
475 if (model.
q_map[j] == parent_link.idx) {
476 model.
parent.push_back(j);
482 model.
parent.push_back(-1);
492 for (
auto link : model.
links) {
493 model.
mass += link.mass;
502 template <
typename Scalar,
int nq>
505 std::ifstream input_file(path_to_urdf);
506 if (!input_file.is_open()) {
507 throw std::runtime_error(
"Could not open the file - '" + path_to_urdf +
"'");
511 std::string urdf_string =
512 std::string(std::istreambuf_iterator<char>(input_file), std::istreambuf_iterator<char>());
515 tinyxml2::XMLDocument xml_doc;
516 xml_doc.Parse(urdf_string.c_str());
517 if (xml_doc.Error()) {
518 throw std::runtime_error(
"Error parsing XML: " + std::string(xml_doc.ErrorStr()));
522 tinyxml2::XMLElement* robot_xml = xml_doc.RootElement();
523 if (!robot_xml || std::string(robot_xml->Value()) !=
"robot") {
524 throw std::runtime_error(
"Error: Could not find the <robot> element in the URDF file");
531 const char* name = robot_xml->Attribute(
"name");
533 throw std::runtime_error(
"Error: No name given for the robot");
538 for (tinyxml2::XMLElement* link_xml = robot_xml->FirstChildElement(
"link"); link_xml;
539 link_xml = link_xml->NextSiblingElement(
"link")) {
542 throw std::runtime_error(
"Error: Duplicate links '" + link.
name +
"' found");
545 model.
links.push_back(link);
547 if (model.
links.empty()) {
548 throw std::runtime_error(
"Error: No link elements found in the URDF file");
552 std::vector<Joint<Scalar>> joints;
553 for (tinyxml2::XMLElement* joint_xml = robot_xml->FirstChildElement(
"joint"); joint_xml;
554 joint_xml = joint_xml->NextSiblingElement(
"joint")) {
556 for (
auto& joint_ : joints) {
557 if (joint.
name == joint_.name) {
558 throw std::runtime_error(
"Error: Duplicate joints '" + joint.
name +
"' found");
561 joints.push_back(joint);
Contains various math related functions.
Eigen::Matrix< Scalar, 6, 6 > translation_to_spatial(const Eigen::Matrix< Scalar, 3, 1 > &xyz)
Spatial coordinate transform from a xyz translation.
Definition: math.hpp:67
Eigen::Matrix< Scalar, 6, 6 > rpy_to_spatial(const Eigen::Matrix< Scalar, 3, 1 > &rpy)
Spatial coordinate transform from roll-pitch-yaw angles.
Definition: math.hpp:80
Contains struct for representing a tinyrobotics model.
Eigen::Matrix< Scalar, 3, 1 > vec_from_string(const std::string &vector_str)
Get Eigen3 vector from a URDF vector element.
Definition: parser.hpp:20
void init_link_tree(Model< Scalar, nq > &model, std::vector< Joint< Scalar > > &joints)
Initialize the link tree in the model.
Definition: parser.hpp:357
Model< Scalar, nq > import_urdf(const std::string &path_to_urdf)
Construct a new Model object from URDF file description.
Definition: parser.hpp:503
void init_dynamics(Model< Scalar, nq > &model)
Updates dynamic links with any fixed joints associated with them, and updates the indices of the dyna...
Definition: parser.hpp:427
Eigen::Transform< Scalar, 3, Eigen::Isometry > transform_from_xml(tinyxml2::XMLElement *xml)
Get the Eigen3 transform from a URDF xml element.
Definition: parser.hpp:70
const char * get_parent_link_name(tinyxml2::XMLElement *c)
Get the name of the link's parent link.
Definition: parser.hpp:119
Eigen::Matrix< Scalar, 3, 3 > rot_from_string(const std::string &rotation_str)
Get Eigen3 rotation from a URDF rpy string.
Definition: parser.hpp:55
Link< Scalar > link_from_xml(tinyxml2::XMLElement *xml)
Construct a Link from a URDF xml element.
Definition: parser.hpp:136
Joint< Scalar > joint_from_xml(tinyxml2::XMLElement *xml)
Construct a new Joint object from a URDF file description.
Definition: parser.hpp:234
Eigen::Matrix< Scalar, 6, 6 > spatial_transform_from_xml(tinyxml2::XMLElement *xml)
Get the spatial transform from a URDF xml element.
Definition: parser.hpp:93
Represents a joint in a tinyrobotics model which connects two links.
Definition: joint.hpp:29
std::string parent_link_name
Name of the joints parent link.
Definition: joint.hpp:55
Eigen::Matrix< Scalar, 3, 1 > axis
Axis of motion for the joint.
Definition: joint.hpp:44
JointType type
Type of the joint.
Definition: joint.hpp:41
std::string child_link_name
Name of the joints child link.
Definition: joint.hpp:58
std::string name
Name of the joint.
Definition: joint.hpp:38
Eigen::Transform< Scalar, 3, Eigen::Isometry > parent_transform
Homogeneous transform to the parent link.
Definition: joint.hpp:47
Eigen::Matrix< Scalar, 6, 1 > S
Spatial axis.
Definition: joint.hpp:64
Represents a link in a tinyrobotics model.
Definition: link.hpp:16
std::string name
Name of the link.
Definition: link.hpp:19
int idx
Index of the link in the model's link vector.
Definition: link.hpp:22
Scalar mass
Mass of the link [kg].
Definition: link.hpp:38
Eigen::Matrix< Scalar, 3, 3 > inertia
Inertia matrix of the link [kg m^2].
Definition: link.hpp:35
Eigen::Transform< Scalar, 3, Eigen::Isometry > center_of_mass
Center of mass of the link.
Definition: link.hpp:31
A tinyrobotics model.
Definition: model.hpp:25
std::vector< int > parent
Vector of parent link indices of the links which have a non-fixed joints.
Definition: model.hpp:42
std::vector< int > q_map
Map to indices of links in the models link vector that have a non-fixed joints.
Definition: model.hpp:39
Scalar mass
Total mass of the model.
Definition: model.hpp:48
int n_q
Number of configuration coordinates (degrees of freedom) in the model.
Definition: model.hpp:33
Link< Scalar > get_link(const std::string &name) const
Get a link in the model by name.
Definition: model.hpp:144
Eigen::Matrix< Scalar, 6, 1 > spatial_gravity
Gravity vector in spatial coordinates.
Definition: model.hpp:128
Eigen::Matrix< Scalar, 3, 1 > gravity
Gravitational acceleration vector experienced by model.
Definition: model.hpp:45
std::string name
**************** Model Information ****************
Definition: model.hpp:30
int base_link_idx
Index of the base link in the models links vector.
Definition: model.hpp:36
std::vector< Link< Scalar > > links
Vector of links in the model.
Definition: model.hpp:51