The code component of our project could be seen as the integration between electrical and mechanical, and was essentially all firmware. We made many design decisions over the course of the project, discussed below.
We switched from using delays to using millis() loops early on. Delays cannot be interrupted, thus user input was not consistently read. Millis() delay loops do the same thing, but are able to continue with other parts of the program and register user input. Shown below, two times are recorded. The desired action is performed, and the second time continuously updated, until the difference between the two times exceeds some limit, at which point the computer moves onto its next action.
We switched from using delays to using millis() loops early on. Delays cannot be interrupted, thus user input was not consistently read. Millis() delay loops do the same thing, but are able to continue with other parts of the program and register user input. Shown below, two times are recorded. The desired action is performed, and the second time continuously updated, until the difference between the two times exceeds some limit, at which point the computer moves onto its next action.
The code was responsible for integrating with both electrical and mechanical, and as both systems became more complex, we moved to having two Arduinos, a sense Arduino and a Think/Act Arduino. The two Arduinos communicate using SoftwareSerial and were wired with a receiving (RX) and transmitting (TX) wire. The sense Arduino was hooked to the electrical circuit. This is the Arduino that determined the period and the bpm of each beat using the original technique. We sent the bpm to the Think/Act Arduino, which took the bpm to actuate the limbs of the drummer in time. Throughout the project, we used the same technique for ensuring that the robot played on the same beat it was detected. The time when the limb began moving was recorded, as well the stop time. We took the difference, subtracted it from the period, and had the limb rest for that time.
Originally, we planned to track the position of both limbs using quadrature encoders, which have two channels that create square waves phase shifted 90 degrees from another. The encoder moves forward or backward depending on which channel is leading. While simple in theory there were a lot of problems with the magnitude of the phase shift, and we realized that using them at this stage in the project was not a productive use of our time if we could accomplish the same task using alternatives with a faster learning curve. We used a potentiometer on the foot in order to allow our foot to move as perfectly as possible from position to position. The potentiometer was rated for 5 volts, and the variable pin was connected to the Arduino and read via analogIn(). The voltage would change based on the rotation of the potentiometer, and we were able to use that data to track the position of the foot and ensured it stay within the physical limits set for the foot. Because the foot had to push down on something so heavy, position-based calibration was needed to prevent drift. However, using time-based calibration worked decently well with the arm after the actuation times were adjusted.
For integration with electrical, we started by using a built in digital Arduino function called “pulseIn” which worked by checking if the input was high or low. We were able to determine time on and time off, and find period of the beat. Integrating with the electrical system was simple while listening to the metronome due to the consistent and heavy nature of a metronome beat: audio detection would give us the same signal. However when the circuit filtered more difficult signals, like a song, the result was a digital wave with several impulses per beat due to some inconsistencies in the song. Thus, Arduino’s pulseIn function would read very quick periods, and not generate an accurate bpm. Digital input functions didn’t have an in-built debouncing function, so we pivoted to reading the electrical signal in an analog pin, where we were able to set a debounce time to filter out any jumps. Our debouncing time was 350 miliseconds, which we chose because it was sufficiently smaller than the time between peaks at 120 bpm is 500 milliseconds, which approaches the top speed of our system.
Originally, we planned to track the position of both limbs using quadrature encoders, which have two channels that create square waves phase shifted 90 degrees from another. The encoder moves forward or backward depending on which channel is leading. While simple in theory there were a lot of problems with the magnitude of the phase shift, and we realized that using them at this stage in the project was not a productive use of our time if we could accomplish the same task using alternatives with a faster learning curve. We used a potentiometer on the foot in order to allow our foot to move as perfectly as possible from position to position. The potentiometer was rated for 5 volts, and the variable pin was connected to the Arduino and read via analogIn(). The voltage would change based on the rotation of the potentiometer, and we were able to use that data to track the position of the foot and ensured it stay within the physical limits set for the foot. Because the foot had to push down on something so heavy, position-based calibration was needed to prevent drift. However, using time-based calibration worked decently well with the arm after the actuation times were adjusted.
For integration with electrical, we started by using a built in digital Arduino function called “pulseIn” which worked by checking if the input was high or low. We were able to determine time on and time off, and find period of the beat. Integrating with the electrical system was simple while listening to the metronome due to the consistent and heavy nature of a metronome beat: audio detection would give us the same signal. However when the circuit filtered more difficult signals, like a song, the result was a digital wave with several impulses per beat due to some inconsistencies in the song. Thus, Arduino’s pulseIn function would read very quick periods, and not generate an accurate bpm. Digital input functions didn’t have an in-built debouncing function, so we pivoted to reading the electrical signal in an analog pin, where we were able to set a debounce time to filter out any jumps. Our debouncing time was 350 miliseconds, which we chose because it was sufficiently smaller than the time between peaks at 120 bpm is 500 milliseconds, which approaches the top speed of our system.
The primary challenge that our code had to overcome was dealing with two different mechanical systems, and calibrating them. Originally, we were dealing with encoders which would have been able to track the positions of both the arm and foot, and could have limited their range of motion in that way. Unfortunately, with the failure of the encoders, a more creative approach was used. The foot kept the same logic: a potentiometer was attached to the shaft of the spinning motor, and was kept within a specific range of motion. The arm proved to be much more difficult, since the drumstick and the spinning motor were so separated. We used a time-based calibration approach here: the arm would move up, stop briefly, then strike down. Because these two calibration methods were so different, they required two almost entirely different sets of code. It also made integration, with both limbs playing at the same time, very difficult because we would have one arduino trying to execute two processes at the same time.
One of the large changes that we made dealt with exactly what information was being sent over the serial connection. The old versions of the code would send bpm information every two inpulses, which encoded information we could use to deal with the phase shift of the signal and what the physical system was playing. However, this resulted in inaccurate signals due to the small sample sizes. The new code continuously updates the bpm since its start time, and will send to the act arduino only when prompted. There were some drawbacks to this: we lost the information that we could use to autonomously calibrate the system. However, the boons outweighed this fact in the short span: processing time was faster, and the bpm was much more robust against inconsistencies in the song we were listening to and the electrical system because we take five samples. Given more time on the project, we would like to go back and try this communication again to autonomously tune the robot.
Because we lost our previous method of calibration, a new one had to be implemented for our final product. A user interface was created so that the user could listen to both the beat and the drummer, and adjust the drummer forward or backwards depending on the offset. Once the drummer was synced with the metronome or song, no more tuning was required. This did require the operator of the drummer to be musically skilled to the point where they would be able to recognize which direction, and by how much, to move the drummer in.
Another change that was implemented in this final iteration dealt with integrating with a sensitive mechanical system: switching the direction of the motors too quickly would result in belt jumping. A small break time on the order of 20 milliseconds was implemented to keep this from happening, and was taken into account when calculating the rest time for each limb in order to stay in time.
Because playing on beat deals with precisely calculating the time to actuate the motors, everything in an ideal system would be kept inside of a continuously running timing loop. On one arduino, this is very possible. However, we needed to split our code onto two arduinos to keep the two continuous processes, so we dealt with sending information over a serial connection. Very quickly, we realized that we wanted to minimize interaction with the serial ports, either with the user or another arduino, because the timing was incredibly inconsistent. Information would be sent, but the time that it was received was unknown. While on one hand we wanted to minimize interaction with the serial ports, a fair amount of on-the-fly user interaction was needed, especially with the most recent method of calibration. We eliminated print statements in our code, and only used the serial connection for calibration.
One of the large changes that we made dealt with exactly what information was being sent over the serial connection. The old versions of the code would send bpm information every two inpulses, which encoded information we could use to deal with the phase shift of the signal and what the physical system was playing. However, this resulted in inaccurate signals due to the small sample sizes. The new code continuously updates the bpm since its start time, and will send to the act arduino only when prompted. There were some drawbacks to this: we lost the information that we could use to autonomously calibrate the system. However, the boons outweighed this fact in the short span: processing time was faster, and the bpm was much more robust against inconsistencies in the song we were listening to and the electrical system because we take five samples. Given more time on the project, we would like to go back and try this communication again to autonomously tune the robot.
Because we lost our previous method of calibration, a new one had to be implemented for our final product. A user interface was created so that the user could listen to both the beat and the drummer, and adjust the drummer forward or backwards depending on the offset. Once the drummer was synced with the metronome or song, no more tuning was required. This did require the operator of the drummer to be musically skilled to the point where they would be able to recognize which direction, and by how much, to move the drummer in.
Another change that was implemented in this final iteration dealt with integrating with a sensitive mechanical system: switching the direction of the motors too quickly would result in belt jumping. A small break time on the order of 20 milliseconds was implemented to keep this from happening, and was taken into account when calculating the rest time for each limb in order to stay in time.
Because playing on beat deals with precisely calculating the time to actuate the motors, everything in an ideal system would be kept inside of a continuously running timing loop. On one arduino, this is very possible. However, we needed to split our code onto two arduinos to keep the two continuous processes, so we dealt with sending information over a serial connection. Very quickly, we realized that we wanted to minimize interaction with the serial ports, either with the user or another arduino, because the timing was incredibly inconsistent. Information would be sent, but the time that it was received was unknown. While on one hand we wanted to minimize interaction with the serial ports, a fair amount of on-the-fly user interaction was needed, especially with the most recent method of calibration. We eliminated print statements in our code, and only used the serial connection for calibration.
The foot was to travel up then down to press the kick drum foot pedal. Unlike the arm, the foot position is based off of a potentiometer rather than time. With the potentiometer, we were able to establish a very consistent range of movement. Due to the power of the motors, they would occasionally drift past one of the potentiometer bounds. To correct for this, we check if the foot is outside of the upper or lower bound every time it moves. If the status is such, the motors drive in the appropriate direction until the potentiometer is inside the desired bounds.
The mechanical system was built for a specific range of motion, and over-extending would and could lead to breakages. After nearly breaking the system a few times, we began to realize the importance of incremental testing. Changed one piece of code at a time, thought about what the changes would be, etc. We couldn’t blindly test code, so really had to think through it. Unfortunately, this didn’t always go well, even with incremental testing. Several start/stop methods had to be implemented such that if something went wrong, which it often did in testing, we would be able to stop the system from hurting itself. Power connections were kept on the table at all times, often in hand, ready to pull apart at the slightest warning of anything gone wrong. The user interface (UI) had to start the robot, such that it wouldn’t do anything even if fully powered automatically. The UI also had a stop feature, which was always used before uploading new code onto the arduinos.
Check out our github for our code, a detailed readme, and more!
|