Add a part
A part is one file, bluerobotics_parts/urdf/<part>.urdf.xacro, holding a
metadata macro (<part>_info) and the macro that instantiates it; the full
contract is in Parts. Write it by hand, starting from
an existing part or from this minimal one, a 10 cm box of 0.5 kg with one
slot on top:
<?xml version="1.0"?>
<robot xmlns:xacro="http://ros.org/wiki/xacro">
<xacro:macro name="my_box_info">
<xacro:property name="part_info" scope="parent" value="${dict(
attach='0 0 -0.05',
slots=dict(
top=dict(xyz='0 0 0.05', rpy='0 0 0', accepts=['ping_singlebeam'], default='none'),
),
frames=dict())}"/>
</xacro:macro>
<xacro:macro name="my_box"
params="name parent xyz:='0 0 0' rpy:='0 0 0' collision:=true joint:=fixed axis:='0 0 1'">
<xacro:my_box_info/>
<link name="${name}">
<inertial>
<origin xyz="0 0 0" rpy="0 0 0"/>
<mass value="0.5"/>
<inertia ixx="0.000833" ixy="0" ixz="0" iyy="0.000833" iyz="0" izz="0.000833"/>
</inertial>
<visual> <!-- a glTF mesh would go through <xacro:part_visual mesh="package://..."/> -->
<geometry><box size="0.1 0.1 0.1"/></geometry>
</visual>
<xacro:if value="${collision}">
<collision>
<geometry><box size="0.1 0.1 0.1"/></geometry>
</collision>
</xacro:if>
</link>
<xacro:part_joint name="${name}" parent="${parent}" xyz="${xyz}" rpy="${rpy}"
joint="${joint}" axis="${axis}" attach="${part_info['attach']}"/>
<xacro:part_slots name="${name}" items="${list(part_info['slots'].items())}"/>
<xacro:part_frames name="${name}" items="${list(part_info['frames'].items())}"/>
</xacro:macro>
</robot>
attach is where the part bolts on, in its own frame (here the bottom face,
so the box stands on whatever it is mounted to); the macro’s axis default
is the part’s own spin axis, used when it is fitted on a turning joint. A
propeller additionally declares drive=dict(max_thrust=..., min_thrust=..., rotation='ccw'|'cw'), which makes the assembly mount it on a
continuous joint and the Gazebo side give it a Thruster and a thrust topic.
Then:
If the part has a real mesh, put it at
models/<part>/<part>.visual.glb(Y-up, PBR materials embedded; see the mesh conventions in Parts) and point the visual at it with<xacro:part_visual mesh="package://bluerobotics_parts/models/<part>/<part>.visual.glb"/>.Add the include line to
urdf/parts.xacro(alphabetical) and, if the part belongs in a slot, add its type to that slot’saccepts(anddefaultif it is the usual occupant) in the carrying part’s_info.Verify:
colcon build --packages-select bluerobotics_parts, then the probe and the review world:PARTS=$(ros2 pkg prefix --share bluerobotics_parts) xacro $PARTS/urdf/part_probe.urdf.xacro part:=<part> > /tmp/probe.urdf && check_urdf /tmp/probe.urdf ros2 run bluerobotics_parts parts_check_world.py --out $PARTS/worlds/parts_check.sdf gz sim $PARTS/worlds/parts_check.sdf # right click a part: View > Collisions / Inertia / Center of Mass
Commit the macro (and the regenerated
worlds/parts_check.sdf).
Where a part goes
In a slot: list its type in the slot’s
accepts; users fit it with{slot: <slot>, type: <part>}, or automatically if it is thedefault.Free placed: nothing to declare; users give
xyz/rpy.As a slot provider: declare slots in its
_info(the Ping bracket declaresping; a battery bay would declarebattery).