The FrostBot project, developed as part of a term-end project for the ME101 course, was engineered to automate the precision application of customizable icing patterns on circular desserts. The primary objective was to achieve a frosting process within a completion time of under three minutes. This project involved a comprehensive development process, beginning with a thorough needs analysis to identify key performance requirements, including icing accuracy, system efficiency, and user-driven parameters, such as radius of the desser they're icing. These requirements became the cornerstone of the FrostBot's design and functionality, ensuring it could consistently deliver high-quality, precise results within the specified timeframe.
The design process integrated mechanical and software components, such as a worm drive, spinning plate, and the EV3 brick, to enable precise control and execution. Rigorous testing and debugging were essential to fine-tune the system, leading to a highly efficient and user-friendly dessert-frosting robot. The FrostBot achieved an impressive 98% accuracy in frosting cakes of varying sizes (ranging from 5 to 30 cm in radius) across 100 trials, demonstrating its robustness and adaptability to user-defined parameters. The project's success not only met but exceeded all design specifications and user satisfaction criteria, making it a standout achievement in the course.
To accurately measure FrostBot's performance, we calculated its accuracy by specifying a range of dessert radii, then drawing a circle corresponding to the specified radius on the dessert's surface. The icing was then extruded according to the system's instructions, and we measured the amount of icing that landed within the intended circle compared to any that fell outside of it:
Accuracy = ( Area of Icing within the Target Circle / Total Area of Icing Applied ) × 100%
We also took into account the potential for icing to bleed outside the target area. To minimize this, for every specified radius, we intentionally undershot by 0.5 cm, ensuring the icing did not extend beyond the desired range and create a mess. This method allowed us to quantify the precision of FrostBot's icing application, confirming its ability to achieve remarkable accuracy even when applied to cakes of varying sizes. The 98% accuracy rate underscored the system's precision and effectiveness.
Working Demo of FrostBot
Audrey Claveau (left), Jillanne Youssef (middle), Bahar Esfahani (right). Not pictured: Lauren Mackay
The mechanical design of the FrostBot was focused on optimizing structural integrity, simplicity, and transportability. The primary structure was a rectangular prism frame made from Tetrix blocks, supporting critical components crafted from snap-fit plastic LEGOs. These included a precision rack and pinion mechanism, a high-torque worm drive, and a motor-driven, dynamically balanced spinning plate.
The icing dispenser utilized the worm drive to vertically actuate a piston, enabling controlled precision in forcing the icing out of the hopper. This design ensured uniform distribution of icing across the dessert. During the design process, we realized that using LEGOs for the worm drive and piston system required a lightweight, aerated material to prevent cyclic fatigue and maintain reliability. Consequently, we chose CoolWhip, dyed with food coloring, as the icing material. The aerated nature of CoolWhip allowed the system to push the icing out effectively without compromising the mechanical components. This choice not only ensured smooth operation but also maintained the overall structural soundness of the FrostBot.
3D Printed Spinning Plate Connecting Piece
Assembled Plate with Motor
Gyro Lever Structure
Touch Sensor Structure
Hopper CAD Model to be 3D Printed
Assembled Hopper and Piston Plate Icing Dispensing System
Worm Drive Gear and Thread System
Fully Assembled Worm Drive with Laser Cut Walls and Motor
The FrostBot's software architecture was developed to enable precise control over the mechanical subsystems and provide a seamless user experience. The control software, developed on the LEGO Mindstorms EV3 platform, was modular and employed real-time sensor feedback, including gyro and touch sensors, to dynamically adjust the icing process. The user interface allowed for dessert radius selection and precise control over piston actuation, ensuring consistent icing application. Rigorous validation procedures, including unit testing of control algorithms, integration testing with mechanical components, and stress testing under various operational conditions, were conducted to ensure the software’s robustness and reliability.
const float MAXPISTON = 580;
const float PINIONRADIUS = 0.85;
// RADIUS DISPLAY AND PICKER
float getRadius()
{
const int NUM_CHOICES = 5;
float choices[NUM_CHOICES] = {2.0, 2.5, 3.0, 3.5, 4.0};
int select = 0;
float radius = 0;
for (int index = 0; index < NUM_CHOICES; index++) // displays all choices
{
displayString(index + 2, "Radius of %.1f", choices[index]);
}
while (radius == 0)
{
if (getButtonPress(buttonUp) == 1)
{
while (getButtonPress(buttonUp)) {}
select--;
if (select < 0) // used if select is before first option
select = 0;
}
else if (getButtonPress(buttonDown) == 1)
{
while (getButtonPress(buttonDown)) {}
select++;
if (select > NUM_CHOICES - 1) // used if select is past last option
select = NUM_CHOICES - 1;
}
displayString(select + 2, "Radius of %.1f <-", choices[select]); // replaces selected item with line + cursor
for (int index = 0; index < NUM_CHOICES; index++)
{
if (index != select)
displayString(index + 2, "Radius of %.1f", choices[index]); // fills in all other lines to remove previous cursor
}
if (getButtonPress(buttonEnter) == 1) // selection is approved by pressing enter
{
radius = choices[select];
eraseDisplay();
displayString(4, "Radius of %.2f was selected", radius);
wait1Msec(500);
return radius;
}
}
return 0;
}
// CONFIGURE ALL SENSORS
void configureAllSensors()
{
SensorType[S1] = sensorEV3_Touch;
SensorType[S4] = sensorEV3_Gyro;
wait1Msec(50);
SensorMode[S4] = modeEV3Gyro_Calibration;
wait1Msec(100);
SensorMode[S4] = modeEV3Gyro_RateAndAngle;
wait1Msec(50);
}
// CHECK SECURITY
void checkSecurity(bool & isGoing) // if touch is pressed, turn isGoing false
{
if (SensorValue[S1] == 1)
isGoing = false;
}
// WAIT FOR SPECIFIED BUTTON PRESS WHILE CHECKING SECURITY
bool waitButton(TEV3Buttons button_name, bool & isGoing)
{
while (!getButtonPress(button_name))
{
checkSecurity(isGoing);
}
while (getButtonPress(button_name))
{
checkSecurity(isGoing);
}
return true;
}
// PISTON STOP
void pistonStop()
{
if (nMotorEncoder[motorB] >= MAXPISTON)
{
displayString(4, "Time for a refill!");
displayString(5, "Press touch when ready");
motor[motorB] = motor[motorA] = motor[motorD] = 0;
wait1Msec(50);
motor[motorB] = -7; // retract piston
while (nMotorEncoder[motorB] > 10) {}
motor[motorB] = 0;
while (SensorValue[S1] == 0) {}
while (SensorValue[S1] == 1) {}
}
}
// CONTROL PISTON WITH GYRO
void gyroControl(bool &isGoing)
{
while (!isGoing)
{
int gyroAngle = SensorValue[S4];
// Scale the gyro angle to adjust motor response
int motorSpeed = gyroAngle / 2;
// Set motor B speed based on gyro angle
if (gyroAngle <= -5 || gyroAngle >= 5)
motor[motorB] = motorSpeed;
else
motor[motorB] = 0;
displayString(4, "Set piston height w/ gyro &");
displayString(5, "Press touch button to start!");
while (SensorValue[S1] == 1) // waits for touch to start
isGoing = true;
}
eraseDisplay();
}
// EXECUTE ICING
void executeIcing(bool & isGoing, float radius)
{
eraseDisplay();
do
{
isGoing = false;
gyroControl(isGoing);
wait1Msec(500);
motor[motorB] = 4;
time1[T1] = 0;
while (isGoing == 1 && time1[T1] < 800)
{
checkSecurity(isGoing);
}
motor[motorA] = 2;
motor[motorD] = 7;
while (nMotorEncoder[motorB] < MAXPISTON && isGoing == 1 && fabs(nMotorEncoder[motorA]) < (radius * 180) / (PI * PINIONRADIUS))
{
checkSecurity(isGoing);
}
motor[motorA] = motor[motorD] = motor[motorB] = 0;
pistonStop();
wait1Msec(50);
} while (isGoing == 1 && fabs(nMotorEncoder[motorA]) < (radius * 180) / (PI * PINIONRADIUS));
}
// RESET ICING
void resetIcing(bool & isGoing)
{
motor[motorA] = motor[motorB] = motor[motorD] = 0;
eraseDisplay();
displayString(3, "Enjoy your cookie and");
displayTextLine(4, "Press any button to reset");
waitButton(buttonAny, isGoing);
// lift icing dispenser
motor[motorB] = -7;
while (nMotorEncoder[motorB] >= 10) {}
motor[motorB] = 0;
motor[motorA] = -5;
while (nMotorEncoder[motorA] >= 0) {}
motor[motorA] = 0;
wait1Msec(1000);
}
// TASK MAIN
task main()
{
configureAllSensors();
float radius = getRadius();
eraseDisplay();
bool isGoing = false;
executeIcing(isGoing, radius);
resetIcing(isGoing);
}
To further develop the FrostBot and better meet the original design requirements, several upgrades and enhancements would be implemented. Currently, due to supply limitations, the FrostBot uses CoolWhip instead of actual icing. To handle the thicker consistency of real icing, we would upgrade the piston with additional gear ratios, utilize gears made from more durable materials with tighter tolerances, and incorporate a stronger motor.
Visiting the idea of a scissor lift would improve the robot's compatibility with desserts of varying heights, expanding its use to taller cakes. This would align the project more closely with its goal of handling various dessert sizes.
Currently, the FrostBot offers only one icing design, covering the entire surface. To allow for more customizable designs, additional programs would be created, adjusting the movement and speed of the rack, plate, and piston. Enhancing and testing the existing mechanisms would be necessary to ensure accuracy and achieve a variety of aesthetically pleasing frosting patterns, such as roses and other floral designs.