urdf_to_xacro.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 # Author: Yuki Furuta <furushchev@jsk.imi.i.u-tokyo.ac.jp>
4 
5 
6 import lxml.etree
7 import lxml.builder
8 import os
9 import rospkg
10 import subprocess
11 import sys
12 
13 class InvalidURDFException(Exception):
14  pass
15 
16 def printf(*args):
17  print "[urdf_to_xacro.py] " + ' '.join(args)
18 
20  def __init__(self):
21  self.rospack = rospkg.RosPack()
22  self.paths = []
23  self.rospkgs = {}
24  self.cached_path = {}
25  self.load_path_from_env()
27 
28  def add_path(self, path):
29  self.paths.insert(0, path)
30 
31  def add_ros_pkg(self, pkg, path):
32  if pkg in self.rospkgs:
33  self.rospkgs[pkg].insert(0, path)
34  else:
35  self.rospkgs[pkg] = [path]
36 
37  def load_path_from_env(self):
38  env_str = os.environ.get("GAZEBO_MODEL_PATH", None)
39  if env_str is None:
40  return
41  for path in env_str.split(':'):
42  if os.path.isdir(path):
43  self.add_path(path)
44 
46  cmd = "rospack plugins --attrib=gazebo_model_path gazebo_ros"
47  lines = subprocess.check_output(cmd, shell=True)
48  if lines:
49  for line in lines.split(os.linesep):
50  if not line:
51  continue
52  pkg, path = line.split()
53  if os.path.isdir(path):
54  self.add_path(path)
55  if pkg not in self.rospkgs:
56  self.rospkgs[pkg] = self.rospack.get_path(pkg)
57 
58  def parse_model_path(self, path):
59  key = None
60  if path.startswith("model://"):
61  spath = path[len("model://"):].split('/')
62  key = spath[0]
63  path = '/'.join(spath[1:])
64  return key, path
65 
66  def search_path(self, key):
67  for base_path in self.paths:
68  path = os.path.join(base_path, key)
69  if os.path.isdir(path):
70  self.cached_path[key] = path
71 
72  def pack_ros_path(self, path):
73  for pkg, pkg_path in self.rospkgs.items():
74  if path.startswith(pkg_path):
75  return "package://" + pkg + path[len(pkg_path):]
76  return path
77 
78  def resolve_path(self, path):
79  key, path = self.parse_model_path(path)
80  if not key:
81  return path
82  if key not in self.cached_path:
83  self.search_path(key)
84  if key in self.cached_path:
85  path = os.path.join(self.cached_path[key], path)
86  else:
87  raise Exception("path %s is not found" % path)
88  return self.pack_ros_path(path)
89 
91 
93  return _gazebo.resolve_path(path)
94 
96  def __init__(self, urdf_path, xacro_path, force_rename=None):
97  printf("loading", urdf_path)
98  self.root = lxml.etree.parse(urdf_path,
99  parser=lxml.etree.XMLParser(remove_blank_text=True))
100  self.xacro_path = xacro_path
101  self.force_rename = None
102 
103  def find_root_link(self):
104  links = self.root.xpath("//robot/link")
105  if len(links) == 0:
106  raise InvalidURDFException("No link found in model")
107  elif len(links) == 1:
108  return links[0]
109  else:
110  link_names = [l.get("name") for l in links]
111  joints = self.root.xpath("//robot/joint")
112  for j in joints:
113  child_link_name = j.find("child").get("link")
114  if child_link_name in link_names:
115  link_names.remove(child_link_name)
116  if len(link_names) != 1:
117  raise InvalidURDFException("Links are not connected with joints: %s" % link_names)
118  return self.root.xpath("//robot/link[@name='%s']" % link_names[0])[0]
119 
120  def add_namespace(self, ns):
121  ns_str = "${%s}_" % ns
122 
123  links = self.root.xpath("//robot/link")
124  for l in links:
125  l.set("name", ns_str + l.get("name"))
126 
127  gazebo = self.root.xpath("//robot/gazebo")
128  for g in gazebo:
129  if g.get("reference") is not None:
130  g.set("reference", ns_str + g.get("reference"))
131 
132  joints = self.root.xpath("//robot/joint")
133  for j in joints:
134  j.set("name", ns_str + j.get("name"))
135  j_parent = j.find("parent")
136  j_parent.set("link", ns_str + j_parent.get("link"))
137  j_child = j.find("child")
138  j_child.set("link", ns_str + j_child.get("link"))
139 
140  transmissions = self.root.xpath("//robot/transmission")
141  for t in transmissions:
142  t.set("name", ns_str + t.get("name"))
143  t_joint = t.find("joint")
144  t_joint.set("name", ns_str + t_joint.get("name"))
145  t_actuator = t.find("actuator")
146  t_actuator.set("name", ns_str + t_actuator.get("name"))
147 
149  try:
150  for param in ["visual", "collision", "inertial"]:
151  for m in self.root.xpath("//robot/link/%s/geometry/mesh" % param):
152  if "filename" in m.attrib:
153  modelpath = m.attrib["filename"]
154  if self.force_rename and modelpath.startswith("model://"):
155  pkgpath = self.force_rename + modelpath[len("model://"):]
156  else:
157  pkgpath = resolve_gazebo_model_path(modelpath)
158  printf(modelpath, "->", pkgpath)
159  m.attrib["filename"] = pkgpath
160  except:
161  pass
162 
163  def inject_macro(self):
164  self.add_namespace("name")
165  root_link = self.find_root_link()
166  robot = self.root.getroot()
167  robot_name = robot.get("name")
168  first_joint = robot.find("joint")
169  virtual_joint = lxml.etree.Element("joint",
170  attrib={"name": "${name}_root_parent_joint", "type": "fixed"})
171  virtual_joint.append(lxml.etree.Element("parent", attrib={"link": "${parent}"}))
172  virtual_joint.append(lxml.etree.Element("child", attrib={"link": root_link.get("name")}))
173  virtual_joint.append(lxml.etree.Element("insert_block", attrib={"name": "origin"}))
174  if first_joint is not None:
175  first_joint.addprevious(virtual_joint)
176  else:
177  robot.append(virtual_joint)
178  macro = lxml.etree.Element("macro",
179  attrib={"name": robot_name, "params": "name parent *origin"})
180  for e in robot.getchildren():
181  macro.append(e)
182  for e in robot.getchildren():
183  robot.remove(e)
184  robot.append(macro)
185 
186  def save(self):
187  out_path = os.path.abspath(self.xacro_path)
188  if not os.path.exists(os.path.dirname(out_path)):
189  os.makedirs(os.path.dirname(out_path))
190  xmlstring = lxml.etree.tostring(self.root,
191  encoding="utf-8",
192  xml_declaration=True,
193  pretty_print=True,
194  with_comments=True)
195  with open(out_path, "w") as f:
196  f.write(xmlstring)
197  printf("saved to", out_path)
198  def convert(self):
199  self.replace_model_path()
200  self.inject_macro()
201  self.save()
202 
203 if __name__ == '__main__':
204  import argparse
205  p = argparse.ArgumentParser(description="xacrify urdf file")
206  p.add_argument("urdf", type=str, help="path to input urdf file")
207  p.add_argument("xacro", type=str, help="path to output xacro file")
208  p.add_argument("-f", "--force-rename", type=str,
209  default=None, help="force replace model:// tag with specified name")
210  args = p.parse_args()
211 
212  c = URDF2XACRO(args.urdf, args.xacro, args.force_rename)
213  c.convert()
def __init__(self, urdf_path, xacro_path, force_rename=None)
def resolve_gazebo_model_path(path)
def add_namespace(self, ns)
def printf(args)


eusurdf
Author(s): Kei Okada , Youhei Kakiuchi , Masaki Murooka
autogenerated on Mon May 4 2020 03:39:35