Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ jobs:
- "humble"
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: stereolabs/zed-ros2-description
ref: 22468247c1653d08a1e213809f3e9ce76291487d
path: .ci/zed_description
- name: Build URDF files
run: |
docker container run \
Expand All @@ -37,7 +42,7 @@ jobs:
osrf/ros:${{ matrix.id }}-desktop-full \
bash -c '
cd /source
colcon build
colcon build --paths /source/.ci/zed_description /source
source install/setup.bash

mkdir -p urdf_output
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ jobs:
- "humble"
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v7
with:
repository: stereolabs/zed-ros2-description
ref: 22468247c1653d08a1e213809f3e9ce76291487d
path: .ci/zed_description
- name: Run tests
run: |
docker container run \
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ Recommended starting point:
- for all new robot assembly, presets, xacro, and URDF generation work, start with `assets/robot/openarm_v2.0`
- only use `assets/robot/openarm_v1.0` when you need the old `v10 + parallel_link` compatibility path -->

## V2 camera assembly

The V2 `default_bimanual` preset includes both arms, pinch grippers, the camera mount, and the official ZED Mini model. Single-arm presets are also available. Use `ros2_control:=false` when exporting a model without control interfaces.

## Component interfaces

Robot entries load `assets/component_loader.xacro`. The loader locates each component using the existing `type`, `product`, and `config` fields, then optionally loads `<component directory>/urdf/component.xacro`. Components without this file use the standard YAML model generator.
Expand All @@ -33,6 +37,8 @@ The default implementations are `openarm_describe_config_component` and `openarm

Interface files declare macros; implementation and third-party model includes belong inside those macros. Each interface is loaded in a local scope, so its definitions do not affect other components. Presets need no implementation selector or product registry.

Component topology uses stable `link1`, `link2`, ... and `joint1`, `joint2`, ... keys. An optional `name` on each topology entry defines its local URDF name; otherwise the key is used. The instance prefix is added once. Geometry, inertia, joint parameters, parent/child links, mimic targets, references, reflection selections and overrides use the stable keys. Changing `name` preserves these config references. Component descriptors expose resolved `link_name_map` and `joint_name_map` entries for emitters and control generation.

## Related links

- 📚 Read the [documentation](https://docs.openarm.dev/software/description)
Expand Down
16 changes: 16 additions & 0 deletions assets/accessory/zed_mount/config/inertials/nominal.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
link1:
origin:
x: 0.014296
y: 0.0034952
z: 0.047826
roll: 0
pitch: 0
yaw: 0
mass: 0.13704
inertia:
xx: 0.00018293
xy: -6.3997e-06
xz: -3.1024e-05
yy: 0.00011247
yz: -8.6526e-06
zz: 0.00012779
29 changes: 29 additions & 0 deletions assets/accessory/zed_mount/config/link/collisions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
link1:
- name: support
origin:
x: 0
y: 0
z: 0
roll: 0
pitch: 0
yaw: 0
geometry:
mesh: package://openarm_description/assets/accessory/zed_mount/meshes/collision/zed_mount_support.stl
scale:
x: 1
y: 1
z: 1
- name: shell
origin:
x: 0
y: 0
z: 0
roll: 0
pitch: 0
yaw: 0
geometry:
mesh: package://openarm_description/assets/accessory/zed_mount/meshes/collision/zed_mount_shell.stl
scale:
x: 1
y: 1
z: 1
14 changes: 14 additions & 0 deletions assets/accessory/zed_mount/config/link/visuals.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
link1:
origin:
x: 0
y: 0
z: 0
roll: 0
pitch: 0
yaw: 0
geometry:
mesh: package://openarm_description/assets/accessory/zed_mount/meshes/visual/zed_mount.dae
scale:
x: 1
y: 1
z: 1
10 changes: 10 additions & 0 deletions assets/accessory/zed_mount/config/struct/reference_points.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
camera_mount_point:
parent: link1
emit: true
origin:
x: 0.0263085336684006
y: 0.00869000000000031
z: 0.065829226007701
roll: 0
pitch: 1.08210413623648
yaw: 0
4 changes: 4 additions & 0 deletions assets/accessory/zed_mount/config/struct/topology.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
links:
link1:
name: base_link
joints: {}
Binary file not shown.
Binary file not shown.
279 changes: 279 additions & 0 deletions assets/accessory/zed_mount/meshes/visual/zed_mount.dae

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion assets/body/v2.0/config/inertials/nominal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

body_link0:
link1:
origin:
x: -0.01627169562
y: 0.0
Expand Down
2 changes: 1 addition & 1 deletion assets/body/v2.0/config/link/collisions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

body_link0:
link1:
origin:
x: 0.0
y: 0.0
Expand Down
2 changes: 1 addition & 1 deletion assets/body/v2.0/config/link/visuals.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

body_link0:
link1:
origin:
x: 0.0
y: 0.0
Expand Down
35 changes: 16 additions & 19 deletions assets/body/v2.0/config/struct/reference_points.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

left_arm_mount_point:
parent: body_link0
parent: link1
origin:
x: 0.0
y: 0.030
Expand All @@ -29,13 +29,13 @@ left_arm_mount_point:
type: fixed
parent_link:
original_name: body_link0
canonical_name: body_link0
canonical_name: link1
child_link:
original_name: body_left_arm_mount_point
canonical_name: null

right_arm_mount_point:
parent: body_link0
parent: link1
origin:
x: 0.0
y: -0.030
Expand All @@ -51,13 +51,14 @@ right_arm_mount_point:
type: fixed
parent_link:
original_name: body_link0
canonical_name: body_link0
canonical_name: link1
child_link:
original_name: body_right_arm_mount_point
canonical_name: null


arm_origin:
parent: body_link0
parent: link1
emit: true
origin:
x: 0.0
Expand All @@ -67,17 +68,13 @@ arm_origin:
pitch: 0.0
yaw: 0.0

# Example body-mounted camera reference point. Tune this pose from CAD or
# calibration before using it on hardware.
# zed_front_mount_point:
# parent: body_link0
# origin:
# x: 0.12
# y: 0.0
# z: 0.82
# roll: 0.0
# pitch: 0.0
# yaw: 0.0
# meta:
# kind: sensor_mount
# sensor_family: zed
camera_mount_point:
parent: link1
emit: true
origin:
x: 0.03
y: 0.0
z: 0.743
roll: 0.0
pitch: 0.0
yaw: 0.0
3 changes: 2 additions & 1 deletion assets/body/v2.0/config/struct/topology.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@
# limitations under the License.

links:
- body_link0
link1:
name: body_link0
joints: {}
8 changes: 4 additions & 4 deletions assets/common/assembly.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ limitations under the License.
<xacro:if value="${parent_ref_should_reflect}">
<xacro:openarm_reflect_pose origin="${reference_origin}" reflect_x="${parent_reflect_cfg['x']}" reflect_y="${parent_reflect_cfg['y']}" reflect_z="${parent_reflect_cfg['z']}" out_name="reference_origin" />
</xacro:if>
<xacro:if value="${parent_ref_canonical_link == parent_desc['topology']['links'][0] and parent_desc.get('base_frame_shift', 'none') != 'none'}">
<xacro:if value="${parent_ref_canonical_link == list(parent_desc['topology']['links'])[0] and parent_desc.get('base_frame_shift', 'none') != 'none'}">
<xacro:openarm_compose_pose first="${parent_desc['base_frame_shift']}" second="${reference_origin}" out_name="reference_origin" />
</xacro:if>
<xacro:openarm_compose_pose first="${reference_origin}" second="${connection_origin}" out_name="reference_mount_origin" />
Expand All @@ -111,9 +111,9 @@ limitations under the License.
<!-- reference point child empty links are already absorbed into config; runtime collapse only targets the parent's terminal empty link -->
<xacro:property
name="parent_terminal_link_empty"
value="${has_parent_desc and parent_desc['collapse_empty_end'] and parent_desc['topology']['links'][-1] not in parent_desc['inertials'] and parent_desc['topology']['links'][-1] not in parent_desc['visuals'] and parent_desc['topology']['links'][-1] not in parent_desc['collisions']}"
value="${has_parent_desc and parent_desc['collapse_empty_end'] and list(parent_desc['topology']['links'])[-1] not in parent_desc['inertials'] and list(parent_desc['topology']['links'])[-1] not in parent_desc['visuals'] and list(parent_desc['topology']['links'])[-1] not in parent_desc['collisions']}"
lazy_eval="false" />
<xacro:if value="${has_parent_reference_key and not has_parent_override and parent_terminal_link_empty and parent_ref_canonical_link == parent_desc['topology']['links'][-1] and component_desc.get('supports_frame_shift', False)}">
<xacro:if value="${has_parent_reference_key and not has_parent_override and parent_terminal_link_empty and parent_ref_canonical_link == list(parent_desc['topology']['links'])[-1] and component_desc.get('supports_frame_shift', False)}">
<xacro:property name="skip_mount_joint" value="${True}" lazy_eval="false" />
<xacro:property name="base_frame_shift" value="${reference_mount_origin}" lazy_eval="false" />
</xacro:if>
Expand All @@ -123,7 +123,7 @@ limitations under the License.
name="attached_children"
value="${[name for name in components if name in robot_preset and robot_preset[name]['enabled'] and 'connection' in robot_preset[name] and 'attach_to' in robot_preset[name]['connection'] and robot_preset[name]['connection']['attach_to'] == component_name]}"
lazy_eval="false" />
<xacro:property name="component_terminal_link" value="${component_desc['topology']['links'][-1]}" lazy_eval="false" />
<xacro:property name="component_terminal_link" value="${list(component_desc['topology']['links'])[-1]}" lazy_eval="false" />
<xacro:property
name="component_terminal_link_empty"
value="${component_desc['collapse_empty_end'] and component_terminal_link not in component_desc['inertials'] and component_terminal_link not in component_desc['visuals'] and component_terminal_link not in component_desc['collisions']}"
Expand Down
40 changes: 40 additions & 0 deletions assets/common/collisions.xacro
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0"?>
<!--
Copyright 2026 Enactic, Inc.
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.
-->

<robot xmlns:xacro="http://www.ros.org/wiki/xacro">
<xacro:include filename="$(find openarm_description)/assets/common/transforms.xacro" />

<xacro:macro name="openarm_emit_collisions" params="link_name entries reflect_x reflect_y reflect_z frame_shift index:=0">
<xacro:if value="${index &lt; len(entries)}">
<xacro:property name="cfg" value="${entries[index]}" />
<collision name="${link_name + '_collision' if len(entries) == 1 else link_name + '_' + cfg.get('name', str(index)) + '_collision'}">
<xacro:if value="${frame_shift != 'none'}">
<xacro:openarm_emit_shifted_origin frame_shift="${frame_shift}" origin="${cfg['origin']}" reflect_x="${reflect_x}" reflect_y="${reflect_y}" reflect_z="${reflect_z}" reflect_rotation="${True}" />
</xacro:if>
<xacro:unless value="${frame_shift != 'none'}">
<xacro:openarm_emit_origin origin="${cfg['origin']}" reflect_x="${reflect_x}" reflect_y="${reflect_y}" reflect_z="${reflect_z}" reflect_rotation="${True}" />
</xacro:unless>
<geometry>
<xacro:if value="${'box' in cfg['geometry']}">
<box size="${cfg['geometry']['box']['x']} ${cfg['geometry']['box']['y']} ${cfg['geometry']['box']['z']}" />
</xacro:if>
<xacro:unless value="${'box' in cfg['geometry']}">
<mesh filename="${cfg['geometry']['mesh']}" scale="${reflect_x * cfg['geometry']['scale']['x']} ${reflect_y * cfg['geometry']['scale']['y']} ${reflect_z * cfg['geometry']['scale']['z']}" />
</xacro:unless>
</geometry>
</collision>
<xacro:openarm_emit_collisions link_name="${link_name}" entries="${entries}" reflect_x="${reflect_x}" reflect_y="${reflect_y}" reflect_z="${reflect_z}" frame_shift="${frame_shift}" index="${index + 1}" />
</xacro:if>
</xacro:macro>
</robot>
14 changes: 9 additions & 5 deletions assets/common/components.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ limitations under the License.
<xacro:property name="component_inertials" value="${xacro.load_yaml(component_cfg_root + '/inertials/' + component_cfg['inertials'] + '.yaml')}" lazy_eval="false" />
<xacro:property name="component_visuals" value="${xacro.load_yaml(component_cfg_root + '/link/visuals.yaml')}" lazy_eval="false" />
<xacro:property name="component_collisions" value="${xacro.load_yaml(component_cfg_root + '/link/collisions.yaml')}" lazy_eval="false" />
<xacro:property name="component_link_name_map" value="${empty_dict}" lazy_eval="false" />
<xacro:property name="component_root_link_actual" value="${component_prefix + component_topology['links'][0]}" lazy_eval="false" />
<xacro:property name="component_default_mount_parent" value="${component_prefix + (component_topology['links'][0] if component_type == 'body' else component_topology['links'][-1])}" lazy_eval="false" />
<xacro:property name="component_collapse_empty_end" value="${component_topology.get('collapse_empty_end', False) and len([j for j in component_topology['joints'] if component_topology['joints'][j]['parent'] == component_topology['links'][-1]]) == 0}" lazy_eval="false" />
<xacro:property name="component_link_name_map" value="${dict([(key, component_prefix + cfg.get('name', key)) for key, cfg in component_topology['links'].items()])}" lazy_eval="false" />
<xacro:property name="component_joint_name_map" value="${dict([(key, component_prefix + cfg.get('name', key)) for key, cfg in component_topology['joints'].items()])}" lazy_eval="false" />
<xacro:property name="component_root_link_actual" value="${component_link_name_map[list(component_topology['links'])[0]]}" lazy_eval="false" />
<xacro:property name="component_default_mount_parent" value="${component_link_name_map[list(component_topology['links'])[0] if component_type == 'body' else list(component_topology['links'])[-1]]}" lazy_eval="false" />
<xacro:property name="component_collapse_empty_end" value="${component_topology.get('collapse_empty_end', False) and len([j for j in component_topology['joints'] if component_topology['joints'][j]['parent'] == list(component_topology['links'])[-1]]) == 0}" lazy_eval="false" />

<xacro:property
name="${out_name}"
Expand All @@ -71,6 +72,7 @@ limitations under the License.
visuals=component_visuals,
collisions=component_collisions,
link_name_map=component_link_name_map,
joint_name_map=component_joint_name_map,
default_mount_parent=component_default_mount_parent,
collapse_empty_end=component_collapse_empty_end,
supports_frame_shift=True)}"
Expand Down Expand Up @@ -99,6 +101,8 @@ limitations under the License.
<xacro:if value="${component_desc['topology']['joints']}">
<xacro:openarm_emit_kinematic_component
prefix="${component_desc['prefix']}"
link_name_map="${component_desc['link_name_map']}"
joint_name_map="${component_desc['joint_name_map']}"
connected_to="${component_runtime['resolved_connected_to']}"
mount_origin="${component_runtime['resolved_mount_origin']}"
topology="${component_desc['topology']}"
Expand Down Expand Up @@ -126,7 +130,7 @@ limitations under the License.
reference_points="${component_desc['reference_points']}"
prefix="${component_desc['prefix']}"
link_name_map="${component_desc['link_name_map']}"
base_link_name="${component_desc['topology']['links'][0]}"
base_link_name="${list(component_desc['topology']['links'])[0]}"
reflect_x="${component_runtime['reflect_x']}"
reflect_y="${component_runtime['reflect_y']}"
reflect_z="${component_runtime['reflect_z']}"
Expand Down
Loading
Loading