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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,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 All @@ -49,3 +51,5 @@ Copyright 2025 Enactic, Inc.

All participation in the OpenArm project is governed by our
[Code of Conduct](CODE_OF_CONDUCT.md).

Control partitions are declared under the preset's top-level `systems` mapping. Each system's `joints` maps component instance names to stable local joint keys with an `idx`. Each active joint requires a unique non-negative integer `idx` within its system; gaps are allowed. Joints are emitted in index order, with `idx` included as a joint parameter for runtime adapters. Disabled components are skipped, and final joint names come from the resolved component descriptions. Standard OpenArm hardware defaults are supplied by the model entry. Per-system hardware_plugin and driver parameters can override those defaults, and CAN launch arguments take precedence. `use_fake_hardware:=true` selects `mock_components/GenericSystem`. Optional `command_interfaces` and `state_interfaces` default to `[position, velocity, effort]`.
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
10 changes: 5 additions & 5 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,13 @@ 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 Down
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: {}
67 changes: 28 additions & 39 deletions assets/common/assembly.xacro
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,21 @@ limitations under the License.

<xacro:include filename="$(find openarm_description)/assets/common/components.xacro" />

<xacro:macro name="openarm_collect_collapsible_children"
params="names robot_preset component_api options out_name index:=0 candidates:=${dict()}">
<xacro:macro name="openarm_collect_component_descriptions"
params="names robot_preset component_api options out_name index:=0 descriptions:=${dict()}">
<xacro:if value="${index &lt; len(names)}">
<xacro:property name="name" value="${names[index]}" />
<xacro:call macro="${component_api['describe']}" component_name="${name}" component_cfg="${robot_preset[name]}"
options="${options}" out_name="description" />
<xacro:if value="${description.get('supports_frame_shift', False)}">
<xacro:property name="candidates" value="${dict(list(candidates.items()) + [(name, description)])}" lazy_eval="false" />
</xacro:if>
<xacro:openarm_collect_collapsible_children names="${names}" robot_preset="${robot_preset}"
component_api="${component_api}" options="${options}" index="${index + 1}" candidates="${candidates}" out_name="candidates" />
<xacro:property name="descriptions" value="${dict(list(descriptions.items()) + [(name, description)])}" lazy_eval="false" />
<xacro:openarm_collect_component_descriptions names="${names}" robot_preset="${robot_preset}"
component_api="${component_api}" options="${options}" index="${index + 1}" descriptions="${descriptions}" out_name="descriptions" />
</xacro:if>
<xacro:property name="${out_name}" value="${candidates}" scope="parent" lazy_eval="false" />
<xacro:property name="${out_name}" value="${descriptions}" scope="parent" lazy_eval="false" />
</xacro:macro>

<xacro:macro name="openarm_resolve_component_runtime"
params="component_api component_name components robot_preset config_root root_frame component_cfg component_desc parent_desc:=none emit_grasp_frame:=false out_name">
params="component_descriptions component_name components robot_preset config_root root_frame component_cfg component_desc parent_desc:=none emit_grasp_frame:=false out_name">

<xacro:property name="none_value" value="${'none'}" lazy_eval="false" />
<xacro:property name="zero_origin" value="${dict(x=0.0, y=0.0, z=0.0, roll=0.0, pitch=0.0, yaw=0.0)}" lazy_eval="false" />
Expand Down Expand Up @@ -96,7 +94,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 +109,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 +121,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 All @@ -133,8 +131,9 @@ limitations under the License.
value="${[name for name in attached_children if component_terminal_link_empty and 'parent_reference_point' in robot_preset[name]['connection'] and robot_preset[name]['connection']['parent_reference_point'] != None and ('parent' not in robot_preset[name]['connection'] or robot_preset[name]['connection']['parent'] == None) and robot_preset[name]['connection']['parent_reference_point'] in component_desc['reference_points'] and component_desc['reference_points'][robot_preset[name]['connection']['parent_reference_point']]['parent'] == component_terminal_link]}"
lazy_eval="false" />

<xacro:openarm_collect_collapsible_children names="${collapse_candidate_names}" robot_preset="${robot_preset}"
component_api="${component_api}" options="${dict(emit_grasp_frame=emit_grasp_frame)}" out_name="candidate_descriptions" />
<xacro:property name="candidate_descriptions"
value="${dict([(name, component_descriptions[name]) for name in collapse_candidate_names if component_descriptions[name].get('supports_frame_shift', False)])}"
lazy_eval="false" />
<xacro:openarm_require condition="${len(candidate_descriptions) &lt;= 1}"
message="${'Invalid robot preset: component %s has multiple children competing to collapse the same terminal empty link.' % component_name}" />
<xacro:if value="${len(candidate_descriptions) == 1}">
Expand Down Expand Up @@ -169,20 +168,12 @@ limitations under the License.
</xacro:macro>

<xacro:macro name="openarm_emit_component_tree"
params="component_api component_name components robot_preset config_root root_frame parent_desc:=none preloaded_desc:=none emit_grasp_frame:=false">
params="component_api component_descriptions component_name components robot_preset config_root root_frame parent_desc:=none emit_grasp_frame:=false">

<xacro:property name="none_value" value="${'none'}" lazy_eval="false" />
<xacro:property name="component_cfg" value="${robot_preset[component_name]}" lazy_eval="false" />
<xacro:property name="component_desc" value="${preloaded_desc}" lazy_eval="false" />
<xacro:property name="component_desc" value="${component_descriptions[component_name]}" lazy_eval="false" />

<xacro:if value="${component_desc == none_value}">
<xacro:call macro="${component_api['describe']}"
component_name="${component_name}"
component_cfg="${component_cfg}"
out_name="component_desc" options="${dict(emit_grasp_frame=emit_grasp_frame)}" />
</xacro:if>

<xacro:openarm_resolve_component_runtime component_api="${component_api}"
<xacro:openarm_resolve_component_runtime component_descriptions="${component_descriptions}"
component_name="${component_name}"
components="${components}"
robot_preset="${robot_preset}"
Expand All @@ -203,21 +194,19 @@ limitations under the License.
component_runtime="${component_runtime}"
options="${dict(emit_grasp_frame=emit_grasp_frame)}" />

<xacro:openarm_emit_attached_components component_api="${component_api}"
<xacro:openarm_emit_attached_components component_api="${component_api}" component_descriptions="${component_descriptions}"
index="0"
components="${components}"
parent_component_name="${component_name}"
robot_preset="${robot_preset}"
config_root="${config_root}"
root_frame="${root_frame}"
parent_desc="${component_desc}"
preloaded_component_name="${component_runtime['collapse_child_name']}"
preloaded_component_desc="${component_runtime['collapse_child_desc']}"
emit_grasp_frame="${emit_grasp_frame}" />
</xacro:macro>

<xacro:macro name="openarm_emit_attached_components"
params="component_api index components parent_component_name robot_preset config_root root_frame parent_desc:=none preloaded_component_name:=none preloaded_component_desc:=none emit_grasp_frame:=false">
params="component_api component_descriptions index components parent_component_name robot_preset config_root root_frame parent_desc:=none emit_grasp_frame:=false">

<xacro:if value="${index &lt; len(components)}">
<xacro:property name="none_value" value="${'none'}" lazy_eval="false" />
Expand All @@ -228,37 +217,32 @@ limitations under the License.
<xacro:property name="attach_to" value="${connection['attach_to'] if 'attach_to' in connection else None}" lazy_eval="false" />
<xacro:property name="is_root_component" value="${parent_component_name == '__root__' and attach_to == None}" lazy_eval="false" />
<xacro:property name="is_attached_child" value="${attach_to == parent_component_name}" lazy_eval="false" />
<!-- Reuse a preloaded descriptor when collapse analysis already loaded it for this child. -->
<xacro:property name="component_desc_override" value="${preloaded_component_desc if component_name == preloaded_component_name else none_value}" lazy_eval="false" />

<xacro:if value="${component_enabled and (is_root_component or is_attached_child)}">
<xacro:openarm_emit_component_tree component_api="${component_api}"
<xacro:openarm_emit_component_tree component_api="${component_api}" component_descriptions="${component_descriptions}"
component_name="${component_name}"
components="${components}"
robot_preset="${robot_preset}"
config_root="${config_root}"
root_frame="${root_frame}"
parent_desc="${none_value if is_root_component else parent_desc}"
preloaded_desc="${component_desc_override}"
emit_grasp_frame="${emit_grasp_frame}" />
</xacro:if>

<xacro:openarm_emit_attached_components component_api="${component_api}"
<xacro:openarm_emit_attached_components component_api="${component_api}" component_descriptions="${component_descriptions}"
index="${index + 1}"
components="${components}"
parent_component_name="${parent_component_name}"
robot_preset="${robot_preset}"
config_root="${config_root}"
root_frame="${root_frame}"
parent_desc="${parent_desc}"
preloaded_component_name="${preloaded_component_name}"
preloaded_component_desc="${preloaded_component_desc}"
emit_grasp_frame="${emit_grasp_frame}" />
</xacro:if>
</xacro:macro>

<xacro:macro name="openarm_assemble_robot"
params="component_api config_root robot_preset emit_grasp_frame:=false">
params="component_api config_root robot_preset emit_grasp_frame:=false out_name:=component_descriptions">

<xacro:property name="robot_cfg" value="${robot_preset['robot']}" lazy_eval="false" />
<xacro:property name="root_frame" value="${robot_cfg['root_frame']}" lazy_eval="false" />
Expand All @@ -268,9 +252,14 @@ limitations under the License.
components="${components}"
robot_preset="${robot_preset}" />

<xacro:property name="enabled_components" value="${[name for name in components if robot_preset[name]['enabled']]}" lazy_eval="false" />
<xacro:openarm_collect_component_descriptions names="${enabled_components}" robot_preset="${robot_preset}"
component_api="${component_api}" options="${dict(emit_grasp_frame=emit_grasp_frame)}" out_name="component_descriptions" />
<xacro:property name="${out_name}" value="${component_descriptions}" scope="parent" lazy_eval="false" />

<link name="${root_frame}" />

<xacro:openarm_emit_attached_components component_api="${component_api}"
<xacro:openarm_emit_attached_components component_api="${component_api}" component_descriptions="${component_descriptions}"
index="0"
components="${components}"
parent_component_name="__root__"
Expand Down
Loading
Loading