RETURN_TO_LOGS
SYS_DOCS: ONLINE
Robotics / Systems Engineering

Navigating Autonomous Systems: A Deep Dive into the ROS2 Navigation Stack

Aditya Pandit Sonwane
June 08, 2026
6 min read

1. Introduction & Kinematics

When deploying autonomous mobile robots, establishing high-accuracy localization and path planning is paramount. In this article, I document my findings configuring a differential-drive robot model in ROS2 Humble using the Navigation 2 (Nav2) stack and Gazebo simulator.

For a differential-drive robot, the kinematics mapping wheel velocities (w_R, w_L) to linear velocity v and angular velocity w is governed by:

text
v = (r / 2) * (w_R + w_L)
w = (r / L) * (w_R - w_L)

Where r is the wheel radius and L is the track width (distance between wheels). Integrating these equations over time provides the dead-reckoning odometry estimation.

[SYS_TRANSITION]

2. Sensor Fusion & EKF Calibration

To account for wheel slippage, odometry data is fused with an Inertial Measurement Unit (IMU) using the robot_localization package. This node runs an Extended Kalman Filter (EKF) to estimate the robot's 3D state.

Here is the parameter configuration matrix utilized in our ekf.yaml:

yaml
ekf_filter_node:
  ros__parameters:
    frequency: 30.0
    sensor_timeout: 0.1
    two_d_mode: true
    publish_tf: true

    map_frame: map
    odom_frame: odom
    base_link_frame: base_link
    world_frame: odom

    odom0: /odom
    odom0_config: [true,  true,  false,
                   false, false, true,
                   true,  false, false,
                   false, false, true,
                   false, false, false]

    imu0: /imu/data
    imu0_config: [false, false, false,
                  false, false, true,
                  false, false, false,
                  false, false, true,
                  true,  false, false]

By filtering coordinate noise, we reduced localization variance by over 40% in our dynamic SLAM trials.

[SYS_TRANSITION]

3. Custom Velocity Command Publisher (ROS2 C++ Node)

To control wheel actuators, I wrote a C++ node that converts cmd_vel geometry messages into raw motor PWM values, publishing telemetry diagnostic reports:

cpp
#include <chrono>
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "std_msgs/msg/float32_multi_array.hpp"

using namespace std::chrono_literals;

class MotorControllerBridge : public rclcpp::Node {
public:
  MotorControllerBridge() : Node("motor_controller_bridge") {
    subscription_ = this->create_subscription<geometry_msgs::msg::Twist>(
      "/cmd_vel", 10, std::bind(&MotorControllerBridge::cmd_vel_callback, this, std::placeholders::_1));
    
    publisher_ = this->create_publisher<std_msgs::msg::Float32MultiArray>("/motor_speeds", 10);
  }

private:
  void cmd_vel_callback(const geometry_msgs::msg::Twist::SharedPtr msg) const {
    double linear = msg->linear.x;
    double angular = msg->angular.z;
    
    // Convert velocities to right/left motor demands
    float right_speed = linear + (angular * 0.22   2.0); // Track width = 0.22m
    float left_speed = linear - (angular * 0.22   2.0);

    auto message = std_msgs::msg::Float32MultiArray();
    message.data = {right_speed, left_speed};
    publisher_->publish(message);
    
    RCLCPP_INFO(this->get_logger(), "Published wheel speeds: R=%.2f, L=%.2f", right_speed, left_speed);
  }

  rclcpp::Subscription<geometry_msgs::msg::Twist>::SharedPtr subscription_;
  rclcpp::Publisher<std_msgs::msg::Float32MultiArray>::SharedPtr publisher_;
};

int main(int argc, char * argv[]) {
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MotorControllerBridge>());
  rclcpp::shutdown();
  return 0;
}
[SYS_TRANSITION]

4. Diagnostics & Testing Metrics

We verified path tracking convergence against dynamic maps generated using Cartographer.

text
[TARGET_MAP] =======================================
[INFO] Loading Cartographer SLAM submaps... Done.
[INFO] Resolving transform: /map -> /odom [0.002s latency]
[METRIC] Target goal distance tolerance: 0.05m
[METRIC] Goal reached. Deviation: X=+0.012m, Y=-0.008m
====================================================

In subsequent sprints, we are planning to replace the local planner configuration with a Model Predictive Control (MPC) node to achieve smoother trajectories around obstacles.

text
Aditya Pandit Sonwane
Aditya Pandit Sonwane

Systems & Robotics Engineer. Developing autonomous mobile platforms, configuring ROS2 EKF nodes, writing real-time CUDA perception pipelines, and embedded microcontrollers logic.