Final project
An omnidirectional autonomous mobile robot will be developed, capable of detecting and avoiding obstacles. The system is designed to operate on solid, flat, and stable surfaces. Figure 1 presents the initial conceptual design of the autonomous mobile robot.
Background Idea
The motivation for this project arises from a strong personal interest in robotics, particularly in the field of space robotics. This work is conceived as an opportunity to consolidate fundamental knowledge in mechanics, electronics, and programming, as well as to apply these skills in the design and construction of a functional autonomous vehicle.
Sketch of the Final Project
The robot will be equipped with four mecanum wheels, which enable movement in any direction depending on the motion applied to each individual wheel. These wheels will be coupled to TT DC motors and complemented with sensor systems located on the lateral, frontal, and rear sections of the autonomous vehicle. Figure 2 shows the overall robot design with the main components integrated. Figure 3 presents a top view of the robot chassis, while Figure 4 illustrates an exploded view highlighting the central components of the system.
Finally, Figure 5 shows the operation of the robot's movement according to the type of motion imparted to its wheels. This information was obtained from Motion Dynamics.
Expected Challenges
One of the main challenges anticipated during the development of this project is the programming and electronic design of the robot. This is primarily due to the fact that my academic background is not directly focused on these areas, resulting in limited prior experience with the programming languages, development environments, and electronic architectures required for the system. Nevertheless, this project represents an ideal opportunity to acquire and strengthen skills that are not typically addressed within my field of study, contributing significantly to my professional development.
General Schedule of Activities for the Final Project
I wrote a general schedule of activities to organize myself better
| Fablab Week | Main Tasks | Estimated Time |
|---|---|---|
| Week 1. Project management |
|
2-3 days |
| Week 2. Computer Aided Design |
|
2-3 days |
| Week 3. Computer Controlled Cutting |
|
2-3 days |
| Week 4. Electronic Production |
|
3-5 days |
| Week 5. 3D Scanning and printing |
|
2-3 days |
| Week 6. Embedded Programming |
|
3-5 days |
| Week 7. Computer Controlled Machining |
|
3-4 days |
| Week 8. Electronics Design |
|
3-4 days |
| Week 9. Output Devices |
|
2-3 days |
| Week 10. Mechanical Design |
|
2-3 days |
| Week 11. Input Devices |
|
3-4 days |
| Week 12. Molding and Casting |
|
3-4 days |
| Week 13. Embedded Nettworking and Communications |
|
4-5 days |
| Week 14. Interface and Application Programming |
|
4-5 days |
Preliminary Bill of Materials
| Componente | Quantity | Cost (MXN) | Application |
|---|---|---|---|
| TT DC Motor | 4 | $59 | Robot locomotion |
| L298N H-Bridge Motor Driver | 1 | $90 | Control of motor speed and rotation direction |
| Rotary Encoders | 4 | $43 | Controlled measurement of vehicle motion |
| ESP32 DevKit V1 | 1 | $124 | Main microcontroller |
| HC-SR04 Ultrasonic Sensor | 3 | $53 | Obstacle detection |
| LM7805 Voltage Regulator | 1 | $48 | Voltage regulation for motors and sensors |
| AMS1117-3.3 Voltage Regulator | 1 | $12 | Voltage regulation for sensors |
| Male/Female Headers | 1 set | $350 | Electrical connections |
| Dupont Cables (MM, MF, FF) | Various | $59 | Electrical connections |
| Rechargeable Battery (2500 mAh) | 2 | $189 | Power supply |
| Power Switch | 1 | $29 | System power on/off |
| Double-sided Copper Board | 10x10cm | $392 | PCB fabrication |
| SMD Resistors 0805 | Various | $0.24 | PCB components |
| SMD Capacitors 0805 | Various | $129 | PCB components |
What is an Omnidirectional Robot? Fundamental Concepts for the Project
Mecanum wheels have free rollers positioned at 45° relative to the wheel axis. Independently controlling each of these wheels allows different rotation combinations, enabling the robot to move along the X and Y axes, rotate in place, and perform any simultaneous combination of these movements.
PCB Design – Planning
A total of three PCB designs will be made.
Brain Board Design
The XIAO ESP32C6 will be used as the brain of the project. Connected to it will be the VL53L0X sensors, WiFi for XBOX controller input, QR code processing, navigation logic, and the transmission of speed or direction commands. This board will be manufactured once.
Components
- Connected sensors (3× VL53L0X)
- QR reader (GM65) – subject to change
- 4.7kΩ pull-up resistors on SDA and SCL
- 10kΩ pull-up resistors on each XSHUT
- 100nF capacitors (3 per sensor)
- 10µF capacitor (to stabilize 3.3V)
The pin assignment plan is as follows:
| PIN | Function | Connected to |
|---|---|---|
| D4 (SDA) | I2C data | SDA of all 3 VL53L0X + 4.7kΩ pull-up to 3.3V |
| D5 (SCL) | I2C clock | SCL of all 3 VL53L0X + 4.7kΩ pull-up to 3.3V |
| D0 | XSHUT sensor 1 | XSHUT VL53L0X #1 |
| D1 | XSHUT sensor 2 | XSHUT VL53L0X #2 |
| D2 | XSHUT sensor 3 | XSHUT VL53L0X #3 |
| D6 (TX) | UART TX | RX of GM65 |
| D7 (RX) | UART RX | TX of GM65 |
| D8 (TX2) | UART toward RP2350 | RX of RP2350 |
| D9 (RX2) | UART from RP2350 | TX of RP2350 |
| 3.3V | Power supply | VIN of all 3 VL53L0X + GM65 + pull-ups. Note: 4 pins for 3.3V |
| GND | Ground | GND of everything. Note: 4 pins for GND |
Motor Board Design
A board based on the XIAO RP2350 will be created to connect the motors and receive commands from the ESP32C6 to independently control the four motors. This board will be manufactured once. The components to be used are:
- 100µF and 100nF capacitors in parallel on the motor power supply.
- 4-output connector toward the 4 driver boards.
The pin assignment plan is as follows:
| PIN | Function | Connected to |
|---|---|---|
| D0 | PWM motor 1 | IN1 driver board #1 |
| D1 | DIR motor 1 | IN2 driver board #1 |
| D2 | PWM motor 2 | IN1 driver board #2 |
| D3 | DIR motor 2 | IN2 driver board #2 |
| D4 | PWM motor 3 | IN1 driver board #3 |
| D5 | DIR motor 3 | IN2 driver board #3 |
| D6 | PWM motor 4 | IN1 driver board #4 |
| D7 | DIR motor 4 | IN2 driver board #4 |
| D8 (TX) | UART from ESP32C6 | TX of ESP32C6 |
| GND | Common ground | GND of ESP32C6 |
H-Bridge Driver Board Design
The DRV8251A driver will be used. This board will be connected to the motor board to prevent component damage. This board will be manufactured 4 times, one per motor. The board components are:
- DRV8251A
- 100nF capacitor between VCC and GND
- 10µF capacitor on VM (motor power supply)
The pin usage plan is:
| DRV8251A Pin | Connected to |
|---|---|
| IN1 | PWM from RP2350 |
| IN2 | DIR from RP2350 |
| OUT1 | Motor terminal A |
| OUT2 | Motor terminal B |
| VM | Motor power supply (3V–6V) |
| GND | Common GND |
| nSLEEP | 3.3V (always active) |
Additionally, it was recommended to organize the pin headers according to their intended use. To keep better order over the work and simplify the design in Altium, the following tables were created:
ESP32C6 Board
| Header Strip | Pin Count | Included Pins | Use |
|---|---|---|---|
| Strip 1 | 5 | GND, 3.3V, SDA, SCL, XSHUT | VL53L0X Sensor #1 |
| Strip 2 | 5 | GND, 3.3V, SDA, SCL, XSHUT | VL53L0X Sensor #2 |
| Strip 3 | 5 | GND, 3.3V, SDA, SCL, XSHUT | VL53L0X Sensor #3 |
| Strip 4 | 4 | GND, 3.3V, TX, RX | GM65 Module (QR) |
| Strip 5 | 3 | GND, TX, RX | UART toward RP2350 |
| Strip 6 | 2 | 5V, GND | Buck module input |
RP2350 Board
| Header Strip | Pin Count | Included Pins | Use |
|---|---|---|---|
| Strip 1 | 4 | GND, VM, IN1, IN2 | DRV8251A Driver #1 |
| Strip 2 | 4 | GND, VM, IN1, IN2 | DRV8251A Driver #2 |
| Strip 3 | 4 | GND, VM, IN1, IN2 | DRV8251A Driver #3 |
| Strip 4 | 4 | GND, VM, IN1, IN2 | DRV8251A Driver #4 |
| Strip 5 | 3 | GND, RX, TX | UART toward ESP32C6 |
| Strip 6 | 2 | 5V, GND | Motor buck input |
| Strip 7 | 2 | VIN, GND | Microcontroller buck input |
H-Bridge Driver Board
| Header Strip | Pin Count | Included Pins | Use |
|---|---|---|---|
| Strip 1 | 4 | GND, VM, IN1, IN2 | Input from RP2350 |
| Strip 2 | 2 | OUT1, OUT2 | Output toward motor |
Following this pin order, it is important to note that strips connecting boards together (such as UART) must be mirrored.
Finally, to regulate the power flow, two MP1584EN buck modules will be used: one for the microcontrollers and one for the four motors.
H-Bridge PCBs – Motor Controllers
For the H-bridge, the following component was used to control the motor: TB67H450FNG (SnapEDA).
To determine the current that must pass through the H-bridge, the startup current was considered. The motors used can instantaneously reach up to 1.5 A. Since this H-bridge is designed to control only one motor, a slightly higher amperage margin was left. This is important because PCB track width depends on the current it must carry. During Week 6, the method for calculating this width using KiCad's automatic calculator was learned and applied, which returned a result of 0.8 mm. This value was then used to set the preferred and allowed track widths in Altium, after which the routing process began. Units can be changed by pressing Q (switching to mm).
For the outline of the H-bridge board, a free space was left to allow for a hole to attach it to the main brain board. The available tooling was also taken into account, so the minimum trace width needed to be 2 mm.
VL53L0X Sensor Testing – Individual and Combined
There are different types of distance-measuring sensors; among them is the VL53L0X. First, it was necessary to test a single sensor. For this purpose, the following code was used:
#include <Wire.h>
#include <VL53L0X.h>
// Pin configuration
#define SENSOR_SDA 6 // GPIO6 = Pin D4
#define SENSOR_SCL 7 // GPIO7 = Pin D5
#define XSHUT_PIN 2 // GPIO2 = Pin D0
VL53L0X sensor;
void setup() {
Serial.begin(115200);
Serial.println("Starting Sensor VL53LXX-V2...");
// Configure I2C bus on pins D4 (SDA) and D5 (SCL)
Wire1.setSDA(SENSOR_SDA);
Wire1.setSCL(SENSOR_SCL);
Wire1.begin();
// Use XSHUT on D0 to reset the sensor
pinMode(XSHUT_PIN, OUTPUT);
digitalWrite(XSHUT_PIN, LOW); // Turn off sensor
delay(10);
digitalWrite(XSHUT_PIN, HIGH); // Turn on sensor
delay(50); // Wait for stabilization
// Initialize sensor on Wire1 bus
sensor.init(true); // true = use the specified I2C bus (Wire1)
sensor.setTimeout(500);
sensor.startContinuous();
Serial.println("Sensor ready!");
}
void loop() {
int distance = sensor.readRangeContinuousMillimeters();
if (sensor.timeoutOccurred()) {
Serial.println("Error: Timeout occurred");
} else {
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" mm");
}
delay(100);
}
Motion Programming
For the wheel programming, research was first conducted on the basic movements possible with the purchased wheel type. Based on these observed movements, a code was written to instruct the robot to perform the following maneuvers: forward, backward, left and right lateral translation, clockwise and counterclockwise rotation, full 360° clockwise and counterclockwise turns, diagonal forward-left, forward-right, backward-left, backward-right, and pivoting around both the front and rear axles.
The programming structure is as follows: first, a single motor is controlled; then multiple motors are combined; next, advanced speed calculations are performed; and finally, movements are executed. The first developed code is:
/*
* Omnidirectional robot with Mecanum wheels – Full movement sequence
* Board: XIAO ESP32C6
* Independent H-bridges (2 pins per motor)
* Movements: forward/backward, lateral, in-place rotation, 360° turn,
* diagonals (4 directions), pivot on front/rear axis.
* A 3-second stop is inserted between each movement.
*/
// ---------- Pins (D0 to D7) ----------
#define FL_PIN1 D0
#define FL_PIN2 D1
#define FR_PIN1 D2
#define FR_PIN2 D3
#define RL_PIN1 D4
#define RL_PIN2 D5
#define RR_PIN1 D6
#define RR_PIN2 D7
// ---------- LEDC Channels (8 channels) ----------
#define CH_FL1 0
#define CH_FL2 1
#define CH_FR1 2
#define CH_FR2 3
#define CH_RL1 4
#define CH_RL2 5
#define CH_RR1 6
#define CH_RR2 7
// ---------- PWM Parameters ----------
#define PWM_FREQ 5000
#define PWM_RES 8 // 0-255
#define SPEED 200 // Base speed (adjustable)
// Time for a full rotation (adjust through testing until 360° is achieved)
#define FULL_ROTATION_TIME_MS 2500
// ---------- Initialization ----------
void setup() {
Serial.begin(115200);
ledcSetup(CH_FL1, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN1, CH_FL1);
ledcSetup(CH_FL2, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN2, CH_FL2);
ledcSetup(CH_FR1, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN1, CH_FR1);
ledcSetup(CH_FR2, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN2, CH_FR2);
ledcSetup(CH_RL1, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN1, CH_RL1);
ledcSetup(CH_RL2, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN2, CH_RL2);
ledcSetup(CH_RR1, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN1, CH_RR1);
ledcSetup(CH_RR2, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN2, CH_RR2);
stopMotors();
Serial.println("Mecanum Robot ready – full sequence");
}
// ---------- Main Loop ----------
void loop() {
// 1-2 Basic linear movements
Serial.println(">> Forward"); forward(); delay(3000);
Serial.println(">> Backward"); backward(); delay(3000);
// 3-4 Lateral translation
Serial.println(">> Left (lateral)");left(); delay(3000);
Serial.println(">> Right (lateral)");right(); delay(3000);
// 5-6 In-place rotation
Serial.println(">> Clockwise rotation (in place)"); rotateClockwise(); delay(3000);
Serial.println(">> Counterclockwise rotation"); rotateCounterClockwise(); delay(3000);
// 7-8 Full 360° turns
Serial.println(">> Full clockwise turn"); rotateFullClockwise(); delay(3000);
Serial.println(">> Full counterclockwise turn"); rotateFullCounterClockwise(); delay(3000);
// 9-10 Forward diagonals
Serial.println(">> Diagonal forward-left"); diagonalForwardLeft(); delay(3000);
Serial.println(">> Diagonal forward-right"); diagonalForwardRight(); delay(3000);
// 11-12 Backward diagonals
Serial.println(">> Diagonal backward-left"); diagonalBackwardLeft(); delay(3000);
Serial.println(">> Diagonal backward-right");diagonalBackwardRight(); delay(3000);
// 13-14 Front axis pivot
Serial.println(">> Front pivot clockwise"); pivotFrontClockwise(); delay(3000);
Serial.println(">> Front pivot counterclockwise");pivotFrontCounterClockwise(); delay(3000);
// 15-16 Rear axis pivot
Serial.println(">> Rear pivot clockwise"); pivotRearClockwise(); delay(3000);
Serial.println(">> Rear pivot counterclockwise"); pivotRearCounterClockwise(); delay(3000);
// Sequence repeats indefinitely
}
// =============================================
// Motor control functions
// =============================================
void setMotor(int chPin1, int chPin2, int speed) {
speed = constrain(speed, -255, 255);
if (speed > 0) { ledcWrite(chPin1, speed); ledcWrite(chPin2, 0); }
else if (speed < 0) { ledcWrite(chPin1, 0); ledcWrite(chPin2, -speed); }
else { ledcWrite(chPin1, 0); ledcWrite(chPin2, 0); }
}
void stopMotors() {
setMotor(CH_FL1,CH_FL2,0); setMotor(CH_FR1,CH_FR2,0);
setMotor(CH_RL1,CH_RL2,0); setMotor(CH_RR1,CH_RR2,0);
}
// =============================================
// Basic movements
// =============================================
void forward() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void backward() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void left() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void right() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void rotateClockwise() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void rotateCounterClockwise(){ setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
// =============================================
// Full 360° turns
// =============================================
void rotateFullClockwise() { rotateClockwise(); delay(FULL_ROTATION_TIME_MS); stopMotors(); }
void rotateFullCounterClockwise() { rotateCounterClockwise(); delay(FULL_ROTATION_TIME_MS); stopMotors(); }
// =============================================
// Diagonals (classic Mecanum)
// =============================================
void diagonalForwardLeft() { setMotor(CH_FL1,CH_FL2,0); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,0); }
void diagonalForwardRight() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,0); setMotor(CH_RL1,CH_RL2,0); setMotor(CH_RR1,CH_RR2, SPEED);}
void diagonalBackwardLeft() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2,0); setMotor(CH_RL1,CH_RL2,0); setMotor(CH_RR1,CH_RR2,-SPEED);}
void diagonalBackwardRight(){ setMotor(CH_FL1,CH_FL2,0); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2,0); }
// =============================================
// Pivots around axes (physically correct)
// =============================================
// Applies speeds calculated with inverse kinematics and scales to avoid saturation
void setMecanumSpeeds(float vx, float vy, float omega) {
// Geometric coefficient (adjust if real dimensions are known)
const float L = 1.0; // sum of (half wheelbase + half track) normalized
float v_fl = vx - vy - L * omega;
float v_fr = vx + vy + L * omega;
float v_rl = vx + vy - L * omega;
float v_rr = vx - vy + L * omega;
// Scale if any absolute value exceeds SPEED
float maxVal = max(max(abs(v_fl), abs(v_fr)), max(abs(v_rl), abs(v_rr)));
if (maxVal > SPEED) {
float scale = SPEED / maxVal;
v_fl *= scale; v_fr *= scale; v_rl *= scale; v_rr *= scale;
}
setMotor(CH_FL1,CH_FL2,(int)v_fl); setMotor(CH_FR1,CH_FR2,(int)v_fr);
setMotor(CH_RL1,CH_RL2,(int)v_rl); setMotor(CH_RR1,CH_RR2,(int)v_rr);
}
// Front pivot (rotation around the center point of the front axle)
void pivotFrontClockwise() { setMecanumSpeeds(0, -SPEED, -SPEED); }
void pivotFrontCounterClockwise() { setMecanumSpeeds(0, SPEED, SPEED); }
// Rear pivot (rotation around the center point of the rear axle)
void pivotRearClockwise() { setMecanumSpeeds(0, SPEED, -SPEED); }
void pivotRearCounterClockwise() { setMecanumSpeeds(0, -SPEED, SPEED); }
This code was loaded into Arduino following the steps learned in the corresponding week, which enabled the observation of the robot's movements.
Robot Kinematics
It is important to understand the kinematic model of the robot. This robot uses standard inverse kinematics. The table below shows the movement direction of each wheel type and the combined motion they produce. Each motor receives a signed value: positive means the wheel spins forward, negative means backward, and 0 means the motor is stopped.
| Movement | FL (Front Left) | FR (Front Right) | RL (Rear Left) | RR (Rear Right) |
|---|---|---|---|---|
| Forward | + | + | + | + |
| Backward | - | - | - | - |
| Left (lateral) | - | + | + | - |
| Right (lateral) | + | - | - | + |
| Clockwise rotation | + | - | + | - |
| Counterclockwise rotation | - | + | - | + |
| Diagonal forward-left | 0 | + | + | 0 |
| Diagonal forward-right | + | 0 | 0 | + |
| Diagonal backward-left | - | 0 | 0 | - |
| Diagonal backward-right | 0 | - | - | 0 |
| Front pivot clockwise | General formula scaled with dynamic speeds | |||
| Front pivot counterclockwise | General formula scaled with dynamic speeds | |||
This basic code provides the movement patterns needed for obstacle avoidance. However, it only allows visualization of the possible motions. The next step toward enabling actual obstacle avoidance is integrating distance sensors that can relay obstacle position data and instruct the program to move according to the type of obstacle encountered.
The following obstacle scenarios and corresponding responses are proposed:
| Active Sensors | Interpretation | Action |
|---|---|---|
| None | Clear path | Move forward |
| Center | Obstacle ahead | Turn toward the side with the greater distance to the obstacle |
| Center + Left | Front and left blocked | Lateral translation to the right |
| Center + Right | Front and right blocked | Lateral translation to the left |
| Center + Left + Right | Corridor blocked | Rotate in place until clear |
| Left only | Obstacle on the left | Lateral translation to the right |
| Right only | Obstacle on the right | Lateral translation to the left |
For the integration of three VL53L0X sensors, the proposed program is:
/*
* Mecanum Robot with obstacle avoidance (3x VL53L0X)
* Board: XIAO ESP32C6
* Independent H-bridges + 3 front ToF sensors
*/
#include <Wire.h>
#include <Adafruit_VL53L0X.h>
// ========== Motor configuration (unchanged) ==========
#define FL_PIN1 D0
#define FL_PIN2 D1
#define FR_PIN1 D2
#define FR_PIN2 D3
#define RL_PIN1 D4
#define RL_PIN2 D5
#define RR_PIN1 D6
#define RR_PIN2 D7
#define CH_FL1 0
#define CH_FL2 1
#define CH_FR1 2
#define CH_FR2 3
#define CH_RL1 4
#define CH_RL2 5
#define CH_RR1 6
#define CH_RR2 7
#define PWM_FREQ 5000
#define PWM_RES 8
#define SPEED 200 // base speed (0-255)
// ========== Sensor configuration ==========
#define I2C_SDA D8
#define I2C_SCL D9
#define XSHUT_LEFT D10
#define XSHUT_CENTER A0
#define XSHUT_RIGHT A1
#define SENSOR_LEFT_ADDR 0x30
#define SENSOR_CENTER_ADDR 0x31
#define SENSOR_RIGHT_ADDR 0x32
#define DISTANCE_THRESHOLD 300 // mm, threshold to consider an obstacle
Adafruit_VL53L0X sensorLeft, sensorCenter, sensorRight;
// ========== Motor functions (identical to above) ==========
void setMotor(int chPin1, int chPin2, int speed) {
speed = constrain(speed, -255, 255);
if (speed > 0) { ledcWrite(chPin1, speed); ledcWrite(chPin2, 0); }
else if (speed < 0) { ledcWrite(chPin1, 0); ledcWrite(chPin2, -speed); }
else { ledcWrite(chPin1, 0); ledcWrite(chPin2, 0); }
}
void stopMotors() { setMotor(CH_FL1,CH_FL2,0); setMotor(CH_FR1,CH_FR2,0); setMotor(CH_RL1,CH_RL2,0); setMotor(CH_RR1,CH_RR2,0); }
void forward() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void left() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void right() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
void rotateClockwise() { setMotor(CH_FL1,CH_FL2, SPEED); setMotor(CH_FR1,CH_FR2,-SPEED); setMotor(CH_RL1,CH_RL2, SPEED); setMotor(CH_RR1,CH_RR2,-SPEED); }
void rotateCounterClockwise() { setMotor(CH_FL1,CH_FL2,-SPEED); setMotor(CH_FR1,CH_FR2, SPEED); setMotor(CH_RL1,CH_RL2,-SPEED); setMotor(CH_RR1,CH_RR2, SPEED); }
// ========== Sensor initialization with XSHUT ==========
void initVL53L0X() {
// 1. Turn off all sensors
pinMode(XSHUT_LEFT, OUTPUT); pinMode(XSHUT_CENTER, OUTPUT); pinMode(XSHUT_RIGHT, OUTPUT);
digitalWrite(XSHUT_LEFT, LOW); digitalWrite(XSHUT_CENTER, LOW); digitalWrite(XSHUT_RIGHT, LOW);
delay(10);
// 2. Initialize left sensor
digitalWrite(XSHUT_LEFT, HIGH); delay(10);
sensorLeft.begin(SENSOR_LEFT_ADDR);
sensorLeft.setMeasurementTimingBudgetMicroSeconds(20000);
sensorLeft.startRangeContinuous();
// 3. Initialize center sensor
digitalWrite(XSHUT_CENTER, HIGH); delay(10);
sensorCenter.begin(SENSOR_CENTER_ADDR);
sensorCenter.setMeasurementTimingBudgetMicroSeconds(20000);
sensorCenter.startRangeContinuous();
// 4. Initialize right sensor
digitalWrite(XSHUT_RIGHT, HIGH); delay(10);
sensorRight.begin(SENSOR_RIGHT_ADDR);
sensorRight.setMeasurementTimingBudgetMicroSeconds(20000);
sensorRight.startRangeContinuous();
}
// ========== Distance reading ==========
void readSensors(uint16_t &distLeft, uint16_t &distCenter, uint16_t &distRight) {
if (sensorLeft.isRangeComplete()) distLeft = sensorLeft.readRange();
if (sensorCenter.isRangeComplete()) distCenter = sensorCenter.readRange();
if (sensorRight.isRangeComplete()) distRight = sensorRight.readRange();
}
// ========== Obstacle avoidance logic ==========
void avoidObstacles() {
uint16_t dL, dC, dR;
readSensors(dL, dC, dR);
bool obsLeft = (dL > 0 && dL < DISTANCE_THRESHOLD);
bool obsCenter = (dC > 0 && dC < DISTANCE_THRESHOLD);
bool obsRight = (dR > 0 && dR < DISTANCE_THRESHOLD);
if (!obsLeft && !obsCenter && !obsRight) {
// Clear path
forward();
}
else if (obsCenter && obsLeft && obsRight) {
// Total blockage: rotate in place until an exit is found
rotateCounterClockwise();
}
else if (obsCenter && obsLeft && !obsRight) {
// Obstacle ahead and to the left -> escape to the right
right();
}
else if (obsCenter && !obsLeft && obsRight) {
// Obstacle ahead and to the right -> escape to the left
left();
}
else if (obsCenter && !obsLeft && !obsRight) {
// Only a frontal obstacle: decide which way to turn
if (dL > dR) { rotateCounterClockwise(); } else { rotateClockwise(); }
}
else if (!obsCenter && obsLeft && !obsRight) {
// Obstacle only to the left -> move right
right();
}
else if (!obsCenter && !obsLeft && obsRight) {
// Obstacle only to the right -> move left
left();
}
else {
// Default case (unlikely)
forward();
}
}
// ========== Setup ==========
void setup() {
Serial.begin(115200);
// Configure I2C with custom pins
Wire.begin(I2C_SDA, I2C_SCL);
// Configure LEDC channels and motors (same as before)
ledcSetup(CH_FL1, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN1, CH_FL1);
ledcSetup(CH_FL2, PWM_FREQ, PWM_RES); ledcAttachPin(FL_PIN2, CH_FL2);
ledcSetup(CH_FR1, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN1, CH_FR1);
ledcSetup(CH_FR2, PWM_FREQ, PWM_RES); ledcAttachPin(FR_PIN2, CH_FR2);
ledcSetup(CH_RL1, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN1, CH_RL1);
ledcSetup(CH_RL2, PWM_FREQ, PWM_RES); ledcAttachPin(RL_PIN2, CH_RL2);
ledcSetup(CH_RR1, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN1, CH_RR1);
ledcSetup(CH_RR2, PWM_FREQ, PWM_RES); ledcAttachPin(RR_PIN2, CH_RR2);
stopMotors();
// Initialize the three sensors
initVL53L0X();
Serial.println("Sensors OK. Starting avoidance...");
}
void loop() {
avoidObstacles();
delay(50); // Small pause for stability
}
Future Work
Based on the initial design, a potential project extension is proposed, focused on the development of a gas-mapping robot operating within a controlled environment. This extension is conceived as an enhancement and expansion of the base system, with potential applications in environmental monitoring and the analysis of current real-world problems.
Design for the Gas-Mapping Robot
The gas-mapping robot will be based on the initial autonomous vehicle design, with modifications to integrate additional sensors and functionalities. The main components of the system will include:
- Chassis: Adapted to accommodate additional sensors and ensure stability during operation.
- Microcontroller: ESP32 or similar, capable of handling multiple sensor inputs and data processing.
- Sensors: Integration of gas sensors such as MQ-135 (smoke), SGP30 (CO₂/VOCs), and DHT22 (temperature and humidity) for comprehensive environmental monitoring.
- Data Storage: Implementation of local data storage using an SD card module, with potential WiFi connectivity for real-time data transmission.
- Power Supply: Enhanced battery system to support additional components and ensure extended operation time.
Schedule for Future Work: Robot Upgrade to Gas-Mapping System
| Fablab Week | Main Tasks | Estimated Time |
|---|---|---|
| Week 1. Project management |
|
1 day |
| Week 2. Computer Aided Design |
|
2-3 days |
| Week 3. Computer Controlled Cutting |
|
1-2 days |
| Week 4. Electronic Production |
|
4-5 days |
| Week 5. 3D Scanning and printing |
|
2-3 days |
| Week 6. Embedded Programming |
|
5-6 days |
| Week 7. Computer Controlled Machining |
|
2-3 days |
| Week 8. Electronics Design |
|
4-6 days |
| Week 9. Output Devices |
|
3-5 days |
| Week 10. Mechanical Design |
|
3-5 days |
| Week 11. Input Devices |
|
3-5 days |
| Week 12. Molding and Casting |
|
2-5 days |
| Week 13. Embedded Nettworking and Communications |
|
3-7 days |
| Week 14. Interface and Application Programming |
|
4-5 days |
Bill of Materials for Future Work
| Componente | Quantity | Cost (MXN) | Application |
|---|---|---|---|
| MQ-135 Gas Sensor | 1 | $53 | Gas and smoke detection |
| SGP30 Sensor | 1 | $199 | CO2 and VOC detection |
| DHT22 Sensor | 1 | $135 | Temperature and humidity measurement |
| SD Card Module | 1 | $49 | Local data storage |
| OLED Display 0.96" | 3* | $103 | Visualization of robot data |
| Motor Rotary Encoders | 2* | $165 | Estimation of traveled distance |
| SD Card Reader Module | 1 | $245 | Local data storage |
| MicroSD Card 16 GB | 1 set | $57 | Data storage |
| Acrylic Sheets | Various | $91 | Enclosure for controlled environment |
Spiral development
As previously discussed, this autonomous mobile robot presents multiple opportunities for future improvement and expansion. Based on this perspective, a Spiral Development is proposed as a visual tool to outline potential milestones and long term development goals, assuming the successful completion of the core project objectives.