Close

Initial Challenges: When SLAM Goes Wrong

A project log for Perceptor - ROS2 Perception, Planning, Control

Modified Roomba with ROS2, LIDAR, and odometry running SLAM Toolbox for real-time mapping and navigation

vipin-mVipin M 07/28/2025 at 02:360 Comments

The Symptoms

My SLAM system showed classic drift symptoms that pointed to deeper issues:

The Confusion

What confused me most: laser scans drifted during straight-line motion but partially realigned during rotation. Initially, I mistook this for working loop closure. But after deeper analysis, I realized loop closure is not functioning properly yet. The system is trying — but it isn’t successfully closing loops or correcting long-term drift.

🔍 Diagnostic Process: Systematic Debugging

Step 1: Transform Tree Analysis

I inspected the transform tree:

Step 2: Topic Rate and Timestamp Validation

I measured the topic rates and confirmed mismatches between TF publishing and sensor data timestamps.

# Check component frequencies
ros2 topic hz /scan # Result: 7.5Hz (good)
ros2 topic hz /tf # Result: 70Hz (excellent) ros2 topic hz /map # Result: 0.2Hz (every 5 seconds)
ros2 topic hz /odom # Result: 10Hz (adequate)

Step 3: Odometry Accuracy Evaluation

To understand how much odometry might be contributing to drift, I tested it in isolation:

# Linear accuracy test
echo "Starting position:"
ros2 topic echo /odom --once | grep -A3 "position:"

# Move exactly 1 meter forward
timeout 20s ros2 topic pub /cmd_vel_nav geometry_msgs/msg/Twist \
  "{linear: {x: 0.05, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}" --rate 10

echo "Final position:"
ros2 topic echo /odom --once | grep -A3 "position:" 

Step 4: SLAM Correction Attempt Monitoring

I tried to monitor how well SLAM was correcting drift over short tests:

# Monitor map-odom transform during movement
ros2 run tf2_ros tf2_echo map odom 

🛠️ Partial Fixes and Improvements So Far

✅ Transform Timing Stabilized

I improved the startup order and transform publication frequency, which eliminated most extrapolation errors.

# Correct startup order
# Terminal 1: Start robot first, wait for full initialization
ros2 launch perceptor launch_robot.launch.py enable_camera:=false

# Terminal 2: Start SLAM after robot is ready  
ros2 launch slam_toolbox online_async_launch.py \
  params_file:=/home/pi/Roomba/slam_dev_ws/src/perceptor/config/mapper_params_online_async.yaml \
  use_sim_time:=false

# Terminal 3: Start RViz last
ros2 run rviz2 rviz2 -d src/perceptor/config/map.rviz

✅ Removed Feedback Loop in Twist_Mux

I had incorrectly set both input and output of twist_mux to /cmd_vel, creating an infinite feedback loop. Separating them fixed the phantom motion issue.

# config/twist_mux.yaml - BEFORE (broken)
navigation:
  topic: cmd_vel      # ❌ Same as output - creates feedback loop!
  timeout: 0.5
  priority: 10

# AFTER (fixed)  
navigation:
  topic: cmd_vel_nav  # ✅ Separate input topic
  timeout: 0.5
  priority: 10 

✅ Simplified Configuration Files

Merged redundant joystick configuration files for better maintainability.

# config/pro_controller.yaml - Consolidated Nintendo Pro Controller config
joy_teleop:
  ros__parameters:
    axis_linear:
      x: 1    # Left stick vertical
    scale_linear:
      x: 0.4  # Fast mode: 0.4 m/s, Slow mode: 0.2 m/s  
    axis_angular:
      yaw: 0  # Left stick horizontal
    scale_angular:
      yaw: 2.4  # Fast mode: 2.4 rad/s, Slow mode: 1.2 rad/s
    enable_button: 3  # Y button deadman switch
    enable_turbo_button: -1 

🛠️ Still Unresolved: Loop Closure

Despite these improvements, the SLAM drift continues to accumulate, and loop closure is not triggering as expected. Maps don’t realign reliably after revisiting known locations. Laser scans still exhibit visible drift over longer distances. Tried the following but did not help.

# config/mapper_params_online_async.yaml - Optimized for poor odometry
slam_toolbox:
  ros__parameters:
    # Faster transform publishing for better RViz sync
    transform_publish_period: 0.02  # Was: 0.05
    transform_timeout: 0.1          # Was: 0.2
    
    # More frequent map updates  
    map_update_interval: 2.0        # Was: 5.0
    
    # Reduce odometry reliance, increase scan matching
    minimum_travel_distance: 0.05   # Was: 0.5
    minimum_travel_heading: 0.05    # Was: 0.5
    distance_variance_penalty: 0.1  # Was: 0.5
    angle_variance_penalty: 0.1     # Was: 1.0
    
    # Correct frame configuration
    base_frame: base_link           # Was: base_footprint
    max_laser_range: 12.0          # Match RPLidar A1M8 specs

⚠️ Key Takeaways (So Far)

1. Not All Drift Is a Bug — But This One Is

SLAM does allow small drift during straight-line motion, but in my case, the system isn’t correcting it later. Loop closure is either failing to trigger or not correcting the map correctly.

2. TF Synchronization Issues Were a Real Problem

Fixing transform timing helped reduce errors in RViz and made debugging easier — but didn’t fix the core SLAM issue.

3. Good Odometry Helps, But Isn’t Enough

Even with decent odometry (~7% error), the SLAM system needs successful loop closures to stay accurate over time.

🔄 Next Steps

💭 In Summary

I’ve addressed some foundational system-level bugs — transform timing, configuration loops, and odometry validation — but true loop closure remains unresolved. Until I see consistent corrections during revisited areas, I won’t consider SLAM working as intended.

This is still very much a work in progress, and I’ll continue refining parameters and adding tools to get to robust, drift-free mapping.

Discussions