IR Ring
General functionality
As previously mentioned, for our IR Ring, we use fifteen TSOP4038 IR sensors placed on a circular form in order to retrieve the approximate angle of the ball. Our method allows us to get an approximate angle error of around \(\pm5^{\circ}\), this is more than enough to work with effectively with the IR ball. The error gets smaller the closer the robot is to ball, and the error only increases up to that range if the robot is not moving due to drift. It is important to mention that drift only occurs in cyclic intervals, and if the robot is not moving for extended periods of time, the drift will automatically fixed itself with SMA. Parallel to this, we use geometric tools in order to narrow down noise. The final result is a very stable angle that allows us to have a very aggressive striker that can chase the ball at high speeds without losing it.
Location of the TSOP4038 IR sensors
The sensors are placed in a circular form, however, they are not uniformly distributed. The distribution was mapped by overlaying the estimated sensor position on a specified image robot:
Retrieving angle method explained
Angle retrieval algorithm
We measure the width under the pulse wave to obtain instantaneous raw angles \(\theta_i\). Instead of using the final averaging formula directly on the raw samples, we first form short batch averages and then perform a final average on all of our batches. Before we begin, it is important to mention the fact that this method is based on Y6tada's work for RCJr_IRball ball mapping. Hence, a lot of our methods and mathematical tools are based on his work, however, we have made some adjustments to it in order to be able to work with the new ball and the new structure of the robot.
We get from each group the sensor pulse width by using the getAllSensorPulseWidthGroup method. This method dynamically updates for each group the pulse width got from each sensor. From there, it calculates the vector components using the calcVectorXYFromPulseWidth method; which grabs the pulse width and extracts the pre-calculated sine and cosine values for each sensor, multiplying them together to get the vector components.
For each batch of k raw angles \(\{\theta_1,\dots,\theta_k\}\) compute the vector components
and obtain the batch angle (in radians)
The final averaged angle \(M_A\) (in degrees) is computed over m such batch angles \(\{\varphi_1,\dots,\varphi_m\}\) as:
Using a larger k reduces per-batch noise at the cost of latency; empirically we use k=25 as a good trade-off. The Arduino is only responsible for sending raw angle readings; the Teensy performs the batch computation and the final averaging, then sends \(M_A\) to the main controller.
Triangulation tuning method
The method explained above is very stable, however, due to the ball size, we are able to improve readings by using triangulation. We know the position of each sensor, therefore, we can read three sensors at a time on the shape of a triangle. This method allows us to triangulate the position of the ball; narrowing down noise as the sensors that are not in the triangle will be ignored. This happens extremely fast, basically granting us an instantaneous angle with a very low error. This is visualized in the following image, where the blue circles represent the active sensors, and the dashed lines represent an invisible triangle that acts like the trigger to enable each sensor individually.
Communication with Teensy
Communication happens via UART; communicating from pins 0 and 1 (RX, TX) on the Arduino, and on pins 14 and 15 (RX, TX) on the Teensy. This communcation can only happen if and only if Arduino is sending data via Serial and the Teensy is receiving data from Serial3.
Reading from Teensy example:
#include <Arduino.h>
// Teensy 4.1 Hardware Serial 3 (Pin 15 = RX3, Pin 14 = TX3)
#define HWSERIAL Serial3
void setup() {
Serial.begin(115200);
HWSERIAL.begin(9600);
}
void loop() {
// Read string from Arduino Uno via UART and print to PC
if (HWSERIAL.available() > 0) {
String incomingData = HWSERIAL.readStringUntil('\n');
incomingData.trim(); // Remove lingering \r carriage returns
if (incomingData.length() > 0) {
Serial.print("UART received Theta: ");
Serial.println(incomingData);
}
}
// Echo anything typed in the PC Serial Monitor back to the Uno (if needed)
if (Serial.available() > 0) {
String usbData = Serial.readStringUntil('\n');
usbData.trim();
HWSERIAL.println(usbData);
Serial.println(usbData);
}
}
Additionally, it is important that in order for the Teensy to be able to communicate with the Arduino, you need to use a level shifter, otherwise, if the Arduino tries to communicate with the Teensy directly; due to voltage differences, the Teensy will literally toast itself.
Code upload to IR
In order to make changes to the code, you have two options:
I.- Use an FTDI to upload code directly into the microcontroller
II.- Use an auxiliary arduino to upload the code from that arduino, remove the microcontroller, and place the microcontroller into the PCB.
If you are using FTDI, consider the following:
1.- Use Arduino IDE as sometimes platformio environment fails to upload automatically with the FTDI.
2.- You need to have RX and TX disconnected from the other microcontroller.
3.- Verify connections from the FTDI before uploading.
4.- Upload it as an arduino UNO.
If it failed, it can be for multiple reasons:
1.- If you get an access denied error, please close all serial monitor instances from arduino IDE. Check whether the solution was fixed. If its still denying you access, open device manager, go to ports, and re-install the port where the FTDI is homed to. After this, disconnect the FTDI from the port and connect it into another port.
2.- If you have a timeout error, this signifies that either your connections are incorrect (RX is connected with RX, or TX is connected with TX) or that the FTDI is fried. Remember that TX goes connected with RX, and RX goes connected with TX.
3.- If you are in the uploading process, arduino attempts to upload it, but constantly fails to the point that it starts making attempts. It means that either you bridge to connect the 5V and VIN malfunctioned, or you accidentally have it connected to the Teensy RX and TX ports, or the jumpers to which you are uploading your code are malfunctioning, or the FTDI is fried.
Issues and planned improvements
The IR ring worked well for the 2025 IR ball, however, experimentally we were able to show that the IR ring will not behave the same with the smaller 2026 IR ball. The issues we were able to detect include: the ball gets lost more easily, the ball is more susceptible to noise, the IR intensity is different; calibration is a requirement, and the ball weights less; making it more sensible to sudden movements. Even though a lot of the possible improvements are mechanical, software changes are needed in order to be able to work with the new ball.
- Radius approximation using
pulseIn: With the ammount of pulses received, we can get an approximation of the magnitude of the distance to the ball. This is not a linear relationship; having the structure of similar of that of a bell curve. - Binary Serialization data: Some experimentation was done in order to have proper communication between Arduino and Teensy; preventing serial bottlenecks. The solution was to use binary serialization, which is a method of converting data into a compact binary format for efficient transmission. This allows us to send the angle data in a more compact form, reducing the amount of data being sent and thus improving communication speed. Some experiments were done, but it remained unused as the current communication speed was sufficient for our needs. However, for future versions, this is a very plausible improvement that can be implemented in order to have a more efficient communication between the microcontrollers.
- Derivative error correction: By keeping track of previous change and current change, we are able to detect whether the angle change is logical or not. Combined with radius approximation, if we detect that the ball is close, but the angle positioning is changing dramatically, we are able to infer that angle's readings are wrong. From this assuption, we can apply a correction to the angle by using the previous angle and the current angle, and applying a weighted average to it. This allows us to have a more stable angle reading, especially when the ball is close and the readings are more noisy.