The overall goal for the Physics Seminar this semester has been to produce an autonomous rover capable of moving throughout a “simple” environment without hitting any obstacles. This complex idea was made more manageable by dividing the responsibilities into three groups: Data Acquisition, Information Processing, and Actuation. Our team, Information Processing, was responsible for receiving data from the acquisition team, making decisions, and relaying those decisions to the actuation team. As the “middle-man” of the project, our actual responsibilities were more fluid since we worked alongside the other two teams to better facilitate inter-process communication. This report will catalog what we did and what we learned as a result.
The first hurdle we addressed was connecting the lidar to the Arduino Uno. The lidar unit connected to the Uno through a 5V power source, ground, and two information wires. Because we were using I2C as opposed to PWM for communication, we connected the information wires into the SDA and SCL pins. This allowed us to completely power and receive data from the lidar using only the Uno. We were also able to read the data using the serial monitor in the Arduino IDE. This was displayed as either an auto-scaling graph or as a series of distances in cm. The most helpful part of this section of the project was being able to use premade libraries specifically for connecting our lidar unit to an Arduino. All we had to do to measure a distance was call the distance() method. Then to view the distance, we only had to call a serial.print() method which displayed the distance on the computer’s serial monitor.
With the lidar functioning correctly, it was time to move on to the servo. This was also fairly simple to use. The IDE came with a premade servo library, so that all we had to do was tell the servo a position to turn to in degrees. This gave us simple and precise control of the servo’s position in a format that was easy to use in the rest of the code. One problem we did discover was the need for a small pause in the code between each order given to the Servo. If we gave consecutive orders too quickly, the servo would not make it all the way to the first position. This problem was easy to fix with a small (15ms) wait in the code, and-for the most part-the servo alone didn’t give us too much trouble.
With the individual components working off of prewritten example code, it was time to operate the lidar and servo together. This required the creation of another program. Essentially, we created two for loops using the counter variable i as the servo angle. The first loop took the servo from 0 to 180 degrees. The second loop took the servo back from 180 to 0 degrees. The values of the distances at each angle were displayed on the serial monitor. Up to this point, the project worked very smoothly.
The problem arose when we tried to save the distances in an array. Instead of storing distances in centimeters, we stored them as 1’s and 0’s depending on their size. We compared the distance to a predetermined “safety distance” – usually 50cm. If a distance was greater than 50cm, it was assigned a 0. If the distance was less than 50cm, it was assigned a 1. The goal of this was to simplify the decisions (and therefore the calculations) as well as to lighten the load on the Uno. Sadly, the switch to binary did not fully accomplish this because the array in which we stored the 1’s and 0’s was still a full int array and so was taking up most of the memory on the Uno.
Before moving to the solution for this problem, let’s pause and take a minute to understand the flow of our code. Essentially, the rover sees the world as a 181 length, integer array (the 181st position is to account for both 0° and 180°). Each position is populated with either a 1 or a 0. A 1 indicates the presence of an object within the safety distance – possibly necessitating some action – while a 0 indicates the absence of any close obstacle. When the rover completes a forward movement, it takes a 180° scan of its forward environment, follows the following logic:
- It checks the center of the array (the actual size of this check is around 60°) for any 1’s.
- If the section is clear, it proceeds forward for 1 second.
- If the section is at all obstructed, it moves to the next set of logic.
- With an object in front of it, it then scans through the array to find the largest series of uninterrupted 0’s.
- If the largest gap is large enough for the rover to fit through, it turns to the center of the gap, scans again, and returns to step 1 of the logic.
- If the largest gap is too small but contains one of the edge points (0° or 180°), it turns to that outermost point, scans again, and repeats step 1.
- If the largest gap is too small and does not contain an edge, the rover backs up for .75 seconds, scans again, and reverts to step 1.
One note, the lidar occasionally throws up false “1’s” depending on the size of the safety distance. This made it turn when its path was unobstructed. To address this, we only consider a 1 to be an object if it is next to at least four other consecutive 1’s. Most objects we encounter are greater than 5° wide by the time they enter the rover’s safety distance, and false 1’s are avoided.
We now return to the narrative. When we realized that the Uno didn’t have enough RAM to handle the array and other processes, we ordered the Arduino Due, which hadconsiderably more memory and power. This solved the problem of insufficient memory on the chip. While the Due did use 3.3V output pins instead of the Uno’s 5V pins, thelidar and servo were still able to operate (this was because the 3.3V of the Due matched the internal operating voltage of the lidar). When tested separately, both the servoand the lidar worked with the Due. However, when operated simultaneously, the servo and lidar would work for only a few sweeps –even correctly diagnosing objects –but aknack error would inevitably come up and the process would stop working. Measurements with an oscilloscope revealed that the 5V supply voltage for the lidar was dropping low but the knack error would still pop up after a few scans even with a steady supply. We solved this by switching to an Arduino Mega –a unit of similar processing power andmemory to the Due but with 5V as its internal voltage. This time the unit worked without producing the knack error. Better still, the Mega’s footprint matched the Due’s sothe motor shields and the mounted plastic holder still fit.
At this point, the rover was fully built and testing could begin. To control the motors, we sent a time, an intensity, and a direction to methods created by the Actuationteam. Since our earlier code specified each turn in degrees, we measured the rover’s average turn rate and used that to convert from degrees to time. We also measured itsforward speed at several intensities to find optimal travel speeds and times. With these conversions in place, a few more hours of tinkering had the rover successfullydriving around an area and dodging obstacles.
Overall we learned several skills and general project lessons. First, we learned how to use an Arduino board. On the coding side, we learned many commands and the basicstructure of the Arduino language. For example, Arduino code contains two main sections: the initializing section and the main loop. Connections to devices must be setup inthe initial section, while the next loop handles the meat of the code. More importantly, we learned how to use the prewritten examples as the foundational code to write ourmore complex algorithms. This was key to learning how to interact with the servo and the lidar. By copying portions of the demos, we were able to piece together workingprograms. It was surprising just how much these bits of code and some basic coding experience were able to accomplish.
A second broader lesson was how to take an abstract logical model and apply it to a physical process. We started early in the semester on chalk boards diagramming ways therover could “understand” and respond to its surroundings which we translated into Arduino code. Then piece-by-piece, we interfaced this code with the servo, lidar, andmotors. This was by far the most difficult part of the project. Going from logic to reality required constant troubleshooting, but after solving enough problems we produceda functional design. It was very gratifying to see the physical machine following the same reasoning that we wrote on the chalkboard months earlier.
The last skill we learned is how to work in a large segmented team on a single project. By necessity, our futures as engineers, scientists, and doctors will be characterizedby working in small teams as part of a larger group on complex problems. As we learned this semester, success requires constant communication between groups. For us, thistook the form of weekly meetings in class, spontaneous hallway discussions, and planned brainstorming sessions. It was very important that we looked up from our own sectionof the project to make sure we were fitting into the other pieces of the overall project. For instance, information processing may have known that differential steeringwould make the coding easier, but we had to actually relay that idea to actuation for that to happen.
Over the semester, we produced a rover capable of avoiding most large objects and meandering around a room. Our team learned how to code in Arduino and produced a programcapable of making decisions for the rover. We also interfaced the lidar and servo with the Arduino using online connection guides and premade libraries. We learned basictroubleshooting skills with small electronics and how to better work in a large team. Overall, the semester was a great learning experience which we will build on in the future.