VC.
ProjectsAboutContact

Varun Cumbamangalam.

Senior IoT and Edge AI Engineer, AI Product Manager at OraLens Healthcare

ProjectsAboutContactPrivacy

© 2026 Varun Cumbamangalam. Built with Next.js and Tailwind CSS.

# Occupancy grid update + RRT path planner import numpy as np import random def update_occupancy_grid(grid, robot_pos, angle, distance, resolution=0.05): """Bresenham ray-cast to mark cells along IR beam.""" x0, y0 = world_to_grid(robot_pos, resolution) end_x = robot_pos[0] + distance * np.cos(angle) end_y = robot_pos[1] + distance * np.sin(angle) xn, yn = world_to_grid((end_x, end_y), resolution) for (cx, cy) in bresenham_line(x0, y0, xn, yn): grid[cy, cx] = max(0, grid[cy, cx] - 0.1) # free grid[yn, xn] = min(1, grid[yn, xn] + 0.9) # occupied def rrt_plan(grid, start, goal, max_iter=2000, step=0.1): """RRT path planner on occupancy grid.""" tree = [start] parent = {start: None} for _ in range(max_iter): rand = random_free_point(grid) if random.random() > 0.05 else goal nearest = min(tree, key=lambda n: dist(n, rand)) new = steer(nearest, rand, step) if is_free(grid, nearest, new): tree.append(new) parent[new] = nearest if dist(new, goal) < step: return reconstruct_path(parent, new) return None
All Projects
ResearchHardwareAI/ML

Sensor-Based SLAM Navigation - iRobot Create

A full SLAM pipeline on embedded hardware, from IR range data to navigation.

0°

Sensor Coverage

RRT*

Path Planner

University of Utah

Research Lab

Tech Stack

MATLABiRobot CreateRRT Path PlanningSLAMDijkstraBluetooth SerialServo Control

The problem

A robot dropped into an unknown room has no map. It can't plan a path to a destination it can't see, and it can't build a map it hasn't explored yet. The only way forward is to do both at once - map the environment while navigating it - using only the readings from its own sensors.

The challenge

The iRobot Create platform has no built-in mapping capability. The challenge was to mount IR rangefinders on servo motors to achieve 360° coverage, build an occupancy grid map from the noisy sensor readings in real-time, and use that map to plan collision-free paths through a maze - all within the compute and power constraints of an embedded system.

Architecture & System Design

Sensor-Based SLAM Navigation - iRobot Create system architecture

Rotating sensors build real-time 2D map of environment from range data. Occupancy grid algorithm marks free and occupied spaces. Path planning algorithm searches map for collision-free navigation routes. Robot executes waypoints using heading control feedback.

Full system schematic available upon request

Two servo motors rotated IR rangefinder sensors to sweep the environment and collect range data at discrete angular increments. An occupancy grid algorithm converted range readings to a 2D binary map (occupied/free). The map was iteratively updated as the robot moved, with odometry used for dead-reckoning position estimates. A Rapidly-exploring Random Tree (RRT) planner searched the map for collision-free paths to the goal, with the robot executing waypoints via a proportional heading controller.

Code Walkthrough

3-step walk-through of the production implementation. File paths and intent appear above each block.

  1. 01

    Step 1 of 3

    IR range sweep → probabilistic occupancy grid

    slam/occupancy.py

    Each IR rangefinder reading gives range and angle relative to the robot. Bresenham's line algorithm traces the beam through the grid: every cell along the ray gets a small free-space credit, and the terminal cell gets a large occupied hit. Probabilistic updates (add/subtract, clamp 0-1) mean a single noisy reading doesn't immediately flip a cell - the map stabilises over repeated sweeps.

    python
    import numpy as np
    
    def update_occupancy_grid(grid, robot_pos, angle, distance, resolution=0.05):
        """
        Bresenham ray-cast to update cells along a single IR beam.
        resolution: metres per grid cell.
        """
        x0, y0 = world_to_grid(robot_pos, resolution)
        end_x   = robot_pos[0] + distance * np.cos(angle)
        end_y   = robot_pos[1] + distance * np.sin(angle)
        xn, yn  = world_to_grid((end_x, end_y), resolution)
    
        # Cells along ray → free evidence (subtract 0.1)
        for (cx, cy) in bresenham_line(x0, y0, xn, yn):
            grid[cy, cx] = max(0.0, grid[cy, cx] - 0.1)
    
        # Terminal cell → occupied evidence (add 0.9, clamp to 1)
        grid[yn, xn] = min(1.0, grid[yn, xn] + 0.9)
    
    
    def world_to_grid(pos, resolution):
        """Convert world coordinates [m] to integer grid indices."""
        return int(pos[0] / resolution), int(pos[1] / resolution)
    Takeaway

    Probabilistic updates tolerate sensor noise - a single return doesn't hard-set a cell; the map stabilises after 3-4 overlapping sweeps, matching what was observed in practice.

  2. 02

    Step 2 of 3

    RRT path planner on the occupancy grid

    slam/rrt.py

    Grid-based search (Dijkstra/A*) is complete but slow on a 200×200 grid with a 3-DOF robot. RRT builds a random tree that biases growth toward unexplored free space, finding a path in milliseconds. A 5% goal-bias nudges the tree toward the target without the greediness that causes local minima in pure goal-directed search.

    python
    import random
    
    def rrt_plan(grid, start, goal, max_iter=2000, step=0.1):
        """RRT path planner operating on a probabilistic occupancy grid."""
        tree   = [start]
        parent = {start: None}
    
        for _ in range(max_iter):
            # 95% random sample, 5% goal bias
            rand    = random_free_point(grid) if random.random() > 0.05 else goal
            nearest = min(tree, key=lambda n: dist(n, rand))
            new     = steer(nearest, rand, step)
    
            if is_free(grid, nearest, new):
                tree.append(new)
                parent[new] = nearest
                if dist(new, goal) < step:
                    return reconstruct_path(parent, new)
    
        return None   # No path found within iteration budget
    
    
    def is_free(grid, a, b, threshold=0.5):
        """Check all cells along segment a→b are below the occupancy threshold."""
        for (cx, cy) in bresenham_line(*world_to_grid(a, 0.05),
                                       *world_to_grid(b, 0.05)):
            if grid[cy, cx] > threshold:
                return False
        return True
    Takeaway

    The 5% goal bias is the key tuning parameter - too low and the tree explores randomly forever, too high and it gets stuck in local narrow passages between obstacles.

  3. 03

    Step 3 of 3

    Waypoint follower - iRobot Create serial execution

    slam/execute_path.py

    The RRT output is a list of (x, y) waypoints in world coordinates. The robot executes them via a proportional heading controller: the bearing error between current heading and the next waypoint drives the turn radius, and odometry from wheel encoder deltas keeps the dead-reckoning position updated without GPS.

    python
    import serial, struct, math, time
    
    DRIVE_DIRECT = 145   # iRobot Create OI opcode
    
    def irobot_drive(ser, vel_l, vel_r):
        """Send DRIVE_DIRECT command: individual wheel velocities in mm/s."""
        cmd = struct.pack('>Bhh', DRIVE_DIRECT, int(vel_r), int(vel_l))
        ser.write(cmd)
    
    def execute_path(ser, waypoints, velocity=150):
        """
        Drive through RRT waypoints using proportional heading control.
        Dead-reckoning position updated from encoder odometry each cycle.
        """
        for wp in waypoints:
            while True:
                dx, dy = wp[0] - robot_pos[0], wp[1] - robot_pos[1]
                if (dx**2 + dy**2) < 0.05**2:   # 50 mm arrival radius
                    break
    
                bearing_err = math.atan2(dy, dx) - robot_heading
                # Wrap to [-π, π]
                bearing_err = (bearing_err + math.pi) % (2*math.pi) - math.pi
    
                # Proportional: large error → tight turn; small error → straight
                turn = bearing_err * 80   # gain: 80 mm/s per radian
                irobot_drive(ser, velocity - turn, velocity + turn)
                time.sleep(0.05)
                update_odometry(ser)
    
        irobot_drive(ser, 0, 0)   # Stop
    Takeaway

    Wrapping bearing error to [-π, π] before the proportional gain prevents the 2π discontinuity from commanding a full reverse spin when the robot overshoots 180°.

Results

The robot successfully mapped lab maze environments and navigated from start to goal positions autonomously. The occupancy grid converged to an accurate representation within 3-4 passes of the environment. RRT path planning consistently found collision-free paths in under 500ms on the embedded hardware.

Gallery & Demos

Occupancy Grid Map - SLAM Output

Occupancy Grid Map - SLAM Output

2D binary map built from 360° IR sensor sweeps showing occupied (walls) and free (navigable) spaces.

Lab Environment 1 - Test Space

Lab Environment 1 - Test Space

First maze-like test environment where the robot performs autonomous mapping and navigation.

Lab Environment 2 - Alternate Configuration

Lab Environment 2 - Alternate Configuration

Second test space with different wall layout to validate robustness of the SLAM pipeline.

RRT Path Planning - Collision-Free Route

RRT Path Planning - Collision-Free Route

Rapidly-exploring Random Tree search result showing planned path from start to goal avoiding obstacles.

Planning Map - Cost Grid

Planning Map - Cost Grid

Navigability cost grid used by the planner to guide the robot through open space while maintaining clearance from walls.

RRT Algorithm Demo - Tree Exploration
Click to expand

RRT Algorithm Demo - Tree Exploration

Real-time visualization of the Rapidly-exploring Random Tree building a collision-free path through the mapped environment.

Navigation - Short Path Execution
Click to expand

Navigation - Short Path Execution

Robot following a brief planned path while continuously updating the occupancy grid from new sensor readings.

Navigation - Long Path Execution
Click to expand

Navigation - Long Path Execution

Extended navigation sequence demonstrating sustained mapping accuracy and path tracking over multiple turns and obstacles.

Servo Motor Control - Sensor Rotation
Click to expand

Servo Motor Control - Sensor Rotation

Mechanism for rotating the IR rangefinder 360° to collect panoramic range data for real-time map updates.

Stepper Motor Initialization Test
Click to expand

Stepper Motor Initialization Test

Hardware testing of stepper motor control for servo mounting and sensor alignment verification.

Click any image or video to expand · ← → keys navigate

University of Utah

More from University of Utah

Vision-Based Autonomous Quadrotor

Researcher

MS thesis project: a quadrotor capable of autonomously taking off, navigating, and perching on branch-like structures using only visual feedback - designed for autonomous crop monitoring in agricultural fields.

OpenCVPID ControlROS

Multi-Arm Coordination - 2-DOF QUANSER

Researcher

Dual-arm robotic manipulation system using 2-DOF QUANSER robots with a master-slave architecture - one arm controlling position, the other controlling force - to collaboratively manipulate objects with precision.

MATLAB/SimulinkQUARCImpedance Control

Adaptive Backstepping - Indoor Micro-Quadrotor

Researcher

Nonlinear controller design for an indoor micro-quadrotor with a suspended pendulum mass - a highly unstable configuration. Adaptive backstepping outperformed classical PID in robustness tests across multiple flight regimes.

MATLAB/SimulinkBackstepping ControlLyapunov Analysis

PUMA 6-DOF Robot Arm - Forward & Inverse Kinematics

Researcher

Forward and inverse kinematics solver for a 6-DOF PUMA 762 robot arm, using Denavit-Hartenberg parameters and an interactive 3D MATLAB GUI with joint sliders, a motion trail, and collision detection.

MATLABDenavit-HartenbergRobotics Toolbox

Sampling-Based & Graph-Search Motion Planning

Researcher

MATLAB implementations of four canonical path-planning algorithms - Dijkstra, A*, PRM, and RRT - applied to a differential-drive robot navigating bitmap maps in configuration space, with real hardware execution on an iRobot Create.

MATLABRRTPRM

Monocular Depth Estimation for UAV Perch Landing

Researcher

C++ and OpenCV vision system that estimates a landing perch's 3D position and orientation from one monocular camera. It uses image moments, covariance eigendecomposition, and focal-length triangulation for closed-loop quadrotor control.

C++OpenCVHSV Segmentation

RC Fixed-Wing Glider - Servo Actuation & Aerodynamics

Researcher

Fixed-wing RC glider with two servos for roll and pitch, a brushless DC motor, and an ESC for forward thrust. Flight-tested outdoors.

Servo ControlBrushless DC MotorESC

Interested in this work?

I can walk through the architecture and code during an interview.

Get in Touch All Projects