Skip to content

About

Mobi: indoor service robot on ROS 2. Teensy 4.1 PID firmware, C++ ros2_control interface, SLAM, AMCL, Nav2 with own planner plugins, undocking action, voice-to-intent agent, PyQt5 launcher.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

Mobi — Indoor Service Robot (ROS 2)

ROS 2 Humble Nav2 Teensy 4.1 Real hardware

Mobi is a differential-drive service robot I designed and built for indoor spaces. It maps a room, localizes, navigates through tight passages, backs out of its dock on its own, and acts on spoken commands such as "undock" or "go to the kitchen". A Raspberry Pi runs the ROS 2 stack; a Teensy 4.1 runs the motor control; the same packages drive a Gazebo twin.

The torso carries a lifting rail and a pan-tilt head, built for arms that were planned but not added. Navigation, voice control and docking run on the real robot.

Demo

Mobi autonomous service robot
Voice-driven service demo
Mobi SLAM and navigation
SLAM and navigation
Robot Launcher GUI
Robot Launcher GUI

Playlist: ROS 2 Projects

Case study: Mobi on my portfolio

Also: joystick teleoperation · face and hand tracking driving the head in Gazebo

Build log on LinkedIn (2025): physical structure · drive base progress · encoder readings at speed · joystick control · SLAM map distortion · autonomous chassis · A* vs Dijkstra · Robot Launcher app · adding the torso · voice docking with an LLM

Building Mobi from CAD to simulation (4-part series): Fusion 360 CAD · URDF part 1 · URDF part 2 · spawning in Gazebo

Key features

  • Mapping and navigation — slam_toolbox, AMCL, Nav2 (Smac Planner 2D + Regulated Pure Pursuit) with replanning and recovery behaviour trees.
  • Own motor control stack — Teensy 4.1 firmware with per-wheel PID at 100 Hz and a C++ ros2_control hardware interface over 500 kbaud serial.
  • Voice → action — Whisper transcribes, Gemini returns an intent (dock, undock, go_to_kitchen, go_to_user, stop), and a task handler turns it into Nav2 goals or an Undock action.
  • Undocking action server — reverses until the ultrasonic sensor clears 0.35 m, then turns away using IMU yaw rate; cancellable, with timeouts.
  • Safety by arbitration — a LiDAR safety-stop node holds a twist_mux lock at priority 255 above joystick, keyboard and Nav2.
  • Robot Launcher — a PyQt5 app that starts the real robot or the simulation with map, world, SLAM mode and serial port chosen from drop-downs, and saves versioned maps.
  • Own planner and controller plugins — A*, Dijkstra, PD and pure-pursuit written as Nav2 plugins in C++ (available, not enabled by default).

Hardware

Compute Raspberry Pi 4B (8 GB) + 7-inch touchscreen
Motion Teensy 4.1, 2 × DC gear motors with high-resolution quadrature encoders, PWM/DIR driver
Sensing RPLiDAR, MPU6050 IMU, ultrasonic sensor, camera
Body Torso on dual lead-screw linear rails, pan-tilt head; 12 V 30 A supply with 5 V 20 A buck during development

Software stack

ROS 2 Humble · Nav2 · slam_toolbox · AMCL · ros2_control · twist_mux · Gazebo Fortress · OpenCV (ArUco) · MediaPipe · Whisper · Gemini API · ElevenLabs · PyQt5 · C++ · Python

Packages

mobi_description URDF and worlds · mobi_firmware Teensy sketch + hardware interface · mobi_controller diff-drive, joystick, twist_mux · mobi_mapping / mobi_localization / mobi_navigation SLAM, AMCL, Nav2 · mobi_planning / mobi_motion own Nav2 plugins · mobi_script task handler + undock action · mobi_speech voice · mobi_vision ArUco, face and hand tracking · mobi_utils safety stop · mobi_gui Robot Launcher · mobi_bringup launch files

Run

mkdir -p ~/mobi_ws/src && cd ~/mobi_ws/src
git clone https://github.com/Curious-Utkarsh/mobi-service-robot.git .
cd ~/mobi_ws && rosdep install --from-paths src --ignore-src -r -y
colcon build --symlink-install && source install/setup.bash

ros2 launch mobi_bringup simulated_robot.launch.py world_name:=small_house map_name:=small_house   # simulation
ros2 launch mobi_bringup real_robot.launch.py serial_port:=/dev/ttyUSB0 map_name:=Room1          # real robot
ros2 topic pub --once /mobi/intents std_msgs/msg/String "data: 'undock'"                           # test a behaviour

The voice node reads its keys from the environment: export GEMINI_API_KEY=... and export ELEVENLABS_API_KEY=... before launching it.

Engineering notes

  • Encoders the ESP32 could not keep up with. With an ESP32 at 240 MHz, wheel counts from the high-resolution encoders were unreliable, which corrupted odometry and everything above it. I isolated the encoder layer, confirmed counts were being missed, and moved motor control to a Teensy 4.1 at 600 MHz, which reads them cleanly.
  • Tight passages. With a 0.58 × 0.56 m costmap footprint the robot had to pass ~0.8 m openings. On the real robot it stopped mid-path even with obstacles far away; I traced it to costmaps not clearing after dynamic obstacles moved. The current workaround disables obstacle checks in the local controller — a stopgap, not a fix; a behaviour-tree recovery is next.

Related

  • MechaBot — the smaller robot I built next, with QR + LiDAR docking
  • Aura — simulated predecessor with an arm and vision docking
  • Bumperbot — an earlier differential-drive robot on real hardware
  • TurtleBot3 ArUco docking — where the docking work started

Credits

Package layout and the mobi_planning / mobi_motion plugins follow Antonio Brandi's Self-Driving and ROS 2 — Learn by Doing course series. House and warehouse worlds use AWS RoboMaker models.


Kumar Utkarsh · LinkedIn · MechaMind Labs

About

Mobi: indoor service robot on ROS 2. Teensy 4.1 PID firmware, C++ ros2_control interface, SLAM, AMCL, Nav2 with own planner plugins, undocking action, voice-to-intent agent, PyQt5 launcher.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages