Building a Smart Roomba: A Symphony of Hardware and ROS

This article presents a detailed exploration of a custom Roomba vacuum system architecture, built using a Raspberry Pi Zero 2 W and the Robot Operating System (ROS). The system integrates various sensors including YDLiDAR X4, MPU6050 IMU, and HMC5883L magnetometer, organized in a layered architecture comprising hardware interface, localization, navigation, high-level functions, and supervisor layers. Each layer serves specific functions, from basic hardware communication to complex autonomous navigation and cleaning path planning, demonstrating how open-source components can be combined to create a sophisticated robotic system.

7 Minutes reading time

Behold the masterpiece that AI hallucinated while reading this post:

"How Little Roomba Learned to Think: A Story of Robots and Raspberry Pies"

(after I fed it way too many marketing blogs and memes)

Created using DALL-E 3

AI-Generated: How Little Roomba Learned to Think: A Story of Robots and Raspberry Pies

Standing on the shoulders of giants

My custom Roomba vacuum uses a lot of free available open source software. Without it, it wouldn’t be possible to create and compose a complex system such as an autonomous robot in a reasonable amount of time.

The core ingredients are:

  • Raspberry Pi Zero 2 W

  • Ubuntu 64bit OS

  • Additional sensors, connected by USB, UART or I2C bus.

  • Robot Operating System (ROS), Noetic Release

The Raspberry Pi 2W is the brain of the system. This brain runs an Ubuntu 64bit operating system. There are additional sensors connected to the Pi such as the Roomba itself using UART and the Roomba Open Interface, a YDLiDAR X4 using USB and a MPU6050 IMU and a small OLED display using the I2C bus. This is more or less the whole hardware layer of the system.

On top of the hardware layer runs the software layer. Heart of this layer is the ROS, the Robot Operating System. It is an open-source robotics middleware suite. It is not a real operating system, but a set of software frameworks for robot software development. It provides services such as hardware abstraction, low-level device control, implementation of commonly used functionality, message-passing between processes, and package management.

The ROS components are controlled by project-specific parts, such as the web-based user interface, system supervision and implementation of the navigation and cleaning functionality. Measured in lines-of-code, the project-specific part is tiny compared to the out-of-the-box functionality of the ROS suite. It is truly standing on the shoulders of giants.

The Roomba layered Architecture

The following diagrams shows the whole hardware wiring of the described components. There is really no magic involved.

roombahardware

The Roomba only has a serial interface, which can be connected to the Raspberry Pi’s serial interface. However, we have to keep in mind the different signal levels, so I am using a bidirectional level shifter between them. The level shifter is not part of the diagram.

The YDLiDAR X4 also has a serial interface. However, it comes with a serial-to-usb adapter, and the whole software suite including the ROS driver fits to this adapter. I wanted to reuse as much as possible, so I used the serial-to-usb adapter and connected the LiDAR with USB to the Raspberry. In the future I also might use a multi-uart-to-i2c adapter, and connect everything using the I2C bus. But I didn’t have time to check this out yet.

The MPU6050 IMU, the HMC5883L magnetometer and the OLED displays are directly connected to the I2C bus of the Raspberry.

Now comes the interesting part. How is the Roomba software organized? First, I’ll show you a diagram, and I’ll explain it later:

roombalayers

This is a very simplistic overview of the whole system. The key building block is the concept of a ROS node. Nodes are processes doing stuff. Nodes can communicate with other nodes by calling provided services or consuming or publishing messages over topics. I didn’t include the relevant topics in the overview to make it simpler for the beginning.

Let’s start from bottom to top:

Hardware interface layer

The hardware interface layer is responsible to communicate with the physical hardware.

roombahardwareinterfacelayer

The roombainterface node reads the Roomba sensor data stream such as wheel encoders or bumpers and publishes the data over topics. It also consumes messages controlling the actuators such as the left or right wheel or the vacuum. This node is Roomba specific.

The imu node reads data from the MPU6050 IMU and publishes the data over topics. The nature of this data is noisy. This node is Roomba specific.

The differentialodometry node reads the Roomba sensor messages and converts data from the wheel encoders to a robot odometry including a pose. A pose includes a position, and orientation and additional velocity and acceleration information. All of this is derived from the wheel encoder data. The nature of this data is noisy and it will drift over time a lot. This node is Roomba specific.

The ydlidar node reads data from the YDLiDAR X4 360 degrees laser scanner and publishes the data over topics. The nature of this data is noisy, but it is the only source of absolute measurements in this stack. We can measure the absolute distance to a wall with a laser scan. All other sensors can only track relative movements and acceleration. This node is provided by the LiDAR manufacturer.

The ekf_localization_node node does sensor fusion. As mentioned above, the IMU and odometry data are noisy, and tend to drift over time. The ekf_localization_node node uses an Extended Kalman filter to create and continuously refine a position estimation based on the noisy sensor data. This should remove drift and increase precision of the measured position, acceleration and velocity. This node is part of the ROS stack.

Localization layer

The localization layer creates a precise localization of the Roomba on a map based on absolute(LiDAR) and relative (fused odometry) data.

roombalocalizationlayer

The gmapping node provides a SLAM(Simultaneous Localization and Mapping) implementation. It consumes LiDAR and odometry data and continuously builds a map and a position of the Roomba on the map. This map and position is refined over time as new sensor data arrives. The Roomba stack uses the gmapping node as long as there is no stored map of a known room to clean available. This node is part of the ROS stack.

The mapserver node is used when the Roomba is started in a known room. It reads a stored map from disk and provides the map data to the ROS stack. This node is part of the ROS stack.

The amcl node is used with a stored map from the mapserver node. It uses a particle filter to find the robot position on a given map based on odometry and LiDAR data. However, it cannot refine or change a stored map. This node is part of the ROS stack.

Either the gmapping or mapserver+amcl nodes are started by the supervisor, depending on the requested operational mode.

Navigation layer

The navigation stack can guide a robot from one place of the map to another place. It reads sensor data and positional information and creates steering commands for the robot to reach a target pose.

roombanavigationlayer

The move_base node is part of the ROS navigation stack and also part of the ROS suite. It consumes map data and positional information and can compute plans to reach a given target pose on the map. It consumes sensor information such as LiDAR or bumper data to add additional information to the provided map. This information is stored as cost maps, which can be either static (the map itself) or volatile (temporal obstacles, detected by LiDAR or bumper data). The computed plan is continuously refined. Move_base can also cancel the computed plan once it detects that the target pose is no longer reachable. This node is part of the ROS stack.

High level functions layer

The high level functions layer implements the cleaning functionality. It can compute a full coverage path for the Roomba based on a map and instructs the navigation layer the follow that path.

roombahighlevelfunctionslayer

The highlevel node implements the whole functionality. It uses the map data to create a minimum spanning tree from the grid cells. The cleaning path is derived from this spanning tree. It also implements recovery mechanisms once the navigation layer detects a part of the path is no longer reachable. The highlevel node also actively decides when to activate or deactivate the vacuum actuators. This node is Roomba specific.

Supervisor functions layer

The supervisor functions layer provides the web-based user interface. It is always active, and starts and stops on demand all other layers of the system.

supervisorlayer

The supervisor node implements the user interface. Once a user decides to clean a room, the supervisor starts all other layers of the stack and controls their lifecycle. It provides visual feedback such as the map, the current robot position, the already cleaned path and so on. It also uses the OLED display to render telemetry and status data directly on the device. This node is Roomba specific.

That’s it

Here we are. Now you’ve got the first overview of the whole Roomba ROS hardware and software stack. If you like to dived deeper into the whole system, please take a look at the source code provided at the GitHub project page. Stay tuned for the next post of the Roomba Series. Thank you for reading!

Git revision: 2e692ad