# Copyright (c) 2024 Franka Robotics GmbH # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import os import xacro def str_to_bool(s): return s.lower() in ["true", "1", "yes", "y"] def convert_xacro_to_urdf(xacro_file, only_ee, with_sc, ee_id, hand, no_prefix, robot): """Convert xacro file into a URDF file.""" mappings = { "with_sc": str(with_sc), "ee_id": str(ee_id), "hand": str(hand), "no_prefix": str(no_prefix), "arm_id": str(robot), } if only_ee and robot == "": mappings["connection"] = "" urdf = xacro.process_file(xacro_file, mappings=mappings) urdf_file = urdf.toprettyxml(indent=" ") return urdf_file def convert_package_name_to_absolute_path(package_name, package_path, urdf_file): """Replace a ROS package names with the absolute paths.""" urdf_file = urdf_file.replace("package://{}".format(package_name), package_path) return urdf_file def urdf_generation( package_path, xacro_file, file_name, ONLY_EE, WITH_SC, EE, HAND, NO_PREFIX, robot ): """Generate URDF file and save it.""" xacro_file = os.path.join(package_path, xacro_file) urdf_file = convert_xacro_to_urdf(xacro_file, ONLY_EE, WITH_SC, EE, HAND, NO_PREFIX, robot) if ABSOLUTE_PATHS and (HOST_DIR is None or HOST_DIR == ""): urdf_file = convert_package_name_to_absolute_path( package_name, package_path, urdf_file ) elif ABSOLUTE_PATHS: urdf_file = convert_package_name_to_absolute_path( package_name, HOST_DIR, urdf_file ) save_urdf_to_file(package_path, urdf_file, file_name) def save_urdf_to_file(package_path, urdf_file, robot): """Save URDF into a file.""" # Check if the folder exists, and if not, create it folder_path = f"{package_path}/urdfs" if not os.path.exists(folder_path): os.makedirs(folder_path) with open(f"{package_path}/urdfs/{robot}.urdf", "w") as f: f.write(urdf_file) if __name__ == "__main__": package_name = "franka_description" if os.getcwd().split("/")[-1] != package_name: print("Call the script from franka_description root folder") exit() ROBOTS = ["moonshot_panda", "multi_arm", "fr3", "fp3", "fer"] END_EFFECTORS = ["none", "franka_hand", "cobot_pump"] parser = argparse.ArgumentParser( description=""" Generate franka robots urdf models. Script to be executed from franka_description root folder! """ ) robots_str = ", ".join([f"{item}" for item in ROBOTS]) parser.add_argument( "robot_model", type=str, nargs='?', default="", help="id of the robot model (accepted values are: {})".format(robots_str), ) parser.add_argument( "--no-ee", help="Disable loading of end-effector.", action="store_const", const=True, ) ee_str = ", ".join([f"{item}" for item in END_EFFECTORS]) parser.add_argument( "--robot-ee", type=str, default="franka_hand", help="id of the robot end effector (accepted values are: {})".format(ee_str), ) parser.add_argument( "--with-sc", help="Include self-collision volumes in the urdf model.", action="store_const", const=True, ) parser.add_argument( "--abs-path", help="Use absolute paths.", action="store_const", const=True ) parser.add_argument( "--host-dir", help="Provide a host directory for the absolute path." ) parser.add_argument( "--only-ee", help="Get urdf with solely end-effector data.", action="store_const", const=True, ) parser.add_argument( "--no-prefix", help="Override the robot prefix of links, joints and visuals in the urdf file.", action="store_const", const=True, ) args = parser.parse_args() ROBOT_MODEL = args.robot_model.lower() HAND = not args.no_ee EE = args.robot_ee.lower() if args.robot_ee is not None else "franka_hand" WITH_SC = args.with_sc if args.with_sc is not None else False ABSOLUTE_PATHS = args.abs_path if args.abs_path is not None else False HOST_DIR = args.host_dir ONLY_EE = args.only_ee if args.only_ee is not None else False NO_PREFIX = args.no_prefix if args.no_prefix is not None else 'false' assert ROBOT_MODEL in ROBOTS or ROBOT_MODEL == "all" or \ ROBOT_MODEL == "none" or ROBOT_MODEL == "" if ROBOT_MODEL != "all" and ROBOT_MODEL != "none": ROBOTS = [ROBOT_MODEL] package_path = os.getcwd() if ONLY_EE: print(f"\n*** Creating URDF for {EE} ***") xacro_file = f"end_effectors/{EE}/{EE}.urdf.xacro" file_name = f"{EE}" if ROBOT_MODEL == "all" or ROBOT_MODEL == "none" or ROBOT_MODEL == "": robot_prefix = "" else: robot_prefix = ROBOT_MODEL urdf_generation( package_path, xacro_file, file_name, ONLY_EE, WITH_SC, EE, HAND, NO_PREFIX, robot_prefix, ) else: if ROBOT_MODEL == "none" or ROBOT_MODEL == "": print("\n*** Robot model must be specified ***") else: for robot in ROBOTS: xacro_file = f"robots/{robot}/{robot}.urdf.xacro" if HAND and EE != "none": print(f"\n*** Creating URDF for {robot} and {EE} ***") file_name = f"{robot}_{EE}" else: if not HAND: print( "\n*** WARNING: --no-ee argument will be removed in future releases, " "introducing none as ee id***" ) print(f"\n*** Creating URDF for {robot} ***") file_name = f"{robot}" urdf_generation( package_path, xacro_file, file_name, ONLY_EE, WITH_SC, EE, HAND, NO_PREFIX, robot, )