Cheese Making Temperature Controller
A friend of mine is a dairy farmer, and he makes small batches of cheese to sell in the local market. The cheese production is more at the "serious hobbiest" level, or you can call it "artisanal", but either way, production involves a vat that holds about 30 gallons of raw milk.
The vat is double walled, so that it can be heated by running hot water through the vat wall. Among other variables in cheese production is the temperature that the raw milk is held at, as it turns into cheese curds and whey. The five or so different types of cheese that my friend produces all have different temperature "profiles". Some example (made up) profiles:
Whilst visiting my friend, I volunteered that computerizing the temperature control should not be "too hard". Right now, they manually control the temperature by switching the heat source (hot water through the jacket) on and off whilst monitoring a temperature gauge in the proto-cheese bath.
I make no excuse for my "love" of the Arduino platform, and this is yet another Arduino controlled thing. The parts list is pretty obvious:
Note: Click on the pictures to embiggen.
The first photo show the hardware, before packaging. The second photo is the hardware, packed in a project case, and attached to the test apparatus.
The hardware, before packaging. Note that the "Mega" clone isn't much bigger than the Ethernet shield.
Finished packed hardware with test apparatus. Electric drill on the right is mounted so it can run a stirrer during tests, to keep the water a uniform temperature.
Cheese computer mounted on the wall in the cheese making room at the farm.
Close-up showing display - program is for "Baby Swiss", first temperature is 84°F, actual temp of the cheese is 101°F, and the computer is "Idle", meaning not running the program.
Close-up showing (left) relay terminals that control the heat source, and (right) terminals for the K thermocouple interface.
Close-up of the inside of the box. The red switch at the bottom is a reset button.
Close up of the back of the front cover, showing the LCD board, with serial "backpack" mounted to that. The five conductor ribbon cable is from the four key keyboard.
That ends the obvious bit. How do we control the temperature of the bath? The "obvious" way would be a PID controlled bath, but we don't have "proportional" control of the heat -- we have on/off, so it looks like we're limited to a "bang-bang" controller.
All my experiments have been with a considerably different apparatus then the actual cheese making equipment that my friend uses. Instead of a 30 gallon double walled tank, I have a 2 quart sauce pan. Instead of circulating hot water as a heat source, I have a $7 hot plate from Walmart. So one thing is certain, the behaviour of my setup is massively different than the "production" setup.
With all this in mind, I decided to try to hack my way to a PID controller. I reasoned that PWM is proportional control. My pot of water responds somewhat slowly to heat input, taking 10 or more minutes to rise from 60F to 80F when heat is applied. What if I were to treat my heat control as PWM instead of "bang-bang"? If the period of the PWM control is 5 minutes, I won't be burning out the relay by making it sound like a buzzer.
So I have implemented a "somewhat crude" PID system by "PWM-ing" the hot plate, using a PWM period of 5 minutes.
You can't tuna fish, but you can tune a PID
There is plenty of information available on how to tune a PID system. Basically, start with the Proportional, and turn the gain up until the system oscillates, and then back it off. Add in the Integral, fiddle with that, and add in the Differential, and fiddle with that.
Tuning this PID is complicated by the fact that the response time is so slow. I found that the oscillation has a period of 30 minutes or so. So my test runs take a long time; I run them for over 2 hours.
I have my code do a "printf" every minute with the current "cheese" temperature, and at the end of the run, I copy and paste this data from the Arduino console/debug window into a file, and then use grap and groff (troff) to graph the result, so I can see the behaviour of my PID loop.
Here's a run with the proportional gain too high, and you can see the temperature oscillating, as the system "hunts". There is no Derivative or Integral component.
The numbers that are printed in a sawtooth shape are times that the heat is turned on, in seconds.
In this run, the gain is about right, but there is no Derivative or Integral component, and the "final" temperature has an offset from the set point.
This run has the Proportional gain about right, but the Integral gain is too high, and we're overshooting the set point.
Here the Proportional and Integral gain look OK, and we seem to be able to get the temperature to the set point and hold it there. I haven't tried optimizing Derivative gain yet.
This is the acid test, and frankly, we're not doing too well. We probably need to speed up the PID loop, but I'm not going to, as the actual cheese vat will have much slower response, so there's not much point in beating on this test setup any further. This run has a rate limit on the temperature increase, which my code approximates as a series of steps.
What we ended up using in production.
When I brought the 'finished' computer to the farm to try it with the real cheese bath, I found that the response of the real bath was waaaay different from my little experimental setup, as expected. The real bath might take 10 minutes to rise just 4 to 6 degrees, instead of the 20° F rise my test setup achieves.
Rather than spend a lot of (possibly wasted) time trying to tune the PID, I decided to try just using Bang-Bang control. The cheese bath heats like a house, not a saucepan, so making the controller act like a slightly clever home thermostat seemed appropriate.
Naturally, I'd spent (wasted) a lot of time fooling around with PID control, so I had to do some coding to get Bang-Bang working properly, add some UI features, etc, but the result works fairly well.
This was an early test run, with water instead of milk. The command was to just heat to 92° F as fast as possible and then hold for two hours. As you can see, the temperature varied between 90 and 92° F.
The next test run of the cheese heat controller using water. The program was to heat to 92 as fast as possible, hold that for 20 minutes, heat to 104° F at 2° per 5 minutes, hold for 30 minutes, and then heat to 120° at 4° per 5 minutes. The amount of heat we can add to the milk limits the rate at which the temperature can rise, to about 2 or 3° F per 5 minutes. The first ramp is full speed, not limited. The second ramp is limited to to 2 degrees in 5 minutes, and we can achieve that. The third ramp is supposed to climb at 4 degrees in 5 minutes, and we obviously cannot achieve that.
Another problem with that run is that the heater is shutting off at 1° below the setpoint, and that means that the milk temperature varies between 1 and 3° below the setpoint. We fixed that by setting the shutoff to be equal to the setpoint, or even to 1° above the setpoint.
For this test run, we raised the temp at which we shut off the heat source by 1°F. The bath temperature has some overshoot, especially at the lower temperature of 92°F. This is because the heat source is a hot water jacket surrounding the milk tank; the water in the jacket is significantly hotter than the milk temperature, and does not transfer all it's heat at one time. So when we shut off the pump that circulates the hot water through the jacket, the hot water in the jacket continues to heat the milk for some time after that. The solution to this problem is to lower the water level of the water in the jacket, so that it holds less heat. Of course, this slows down the maximum rate of rise of the temperature, so it's a trade-off.
This was our final test run, where we adjusted the "cut-off temperature" so that it would be 1 degree below setpoint for temps below 100°F, equal to the setpoint for temps between 100°F and 110°F, and 1° greater than setpoint for temps above 110°F. We still cannot make a 4° in 5 minute heating rate, but other than that, temperature regulation is within 1°.
The cheese heater logs the results of a run to internal EEPROM, and can also generate an SVG plot of that most recent run. The above is one such plot, generated by the cheese heater. The blue line is the setpoint(s), the green line shows the rate limited setpoints, and the red line is the actual temperature.
If your browser doesn't render the above graph (the one with with colored lines), then you need a better browser. Firefox, Chrome, and Opera all support SVG.
comments? Email bill at dudley . nu