moveit2
The MoveIt Motion Planning Framework for ROS 2.
get_group_urdf_capability.cpp
Go to the documentation of this file.
1 /*********************************************************************
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2024, PickNik Inc.
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 Inc. 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: Sebastian Jahr */
36 
38 
41 #include <moveit/utils/logger.hpp>
42 #include <urdf_parser/urdf_parser.h>
43 
44 namespace move_group
45 {
46 
47 namespace
48 {
49 rclcpp::Logger getLogger()
50 {
51  return moveit::getLogger("get_urdf_service");
52 }
53 const auto JOINT_ELEMENT_CLOSING = std::string("</joint>");
54 const auto LINK_ELEMENT_CLOSING = std::string("</link>");
55 const auto ROBOT_ELEMENT_CLOSING = std::string("</robot>");
56 const auto GENERAL_ELEMENT_CLOSING = std::string("/>");
57 } // namespace
58 
60 {
61 }
62 
64 {
65  get_urdf_service_ = context_->moveit_cpp_->getNode()->create_service<moveit_msgs::srv::GetGroupUrdf>(
66  GET_URDF_SERVICE_NAME,
67  [this](const std::shared_ptr<moveit_msgs::srv::GetGroupUrdf::Request>& req,
68  const std::shared_ptr<moveit_msgs::srv::GetGroupUrdf::Response>& res) {
69  res->error_code.source = std::string("GetUrdfService");
70  const auto subgroup = context_->moveit_cpp_->getRobotModel()->getJointModelGroup(req->group_name);
71  // Check if group exists in loaded robot model
72  if (!subgroup)
73  {
74  const auto error_string = std::string("Cannot create URDF because planning group '") + req->group_name +
75  std::string("' does not exist");
76  RCLCPP_ERROR(getLogger(), "%s", error_string.c_str());
77  res->error_code.message = error_string;
78  res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
79  return;
80  }
81 
82  std::string full_urdf_string =
83  context_->moveit_cpp_->getPlanningSceneMonitor()->getRobotModelLoader()->getRDFLoader()->getURDFString();
84 
85  // Check if robot description string is empty
86  if (full_urdf_string.empty())
87  {
88  const auto error_string = std::string("Couldn't get the robot description string from MoveItCpp");
89  RCLCPP_ERROR(getLogger(), "%s", error_string.c_str());
90  res->error_code.message = error_string;
91  res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
92  return;
93  }
94 
95  // Create subgroup urdf
96  // Create header
97  res->urdf_string = std::string("<?xml version=\"1.0\" ?><robot name=\"") + req->group_name +
98  std::string("\" xmlns:xacro=\"http://ros.org/wiki/xacro\">");
99 
100  // Create links
101  auto link_names = subgroup->getLinkModelNames();
102  // Remove duplicates
103  std::sort(link_names.begin(), link_names.end());
104  link_names.erase(std::unique(link_names.begin(), link_names.end()), link_names.end());
105 
106  for (const auto& link_name : link_names)
107  {
108  const auto start = full_urdf_string.find("<link name=\"" + link_name + "\"");
109  auto substring = full_urdf_string.substr(start, full_urdf_string.size() - start);
110 
111  // Link elements can be closed either by "/>" or "</link>" so we need to consider both cases
112  auto const substring_without_opening = substring.substr(1, substring.size() - 2);
113  auto const general_opening_pos_a = substring_without_opening.find('<');
114  auto const link_closing_pos_b = substring.find(GENERAL_ELEMENT_CLOSING);
115  // Case "/>"
116  if (link_closing_pos_b < general_opening_pos_a)
117  {
118  res->urdf_string += substring.substr(0, link_closing_pos_b + GENERAL_ELEMENT_CLOSING.size());
119  }
120  // Case </link>
121  else
122  {
123  res->urdf_string += substring.substr(0, substring.find(LINK_ELEMENT_CLOSING) + LINK_ELEMENT_CLOSING.size());
124  }
125  }
126 
127  // Create joints
128  auto joint_names = subgroup->getJointModelNames();
129  // Remove duplicates
130  std::sort(joint_names.begin(), joint_names.end());
131  joint_names.erase(std::unique(joint_names.begin(), joint_names.end()), joint_names.end());
132  for (const auto& joint_name : joint_names)
133  {
134  const auto start = full_urdf_string.find("<joint name=\"" + joint_name + "\" type");
135  auto substring = full_urdf_string.substr(start, full_urdf_string.size() - start);
136  res->urdf_string += substring.substr(0, substring.find(JOINT_ELEMENT_CLOSING) + JOINT_ELEMENT_CLOSING.size());
137  RCLCPP_ERROR(getLogger(), "%s", joint_name.c_str());
138 
139  // If parent link model is not part of the joint group, add it
140  const auto parent_link_element = subgroup->getJointModel(joint_name)->getParentLinkModel()->getName();
141  if (std::find(link_names.begin(), link_names.end(), parent_link_element) == link_names.end())
142  {
143  auto const base_link_element = "<link name=\"" + parent_link_element + "\"/>";
144  res->urdf_string += base_link_element;
145  link_names.push_back(parent_link_element);
146  }
147  }
148 
149  // Add closing
150  res->urdf_string += ROBOT_ELEMENT_CLOSING;
151 
152  // Validate urdf file
153  if (!urdf::parseURDF(res->urdf_string))
154  {
155  const std::string error_string = std::string("Failed to create valid urdf");
156  RCLCPP_ERROR(getLogger(), "%s", error_string.c_str());
157  res->error_code.message = error_string;
158  res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::FAILURE;
159  return;
160  }
161  res->error_code.val = moveit_msgs::msg::MoveItErrorCodes::SUCCESS;
162  } // End of callback function
163  );
164 }
165 } // namespace move_group
166 
167 #include <pluginlib/class_list_macros.hpp>
168 
PLUGINLIB_EXPORT_CLASS(cached_ik_kinematics_plugin::CachedIKKinematicsPlugin< kdl_kinematics_plugin::KDLKinematicsPlugin >, kinematics::KinematicsBase)
Move group capability to create an URDF string for a joint model group.
void initialize() override
Initializes service when plugin is loaded.
rclcpp::Logger getLogger(const std::string &name)
Creates a namespaced logger.
Definition: logger.cpp:79