2 from __future__
import print_function
5 IKFast Plugin Generator for MoveIt
7 Creates a kinematics plugin using the output of IKFast from OpenRAVE.
8 This plugin and the move_group node can be used as a general
9 kinematics service, from within the moveit planning environment, or in
12 Author: Dave Coleman, PickNik Inc.
13 Michael Lautman, PickNik Inc.
14 Based heavily on the arm_kinematic_tools package by Jeremy Zoss, SwRI
15 and the arm_navigation plugin generator by David Butterworth, KAIST
21 Copyright (c) 2013, Jeremy Zoss, SwRI
24 Redistribution and use in source and binary forms, with or without
25 modification, are permitted provided that the following conditions are met:
27 * Redistributions of source code must retain the above copyright
28 notice, this list of conditions and the following disclaimer.
29 * Redistributions in binary form must reproduce the above copyright
30 notice, this list of conditions and the following disclaimer in the
31 documentation and/or other materials provided with the distribution.
32 * Neither the name of the Willow Garage, Inc. nor the names of its
33 contributors may be used to endorse or promote products derived from
34 this software without specific prior written permission.
36 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
37 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
40 IABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
41 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
42 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
43 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
44 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
45 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
46 POSSIBILITY OF SUCH DAMAGE.
52 from lxml
import etree
53 from getpass
import getuser
58 from ament_index_python.packages
import (
59 get_package_share_directory,
64 "Failed to import ament_index_python. No ROS2 environment available? Trying without."
71 raise PackageNotFoundError
75 plugin_gen_pkg =
"moveit_kinematics"
77 search_modes = [
"OPTIMIZE_MAX_JOINT",
"OPTIMIZE_FREE_JOINT"]
81 parser = argparse.ArgumentParser(
82 description=
"Generate an IKFast MoveIt kinematic plugin"
84 parser.add_argument(
"robot_name", help=
"The name of your robot")
86 "planning_group_name",
87 help=
"The name of the planning group for which your IKFast solution was generated",
91 help=
"The name of the MoveIt IKFast Kinematics Plugin to be created/updated",
95 help=
"The name of the base link that was used when generating your IKFast solution",
99 help=
"The name of the end effector link that was used when generating your IKFast solution",
102 "ikfast_output_path",
103 help=
"The full path to the analytic IK solution output by IKFast",
107 default=search_modes[0],
108 help=
"The search mode used to solve IK for robots with more than 6DOF",
111 "--srdf_filename", help=
"The name of your robot. Defaults to <robot_name>.srdf"
114 "--robot_name_in_srdf",
115 help=
"The name of your robot as defined in the srdf. Defaults to <robot_name>",
118 "--moveit_config_pkg",
119 help=
"The robot moveit_config package. Defaults to <robot_name>_moveit_config",
125 if args.srdf_filename
is None:
126 args.srdf_filename = args.robot_name +
".srdf"
127 if args.robot_name_in_srdf
is None:
128 args.robot_name_in_srdf = args.robot_name
129 if args.moveit_config_pkg
is None:
130 args.moveit_config_pkg = args.robot_name +
"_moveit_config"
134 print(
"Creating IKFastKinematicsPlugin with parameters: ")
135 print(
" robot_name: %s" % args.robot_name)
136 print(
" base_link_name: %s" % args.base_link_name)
137 print(
" eef_link_name: %s" % args.eef_link_name)
138 print(
" planning_group_name: %s" % args.planning_group_name)
139 print(
" ikfast_plugin_pkg: %s" % args.ikfast_plugin_pkg)
140 print(
" ikfast_output_path: %s" % args.ikfast_output_path)
141 print(
" search_mode: %s" % args.search_mode)
142 print(
" srdf_filename: %s" % args.srdf_filename)
143 print(
" robot_name_in_srdf: %s" % args.robot_name_in_srdf)
144 print(
" moveit_config_pkg: %s" % args.moveit_config_pkg)
149 curr_deps = [e.text
for e
in e_parent.findall(req_type)]
150 missing_deps = set(reqd_deps) - set(curr_deps)
151 for dep
in missing_deps:
152 etree.SubElement(e_parent, req_type).text = dep
156 if not os.path.exists(args.ikfast_output_path):
157 raise Exception(
"Can't find IKFast source code at " + args.ikfast_output_path)
161 with open(args.ikfast_output_path,
"r")
as src:
163 if line.startswith(
"/// ikfast version"):
164 line_search = re.search(
"ikfast version (.*) generated", line)
166 solver_version = int(line_search.group(1), 0) & ~0x10000000
168 print(
"Found source code generated by IKFast version %s" % str(solver_version))
171 if solver_version >= 56:
172 setattr(args,
"template_version", 61)
174 raise Exception(
"This converter requires IKFast 0.5.6 or newer.")
178 e = etree.Element(name, **attributes)
187 "ikfast_plugin_pkg_path",
190 except PackageNotFoundError:
191 args.ikfast_plugin_pkg_path = os.path.abspath(args.ikfast_plugin_pkg)
193 "Failed to find package: %s. Will create it in %s."
194 % (args.ikfast_plugin_pkg, args.ikfast_plugin_pkg_path)
197 args.ikfast_plugin_pkg = os.path.basename(args.ikfast_plugin_pkg_path)
199 src_path = args.ikfast_plugin_pkg_path +
"/src/"
200 if not os.path.exists(src_path):
201 os.makedirs(src_path)
203 include_path = args.ikfast_plugin_pkg_path +
"/include/"
204 if not os.path.exists(include_path):
205 os.makedirs(include_path)
208 pkg_xml_path = args.ikfast_plugin_pkg_path +
"/package.xml"
209 if not os.path.exists(pkg_xml_path):
211 root.append(
xmlElement(
"name", text=args.ikfast_plugin_pkg))
212 root.append(
xmlElement(
"version", text=
"0.0.0"))
214 xmlElement(
"description", text=
"IKFast plugin for " + args.robot_name)
216 root.append(
xmlElement(
"license", text=
"BSD"))
217 user_name = getuser()
221 root.append(
xmlElement(
"buildtool_depend", text=
"ament_cmake"))
223 export.append(
xmlElement(
"build_type", text=
"ament_cmake"))
225 etree.ElementTree(root).write(
226 pkg_xml_path, xml_declaration=
True, pretty_print=
True, encoding=
"UTF-8"
228 print(
"Created package.xml at: '%s'" % pkg_xml_path)
232 for candidate
in [os.path.dirname(__file__) +
"/../templates"]:
233 if os.path.exists(candidate)
and os.path.exists(candidate +
"/ikfast.h"):
234 return os.path.realpath(candidate)
238 "ikfast_kinematics_plugin/templates",
240 except PackageNotFoundError:
241 raise Exception(
"Can't find package %s" % plugin_gen_pkg)
246 src_path = args.ikfast_plugin_pkg_path +
"/src/"
251 + args.planning_group_name
252 +
"_ikfast_solver.cpp"
254 if not os.path.exists(solver_file_path)
or not os.path.samefile(
255 args.ikfast_output_path, solver_file_path
257 shutil.copy2(args.ikfast_output_path, solver_file_path)
259 if not os.path.exists(solver_file_path):
261 "Failed to copy IKFast source code from '%s' to '%s'\n"
262 "Manually copy the source file generated by IKFast to this location and re-run"
263 % (args.ikfast_output_path, solver_file_path)
266 args.ikfast_output_path = solver_file_path
272 setattr(args,
"namespace", args.robot_name +
"_" + args.planning_group_name)
274 _ROBOT_NAME_=args.robot_name,
275 _GROUP_NAME_=args.planning_group_name,
276 _SEARCH_MODE_=args.search_mode,
277 _EEF_LINK_=args.eef_link_name,
278 _BASE_LINK_=args.base_link_name,
279 _PACKAGE_NAME_=args.ikfast_plugin_pkg,
280 _NAMESPACE_=args.namespace,
285 template_dir +
"/ikfast.h",
286 args.ikfast_plugin_pkg_path +
"/include/ikfast.h",
287 "ikfast header file",
293 + str(args.template_version)
294 +
"_moveit_plugin_template.cpp",
295 args.ikfast_plugin_pkg_path
299 + args.planning_group_name
300 +
"_ikfast_moveit_plugin.cpp",
301 "ikfast plugin file",
306 ik_library_name = args.namespace +
"_moveit_ikfast_plugin"
307 plugin_def = etree.Element(
"library", path=ik_library_name)
308 setattr(args,
"plugin_name", args.namespace +
"/IKFastKinematicsPlugin")
309 cl = etree.SubElement(
312 name=args.plugin_name,
313 type=args.namespace +
"::IKFastKinematicsPlugin",
314 base_class_type=
"kinematics::KinematicsBase",
316 desc = etree.SubElement(cl,
"description")
318 "IKFast{template} plugin for closed-form kinematics of {robot} {group}".format(
319 template=args.template_version,
320 robot=args.robot_name,
321 group=args.planning_group_name,
326 plugin_file_name = ik_library_name +
"_description.xml"
327 plugin_file_path = args.ikfast_plugin_pkg_path +
"/" + plugin_file_name
328 etree.ElementTree(plugin_def).write(
329 plugin_file_path, xml_declaration=
True, pretty_print=
True, encoding=
"UTF-8"
331 print(
"Created plugin definition at '%s'" % plugin_file_path)
334 replacements.update(dict(_LIBRARY_NAME_=ik_library_name))
336 template_dir +
"/CMakeLists.txt",
337 args.ikfast_plugin_pkg_path +
"/CMakeLists.txt",
343 parser = etree.XMLParser(remove_blank_text=
True)
344 package_file_name = args.ikfast_plugin_pkg_path +
"/package.xml"
345 package_xml = etree.parse(package_file_name, parser).getroot()
356 run_deps = [
"liblapack-dev",
"moveit_core",
"pluginlib",
"rclcpp"]
358 update_deps(build_deps,
"build_depend", package_xml)
362 new_export = etree.Element(
"moveit_core", plugin=
"${prefix}/" + plugin_file_name)
364 export_element = package_xml.find(
"export")
365 if export_element
is None:
366 export_element = etree.SubElement(package_xml,
"export")
369 for el
in export_element.findall(
"moveit_core"):
370 found = etree.tostring(new_export) == etree.tostring(el)
375 export_element.append(new_export)
379 etree.ElementTree(package_xml).write(
380 package_file_name, xml_declaration=
True, pretty_print=
True, encoding=
"UTF-8"
382 print(
"Wrote package.xml at '%s'" % package_file_name)
385 easy_script_file_path = args.ikfast_plugin_pkg_path +
"/update_ikfast_plugin.sh"
386 with open(easy_script_file_path,
"w")
as f:
394 +
"robot_name_in_srdf="
395 + args.robot_name_in_srdf
397 +
"moveit_config_pkg="
398 + args.moveit_config_pkg
403 +
"planning_group_name="
404 + args.planning_group_name
406 +
"ikfast_plugin_pkg="
407 + args.ikfast_plugin_pkg
410 + args.base_link_name
415 +
"ikfast_output_path="
416 + args.ikfast_output_path
418 +
"rosrun moveit_kinematics create_ikfast_moveit_plugin.py\\\n"
419 +
" --search_mode=$search_mode\\\n"
420 +
" --srdf_filename=$srdf_filename\\\n"
421 +
" --robot_name_in_srdf=$robot_name_in_srdf\\\n"
422 +
" --moveit_config_pkg=$moveit_config_pkg\\\n"
424 +
" $planning_group_name\\\n"
425 +
" $ikfast_plugin_pkg\\\n"
426 +
" $base_link_name\\\n"
427 +
" $eef_link_name\\\n"
428 +
" $ikfast_output_path\n"
431 print(
"Created update plugin script at '%s'" % easy_script_file_path)
437 except PackageNotFoundError:
438 raise Exception(
"Failed to find package: " + args.moveit_config_pkg)
441 srdf_file_name = moveit_config_pkg_path +
"/config/" + args.srdf_filename
442 srdf = etree.parse(srdf_file_name).getroot()
444 raise Exception(
"Failed to find SRDF file: " + srdf_file_name)
445 except etree.XMLSyntaxError
as err:
447 "Failed to parse xml in file: %s\n%s" % (srdf_file_name, err.msg)
450 if args.robot_name_in_srdf != srdf.get(
"name"):
452 "Robot name in srdf ('%s') doesn't match expected name ('%s')"
453 % (srdf.get(
"name"), args.robot_name_in_srdf)
456 groups = srdf.findall(
"group")
458 raise Exception(
"No planning groups are defined in the SRDF")
460 planning_group =
None
462 if group.get(
"name").lower() == args.planning_group_name.lower():
463 planning_group = group
465 if planning_group
is None:
467 "Planning group '%s' not defined in the SRDF. Available groups: \n%s"
469 args.planning_group_name,
470 ", ".join([group_name.get(
"name")
for group_name
in groups]),
475 kin_yaml_file_name = moveit_config_pkg_path +
"/config/kinematics.yaml"
476 with open(kin_yaml_file_name,
"r")
as f:
477 kin_yaml_data = yaml.safe_load(f)
479 kin_yaml_data[args.planning_group_name][
"kinematics_solver"] = args.plugin_name
480 with open(kin_yaml_file_name,
"w")
as f:
481 yaml.dump(kin_yaml_data, f, default_flow_style=
False)
483 print(
"Modified kinematics.yaml at '%s'" % kin_yaml_file_name)
486 def copy_file(src_path, dest_path, description, replacements=None):
487 if not os.path.exists(src_path):
488 raise Exception(
"Can't find %s at '%s'" % (description, src_path))
490 if replacements
is None:
491 replacements = dict()
493 with open(src_path,
"r")
as f:
497 for key, value
in replacements.items():
498 content = re.sub(key, value, content)
500 with open(dest_path,
"w")
as f:
502 print(
"Created %s at '%s'" % (description, dest_path))
507 args = parser.parse_args()
516 except Exception
as e:
517 print(
"Failed to update MoveIt package:\n" + str(e))
520 if __name__ ==
"__main__":
def get_package_share_directory(pkg_name)
def xmlElement(name, text=None, **attributes)
def validate_openrave_version(args)
def update_deps(reqd_deps, req_type, e_parent)
def populate_optional(args)
def update_moveit_package(args)
def create_ikfast_package(args)
def copy_file(src_path, dest_path, description, replacements=None)
def update_ikfast_package(args)
void print(PropagationDistanceField &pdf, int numX, int numY, int numZ)