As part of a course at UCI (MAE 106), I conceptualized, designed, and manufactured an autonomous pneumatic-piston-powered robot. My role was primarily to program the rover in C++ and assist with the design and CAD modeling of the rover's components, along with manufacturing and assembly. The project posed the following design requirements:
The project took about 7 weeks to complete, with about 4 weeks of it being testing and improving systems.
I programmed the entirety of the robot's navigation using C++ in PlatformIO, a popular open-source embeded system IDE. I utilized best-practices to create clean, easy to modify code for each part of the robot's automated program. I divided the project into multiple .cpp and .hpp files to abstract each type of operation. For example, I create an Actuation file, a Navigation file, etc. which would all contain their respective functions to be called on a global scope. This allowed me to more easily modify my code and track bugs.
The rover ran all autonomous systems on an Arduino Uno, which has very limited RAM (2kB) and flash (32kB).
My rover utilized more complex systems than most other teams because I wanted to challenge myself with coding and I wanted to ensure optimal track performance even during times of poor sensor data.
One of the first major issues I ran into during this project was running out of RAM. Despite the device being preloaded with only around 80% of its max RAM, I found that mysterious issues were occuring with corrupted data. I discovered this issue was due to the arduino running out of RAM during its processing (due to fragmentation) and needed to find a solution. I then discovered that all of the Serial.print("debug info here") commands used to provide debug info to the serial port were actually storing their strings in RAM. Using the F("mystring") macro, I moved all of those strings into flash (which has a lot of extra capacity) and freed up around 40% of the RAM capacity, resolving the issue.
The next greatest issue I faced was getting useable magnetometer data. The piston driving the vehicle utilizes a solenoid valve, which produces a significant magnetic field when actuated. Despite positioning the magnetometer as far as possible from the solenoid, the valve could still induce the equivalent of 10 degrees of heading offset. This would cause the rover to take a wavy path between waypoints and could ultimately make it unstable and fall over. To correct for this, I calibrated a solenoid offset which would be applied to the raw magnetometer data whenever the solenoid fired. I also implemented a feature to ignore the transient data during the first 100ms after the piston changes state. This was required to avoid a spike in the heading leading to instability.
In addition to the solenoid interfering with the magnetometer, I identified a non-linear relationship between the actual heading of the rover and the calculated heading based on the magnetometer, even after multiple calibrations. During the autonomous sequence, the rover is expected to move straight forward, then turn 90 degrees, then move forward more. Because of the non-linear mapping, orthogonal directions in the physical world were not interpreted as orthogonal to the program. This meant that the actual path taken by the robot involved moving in non-orthogonal directions, which inhibited the rover's ability to understand its current position. Therefore, a small offset was added to the intended waypoint positions to ensure it ended at the expected target for full points. This fix was not ideal and require a lot of tweaking, and was very sensitive to small changes. In the future, I might consider doing a more thorough mapping of the actual magnetometer response to provide more accurate navigation.
During the final competition, my rover won first place due to its fairly consisent run-to-run behavior and ability to reach to bonus point square. One of the greatest challenges during the competition was having other robots navigating at the same time. The robots do not have any way of sensing or avoiding other robots, so there were multiple occasions where a very capable robot would get hit or knocked over by another robot. My robot got trapped in a big pile up on one of the three rounds, and managed to do very well on the other two.
My effort to make the code readable and geometry-based paid off when various track factors, such as the slope of the pavement or magnetometer issues required adjustments to the autonomous program that were impossible to predict.