The Arduino subsystem comprises of the physical hardware components and an Arduino sketch responsible for ensuring that the robot is able to traverse through the unknown arena quickly, accurately, and safely. The subsystem operates mainly via commands issued by the other subsystems and makes little decision on its own, except in time-critical situations.
Core hardware components
For sensors, see Sensors. For Raspberry Pi, see Raspberry Pi Overview.
Receiving commands from other subsystems
The Arduino subsystem operates based on letter commands received from the other subsystems. The Arduino readily listens for commands via the serial port. When a command is received, it iterates through a look-up table (switch case), searching for an entry that matches the received command and executes its associated action accordingly. Multiple commands may be received as a whole string; the commands are queued and executed sequentially one at a time.
Robot movement
The robot moves 1 grid, or turn 90 degrees per command received. Movement is achieved by altering the voltage level of the motor shield inputs (A and B) to start each motor spinning in the desired direction, relative to the robot’s current heading. While voltage levels are altered by the Pololu’s Motor Shield Library, a custom motor library is made for our sketch and the tables below are for references.
Motor spinning direction | Input A voltage | Input B voltage |
Forward | HIGH | LOW |
Reverse | LOW | HIGH |
A move command sets both motors to spin in the same direction. A turn command instead sets the motors to spin in opposite directions.
Command | Left motor direction | Right motor direction |
TURN LEFT | Reverse | Forward |
TURN RIGHT | Forward | Reverse |
Moving multiple grids at a time
When a multi-command string is received, a check is performed for series of ‘Forward’ or ‘Reverse’ commands. When a series is at the head of the queue, it is collated and removed altogether from the queue and a multi-grid movement corresponding to the number of commands in the series is executed.
Tracking distance travelled
Each gear-motor has a quadrature encoder attached generating up to 562.25 square-waves (ticks) per revolution of the wheel. Pins connected to the encoders are programmed to generate an interrupt on every rising and falling edge of a square-wave to increment the number of cumulative ticks in the interrupt routine. Once the count is within +- 0.25 of a target number, the motors will be set to stop in the interrupt routine as well by altering the shield inputs’ voltage levels to LOW for each motor.
The target number is calculated based on the fact that 562.25 ticks are generated per wheel revolution (about 18.85cm covered). After calculations and multiple trial-and-error, a single grid movement takes approximately 298 waves and a 90-degree turn takes about 414 waves.
Straight-line traversal
Straight-line traversal is achieved by using odometry to calculate the robot’s deviation from a straight line referenced from the robot’s starting position and heading. This deviation, or error, is then fed into a PID controller to calculate a speed offset to be applied to each motor. The process then repeats until the robot comes to a stop.
The robot also performs alignment to any front or left wall (3 obstacles in a straight row) on every stop to ensure that the robot heading is very close to the intended heading before each subsequent move. For example, if heading north, it ensures that the heading is as close to 0 degrees as possible.
Odometry
Odometry estimates the robot’s current position and heading based on known information: the distance travelled by each wheel and wheel axis length. The accuracy is dependent on wheel slippage, with lesser wheel slippage resulting in higher accuracy. As the arena is coated with anti-slip material, the accuracy is high enough for odometry to be used.
1. Encoder inputs are sampled at short, regular intervals (between 5 to 20ms) to estimate the distance travelled by each wheel during that interval. The number of ticks per mm is calculated from the fact that there are 562.25 ticks per wheel revolution.
-
- Distance travelled = (current ticks count – previous ticks count) * ticks per mm
2. The estimated distance for each wheel is used to estimate the current heading offset of the robot (in radians). The wheel axis length is the distance between the left and right wheel, which is ~17.9cm. The calculated offset is added on to the current heading of the robot as the heading offset is calculated relative to a heading of 0.
-
- Current heading offset = (distance travelled right – distance travelled left) * wheel axis length
- Robot heading = current heading + current heading offset
3. The deviation is estimated by multiplying the average distance travelled by both wheels with the sine value of the heading.
-
- Average distance = (distance travelled right + distance travelled left) * 0.5
- Deviation = average distance * sin(heading)
To further illustrate, the above calculations may be represented in the form of X-Y axis graph. The X-axis represents the distance travelled and the Y-axis represent the deviation.
PID controller
Simple video reference: What PIDs do and how they do it
The PID controller calculates an error value as the difference between a measured input and a desired setpoint. An output is then calculated to bring the input as close to the setpoint as possible. In this case, the output is the speed offset that is applied to the speed of both wheels (in opposite polarities) to keep the input (or deviation) as close to a setpoint of 0 (means no deviation).
The output is calculated using three parameters:
Proportional (P) | Current error value |
Integral (I) | Summation of past and current error values |
Derivative (D) | Difference between current and previous error value |
Each parameter is then multiplied with a weightage that determines how aggressively it influences the output. The result of each parameter’s calculation is summed up and the final result is the output of the PID controller.
Formula reference
-
- Error = setpoint – input
- Integral = integral + error, constrained to -255 to 255
- Derivative = error – previous error
- Output = p-weight * error + i-weight * integral + d-weight * derivative
For reference, the weights used in our sketch are:
P | 80 |
I | 10 |
D | 700 |
Integrating odometry and PID with robot movement
Once the motors start running, a moving flag is set to maintain a while loop where the workings of odometry and PID are executed every iteration until the robot comes to a stop. The speed offset calculated from the PID controller is applied to the motors at the end of every loop via the Motor Shield Library.
Wall alignment
For an introduction on sensor placements, see Sensors.
Note: Distances mentioned in this section is obtained via calculation and refined after multiple trial-and-errors.
Wall alignment makes use of readings obtained from the two front corner sensors or the two left sensors to make the robot as perpendicular or parallel, respectively, as possible to the wall. Alignment is performed at every stop when there is a wall (3 obstacles in a row) in front or to the left of the robot and the following conditions are achieved:
-
- The robot is at most 12cm away from the wall
- Readings difference between the front or left sensors is at most 3cm.
These conditions ensure that the readings are accurate due to close proximity and that the robot does not perform alignment accidentally at an L-shaped wall.
The robot turns on the spot at a low speed in the direction based on the reading difference (see figure below). A while loop is maintained to check the reading difference at a set interval. The robot stops when the difference is below a threshold or when a certain amount of time has passed, whichever happens first. The latter condition is added to ensure the robot is never stuck in the alignment phase.
Gap closer / opener
Note: Distances mentioned in this section is obtained via calculation and refined after multiple trial-and-errors.
There are possibilities that the robot under-moves or over-moves due to the nature of hardware uncertainty. As such, a gap closing / opening mechanism has been implemented to minimise the chances of “error carry forward” whenever a turn command is received and front wall alignment is possible.
Gap closing occurs when the robot is between 4.4cm to 9cm of a front wall. In this scenario, the robot is considered to have under-moved and the robot is set to move forward, independent of other subsystems, until the robot is within the threshold of 4cm to 4.3cm. Gap opening occurs when the robot is less than 4cm from a front wall and the robot is set to reverse until it is within the threshold. The process is similar to wall alignment but instead moves forward or reverse.
Obstacle location detection
Obstacle locations are detected and calculated using the arena grid system. The actual distance of an obstacle, or lack thereof, is derived from the median of 11 consecutive sensor readings. The calculated distance is then mapped into a grid distance denoting the number grids the obstacle is away from the robot. The grid distances for each sensor is sent to the Algorithms subsystem after every move for arena plotting and decision making.
Actual distance (cm) | Grid distance |
0 – 7.50 | 1 |
7.51 – 17.50 | 2 |
17.51 – 27.50 | 3 |
27.51 – 37.50 | 4 |
37.51 – 47.50 | 5 |
Above 47.50 | 6 (ignore) |
Distances above 47.5cm denote no obstacle presence and are all assigned the same grid distance.
Collision avoidance
The collision avoidance mechanism prevents the robot from crashing into obstacles ahead of it. It is achieved by obtaining a coarse sensor reading of all three front sensors at every iteration of the movement while loop (see Integrating with odometry and PID). If any of the sensors return a distance lesser than or equal to 5cm, an “emergency brake” occurs and the robot is set to stop, independent of other subsystems.
When a move command is received, a distance check for all three front sensors is also performed before the motors are set to start. If the distance is below the threshold, the move command is terminated.
Synchronisation with the Algorithms subsystem
Note: Distances mentioned in this section is obtained via calculation and refined after multiple trial-and-errors.
There is a possibility that the Algorithms and Arduino subsystems become de-synced due to under or over movements, or if collision avoidance kicks in. For example, if collision avoidance kicks in due to mis-plots and the robot is still in the same coordinates, it is impossible for the Algorithms subsystem to know without any synchronisation mechanism.
As such, a “moved” count is appended to the end of the obstacle distances sent to the Algorithms subsystem to indicate the number of grids moved by the robot. The robot is considered to have moved one grid if it moved at least 3cm, the distance calculated by the Arduino using the cumulative ticks count. For example:
Calculated distance moved | “Moved” count |
3cm | 1 |
13cm | 2 |
21cm | 2 |
23cm | 3 |