Skip to content

Commit 65c7b22

Browse files
authored
Add convert-wheel-collision feature to automatically change wheel links to have cylinder collision shapes (#581)
1 parent 74a199a commit 65c7b22

File tree

4 files changed

+481
-0
lines changed

4 files changed

+481
-0
lines changed

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ def remove_from_requirements(install_requires, remove_req):
8787
console_scripts.append("change-urdf-root=skrobot.apps.change_urdf_root:main")
8888
console_scripts.append("visualize-mesh=skrobot.apps.visualize_mesh:main")
8989
console_scripts.append("urdf-hash=skrobot.apps.urdf_hash:main")
90+
console_scripts.append("convert-wheel-collision=skrobot.apps.convert_wheel_collision:main")
9091

9192

9293
setup(
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python
2+
"""
3+
Convert continuous joint child link collision meshes to cylinders.
4+
5+
This tool identifies continuous joints in a URDF file and converts
6+
their child links' collision meshes to cylinder primitives. This is
7+
particularly useful for wheel representations where cylinder collisions
8+
are more efficient for physics simulations.
9+
"""
10+
11+
import argparse
12+
from pathlib import Path
13+
import sys
14+
15+
from skrobot.urdf import convert_wheel_collisions_to_cylinders
16+
17+
18+
def main():
19+
parser = argparse.ArgumentParser(
20+
description='Convert continuous joint child link collision meshes to cylinders',
21+
formatter_class=argparse.RawDescriptionHelpFormatter,
22+
epilog="""
23+
Examples:
24+
# Convert wheel collisions in a URDF file
25+
convert-wheel-collision robot.urdf
26+
27+
# Specify output file
28+
convert-wheel-collision robot.urdf -o robot_cylinder.urdf
29+
30+
# Modify the input file in place
31+
convert-wheel-collision robot.urdf --inplace
32+
33+
# Process with verbose output
34+
convert-wheel-collision robot.urdf -v
35+
"""
36+
)
37+
38+
parser.add_argument(
39+
'urdf_file',
40+
type=str,
41+
help='Path to the input URDF file'
42+
)
43+
44+
parser.add_argument(
45+
'-o', '--output',
46+
type=str,
47+
default=None,
48+
help='Path to the output URDF file (default: input_cylinder_collision.urdf)'
49+
)
50+
51+
parser.add_argument(
52+
'--inplace',
53+
action='store_true',
54+
help='Modify the input URDF file in place'
55+
)
56+
57+
parser.add_argument(
58+
'-v', '--verbose',
59+
action='store_true',
60+
help='Enable verbose output'
61+
)
62+
63+
parser.add_argument(
64+
'--force',
65+
action='store_true',
66+
help='Overwrite output file if it exists'
67+
)
68+
69+
args = parser.parse_args()
70+
71+
# Configure logging
72+
import logging
73+
if args.verbose:
74+
logging.basicConfig(level=logging.INFO, format='%(message)s')
75+
else:
76+
logging.basicConfig(level=logging.WARNING, format='%(message)s')
77+
78+
# Check input file
79+
input_path = Path(args.urdf_file)
80+
if not input_path.exists():
81+
print(f"Error: Input file '{args.urdf_file}' does not exist", file=sys.stderr)
82+
return 1
83+
84+
# Check for conflicting options
85+
if args.output and args.inplace:
86+
print("Error: Cannot use both --output and --inplace options", file=sys.stderr)
87+
return 1
88+
89+
# Determine output file
90+
if args.inplace:
91+
output_path = None # Will modify in place
92+
elif args.output:
93+
output_path = Path(args.output)
94+
# Check if output exists
95+
if output_path.exists() and not args.force:
96+
print(f"Error: Output file '{output_path}' already exists. Use --force to overwrite.", file=sys.stderr)
97+
return 1
98+
else:
99+
output_path = input_path.parent / f"{input_path.stem}_cylinder_collision.urdf"
100+
# Check if output exists
101+
if output_path.exists() and not args.force:
102+
print(f"Error: Output file '{output_path}' already exists. Use --force to overwrite.", file=sys.stderr)
103+
return 1
104+
105+
try:
106+
# Load and convert URDF
107+
print(f"Loading URDF from: {input_path}")
108+
print("Converting wheel collisions to cylinders...")
109+
110+
# Call the conversion function
111+
modified_links = convert_wheel_collisions_to_cylinders(
112+
str(input_path),
113+
str(output_path) if output_path else None
114+
)
115+
116+
if modified_links:
117+
print(f"Modified {len(modified_links)} links:")
118+
for link_name in modified_links:
119+
print(f" - {link_name}")
120+
else:
121+
print("No continuous joint child links found to convert")
122+
123+
if args.inplace:
124+
print(f"Modified URDF saved in place: {input_path}")
125+
elif output_path:
126+
print(f"Modified URDF saved to: {output_path}")
127+
128+
print("Conversion completed successfully!")
129+
return 0
130+
131+
except Exception as e:
132+
print(f"Error: {e}", file=sys.stderr)
133+
if args.verbose:
134+
import traceback
135+
traceback.print_exc()
136+
return 1
137+
138+
139+
if __name__ == '__main__':
140+
sys.exit(main())

skrobot/urdf/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from .aggregate import aggregate_urdf_mesh_files
22
from .modularize_urdf import find_root_link
33
from .modularize_urdf import transform_urdf_to_macro
4+
from .wheel_collision_converter import convert_wheel_collisions_to_cylinders
5+
from .wheel_collision_converter import get_mesh_dimensions
46
from .xml_root_link_changer import change_urdf_root_link
57
from .xml_root_link_changer import URDFXMLRootLinkChanger
68

@@ -11,4 +13,6 @@
1113
'find_root_link',
1214
'transform_urdf_to_macro',
1315
'aggregate_urdf_mesh_files',
16+
'convert_wheel_collisions_to_cylinders',
17+
'get_mesh_dimensions',
1418
]

0 commit comments

Comments
 (0)