Skip to content

Commit f009ddc

Browse files
scchowrhaschke
authored andcommitted
Backporting joint properties from MoveIt2 (moveit#109)
This is a backport of moveit#77
1 parent 177742b commit f009ddc

File tree

7 files changed

+163
-36
lines changed

7 files changed

+163
-36
lines changed

include/srdfdom/model.h

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ class Model
9595
/// be added to the group. Each chain is specified as a
9696
/// pair of base link and tip link. It is checked that the
9797
/// chain is indeed a chain in the specified URDF.
98-
std::vector<std::pair<std::string, std::string> > chains_;
98+
std::vector<std::pair<std::string, std::string>> chains_;
9999

100100
/// It is sometimes convenient to refer to the content of
101101
/// another group. A group can include the content of the
@@ -150,7 +150,7 @@ class Model
150150

151151
/// The values of joints for this state. Each joint can have a value. We use a vector for the 'value' to support
152152
/// multi-DOF joints
153-
std::map<std::string, std::vector<double> > joint_values_;
153+
std::map<std::string, std::vector<double>> joint_values_;
154154
};
155155

156156
/// The definition of a sphere
@@ -201,6 +201,19 @@ class Model
201201
return name_;
202202
}
203203

204+
// Some joints may have additional properties.
205+
struct JointProperty
206+
{
207+
/// The name of the joint that this property belongs to
208+
std::string joint_name_;
209+
210+
/// The name of the property
211+
std::string property_name_;
212+
213+
/// The value of the property. Type not specified.
214+
std::string value_;
215+
};
216+
204217
/// Get the list of links that should have collision checking disabled by default (and only selectively enabled)
205218
const std::vector<std::string>& getNoDefaultCollisionLinks() const
206219
{
@@ -255,10 +268,31 @@ class Model
255268
return link_sphere_approximations_;
256269
}
257270

271+
/// Get the joint properties for a particular joint (empty vector if none)
272+
const std::vector<JointProperty>& getJointProperties(const std::string& joint_name) const
273+
{
274+
std::map<std::string, std::vector<JointProperty>>::const_iterator iter = joint_properties_.find(joint_name);
275+
if (iter == joint_properties_.end())
276+
{
277+
// We return a standard empty vector here rather than insert a new empty vector
278+
// into the map in order to keep the method const
279+
return empty_vector_;
280+
}
281+
return iter->second;
282+
}
283+
284+
/// Get the joint properties list
285+
const std::map<std::string, std::vector<JointProperty>>& getJointProperties() const
286+
{
287+
return joint_properties_;
288+
}
289+
258290
/// Clear the model
259291
void clear();
260292

261293
private:
294+
bool isValidJoint(const urdf::ModelInterface& urdf_model, const std::string& name) const;
295+
262296
void loadVirtualJoints(const urdf::ModelInterface& urdf_model, tinyxml2::XMLElement* robot_xml);
263297
void loadGroups(const urdf::ModelInterface& urdf_model, tinyxml2::XMLElement* robot_xml);
264298
void loadGroupStates(const urdf::ModelInterface& urdf_model, tinyxml2::XMLElement* robot_xml);
@@ -268,6 +302,7 @@ class Model
268302
void loadCollisionPairs(const urdf::ModelInterface& urdf_model, tinyxml2::XMLElement* robot_xml, const char* tag_name,
269303
std::vector<CollisionPair>& pairs);
270304
void loadPassiveJoints(const urdf::ModelInterface& urdf_model, tinyxml2::XMLElement* robot_xml);
305+
void loadJointProperties(const urdf::ModelInterface& urdf_model, tinyxml2::XMLElement* robot_xml);
271306

272307
std::string name_;
273308
std::vector<Group> groups_;
@@ -279,6 +314,10 @@ class Model
279314
std::vector<CollisionPair> enabled_collision_pairs_;
280315
std::vector<CollisionPair> disabled_collision_pairs_;
281316
std::vector<PassiveJoint> passive_joints_;
317+
std::map<std::string, std::vector<JointProperty>> joint_properties_;
318+
319+
// Empty joint property vector
320+
static const std::vector<JointProperty> empty_vector_;
282321
};
283322
typedef std::shared_ptr<Model> ModelSharedPtr;
284323
typedef std::shared_ptr<const Model> ModelConstSharedPtr;

include/srdfdom/srdf_writer.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,13 @@ class SRDFWriter
167167
*/
168168
void createPassiveJointsXML(tinyxml2::XMLElement* root);
169169

170+
/**
171+
* Generate XML for SRDF joint properties
172+
*
173+
* @param root - TinyXML root element to attach sub elements to
174+
*/
175+
void createJointPropertiesXML(tinyxml2::XMLElement* root);
176+
170177
protected:
171178
/**
172179
* Generate XML for SRDF disabled/enabled collisions of robot link pairs
@@ -190,6 +197,7 @@ class SRDFWriter
190197
std::vector<Model::CollisionPair> disabled_collision_pairs_;
191198
std::vector<Model::CollisionPair> enabled_collision_pairs_;
192199
std::vector<Model::PassiveJoint> passive_joints_;
200+
std::map<std::string, std::vector<srdf::Model::JointProperty>> joint_properties_;
193201

194202
// Store the SRDF Model for updating the kinematic_model
195203
ModelSharedPtr srdf_model_;

src/model.cpp

Lines changed: 66 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@
4545

4646
using namespace tinyxml2;
4747

48+
const std::vector<srdf::Model::JointProperty> srdf::Model::empty_vector_;
49+
50+
bool srdf::Model::isValidJoint(const urdf::ModelInterface& urdf_model, const std::string& name) const
51+
{
52+
if (urdf_model.getJoint(name))
53+
{
54+
return true;
55+
}
56+
for (const srdf::Model::VirtualJoint& vj : virtual_joints_)
57+
{
58+
if (vj.name_ == name)
59+
{
60+
return true;
61+
}
62+
}
63+
return false;
64+
}
65+
4866
void srdf::Model::loadVirtualJoints(const urdf::ModelInterface& urdf_model, XMLElement* robot_xml)
4967
{
5068
for (XMLElement* vj_xml = robot_xml->FirstChildElement("virtual_joint"); vj_xml;
@@ -145,20 +163,10 @@ void srdf::Model::loadGroups(const urdf::ModelInterface& urdf_model, XMLElement*
145163
continue;
146164
}
147165
std::string jname_str = boost::trim_copy(std::string(jname));
148-
if (!urdf_model.getJoint(jname_str))
166+
if (!isValidJoint(urdf_model, jname_str))
149167
{
150-
bool missing = true;
151-
for (std::size_t k = 0; k < virtual_joints_.size(); ++k)
152-
if (virtual_joints_[k].name_ == jname_str)
153-
{
154-
missing = false;
155-
break;
156-
}
157-
if (missing)
158-
{
159-
CONSOLE_BRIDGE_logError("Joint '%s' declared as part of group '%s' is not known to the URDF", jname, gname);
160-
continue;
161-
}
168+
CONSOLE_BRIDGE_logError("Joint '%s' declared as part of group '%s' is not known to the URDF", jname, gname);
169+
continue;
162170
}
163171
g.joints_.push_back(jname_str);
164172
}
@@ -333,21 +341,11 @@ void srdf::Model::loadGroupStates(const urdf::ModelInterface& urdf_model, XMLEle
333341
continue;
334342
}
335343
std::string jname_str = boost::trim_copy(std::string(jname));
336-
if (!urdf_model.getJoint(jname_str))
344+
if (!isValidJoint(urdf_model, jname_str))
337345
{
338-
bool missing = true;
339-
for (std::size_t k = 0; k < virtual_joints_.size(); ++k)
340-
if (virtual_joints_[k].name_ == jname_str)
341-
{
342-
missing = false;
343-
break;
344-
}
345-
if (missing)
346-
{
347-
CONSOLE_BRIDGE_logError("Joint '%s' declared as part of group state '%s' is not known to the URDF", jname,
348-
sname);
349-
continue;
350-
}
346+
CONSOLE_BRIDGE_logError("Joint '%s' declared as part of group state '%s' is not known to the URDF", jname,
347+
sname);
348+
continue;
351349
}
352350
try
353351
{
@@ -597,13 +595,7 @@ void srdf::Model::loadPassiveJoints(const urdf::ModelInterface& urdf_model, XMLE
597595
PassiveJoint pj;
598596
pj.name_ = boost::trim_copy(std::string(name));
599597

600-
// see if a virtual joint was marked as passive
601-
bool vjoint = false;
602-
for (std::size_t i = 0; !vjoint && i < virtual_joints_.size(); ++i)
603-
if (virtual_joints_[i].name_ == pj.name_)
604-
vjoint = true;
605-
606-
if (!vjoint && !urdf_model.getJoint(pj.name_))
598+
if (!isValidJoint(urdf_model, pj.name_))
607599
{
608600
CONSOLE_BRIDGE_logError("Joint '%s' marked as passive is not known to the URDF. Ignoring.", name);
609601
continue;
@@ -612,6 +604,45 @@ void srdf::Model::loadPassiveJoints(const urdf::ModelInterface& urdf_model, XMLE
612604
}
613605
}
614606

607+
void srdf::Model::loadJointProperties(const urdf::ModelInterface& urdf_model, XMLElement* robot_xml)
608+
{
609+
for (XMLElement* prop_xml = robot_xml->FirstChildElement("joint_property"); prop_xml;
610+
prop_xml = prop_xml->NextSiblingElement("joint_property"))
611+
{
612+
const char* jname = prop_xml->Attribute("joint_name");
613+
const char* pname = prop_xml->Attribute("property_name");
614+
const char* pval = prop_xml->Attribute("value");
615+
if (!jname)
616+
{
617+
CONSOLE_BRIDGE_logError("joint_property is missing a joint name");
618+
continue;
619+
}
620+
if (!pname)
621+
{
622+
CONSOLE_BRIDGE_logError("Property name for joint '%s' is not specified", jname);
623+
continue;
624+
}
625+
if (!pval)
626+
{
627+
CONSOLE_BRIDGE_logError("Value is not specified for property '%s' of joint '%s'", pname, jname);
628+
continue;
629+
}
630+
631+
JointProperty jp;
632+
jp.joint_name_ = boost::trim_copy(std::string(jname));
633+
jp.property_name_ = boost::trim_copy(std::string(pname));
634+
jp.value_ = std::string(pval);
635+
636+
if (!isValidJoint(urdf_model, jp.joint_name_))
637+
{
638+
CONSOLE_BRIDGE_logError("Property defined for a joint '%s' that is not known to the URDF. Ignoring.",
639+
jp.joint_name_.c_str());
640+
continue;
641+
}
642+
joint_properties_[jp.joint_name_].push_back(jp);
643+
}
644+
}
645+
615646
bool srdf::Model::initXml(const urdf::ModelInterface& urdf_model, XMLElement* robot_xml)
616647
{
617648
clear();
@@ -642,6 +673,7 @@ bool srdf::Model::initXml(const urdf::ModelInterface& urdf_model, XMLElement* ro
642673
loadCollisionPairs(urdf_model, robot_xml, "enable_collisions", enabled_collision_pairs_);
643674
loadCollisionPairs(urdf_model, robot_xml, "disable_collisions", disabled_collision_pairs_);
644675
loadPassiveJoints(urdf_model, robot_xml);
676+
loadJointProperties(urdf_model, robot_xml);
645677

646678
return true;
647679
}

src/srdf_writer.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ void SRDFWriter::initModel(const urdf::ModelInterface& robot_model, const srdf::
107107
end_effectors_ = srdf_model_->getEndEffectors();
108108
group_states_ = srdf_model_->getGroupStates();
109109
passive_joints_ = srdf_model_->getPassiveJoints();
110+
joint_properties_ = srdf_model_->getJointProperties();
110111

111112
// Copy the robot name b/c the root xml element requires this attribute
112113
robot_name_ = robot_model.getName();
@@ -193,6 +194,9 @@ void SRDFWriter::generateSRDF(XMLDocument& document)
193194
// Add Passive Joints
194195
createPassiveJointsXML(robot_root);
195196

197+
// Add Joint Properties
198+
createJointPropertiesXML(robot_root);
199+
196200
// Add Link Sphere approximations
197201
createLinkSphereApproximationsXML(robot_root);
198202

@@ -498,4 +502,27 @@ void SRDFWriter::createPassiveJointsXML(XMLElement* root)
498502
root->InsertEndChild(p_joint);
499503
}
500504
}
505+
506+
void SRDFWriter::createJointPropertiesXML(tinyxml2::XMLElement* root)
507+
{
508+
XMLDocument* doc = root->GetDocument();
509+
510+
if (!joint_properties_.empty())
511+
{
512+
XMLComment* comment = doc->NewComment(
513+
"JOINT PROPERTIES: Purpose: Define a property for a particular joint (could be a virtual joint)");
514+
root->InsertEndChild(comment);
515+
}
516+
for (const auto& joint_properties : joint_properties_)
517+
{
518+
for (const auto& joint_property : joint_properties.second)
519+
{
520+
XMLElement* p_joint = doc->NewElement("joint_property");
521+
p_joint->SetAttribute("joint_name", joint_property.joint_name_.c_str());
522+
p_joint->SetAttribute("property_name", joint_property.property_name_.c_str());
523+
p_joint->SetAttribute("value", joint_property.value_.c_str());
524+
root->InsertEndChild(p_joint);
525+
}
526+
}
527+
}
501528
} // namespace srdf

test/resources/pr2_desc.3-normalized.srdf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
<end_effector name="l_end_effector" parent_link="l_wrist_roll_link" group="l_end_effector"/>
6464
<!--VIRTUAL JOINT: Purpose: this element defines a virtual joint between a robot link and an external frame of reference (considered fixed with respect to the robot)-->
6565
<virtual_joint name="world_joint" type="planar" parent_frame="odom" child_link="base_footprint"/>
66+
<!--JOINT PROPERTIES: Purpose: Define a property for a particular joint (could be a virtual joint)-->
67+
<joint_property joint_name="world_joint" property_name="angular_distance_weight" value="0.5"/>
6668
<!--DISABLE COLLISIONS: By default it is assumed that any link of the robot could potentially come into collision with any other link in the robot. This tag disables collision checking between a specified pair of links. -->
6769
<disable_collisions link1="r_shoulder_pan_link" link2="r_shoulder_lift_link" reason="adjacent"/>
6870
<disable_collisions link1="r_shoulder_pan_link" link2="l_gripper_palm_link" reason=""/>

test/resources/pr2_desc.3.srdf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,9 @@
6969
<disable_collisions link1="r_shoulder_pan_link" link2="l_gripper_palm_link" />
7070
<!-- and many more disable_collisions tags -->
7171

72+
<joint_property joint_name="world_joint" property_name="angular_distance_weight" value="0.5" />
73+
74+
<!-- When parsing, this made up joint that is not present in the URDF is expected to print an error -->
75+
<joint_property joint_name="made_up_joint" property_name="angular_distance_weight" value="0.5" />
76+
7277
</robot>

test/test_parser.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,20 @@ TEST(TestCpp, testComplex)
177177
EXPECT_TRUE(s.getEndEffectors()[index].name_ == "r_end_effector");
178178
EXPECT_TRUE(s.getEndEffectors()[index].component_group_ == "r_end_effector");
179179
EXPECT_TRUE(s.getEndEffectors()[index].parent_link_ == "r_wrist_roll_link");
180+
181+
// Joint Properties
182+
const std::vector<srdf::Model::JointProperty>& gripper_props = s.getJointProperties("r_gripper_tool_joint");
183+
EXPECT_EQ(gripper_props.size(), 0u);
184+
185+
// When parsing, this made up joint that is not present in the URDF is expected to print an error
186+
// AND the property should not be made available in the srdf::Model
187+
const std::vector<srdf::Model::JointProperty>& made_up_props = s.getJointProperties("made_up_joint");
188+
EXPECT_EQ(made_up_props.size(), 0u);
189+
190+
const std::vector<srdf::Model::JointProperty>& world_props = s.getJointProperties("world_joint");
191+
ASSERT_EQ(world_props.size(), 1u);
192+
EXPECT_EQ(world_props[0].property_name_, "angular_distance_weight");
193+
EXPECT_EQ(world_props[0].value_, "0.5");
180194
}
181195

182196
TEST(TestCpp, testReadWrite)

0 commit comments

Comments
 (0)