DIY LiDAR SLAM Robot from Scratch
Download Vagabond: https://github.com/NexusAurora/Vagabond
Welcome
to this exciting exploration into the world of SLAM—Simultaneous
Localization and Mapping. Most people working on SLAM typically rely on
Robot Operating System (ROS) and predefined libraries, using simulations
instead of real-world scenarios. But we decided to take a different
route. We believed that if we wanted to truly master SLAM, we needed to
do it the right way—from scratch.
Rather than using pre-built
templates and libraries, which can be picked up at any time, we
challenged ourselves to build a SLAM system manually. Now, we didn’t
have the budget to buy a professional LiDAR sensor, which can be quite
expensive, so we decided to create our own using a servo motor and a
Time-of-Flight laser distance sensor. Along with that, we built a
robotic car, calibrated the entire system, and coded the entire SLAM
architecture by hand in pure Python. We didn’t stop there; we also used
OpenCV to display the results.
Why go through all this trouble?
Because real-world data is messy—it's noisy, error-prone, and
unpredictable. By learning how to make a DIY sensor work effectively, we
prepared ourselves to handle the complexities of real-world SLAM. We
implemented particle filters, noise reduction techniques, temporal
filtering, and probabilistic methods. To refine our data, we also used
AI to calibrate and adjust the incoming data, providing us with a
cleaner, more accurate experience. On top of that, we applied advanced
techniques like erosion to process and refine probabilistic maps,
leading to highly accurate results.
This project required a deep dive
into mathematics and algorithms, and the only way to fully grasp these
concepts was to recreate the entire system from scratch using
Python—coding every line by hand. Although many people use ROS, and we
did too eventually, our journey began with Python. By the end of this
video, you’ll see how we ported and transferred our Python-based system
into ROS on a Linux platform, displayed the results using RViz, and how
the entire system functions with nodes, topics, listeners, and
publishers in the ROS environment.
Before we get into the ROS part,
let's break down how we developed the system in Python. We divided the
program into four key parts, each serving a crucial role:
1. Driver
Node: This bridges the gap between the program running on the computer
and the robot itself. It handles all the functionalities required to
communicate with the robot, such as receiving and calibrating data to
ensure accuracy. It also sends commands back to the robot when we want
it to move.
2. Update Grids Node: This part is responsible for
converting all measurements into Cartesian coordinates, updating
occupancy maps, and plotting probabilistic grids that represent free
space. It also pinpoints the robot's pose and gives us the grids we need
to visualize the environment.
3. Path Finding: This node ensures
that we always have the latest path available. Whenever the robot’s
position, the occupancy grid, or the goal position changes, this node
updates the path accordingly. It also listens for mouse clicks in RViz
within ROS, allowing us to update the robot’s path to any clicked goal
position.
4. Navigation: This final part calculates the robot’s next
steps—how much it should turn, whether it has reached the goal, and in
which direction it should move. It uses models that we’ll discuss later
to send the appropriate commands to the robot with the right timing and
precision.
Additionally, we've published a Python library called
"Vagabond," which you can use for pathfinding in your own projects.
We’ll provide more details on this later in the video.
So, stay tuned
as we dive deeper into the technical aspects of SLAM, explore our DIY
solutions, and reveal how you can apply these techniques to your own
robotics projects!
Before diving into the project, let me walk
you through how we built the robot itself. To work effectively with
SLAM, it's crucial to understand how the incoming data behaves, and for
that, we needed a reliable distance sensor. Initially, we opted for an
ultrasonic sensor, mainly because of our previous project, SARDA, which
also used ultrasonic technology. That project involved navigation, but
it wasn’t as advanced as what we’re tackling here. Back then, we were
just beginning to explore robotics, focusing on basic navigation and
mapping, but now we’re ready to take it to the next level.
We already
had the know-how on using ultrasonic sensors to transmit data from the
robot to a computer, as well as how to drive the mobile robot using
motor driver modules. So, we began by gathering all the necessary
components: DC motors, wheels, battery holders, ultrasonic sensors, and
of course, breadboards.
Initially, our plan was to incorporate two
distance sensors—specifically, ultrasonic sensors—mounted on a servo to
rotate and scan the front and back of the map. However, we later
upgraded to a single time-of-flight (TOF) laser distance sensor for
better accuracy. The mobile robot is powered by an ESP32
microcontroller, which communicates with the servo, distance sensors,
and motor driver. We also included a logic level shifter to manage the
voltage between 5V and 3V. The robot is equipped with three 3.7V Li-ion
batteries and a power switch to easily turn the system on and off.
This
time around, we decided to move away from the laminated fiber materials
used in our previous robots. Instead, we chose fiberglass for its
transparency and increased thickness. The new design included a large
chassis to house all the components, with wheels attached, and a
longitudinal piece that would hold the ultrasonic sensors on either
side.
We began by working on the ultrasonic sensors, which were
mounted on a very thin, perforated breadboard. We carefully soldered the
two ultrasonic sensors onto the breadboard, ensuring they were securely
fixed. This setup was then attached to the longitudinal piece of
fiberglass. Additionally, we mounted a servo on this longitudinal piece,
allowing it to rotate and scan the surroundings. The servo could pivot
between 0 and 100 degrees at both the front and the back, providing
comprehensive scanning capabilities for the robot.
Initially, the
ultrasonic sensors proved to be a poor choice for our project. They
were highly unpredictable, noisy, and inaccurate, though they did manage
to get the job done for a time. However, we soon realized that a
time-of-flight laser distance sensor would be a better option. At that
moment, we didn’t have that upgrade yet, so we worked with what we had,
focusing on optimizing and reducing noise to make the ultrasonic sensors
function as well as possible.
Once the mechanical assembly was
complete, we physically tested the servo’s rotation from left to right,
ensuring it could scan effectively. The data from the ultrasonic sensors
was sent to the ESP32 microcontroller, which then transmitted it to the
computer via UDP in JSON format for processing.
We faced significant
challenges in converting this data into Cartesian coordinates, plotting
the points accurately, and accounting for various error states. Testing
was particularly difficult due to the cluttered environment at home,
filled with furniture and small objects that interfered with accurate
sensor readings. To address this, we needed a compact, controlled
environment where we could effectively evaluate the sensor’s
performance, especially given its limited range. This led us to build a
small DIY testing setup to validate our ultrasonic sensor’s
measurements.
To effectively test the ultrasonic sensors, we
needed a controlled environment where our robot could move around
freely. We repurposed an old TV box to create a makeshift playground.
After laying the box flat on the ground, we cut one side to form a small
arena with walls for the robot to navigate and scan.
The original
walls were too short, so we extended their height by mounting them on
pillars, ensuring the ultrasonic sensors could adequately detect and
measure distances. To make this setup more functional, we used tape and
markers to create measurement guides and ensured the walls were as
straight as possible. We also covered the corners with pieces of
cardboard to smooth out the edges, preventing the sound waves from
getting trapped or deflected.
The result was a well-constructed
mini-arena where our robot could test its navigation and measurement
capabilities. We even added small blocks to simulate obstacles and
walls, giving us a comprehensive environment to evaluate how well the
sensors performed in detecting and mapping the surroundings.
We
ran into a significant issue with the ultrasonic sensors. When we placed
them in the arena, their performance was inconsistent. The sensors
struggled, especially when scanning corners, where the sound waves
seemed to get trapped or reflected unpredictably. Additionally, when we
tested them against various surfaces, like a marble wall, the sensors
often failed to detect the surface accurately, leading to unreliable
data.
Recognizing the limitations of the ultrasonic sensors, we
decided to switch to a time-of-flight laser distance sensor from a
previous project. This sensor promised more accurate and stable
measurements compared to the noisy and erratic ultrasonic sensors. We
carefully disassembled our old robot, removed the time-of-flight sensor,
and mounted it on a servo in place of the ultrasonic sensors. This
upgrade significantly improved the robot's performance.
With the
time-of-flight laser distance sensor now in place, the robot’s scanning
and measurement capabilities were greatly enhanced. But before we dive
into the impressive results of this upgrade, let’s take a closer look at
how we built the robot chassis itself.
Building on our previous
experience with SARDA, our latest robot chassis was crafted from thick,
transparent fiberglass. This time, we added some modularity to our
design for added flexibility. We secured DC motors on either side of the
robot and installed a freely rotating wheel at the front and back to
ensure smooth movement. The setup also included battery holders, a motor
driver, and other essential components.
To make the system modular,
we designed it so that key components could be easily swapped or
upgraded. For instance, we soldered header pins onto a laminated
perforated board, allowing us to plug and unplug the ESP32
microcontroller as needed. This 'plug-and-play' approach meant that if
we needed to replace the ESP32 or repurpose it for another project, we
could simply disconnect it and reconnect it later.
Similarly, for the
motor driver, we avoided permanent soldering by using header pins and
plug-in connectors. This way, we could easily detach the motor driver if
necessary. All the wiring was meticulously routed, secured with zip
ties, and organized for a clean, efficient setup. Finally, we mounted
the servo and the compact time-of-flight laser distance sensor on top of
the chassis, completing the assembly with a system that was both
practical and innovative.
Coding the entire system was quite a
challenge, especially with the ESP32's limited number of pins. When
using Wi-Fi to broadcast data, many of the available pins were occupied,
leaving us with only half of the pins to manage all the
functionalities. We had to carefully allocate these pins and write
efficient, high-speed code to ensure that the scanning and data
transmission processes were smooth and lag-free. We'll dive into the
coding details later, but first, let's address another major issue we
faced.
The robot’s turning mechanism initially posed a significant
problem. The wheel mounted at the back of the robot would rotate
erratically and often get stuck, making it difficult to drive the robot
straight or turn accurately. Despite both forward motors working in
unison, the robot would veer off course. To resolve this, we replaced
the problematic wheel with two additional motors at the back, giving the
robot a total of four powered wheels. This adjustment, powered by a
single motor driver, provided much better stability and control.
With
this upgrade, the robot could now turn 360 degrees smoothly and
maintain a more stable movement compared to its previous two-wheel setup
with a non-powered third wheel. With the mechanical issues sorted, we
were ready to dive into implementing the SLAM system.
Our first
major change was moving away from traditional binary occupancy grids and
adopting a probabilistic approach. This shift significantly reduced
noise and provided a more accurate representation of the environment. We
implemented probabilistic occupancy grids and free-space grids to
better handle the inherent noise in our system.
To further enhance
accuracy, we employed various noise reduction techniques, including
temporal filters and probabilistic filtering. We fine-tuned raw
Cartesian coordinate points calculated from the probabilistic grids,
addressing minor errors with erosion techniques to eliminate minute
inaccuracies. We also developed programs to mitigate clamping artifacts,
ensuring that our grid representations aligned more closely with
real-world scenarios.
There were minor imperfections in our setup,
such as the servo not rotating precisely and occasional calibration
issues with the LiDAR sensor. To address these, we used deep learning.
We designed a specialized model with layers that processed each value
independently, reflecting the real-world scenario where data points are
largely independent.
Training this model required considerable
effort. We manually measured distances by positioning the robot in front
of a straight wall and taking multiple observations. This data was then
used to synthesize a diverse dataset, which was fed into the model for
training. Once trained, the lightweight model was saved and could be
easily loaded for real-time use, without significantly impacting
performance during operations.
Let’s keep the details of the SLAM
implementation brief. We used historical data and various filtering
techniques to achieve a clean and noise-free map for our algorithms.
Initially, we employed different types of particle filters to pinpoint
the robot's location within the map. The particle filtering was so
precise that it could update the robot’s position even without odometry
information when the robot was moved manually. However, for complex
motions and actions, odometry was essential.
To integrate odometry,
we created a model based on simple equations to estimate the robot's
movement. Although our robot lacked wheel encoders and gyroscopes, we
could estimate distance and rotation by controlling motor speed and
duration. We used these estimates to command the robot to move specific
distances or angles, adjusting commands based on the model's
calculations. This model allowed us to update the robot's position in
real time on the occupancy grid.
In summary, our robot was controlled
not by velocity commands but by specific distance or angle commands. We
calculated the required parameters for accurate movement based on
various measurements and adjustments, which were then applied through
the trained model. The particle filters accounted for any small
discrepancies, ensuring precise positioning and orientation.
Next,
we focused on pathfinding. To make our SLAM project effective, we
needed a robust pathfinding algorithm to navigate the robot to its goal
position. Implementing this was challenging. We chose the A* algorithm
for pathfinding and leveraged our own library, Vagabond, which we
developed years ago. Vagabond allowed us to perform pathfinding using
the A* algorithm efficiently. We integrated functions to find neighbors,
calculate costs, and apply heuristics. We ensured that the cost
calculations minimized unnecessary deviations, directing the robot
towards the goal smoothly.
We also included a path simplification
algorithm to streamline the path, making it easier for the robot to
navigate. This simplified path was then used by the navigation module to
determine the robot’s movements. The navigation system calculated the
robot's current position and orientation, determining the necessary
turns and adjustments to approach the goal accurately. This process
involved iterative adjustments with appropriate weights and feedback
control to ensure the robot reached the target position. Additionally,
the pre-trained models were used to generate precise commands for the
robot.
With these systems in place, the robot could autonomously
navigate and follow the path effectively, performing multiple maneuvers
to reach the goal position with a specific tolerance.
One of the
standout features of this project is the Vagabond library, which
simplifies pathfinding tasks. We’re excited to release Vagabond to the
public, allowing you to easily integrate pathfinding capabilities into
your own projects. You can install Vagabond via pip or check out the
GitHub link in the video description.
Vagabond offers an intuitive
way to handle pathfinding problems. The library requires you to define a
function that identifies the neighbors of a given node in your graph.
You can assign different costs to these neighbors, which helps the
algorithm determine the most efficient path. With a specific node object
designed for pathfinding, Vagabond automates the process of finding the
optimal route.
To get started, install the library using: pip install py-vagabond
Here’s
a quick overview of how to use Vagabond for pathfinding. You begin by
defining your environment, such as a grid representing free and occupied
spaces. Next, specify the starting and ending points for your path.
You
then create a function to determine the neighbors of a node based on
its position in the grid. For each neighbor, you can set costs that
reflect the difficulty of moving to that neighbor. This cost helps guide
the algorithm in selecting the most efficient path.
The library
provides methods to execute the A* pathfinding algorithm, which involves
calculating the shortest path from the start to the end node while
considering the defined costs. Once the path is computed, you can
visualize it using plotting tools, showing the optimal route on a grid.
To
illustrate, Vagabond can handle tasks like navigating a robot through a
grid by calculating distances and adjusting movements to reach the goal
efficiently. This example demonstrates how the library can be applied
to a grid with different cost values, calculating and visualizing the
shortest path between two points.
Give Vagabond a try in your
projects. Just define the necessary functions, install the library, and
let it handle the complex pathfinding tasks for you.
At the final
stage of the project, we integrated everything into a ROS2-driven
program. This required setting up the necessary nodes, topics,
publishers, and listeners to ensure smooth operation within the ROS2
environment. To streamline the process, we transitioned from using
OpenCV for plotting to utilizing RViz for visualization.
To achieve
this, we made sure that all data being broadcasted adhered to the
specific ROS2 datatypes, ensuring compatibility and proper communication
across the system.
We developed four distinct nodes to handle various aspects of the system:
1. Communication Node: Manages the interface between the robot and the driver, ensuring accurate updates and calculations.
2.
Grid Management Node: Handles the probabilistic grids, particle
filtering, and conversion of distance information into Cartesian
coordinates to track the robot’s position.
3. Pathfinding Node: Ensures that a viable path is continuously available and updated whenever changes occur in the environment.
4.
Navigation Node: Directs the robot’s movements, including how far and
in what direction to move, while providing feedback control to adjust
the robot’s orientation.
After the conversion to ROS2, we
meticulously refined the system to ensure everything functioned
seamlessly. The final result is showcased in RViz, where you can see the
map being plotted and updated in real time.
So, that’s a wrap!
We've successfully built a SLAM system from scratch, demonstrating not
only our ability to handle complex algorithms but also our flexibility
in integrating with ROS2. From creating and fine-tuning probabilistic
occupancy grids and particle filtering to implementing efficient
pathfinding and navigation, every step has been a journey of innovation
and learning.
We’ve showcased how our system can function seamlessly
within the ROS environment, leveraging RViz for real-time visualization
and ensuring precise robot control and positioning. This project
highlights our commitment to pushing the boundaries of robotics and
automation, and we’re excited about the potential applications and
improvements that lie ahead.
Thank you for following along with our
development process. If you have any questions or want to dive deeper
into the details, feel free to reach out. Stay tuned for more updates
and projects as we continue to explore and innovate in the field of
robotics!
Comments
Post a Comment