Sources:
- /home/erik/a4wd3-git/launch/a4wd3_launch.py static transforms (tf_base_scan, tf_base_imu)
- A4WD3-Wheeled.stp (user CAD, true scale: overall width 437.0 mm matches measurement)
- - rplidar-a2m4-r1.stl (Slamtec official model; flat-panel lidar, scan axis = mesh Z)
+ - rplidar-a2m4-r1.stl (Slamtec official model; flat-panel lidar, scan axis = mesh Y,
+ window face at mesh y_min; verified by slice analysis, not mesh Z)
Frame conventions:
LIVE base_link (as published by hw_node.cpp odom TF): x = forward, y = left, z = up,
ORIGIN AT WHEELBASE CENTER / AXLE HEIGHT (z=0 at axle plane; ground ~79 mm below,
<visual>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
- <mesh filename="package://a4wd3/meshes/a4wd3_base_rviz.stl"/>
+ <!-- STL stores millimeter coordinates; URDF expects meters -->
+ <mesh filename="package://a4wd3/meshes/a4wd3_base_rviz.stl" scale="0.001 0.001 0.001"/>
</geometry>
<material name="chassis_grey">
<color rgba="0.45 0.45 0.45 1.0"/>
<collision>
<origin xyz="0 0 0" rpy="0 0 0"/>
<geometry>
- <mesh filename="package://a4wd3/meshes/a4wd3_base_rviz.stl"/>
+ <mesh filename="package://a4wd3/meshes/a4wd3_base_rviz.stl" scale="0.001 0.001 0.001"/>
</geometry>
</collision>
</link>
- <!-- RP-LIDAR A2M4 (r1), flat panel. Launch tf_base_scan VERBATIM:
- t=(+0.08125, +0.07425, 0) m, yaw = pi (live base_link frame).
- => 81.25 mm forward, 74.25 mm left, at axle height; scan plane ~74 mm above axles
- (~153 mm above floor). Visual origin offset puts the mesh's scan plane
- (mesh z=+0.1125, body axis at mesh x=-0.006, y=+0.028) onto the laser frame origin. -->
+ <!-- RP-LIDAR A2M4 (r1), flat panel. Launch tf_base_scan VERBATIM for x/y:
+ t=(+0.08125, +0.07425) m, yaw = pi (live base_link frame).
+ => 81.25 mm forward, 74.25 mm left.
+ Z (NOT in launch file, measured 2026-10-11): unit sits 21 mm ABOVE the LOCAL top
+ plate under the lidar (z=+46.0 mm there; the 55.8 mm global max is a raised area
+ elsewhere) -> unit bottom at z=+67.0 mm. After the 180 deg flip the full mesh spans
+ laser z -0.0009..+0.0399 -> joint z = 0.0679 m.
+ MESH GEOMETRY (verified by slice analysis 2026-10-11): the body is a cylinder
+ with its AXIS ALONG MESH Y (slices perp to Y: ~75 mm circles; perp to X/Z: ellipses).
+ Visual transform: Rx(+90 deg) maps mesh +Y -> laser +z, i.e. the unit is FLIPPED
+ 180 deg about the laser x axis relative to the raw STL (user confirmed the raw
+ orientation rendered upside down). Origin offset puts the scan-face center
+ (mesh 7.26, 9.15, 75.24 mm) at the laser frame origin. Body then spans laser
+ z -0.0009..+0.0399 m (unit sits ABOVE the frame; bracket arm at unit top). -->
<joint name="base_to_laser" type="fixed">
<parent link="base_link"/>
<child link="laser"/>
- <origin xyz="0.08125 0.07425 0" rpy="0 0 3.14159"/>
+ <origin xyz="0.08125 0.07425 0.0679" rpy="0 0 3.14159"/>
</joint>
<link name="laser">
<inertial>
<inertia ixx="0.0001" ixy="0" ixz="0" iyy="0.0001" iyz="0" izz="0.0002"/>
</inertial>
<visual>
- <origin xyz="0.006 -0.028 -0.1125" rpy="0 0 0"/>
+ <origin xyz="-0.0073 0.0752 -0.0092" rpy="1.5708 0 0"/>
<geometry>
- <mesh filename="package://a4wd3/meshes/rplidar_a2m4_decimated.stl"/>
+ <!-- STL stores millimeter coordinates; URDF expects meters -->
+ <mesh filename="package://a4wd3/meshes/rplidar_a2m4_decimated.stl" scale="0.001 0.001 0.001"/>
</geometry>
<material name="lidar_black">
<color rgba="0.1 0.1 0.1 1.0"/>
</material>
</visual>
<collision>
- <origin xyz="0 0 0" rpy="0 0 0"/>
+ <!-- body only (excl. bracket arm): mesh 79 x 40.8 x 75 mm, axis along mesh Y;
+ center transformed into the laser frame by the same Rx(+90)+offset as the visual -->
+ <origin xyz="-0.0113 0.0002 0.0195" rpy="0 0 0"/>
<geometry>
- <box size="0.136 0.041 0.075"/>
+ <box size="0.079 0.075 0.041"/>
</geometry>
</collision>
</link>
+ <!-- WHEELS: the chassis mesh EXCLUDES the tire solids (hub + motor housing remain in the
+ mesh and look lumpy without the tire around them). These cylinder visuals restore
+ the true tire outline: dia 158 mm, width 84 mm, centers at (+-110, +-176.5) mm,
+ axle height z=0 (measured from STEP). Visuals only - no collision, so the
+ chassis collision mesh stays the sole collision source. -->
+ <joint name="base_to_wheel_fl" type="fixed">
+ <parent link="base_link"/>
+ <child link="wheel_fl"/>
+ <origin xyz="0.110 0.1765 0" rpy="0 0 0"/>
+ </joint>
+ <link name="wheel_fl">
+ <visual>
+ <origin xyz="0 0 0" rpy="1.5708 0 0"/>
+ <geometry>
+ <cylinder length="0.084" radius="0.079"/>
+ </geometry>
+ <material name="tire_black"><color rgba="0.15 0.15 0.15 1.0"/></material>
+ </visual>
+ </link>
+ <joint name="base_to_wheel_fr" type="fixed">
+ <parent link="base_link"/>
+ <child link="wheel_fr"/>
+ <origin xyz="0.110 -0.1765 0" rpy="0 0 0"/>
+ </joint>
+ <link name="wheel_fr">
+ <visual>
+ <origin xyz="0 0 0" rpy="1.5708 0 0"/>
+ <geometry>
+ <cylinder length="0.084" radius="0.079"/>
+ </geometry>
+ <material name="tire_black"><color rgba="0.15 0.15 0.15 1.0"/></material>
+ </visual>
+ </link>
+ <joint name="base_to_wheel_al" type="fixed">
+ <parent link="base_link"/>
+ <child link="wheel_al"/>
+ <origin xyz="-0.110 0.1765 0" rpy="0 0 0"/>
+ </joint>
+ <link name="wheel_al">
+ <visual>
+ <origin xyz="0 0 0" rpy="1.5708 0 0"/>
+ <geometry>
+ <cylinder length="0.084" radius="0.079"/>
+ </geometry>
+ <material name="tire_black"><color rgba="0.15 0.15 0.15 1.0"/></material>
+ </visual>
+ </link>
+ <joint name="base_to_wheel_ar" type="fixed">
+ <parent link="base_link"/>
+ <child link="wheel_ar"/>
+ <origin xyz="-0.110 -0.1765 0" rpy="0 0 0"/>
+ </joint>
+ <link name="wheel_ar">
+ <visual>
+ <origin xyz="0 0 0" rpy="1.5708 0 0"/>
+ <geometry>
+ <cylinder length="0.084" radius="0.079"/>
+ </geometry>
+ <material name="tire_black"><color rgba="0.15 0.15 0.15 1.0"/></material>
+ </visual>
+ </link>
+
<!-- BNO085 IMU. Launch tf_base_imu VERBATIM: t=(-0.02, -0.013, 0) m, roll = pi. -->
<joint name="base_to_imu" type="fixed">
<parent link="base_link"/>