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."
72 raise PackageNotFoundError
76 plugin_gen_pkg =
"moveit_kinematics"
78 search_modes = [
"OPTIMIZE_MAX_JOINT",
"OPTIMIZE_FREE_JOINT"]
82 parser = argparse.ArgumentParser(
83 description=
"Generate an IKFast MoveIt kinematic plugin"
85 parser.add_argument(
"robot_name", help=
"The name of your robot")
87 "planning_group_name",
88 help=
"The name of the planning group for which your IKFast solution was generated",
92 help=
"The name of the MoveIt IKFast Kinematics Plugin to be created/updated",
96 help=
"The name of the base link that was used when generating your IKFast solution",
100 help=
"The name of the end effector link that was used when generating your IKFast solution",
103 "ikfast_output_path",
104 help=
"The full path to the analytic IK solution output by IKFast",
108 default=search_modes[0],
109 help=
"The search mode used to solve IK for robots with more than 6DOF",
112 "--srdf_filename", help=
"The name of your robot. Defaults to <robot_name>.srdf"
115 "--robot_name_in_srdf",
116 help=
"The name of your robot as defined in the srdf. Defaults to <robot_name>",
119 "--moveit_config_pkg",
120 help=
"The robot moveit_config package. Defaults to <robot_name>_moveit_config",
126 if args.srdf_filename
is None:
127 args.srdf_filename = args.robot_name +
".srdf"
128 if args.robot_name_in_srdf
is None:
129 args.robot_name_in_srdf = args.robot_name
130 if args.moveit_config_pkg
is None:
131 args.moveit_config_pkg = args.robot_name +
"_moveit_config"
135 print(
"Creating IKFastKinematicsPlugin with parameters: ")
136 print(
" robot_name: %s" % args.robot_name)
137 print(
" base_link_name: %s" % args.base_link_name)
138 print(
" eef_link_name: %s" % args.eef_link_name)
139 print(
" planning_group_name: %s" % args.planning_group_name)
140 print(
" ikfast_plugin_pkg: %s" % args.ikfast_plugin_pkg)
141 print(
" ikfast_output_path: %s" % args.ikfast_output_path)
142 print(
" search_mode: %s" % args.search_mode)
143 print(
" srdf_filename: %s" % args.srdf_filename)
144 print(
" robot_name_in_srdf: %s" % args.robot_name_in_srdf)
145 print(
" moveit_config_pkg: %s" % args.moveit_config_pkg)
150 curr_deps = [e.text
for e
in e_parent.findall(req_type)]
151 missing_deps = set(reqd_deps) - set(curr_deps)
152 for dep
in missing_deps:
153 etree.SubElement(e_parent, req_type).text = dep
157 if not os.path.exists(args.ikfast_output_path):
158 raise Exception(
"Can't find IKFast source code at " + args.ikfast_output_path)
162 with open(args.ikfast_output_path,
"r")
as src:
164 if line.startswith(
"/// ikfast version"):
165 line_search = re.search(
"ikfast version (.*) generated", line)
167 solver_version = int(line_search.group(1), 0) & ~0x10000000
169 print(
"Found source code generated by IKFast version %s" % str(solver_version))
172 if solver_version >= 56:
173 setattr(args,
"template_version", 61)
175 raise Exception(
"This converter requires IKFast 0.5.6 or newer.")
179 e = etree.Element(name, **attributes)
186 "ikfast_kinematics": {
190 "description":
"prefix added to tip- and baseframe to allow different namespaces or multi-robot setups",
194 return parameter_dict
201 "ikfast_plugin_pkg_path",
204 except PackageNotFoundError:
205 args.ikfast_plugin_pkg_path = os.path.abspath(args.ikfast_plugin_pkg)
207 "Failed to find package: %s. Will create it in %s."
208 % (args.ikfast_plugin_pkg, args.ikfast_plugin_pkg_path)
211 args.ikfast_plugin_pkg = os.path.basename(args.ikfast_plugin_pkg_path)
213 src_path = args.ikfast_plugin_pkg_path +
"/src/"
214 if not os.path.exists(src_path):
215 os.makedirs(src_path)
217 include_path = args.ikfast_plugin_pkg_path +
"/include/"
218 if not os.path.exists(include_path):
219 os.makedirs(include_path)
222 pkg_xml_path = args.ikfast_plugin_pkg_path +
"/package.xml"
223 if not os.path.exists(pkg_xml_path):
225 root.append(
xmlElement(
"name", text=args.ikfast_plugin_pkg))
226 root.append(
xmlElement(
"version", text=
"0.0.0"))
228 xmlElement(
"description", text=
"IKFast plugin for " + args.robot_name)
230 root.append(
xmlElement(
"license", text=
"BSD"))
231 user_name = getuser()
233 xmlElement(
"maintainer", email=
"%s@todo.todo" % user_name, text=user_name)
235 root.append(
xmlElement(
"buildtool_depend", text=
"ament_cmake"))
237 export.append(
xmlElement(
"build_type", text=
"ament_cmake"))
239 etree.ElementTree(root).write(
240 pkg_xml_path, xml_declaration=
True, pretty_print=
True, encoding=
"UTF-8"
242 print(
"Created package.xml at: '%s'" % pkg_xml_path)
245 parameters_yaml_path = src_path +
"ikfast_kinematics_parameters.yaml"
246 if not os.path.exists(parameters_yaml_path):
247 print(
"Create parameters.yaml at: '%s'" % parameters_yaml_path)
248 with open(parameters_yaml_path,
"w")
as file:
253 for candidate
in [os.path.dirname(__file__) +
"/../templates"]:
254 if os.path.exists(candidate)
and os.path.exists(candidate +
"/ikfast.h"):
255 return os.path.realpath(candidate)
259 "ikfast_kinematics_plugin/templates",
261 except PackageNotFoundError:
262 raise Exception(
"Can't find package %s" % plugin_gen_pkg)
267 src_path = args.ikfast_plugin_pkg_path +
"/src/"
272 + args.planning_group_name
273 +
"_ikfast_solver.cpp"
275 if not os.path.exists(solver_file_path)
or not os.path.samefile(
276 args.ikfast_output_path, solver_file_path
278 shutil.copy2(args.ikfast_output_path, solver_file_path)
280 if not os.path.exists(solver_file_path):
282 "Failed to copy IKFast source code from '%s' to '%s'\n"
283 "Manually copy the source file generated by IKFast to this location and re-run"
284 % (args.ikfast_output_path, solver_file_path)
287 args.ikfast_output_path = solver_file_path
293 setattr(args,
"namespace", args.robot_name +
"_" + args.planning_group_name)
295 _ROBOT_NAME_=args.robot_name,
296 _GROUP_NAME_=args.planning_group_name,
297 _SEARCH_MODE_=args.search_mode,
298 _EEF_LINK_=args.eef_link_name,
299 _BASE_LINK_=args.base_link_name,
300 _PACKAGE_NAME_=args.ikfast_plugin_pkg,
301 _NAMESPACE_=args.namespace,
306 template_dir +
"/ikfast.h",
307 args.ikfast_plugin_pkg_path +
"/include/ikfast.h",
308 "ikfast header file",
314 + str(args.template_version)
315 +
"_moveit_plugin_template.cpp",
316 args.ikfast_plugin_pkg_path
320 + args.planning_group_name
321 +
"_ikfast_moveit_plugin.cpp",
322 "ikfast plugin file",
327 ik_library_name = args.namespace +
"_moveit_ikfast_plugin"
328 plugin_def = etree.Element(
"library", path=ik_library_name)
329 setattr(args,
"plugin_name", args.namespace +
"/IKFastKinematicsPlugin")
330 cl = etree.SubElement(
333 name=args.plugin_name,
334 type=args.namespace +
"::IKFastKinematicsPlugin",
335 base_class_type=
"kinematics::KinematicsBase",
337 desc = etree.SubElement(cl,
"description")
339 "IKFast{template} plugin for closed-form kinematics of {robot} {group}".format(
340 template=args.template_version,
341 robot=args.robot_name,
342 group=args.planning_group_name,
347 plugin_file_name = ik_library_name +
"_description.xml"
348 plugin_file_path = args.ikfast_plugin_pkg_path +
"/" + plugin_file_name
349 etree.ElementTree(plugin_def).write(
350 plugin_file_path, xml_declaration=
True, pretty_print=
True, encoding=
"UTF-8"
352 print(
"Created plugin definition at '%s'" % plugin_file_path)
355 replacements.update(dict(_LIBRARY_NAME_=ik_library_name))
357 template_dir +
"/CMakeLists.txt",
358 args.ikfast_plugin_pkg_path +
"/CMakeLists.txt",
364 parser = etree.XMLParser(remove_blank_text=
True)
365 package_file_name = args.ikfast_plugin_pkg_path +
"/package.xml"
366 package_xml = etree.parse(package_file_name, parser).getroot()
377 run_deps = [
"liblapack-dev",
"moveit_core",
"pluginlib",
"rclcpp"]
379 update_deps(build_deps,
"build_depend", package_xml)
383 new_export = etree.Element(
"moveit_core", plugin=
"${prefix}/" + plugin_file_name)
385 export_element = package_xml.find(
"export")
386 if export_element
is None:
387 export_element = etree.SubElement(package_xml,
"export")
390 for el
in export_element.findall(
"moveit_core"):
391 found = etree.tostring(new_export) == etree.tostring(el)
396 export_element.append(new_export)
400 etree.ElementTree(package_xml).write(
401 package_file_name, xml_declaration=
True, pretty_print=
True, encoding=
"UTF-8"
403 print(
"Wrote package.xml at '%s'" % package_file_name)
406 easy_script_file_path = args.ikfast_plugin_pkg_path +
"/update_ikfast_plugin.sh"
407 with open(easy_script_file_path,
"w")
as f:
415 +
"robot_name_in_srdf="
416 + args.robot_name_in_srdf
418 +
"moveit_config_pkg="
419 + args.moveit_config_pkg
424 +
"planning_group_name="
425 + args.planning_group_name
427 +
"ikfast_plugin_pkg="
428 + args.ikfast_plugin_pkg
431 + args.base_link_name
436 +
"ikfast_output_path="
437 + args.ikfast_output_path
439 +
"rosrun moveit_kinematics create_ikfast_moveit_plugin.py\\\n"
440 +
" --search_mode=$search_mode\\\n"
441 +
" --srdf_filename=$srdf_filename\\\n"
442 +
" --robot_name_in_srdf=$robot_name_in_srdf\\\n"
443 +
" --moveit_config_pkg=$moveit_config_pkg\\\n"
445 +
" $planning_group_name\\\n"
446 +
" $ikfast_plugin_pkg\\\n"
447 +
" $base_link_name\\\n"
448 +
" $eef_link_name\\\n"
449 +
" $ikfast_output_path\n"
452 print(
"Created update plugin script at '%s'" % easy_script_file_path)
458 except PackageNotFoundError:
459 raise Exception(
"Failed to find package: " + args.moveit_config_pkg)
462 srdf_file_name = moveit_config_pkg_path +
"/config/" + args.srdf_filename
463 srdf = etree.parse(srdf_file_name).getroot()
465 raise Exception(
"Failed to find SRDF file: " + srdf_file_name)
466 except etree.XMLSyntaxError
as err:
468 "Failed to parse xml in file: %s\n%s" % (srdf_file_name, err.msg)
471 if args.robot_name_in_srdf != srdf.get(
"name"):
473 "Robot name in srdf ('%s') doesn't match expected name ('%s')"
474 % (srdf.get(
"name"), args.robot_name_in_srdf)
477 groups = srdf.findall(
"group")
479 raise Exception(
"No planning groups are defined in the SRDF")
481 planning_group =
None
483 if group.get(
"name").lower() == args.planning_group_name.lower():
484 planning_group = group
486 if planning_group
is None:
488 "Planning group '%s' not defined in the SRDF. Available groups: \n%s"
490 args.planning_group_name,
491 ", ".join([group_name.get(
"name")
for group_name
in groups]),
496 kin_yaml_file_name = moveit_config_pkg_path +
"/config/kinematics.yaml"
497 with open(kin_yaml_file_name,
"r")
as f:
498 kin_yaml_data = yaml.safe_load(f)
500 kin_yaml_data[args.planning_group_name][
"kinematics_solver"] = args.plugin_name
501 with open(kin_yaml_file_name,
"w")
as f:
502 yaml.dump(kin_yaml_data, f, default_flow_style=
False)
504 print(
"Modified kinematics.yaml at '%s'" % kin_yaml_file_name)
507 def copy_file(src_path, dest_path, description, replacements=None):
508 if not os.path.exists(src_path):
509 raise Exception(
"Can't find %s at '%s'" % (description, src_path))
511 if replacements
is None:
512 replacements = dict()
514 with open(src_path,
"r")
as f:
518 for key, value
in replacements.items():
519 content = re.sub(key, value, content)
521 with open(dest_path,
"w")
as f:
523 print(
"Created %s at '%s'" % (description, dest_path))
528 args = parser.parse_args()
537 except Exception
as e:
538 print(
"Failed to update MoveIt package:\n" + str(e))
541 if __name__ ==
"__main__":
def get_package_share_directory(pkg_name)
def create_parameter_dict()
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)