Transform errors with slam toolbox

slam_toolbox “Failed to compute odom pose” — /tf not flowing from ROSbot XL to Docker container

Hi,

I am having an issue running slam_toolbox on a Husarion ROSbot XL. My setup:

  • Robot: ROSbot XL (Jetson Orin), ROS2 Humble, CycloneDDS, ROS_DOMAIN_ID=0, running rosbot.start (snap)

  • Laptop: Docker container (--net host), ROS2 Humble, CycloneDDS, ROS_DOMAIN_ID=0

  • Connected via USB ethernet

Problem: /tf does not flow from the robot to the laptop container. ros2 topic info /tf shows 0 publishers from inside the container, even though ros2 topic list shows /tf as a known topic. Interestingly, /scan and /odometry/filtered flow correctly after configuring CYCLONEDDS_URI with the correct interface and unicast peer address.

Because /tf doesn’t arrive, slam_toolbox cannot look up odom → base_link and outputs:

[slam_toolbox]: Failed to compute odom pose

Confirmed with:

ros2 run tf2_ros tf2_echo odom base_link
# Output: Invalid frame ID "odom" - frame does not exist

Topic list visible from container:

/odometry/filtered
/scan
/tf        ← discovered but 0 publishers
/tf_static ← same issue

slam_toolbox params:

slam_toolbox:
  ros__parameters:
    use_sim_time: false
    odom_frame: odom
    base_frame: base_link
    scan_topic: /scan

Question: Why would /scan and /odometry/filtered flow via CycloneDDS unicast but /tf does not, is this a QoS mismatch? Could the rosbot.start snap be publishing /tf with a different CycloneDDS configuration than the rplidar container (which publishes /scan)?

Hi @raquel_kampel,

This is probably snap isolation issue. Each Husarion snap runs in its own confined environment and does not inherit RMW environment variables from the host (RMW_IMPLEMENTATION, CYCLONEDDS_URI, ROS_DOMAIN_ID). Just like Docker containers, every snap must be configured separately via snap set.

Fix

Husarion snaps expose ROS settings under ros.* keys:

# Configure the rosbot snap
sudo snap set rosbot ros.transport=rmw_cyclonedds_cpp
sudo snap set rosbot ros.domain-id=0

# Mirror config to every other ROS 2 snap
sudo snap set husarion-rplidar $(xargs -a /var/snap/rosbot/common/ros_snap_args)
sudo snap set husarion-webui   $(xargs -a /var/snap/rosbot/common/ros_snap_args)

# Apply same env to your shell
source /var/snap/rosbot/common/ros.env

For a custom Cyclone XML (your USB-ethernet interface + laptop as unicast peer), drop it in the snap’s common dir and select by name:

sudo cp cyclonedds.xml /var/snap/rosbot/common/dds-config-mysetup.xml
sudo snap set rosbot ros.transport=mysetup

See ROSbot XL quick start for listing available DDS setups.

Verify

ros2 run tf2_ros tf2_echo odom base_link

Key takeaway: one snap = one isolated RMW environment. ros.transport and ros.domain-id must be set on every Husarion snap that publishes or subscribes — the ros_snap_args trick keeps them in sync.