moveit2
The MoveIt Motion Planning Framework for ROS 2.
Loading...
Searching...
No Matches
simulation.cpp
Go to the documentation of this file.
1/*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2021, PickNik Robotics
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of PickNik Robotics nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34
35/* Author: David V. Lu!! */
36
37#include <fstream>
38
40
41namespace moveit_setup
42{
43namespace simulation
44{
46{
47 urdf_config_ = config_data_->get<URDFConfig>("urdf");
48}
49
50// ******************************************************************************************
51// Helper function to get the controller that is controlling the joint
52// ******************************************************************************************
53std::string Simulation::getJointHardwareInterface(const std::string& joint_name)
54{
73 // If the joint was not found in any controller return EffortJointInterface
74 return "hardware_interface/EffortJointInterface";
75}
76
77std::string printXML(const tinyxml2::XMLDocument& doc)
78{
79 tinyxml2::XMLPrinter printer;
80 doc.Accept(&printer);
81 return printer.CStr();
82}
83
84// ******************************************************************************************
85// Writes a Gazebo compatible robot URDF to gazebo_compatible_urdf_string_
86// ******************************************************************************************
88{
89 using tinyxml2::XMLElement;
90
91 tinyxml2::XMLDocument doc;
92 std::string urdf_string = urdf_config_->getURDFContents();
93 doc.Parse(urdf_string.c_str());
94 auto root = doc.RootElement();
95
96 // Normalize original urdf_string_
97 std::string orig_urdf = printXML(doc);
98
99 // Map existing SimpleTransmission elements to their joint name
100 std::map<std::string, XMLElement*> transmission_elements;
101 for (XMLElement* element = root->FirstChildElement("transmission"); element != nullptr;
102 element = element->NextSiblingElement(element->Value()))
103 {
104 auto type_tag = element->FirstChildElement("type");
105 auto joint_tag = element->FirstChildElement("joint");
106 if (!type_tag || !type_tag->GetText() || !joint_tag || !joint_tag->Attribute("name"))
107 continue; // ignore invalid tags
108 if (std::string(type_tag->GetText()) == "transmission_interface/SimpleTransmission")
109 transmission_elements[element->FirstChildElement("joint")->Attribute("name")] = element;
110 }
111
112 // Loop through Link and Joint elements and add Gazebo tags if not present
113 for (XMLElement* element = root->FirstChildElement(); element != nullptr; element = element->NextSiblingElement())
114 {
115 const std::string tag_name(element->Value());
116 if (tag_name == "link" && element->FirstChildElement("collision"))
117 {
118 XMLElement* inertial = uniqueInsert(doc, *element, "inertial");
119 uniqueInsert(doc, *inertial, "mass", { { "value", "0.1" } });
120 uniqueInsert(doc, *inertial, "origin", { { "xyz", "0 0 0" }, { "rpy", "0 0 0" } });
121 uniqueInsert(doc, *inertial, "inertia",
122 { { "ixx", "0.03" },
123 { "iyy", "0.03" },
124 { "izz", "0.03" },
125 { "ixy", "0.0" },
126 { "ixz", "0.0" },
127 { "iyz", "0.0" } });
128 }
129 else if (tag_name == "joint")
130 {
131 const char* joint_type = element->Attribute("type");
132 const char* joint_name = element->Attribute("name");
133 if (!joint_type || !joint_name || strcmp(joint_type, "fixed") == 0)
134 continue; // skip invalid or fixed joints
135
136 // find existing or create new transmission element for this joint
137 XMLElement* transmission;
138 auto it = transmission_elements.find(joint_name);
139 if (it != transmission_elements.end())
140 transmission = it->second;
141 else
142 {
143 transmission = doc.NewElement("transmission");
144 root->InsertEndChild(transmission);
145 transmission->SetAttribute("name", (std::string("trans_") + joint_name).c_str());
146 }
147
148 uniqueInsert(doc, *transmission, "type", {}, "transmission_interface/SimpleTransmission");
149
150 std::string hw_interface = getJointHardwareInterface(joint_name);
151 auto* joint = uniqueInsert(doc, *transmission, "joint", { { "name", joint_name } });
152 uniqueInsert(doc, *joint, "hardwareInterface", {}, hw_interface.c_str());
153
154 auto actuator_name = joint_name + std::string("_motor");
155 auto* actuator = uniqueInsert(doc, *transmission, "actuator", { { "name", actuator_name.c_str() } });
156 uniqueInsert(doc, *actuator, "hardwareInterface", {}, hw_interface.c_str());
157 uniqueInsert(doc, *actuator, "mechanicalReduction", {}, "1");
158 }
159 }
160
161 // Add gazebo_ros_control plugin which reads the transmission tags
162 XMLElement* gazebo = uniqueInsert(doc, *root, "gazebo");
163 XMLElement* plugin = uniqueInsert(doc, *gazebo, "plugin", { { "name", "gazebo_ros_control", true } });
164 uniqueInsert(doc, *plugin, "robotNamespace", {}, "/");
165
166 // generate new URDF
167 std::string new_urdf = printXML(doc);
168 // and return it when there are changes
169 return orig_urdf == new_urdf ? std::string() : new_urdf;
170}
171
172// ******************************************************************************************
173// Output Gazebo URDF file
174// ******************************************************************************************
175bool Simulation::outputGazeboURDFFile(const std::filesystem::path& file_path)
176{
177 std::ofstream os(file_path, std::ios_base::trunc);
178 if (!os.good())
179 {
180 RCLCPP_ERROR_STREAM(*logger_, "Unable to open file for writing " << file_path);
181 return false;
182 }
183
184 os << gazebo_urdf_string_.c_str() << std::endl;
185 os.close();
186
187 return true;
188}
189
190bool Simulation::isValidXML(const std::string& new_urdf_contents, int& error_row, std::string& error_description) const
191{
192 tinyxml2::XMLDocument doc;
193 doc.Parse(new_urdf_contents.c_str());
194 if (doc.Error())
195 {
196 error_row = doc.ErrorLineNum();
197 error_description = doc.ErrorStr();
198 }
199 return !doc.Error();
200}
201
202} // namespace simulation
203} // namespace moveit_setup
DataWarehousePtr config_data_
std::shared_ptr< rclcpp::Logger > logger_
std::string gazebo_urdf_string_
Gazebo URDF robot model string.
void onInit() override
Overridable initialization method.
std::shared_ptr< URDFConfig > urdf_config_
bool isValidXML(const std::string &new_urdf_contents, int &error_row, std::string &error_description) const
Check if the given xml is valid.
bool outputGazeboURDFFile(const std::filesystem::path &file_path)
std::string getGazeboCompatibleURDF()
Parses the existing urdf and constructs a string from it with the elements required by gazebo simulat...
std::string getJointHardwareInterface(const std::string &joint_name)
Helper function to get the controller that is controlling the joint.
std::string printXML(const tinyxml2::XMLDocument &doc)
tinyxml2::XMLElement * uniqueInsert(tinyxml2::XMLDocument &doc, tinyxml2::XMLElement &element, const char *tag, const std::vector< XMLAttribute > &attributes={}, const char *text=nullptr)
Insert a new XML element with a given tag, attributes, and text.