Skip to content

Commit b6c5ee5

Browse files
authored
Merge pull request #95 from AGH-CEAI/feature/generate_standalone_urdf
`aegis_description` - Launch script to generate stadalone URDF model
2 parents 6808b54 + 876701d commit b6c5ee5

File tree

3 files changed

+102
-14
lines changed

3 files changed

+102
-14
lines changed

aegis_description/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
* [PR-95](https://github.com/AGH-CEAI/aegis_ros/pull/95) - Added standalone launch script to extract whole URDF model to a given directory.
1213
* [PR-78](https://github.com/AGH-CEAI/aegis_ros/pull/78) - Added logic to allow selective inclusion of cell visual and collision geometry.
1314

1415
### Changed

aegis_description/README.md

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ aegis_description/
3737
│ └── initial_positions.yaml
3838
├── launch
3939
│ ├── aegis_preview.launch.py
40+
│ ├── generate_standalone_urdf.launch.py
4041
│ └── robot_description.launch.py
4142
├── urdf
4243
│ ├── aegis.urdf.xacro
@@ -49,20 +50,21 @@ Legend:
4950
* 📜 - URDF files
5051
* 🚀 - Launch files
5152

52-
| File | Used in | Description |
53-
| ------------------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------- |
54-
| [config/ur5e/calibration.yaml](./config/ur5e/calibration.yaml) | 📜🚀 | Calibration parameters extracted from the UR5e robot. |
55-
| [config/ur5e/joint_limits.yaml](./config/ur5e/joint_limits.yaml) | 📜🚀 | Joint limits used by both `ur_robot_driver` and `aegis_moveit_config`. |
56-
| [config/ur5e/physical_parameters.yaml](./config/ur5e/physical_parameters.yaml) | 📜 | Reference to masses and intertias taken from the official `ur_robot_driver`. |
57-
| [config/ur5e/update_rate.yaml](./config/ur5e/update_rate.yaml) | 🚀 | Configuration for the `controller_manager` node from the `ur_robot_driver`. |
58-
| [config/ur5e/visual_parameters.yaml](./config/ur5e/visual_parameters.yaml) | 📜 | Paths to the UR5e meshes in the `ur_description` package. |
59-
| [config/controllers.yaml](./config/controllers.yaml) | 🚀 | Configuration for the `ros2_control` controllers, combining outputs from `ur_robot_driver` and MoveIt2 Setup Assistant. |
60-
| [config/initial_positions.yaml](./config/initial_positions.yaml) | 📜 | Default joints positions. |
61-
| [launch/aegis_preview.launch.py](./launch/aegis_preview.launch.py) | | Simple launch file to preview in RViz the Aegis model. |
62-
| [launch/robot_description.launch.py](./launch/robot_description.launch.py) | | Launch file to run the robot description node with the Aegis URDF model. |
63-
| [rviz/preview.rviz](./rviz/preview.rviz) | 🚀 | Default RViz configuration for the `aegis_display.launch.py` |
64-
| [urdf/aegis.urdf.xacro](./urdf/aegis.urdf.xacro) | 🚀 | The main URDF file, which defines the initial link for the `aegis.xacro` file. |
65-
| [urdf/aegis.xacro](./urdf/aegis.xacro) | 📜 | The main xacro file, which imports all modules from the `urdf/modules/` dir. |
53+
| File | Used in | Description |
54+
| ---------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- |
55+
| [config/ur5e/calibration.yaml](./config/ur5e/calibration.yaml) | 📜🚀 | Calibration parameters extracted from the UR5e robot. |
56+
| [config/ur5e/joint_limits.yaml](./config/ur5e/joint_limits.yaml) | 📜🚀 | Joint limits used by both `ur_robot_driver` and `aegis_moveit_config`. |
57+
| [config/ur5e/physical_parameters.yaml](./config/ur5e/physical_parameters.yaml) | 📜 | Reference to masses and intertias taken from the official `ur_robot_driver`. |
58+
| [config/ur5e/update_rate.yaml](./config/ur5e/update_rate.yaml) | 🚀 | Configuration for the `controller_manager` node from the `ur_robot_driver`. |
59+
| [config/ur5e/visual_parameters.yaml](./config/ur5e/visual_parameters.yaml) | 📜 | Paths to the UR5e meshes in the `ur_description` package. |
60+
| [config/controllers.yaml](./config/controllers.yaml) | 🚀 | Configuration for the `ros2_control` controllers, combining outputs from `ur_robot_driver` and MoveIt2 Setup Assistant. |
61+
| [config/initial_positions.yaml](./config/initial_positions.yaml) | 📜 | Default joints positions. |
62+
| [launch/aegis_preview.launch.py](./launch/aegis_preview.launch.py) | | Simple launch file to preview in RViz the Aegis model. |
63+
| [launch/generate_standalone_urdf.launch.py](./launch/generate_standalone_urdf.launch.py) | | Script to generate standalone URDF to a given directory with copy of all 3D models. |
64+
| [launch/robot_description.launch.py](./launch/robot_description.launch.py) | | Launch file to run the robot description node with the Aegis URDF model. |
65+
| [rviz/preview.rviz](./rviz/preview.rviz) | 🚀 | Default RViz configuration for the `aegis_display.launch.py` |
66+
| [urdf/aegis.urdf.xacro](./urdf/aegis.urdf.xacro) | 🚀 | The main URDF file, which defines the initial link for the `aegis.xacro` file. |
67+
| [urdf/aegis.xacro](./urdf/aegis.xacro) | 📜 | The main xacro file, which imports all modules from the `urdf/modules/` dir. |
6668

6769

6870

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import re
2+
import shutil
3+
from pathlib import Path
4+
5+
from ament_index_python.packages import get_package_share_directory
6+
7+
from launch import LaunchDescription
8+
from launch.actions import DeclareLaunchArgument, OpaqueFunction
9+
from launch.substitutions import Command, LaunchConfiguration, PathJoinSubstitution
10+
from launch_ros.substitutions import FindPackageShare
11+
from launch.substitutions import FindExecutable
12+
13+
14+
def generate_launch_description() -> LaunchDescription:
15+
return LaunchDescription(
16+
[
17+
DeclareLaunchArgument("save_dir", default_value="~/ceai_ws/aegis_urdf"),
18+
DeclareLaunchArgument("tf_prefix", default_value=""),
19+
DeclareLaunchArgument("mock_hardware", default_value="false"),
20+
DeclareLaunchArgument("disable_cell", default_value="false"),
21+
DeclareLaunchArgument("disable_cell_collision", default_value="false"),
22+
OpaqueFunction(function=evaluate_robot_description),
23+
]
24+
)
25+
26+
27+
def evaluate_robot_description(context, *args, **kwargs) -> list:
28+
save_dir = (
29+
Path(LaunchConfiguration("save_dir").perform(context)).expanduser().resolve()
30+
)
31+
save_dir.mkdir(exist_ok=True)
32+
urdf_path = save_dir / "aegis.urdf"
33+
34+
tf_prefix = LaunchConfiguration("tf_prefix")
35+
mock_hardware = LaunchConfiguration("mock_hardware")
36+
disable_cell = LaunchConfiguration("disable_cell")
37+
disable_cell_collision = LaunchConfiguration("disable_cell_collision")
38+
39+
robot_description_str = Command(
40+
[
41+
PathJoinSubstitution([FindExecutable(name="xacro")]),
42+
" ",
43+
PathJoinSubstitution(
44+
[FindPackageShare("aegis_description"), "urdf", "aegis.urdf.xacro"]
45+
),
46+
" ",
47+
"tf_prefix:=",
48+
tf_prefix,
49+
" ",
50+
"mock_hardware:=",
51+
mock_hardware,
52+
" ",
53+
"disable_cell:=",
54+
disable_cell,
55+
" ",
56+
"disable_cell_collision:=",
57+
disable_cell_collision,
58+
]
59+
).perform(context)
60+
urdf_str, files_paths = extract_files_paths(robot_description_str)
61+
print(f"> Found {len(files_paths)} 3D models to be copied")
62+
63+
for file in files_paths:
64+
shutil.copy(src=file, dst=save_dir / file.name)
65+
print(f"> Copied all 3D model files files to '{save_dir}'")
66+
67+
urdf_path.write_bytes(urdf_str.encode("utf-8"))
68+
print(f"> URDF generated to '{urdf_path}'")
69+
70+
return []
71+
72+
73+
def extract_files_paths(urdf_str: str) -> tuple[str, set[Path]]:
74+
# Regex to get: 1) package name 2) rel. 3D model path 3) 3D model filename
75+
pattern = r"filename=\"package://(\w+)/?(.+/)?([^/]+\.(?:stl|dae|obj|3ds))\""
76+
matches = re.findall(pattern, urdf_str)
77+
78+
files_paths = set()
79+
for pkg_name, rel_model_path, filename in matches:
80+
package_path = Path(get_package_share_directory(pkg_name))
81+
file_path = package_path / rel_model_path / filename
82+
83+
files_paths.add(file_path)
84+
urdf_str = urdf_str.replace(f"package://{pkg_name}/{rel_model_path}", "./")
85+
return urdf_str, files_paths

0 commit comments

Comments
 (0)