generate_moveit_config.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # Software License Agreement (BSD License)
3 #
4 # Copyright (c) 2015, CITEC, Bielefeld University
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 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 # POSSIBILITY OF SUCH DAMAGE.
30 
31 # Author: Guillaume Walck <gwalck@techfak.uni-bielefeld.de>
32 
33 """
34 generate_moveit_config provides:
35  generate_fake_controllers : generate a fake controllers config file
36  generate_real_controllers : generate a controllers config file
37  generate_ompl_planning : generate ompl planning config file
38  generate_kinematics : generate kinematics config file
39  generate_joint_limits : generate joint limits config file
40 """
41 
42 import argparse
43 import yaml
44 import re
45 
46 import rospy
47 import rosparam
48 from srdfdom.srdf import SRDF
49 from copy import deepcopy
50 import rospkg
51 
52 from urdf_parser_py.urdf import URDF
53 
54 
55 def yaml_reindent(in_str, numspaces):
56  """
57  Add numspaces space in fron of each line of the input string
58  @param in_str: input string
59  @type in_str: str
60  @param numspaces: number of spaces to indent the string with
61  @type numspaces: int
62  @return s_intend: indented string
63  """
64  s_indent = "\n".join((numspaces * " ") + i for i in in_str.splitlines())
65  return s_indent
66 
67 
68 def find_prefix(robot):
69  """
70  Find the prefix using the always available shadow_hand group name
71  @param robot: parsed SRDF
72  @type robot: SRDF object
73  @return prefix: prefix in a string
74  """
75  prefix = ""
76  for key in robot.group_map:
77  if key.endswith("fingers"):
78  prefix = key[0:key.find("fingers")]
79  return prefix
80 
81 
82 def upload_output_params(upload_str, output_path=None, upload=True, ns_=None):
83  """
84  Upload or output the input string on the correct param ns or file
85  @param upload_str: string to be uploaded or written
86  @type upload_str: str
87  @param output_path: output path of file to be written.
88  Upload if None
89  @type output_path: str
90  @param ns_: namespace to use when uploading to param server
91  @type ns_: str
92  """
93  if upload:
94  paramlist = rosparam.load_str(upload_str, "generated",
95  default_namespace=ns_)
96  for params, namespace in paramlist:
97  rosparam.upload_params(namespace, params)
98  if output_path is not None:
99  file_writer = open(output_path, "wb")
100  file_writer.write(upload_str)
101  file_writer.close()
102 
103 
104 def generate_fake_controllers(robot, output_path=None, ns_=None):
105  """
106  Generate fake_controller yaml and direct it to file
107  or load it to parameter server.
108  @param robot: Parsed SRDF
109  @type robot: SRDF object
110  @param output_path: file_path to save the generated data in,
111  will load on parameter server if empty
112  @type output_path: str
113  @param ns_: namespace
114  @type ns_: str
115  """
116  output_str = ""
117  output_str += "controller_list:\n"
118  # for each group
119  for group in robot.groups:
120  controller_name = " - name: fake_" + group.name + "_controller\n"
121  output_str += controller_name
122  output_str += " joints:\n"
123  if len(group.joints) == 0:
124  output_str += " []\n"
125  else:
126  for joint in group.joints:
127  output_str += " - " + joint.name + "\n"
128  # load on param server or output to file
129  upload_output_params(output_str, output_path, ns_)
130 
131 
132 def generate_real_controllers(robot, output_path=None, ns_=None):
133  """
134  Generate controller yaml and direct it to file
135  or load it to parameter server.
136  @param robot: Parsed SRDF
137  @type robot: SRDF object
138  @param output_path: file_path to save the generated data in,
139  will load on parameter server if empty
140  @type output_path: str
141  @param ns_: namespace
142  @type ns_: str
143  """
144  output_str = ""
145  output_str += "controller_list:\n"
146  prefix = find_prefix(robot)
147 
148  # find full hand key name
149  sh_group = None
150  for group in robot.groups:
151  if group.name.endswith("_hand"):
152  sh_group = group
153  break
154 
155  controller_name = " - name: " + prefix + "trajectory_controller\n"
156  output_str += controller_name
157  output_str += " action_ns: follow_joint_trajectory\n"
158  output_str += " type: FollowJointTrajectory\n"
159  output_str += " default: true\n"
160  output_str += " joints:\n"
161  if len(group.joints) == 0:
162  output_str += " []\n"
163  else:
164  for joint in group.joints:
165  if joint.name[-3:] != "tip":
166  output_str += " - " + joint.name + "\n"
167  # load on param server or output to file
168  upload_output_params(output_str, output_path, ns_)
169  return output_str
170 
171 
172 def generate_ompl_planning(robot,
173  template_path="ompl_planning_template.yaml",
174  output_path=None, ns_=None):
175  '''
176  Generate ompl_planning yaml and direct it to file
177  or load it to parameter server.
178  @param robot: Parsed SRDF
179  @type robot: SRDF object
180  @param template_path: file_path to the req. yaml template
181  @type template_path: str
182  @param output_path: file_path to save the generated data in,
183  will load on parameter server if empty
184  @type output_path: str
185  @param ns_: namespace
186  @type ns_: str
187  '''
188  output_str = ""
189 
190  stream = open(template_path, 'r')
191  yamldoc = yaml.load(stream)
192  output_str += "planner_configs:\n"
193  output_str += yaml_reindent(yaml.dump(
194  yamldoc["planner_configs"],
195  default_flow_style=False,
196  allow_unicode=True),
197  2)
198  output_str += "\n"
199  # find prefix
200  prefix = find_prefix(robot)
201  if prefix:
202  proj_eval_re = re.compile(r'joints\(([TFMRLW][FHR]J[0-5]),([TFMRLW][FHR]J[0-5])\)')
203  # for each group
204  for group in robot.groups:
205  # strip prefix if any
206  group_name = group.name
207  if re.match("^"+str(prefix), group.name) is not None:
208  group_name = group.name[len(prefix):]
209  elif group.name in ["left_hand", "right_hand"]:
210  group_name = "hand"
211 
212  if group_name in yamldoc:
213  output_str += group.name + ":\n"
214  group_config = yamldoc[group_name]
215  # prepend prefix on projection_evaluator
216  if prefix:
217  if "projection_evaluator" in group_config:
218  proj_eval = group_config["projection_evaluator"]
219  proj_eval.strip()
220  proj_eval_new = proj_eval_re.sub(r'joints(' +
221  prefix +
222  r'\g<1>,' +
223  prefix +
224  r'\g<2>)',
225  proj_eval)
226  group_config["projection_evaluator"] = proj_eval_new
227  group_dump = yaml.dump(group_config,
228  default_flow_style=False,
229  allow_unicode=True)
230  output_str += yaml_reindent(group_dump, 2)
231  output_str += "\n"
232  stream.close()
233  # load on param server or output to file
234  upload_output_params(output_str, output_path, ns_)
235 
236 
237 def generate_kinematics(robot, template_path="kinematics_template.yaml",
238  output_path=None, ns_=None):
239  """
240  Generate kinematics yaml and direct it to file
241  or load it to parameter server.
242  @param srdf: Parsed SRDF
243  @type srdf: XML object
244  @param template_path: file_path to the req. yaml template
245  (biotac version will be loaded automatically)
246  @type template_path: str
247  @param output_path: file_path to save the generated data in,
248  will load on parameter server if empty
249  @type output_path: str
250  @param ns_: namespace
251  @type ns_: str
252  """
253  output_str = ""
254  group_name = None
255 
256  while not rospy.has_param('/robot_description'):
257  rospy.sleep(0.5)
258  rospy.loginfo("waiting for robot_description")
259  urdf_str = rospy.get_param('/robot_description')
260  robot_urdf = URDF.from_xml_string(urdf_str)
261 
262  # open template file
263  stream = open(template_path, 'r')
264  yamldoc = yaml.load(stream)
265  stream.close()
266 
267  if 'kinematics_template' in template_path:
268  default_solver_for_fixed_joint = "trac_ik"
269  fixed_joint_template_path = rospkg.RosPack().get_path(
270  'sr_moveit_hand_config') + "/config/kinematics_" + default_solver_for_fixed_joint + "_template.yaml"
271 
272  with open(fixed_joint_template_path, 'r') as stream:
273  yamldoc_fixed_joint = yaml.load(stream)
274  else:
275  yamldoc_fixed_joint = deepcopy(yamldoc)
276 
277  # find prefix
278  prefix = find_prefix(robot)
279  finger_prefixes = ["FF", "MF", "RF", "LF", "TH"]
280 
281  # find full hand key name
282  sh_group = None
283  for group in robot.groups:
284  if group.name.endswith("_hand"):
285  sh_group = group
286  break
287 
288  # detect biotac fingers. I think this is not needed any more as all links are called the same even for biotac hand
289  is_fixed = {"first_finger": False,
290  "middle_finger": False,
291  "ring_finger": False,
292  "little_finger": False,
293  "thumb": False}
294 
295  # Find in any finger has a fix joint apart from the tip as it needs to use a different kinematics
296  finger_with_fixed_joint = [False, False, False, False, False]
297  for joint in robot_urdf.joints:
298  joint_name = joint.name[len(prefix):]
299  for index, finger_prefix in enumerate(finger_prefixes):
300  if joint_name[0:2].upper() == finger_prefix and joint_name[-3:] != "tip" and joint.type == "fixed":
301  finger_with_fixed_joint[index] = True
302 
303  is_fixed['first_finger'] = finger_with_fixed_joint[0]
304  is_fixed['middle_finger'] = finger_with_fixed_joint[1]
305  is_fixed['ring_finger'] = finger_with_fixed_joint[2]
306  is_fixed['little_finger'] = finger_with_fixed_joint[3]
307  is_fixed['thumb'] = finger_with_fixed_joint[4]
308 
309  # for each group
310  for group in robot.groups:
311  kinematics_config = None
312  # strip prefix if any
313  group_name = group.name[len(prefix):]
314  # check for fixed joint for this group
315  if is_fixed.get(group_name):
316  if group_name in yamldoc_fixed_joint:
317  kinematics_config = yamldoc_fixed_joint[group_name]
318  else:
319  if group_name in yamldoc:
320  kinematics_config = yamldoc[group_name]
321 
322  if kinematics_config is not None:
323  if prefix:
324  if "tip_name" in kinematics_config:
325  tip_name = kinematics_config["tip_name"]
326  kinematics_config["tip_name"] = prefix + tip_name
327  if "root_name" in kinematics_config:
328  root_name = kinematics_config["root_name"]
329  kinematics_config["root_name"] = prefix + root_name
330 
331  output_str += group.name + ":\n"
332  output_str += yaml_reindent(yaml.dump(kinematics_config,
333  default_flow_style=False,
334  allow_unicode=True), 2)
335  output_str += "\n"
336  # load on param server or output to file
337  upload_output_params(output_str, output_path, ns_)
338 
339 
340 def generate_joint_limits(robot,
341  template_path="joint_limits_template.yaml",
342  output_path=None, ns_=None):
343  """
344  Generate joint_limits yaml and direct it to file
345  or load it to parameter server.
346  @param robot: Parsed SRDF
347  @type robot: SRDF object
348  @param template_path: file_path to the required yaml template file
349  @type template_path: str
350  @param output_path: file_path to save the generated data in,
351  will load on parameter server if empty
352  @type output_path: str
353  @param ns_: namespace
354  @type ns_: str
355  """
356  output_str = ""
357  stream = open(template_path, 'r')
358  yamldoc = yaml.load(stream)
359  output_str += "joint_limits:\n"
360  group_name = None
361  # find full hand key name
362  for key in robot.group_map:
363  if key.endswith("_hand"):
364  group_name = key
365 
366  if group_name is not None:
367  # for each joint in full hand group
368  for joint in robot.group_map[group_name].joints:
369  joint_name = joint.name[-4:]
370  if joint_name in yamldoc["joint_limits"]:
371  joint_limits_config = yamldoc["joint_limits"][joint_name]
372  output_str += " " + joint.name + ":\n"
373  joint_limits_dump = yaml.dump(
374  joint_limits_config,
375  default_flow_style=False,
376  allow_unicode=True)
377  output_str += yaml_reindent(joint_limits_dump, 4)
378  output_str += "\n"
379  stream.close()
380  # load on param server or output to file
381  upload_output_params(output_str, output_path, ns_)
382 
383 
384 if __name__ == '__main__':
385 
386  PARSER = argparse.ArgumentParser(usage='Load an SRDF file')
387  PARSER.add_argument('file', type=argparse.FileType('r'), nargs='?',
388  default=None,
389  help='File to load. Use - for stdin')
390  ARGS = PARSER.parse_args()
391 
392  if ARGS.file is not None:
393 
394  ROBOT = SRDF.from_xml_string(ARGS.file.read())
396  output_path="fake_controllers.yaml")
398  output_path="controllers.yaml")
400  "ompl_planning_template.yaml",
401  output_path="ompl_planning.yaml")
402  generate_kinematics(ROBOT,
403  "kinematics_template.yaml",
404  output_path="kinematics.yaml")
405  generate_joint_limits(ROBOT,
406  "joint_limits_template.yaml",
407  output_path="joint_limits.yaml")
408  else:
409  rospy.logerr("No file SRDF provided")
def generate_real_controllers(robot, output_path=None, ns_=None)
def generate_ompl_planning(robot, template_path="ompl_planning_template.yaml", output_path=None, ns_=None)
def generate_kinematics(robot, template_path="kinematics_template.yaml", output_path=None, ns_=None)
def generate_fake_controllers(robot, output_path=None, ns_=None)
def generate_joint_limits(robot, template_path="joint_limits_template.yaml", output_path=None, ns_=None)
def upload_output_params(upload_str, output_path=None, upload=True, ns_=None)


sr_moveit_hand_config
Author(s): MoveIt Setup Assistant , Guillaume Walck
autogenerated on Wed Oct 14 2020 04:05:17