Cookpadに投稿しようかと思いましたが、せっかくRaspberry Pi Advent Calendar 2015の25日枠を頂けたのでこちらに参加させて頂きました。
クリスマスといえばローストビーフですね♪ 真空低温調理法という科学的に裏付けられた調理法は肉本来の旨さを最大限引き出してくれます。 真空低温調理法についての詳細はCooking for Geeks ―料理の科学と実践レシピなどを読んでみてください。
Cookpadなどでは炊飯器の保温機能を利用してこの調理をやっている人を見かけますがこれはオススメ出来ません。 理由はこの記事の最後に追記しましたのでこちらを読んでください。
これはRaspberry Pi 2で低温調理器を作成してローストビーフを作るためのレシピです。
材料
材料 | 分量 |
---|---|
牛肉 | 500g |
塩、胡椒、にんにく、ローズマリー | 適量 |
Raspberry Pi 2 | 1基 |
ソリッド・ステート・リレー(大容量) | 1基 |
1-Wire温度センサー DS18B20 | 1個 |
トランジスタ・抵抗 | 適量 |
電気ポット | 1個 |
仕込み
肉に塩コショウで下味をつけたらジップロックで真空パックし、冷蔵庫でしばらく寝かせます。
回路設計
回路図です。
実装
電気ポットの電源ケーブルを切断し、ソリッド・ステート・リレーと接続します。
こちらのリレーですが、スペック的にはGPIOの3.3Vで制御できるはずですが何故か動作しないのでトランジスタを入れて5Vでスイッチしています。
あと結構熱くなるのでヒートシンクをつけた方がいいと思います。
Raspberry Pi 2のポート20番に1-Wireの温度計(DS18B20)を接続し、ポート21番にリレーをつないで、電気ポットを制御します。
Raspberry Piの起動時に1-wireのドライバをロードするようにします
# echo w1-gpio >> /etc/modules
# echo w1-therm >> /etc/modules
1-Wireの温度計はプルアップで接続したので以下の設定をして再起動する必要があります。
# echo 'dtoverlay=w1-gpio-pullup,gpiopin=20' >> /boot/config.txt
# reboot
正しく接続されていると、以下のようにして温度を読み取れます(28-03146b771affはデバイス固有のIDです)。
$ cat /sys/bus/w1/devices/28-03146b771aff/w1_slave
f9 00 55 00 7f ff 0c 10 bb : crc=bb YES
f9 00 55 00 7f ff 0c 10 bb t=15562
このt=15562というのが実際の温度を1000倍したものです。 Pythonならこんなコードで読み出せるでしょう。
DEVICE_ID='28-03146b771aff'
DEVICE_FILE='/sys/bus/w1/devices/%s/w1_slave' % (DEVICE_ID)
def read_temp():
try:
file = open(DEVICE_FILE)
lines = file.readlines()
temp = lines[1].split('t=')[1]
return float(temp) / 1000
except e:
return -1
制御プログラムの実装
電気ポットは放っておいたら沸騰するやつなので、ポットの電源を制御するプログラムを実装します。
低温調理法は温度制御が命ですので、一定の温度を保つことが美味しいローストビーフを作る秘訣となります。
niku.py version 1.0
最初はこんな感じで、水温が60度未満なら電気ポットのスイッチを入れて、60度を超えたら切る。という単純な実装で行けるだろうと、そんな風に思っていました。
TARGET_TEMP = 60
while True:
temp = read_temp()
if temp < TARGET_TEMP:
GPIO.output(21,GPIO.HIGH)
else:
GPIO.output(21,GPIO.LOW)
time.sleep(1)
だがしかし…
実際に上記のプログラムを動かすと、水温は以下の様に推移しました。
ときどき60度を超えていることがわかります。
温まり始めた水は加熱を止めても温度が上昇し続けるのです。
特に初回は65度以上に上昇しています。これはいけません、お肉のタンパク質を変性させてしまいます。
PID制御
こんなとき必要になる技術がPID制御です。
PIDとは比例・積分・微分の略で制御工学の基礎に出てくるやつです。
P制御
まずは比例制御を実装してみましょう。
比例制御はy(t)を現在値、r(t)を目標値とすると以下の式で操作量u(t)を求めることができます。
$u(t) = K_p(y(t) - r(t))$
ようするに目的の温度が遠ければ強い出力を行い、目的の温度に近づいたら出力を下げるというだけの事です。
まず、加熱の間隔を10杪単位で分割し、10%出力なら1杪加熱して9杪OFF、50%出力なら5杪加熱して5杪OFFという様に出力を連続値にします。
こんなコードになるはず。
def output(power):
'''
powerは0〜1の実数値
'''
on = power * 10
off = (1 - power) * 10
if on > 0:
GPIO.output(21,GPIO.HIGH)
time.sleep(on)
if off > 0:
GPIO.output(21,GPIO.LOW)
time.sleep(off)
比例ゲインをkpとすると出力値を算出するフィードバック関数はこんなコードになるだろう。
def p(temp, target, kp):
d = target - temp
if d < 0:
return 0
power = d / target * kp
return power
比例ゲインKpを変えていろいろ試してみると電気ポットの水温は、以下の様に変化した。
Kp=3とか4が良い線いっているように見えますが、温度の立ち上がりに時間が掛かっていて、最終的に安定する温度がやや力不足だ。
なにより、季節などの環境の変化によってKpを調整しなければならない事がP制御の欠点でしょう。
PI制御
P制御に加えてI(積分)制御を実装すると、上記の欠点を補うことができる。
PI制御は以下の式で表せます。
$u(t) = K_p e(t) + K_i \int^{t}_{0} e(\tau)d\tau$
積分するといっても現在値と目的値と差となる領域の面積を求めるだけなので台形に近似すれば簡単です。
台形の面積は (上底+下底) × 高さ ÷ 2
で求められます。台形の面積の公式が将来何の役に立つの? などというクソガキがいたら美味しいローストビーフを焼くのに必要だと教えてやりましょう。
いろいろ試行錯誤した結果、Kp=2.7、Ki=0.005というパラメーターで以下の結果が得られました。
なかなか良い曲線を描けたのではないでしょうか。
PID制御
D(微分)制御を加えたPID制御は以下の式で表されます。
$u(t) = K_p e(t) + K_i \int^{t}_{0} e(\tau)d\tau + K_d {{d} e(t) \over dt}$
D制御は主に急激な環境の変化(外乱)に適応する効果があるそうです。
室内で肉を調理する際に急激な気温変化は無いし、お腹も空いたことですしこれは実装しなくて大丈夫でしょう。
niku.py version 2.0
最終的にこのようなコードになりました。
https://github.com/hamano/python-niku/blob/master/niku.py
このPID制御は今後ローストビーフ以外の肉の調理に応用できそうなのでPyPIに登録してみました。
$ pip install niku
でインストールできます。
肉を投入
温度制御プログラムを実行し、肉を投入します。
今回は59℃で90分かけて調理します。
$ niku 59
完成!
軽くフライパンで焼き目を付けて完成です! 赤ワインと一緒に頂きましょう!
備考: なぜ炊飯器で低温調理をしてはいけないのか
炊飯器の保温機能を利用して手軽に低温調理する方法が知られていますがこれはオススメできません。 温度の上昇が遅いので最初に適切な水温調整を行う必要があることと、鶏肉や豚肉などでタイミングを誤ると食中毒になる危険性があります(なりました)。 また、放っておくと水温は68℃を超えて上がり続けるので油断するとアクチンの破壊が始まってしまいます。 以下はタイガー炊飯器の保温機能を利用して、常温からの温度変化をプロットしたものです。
このように水温が変化するなかで適切なタイミングで調理するのは難しいことがわかるでしょう。
どうしても炊飯器でやりたい人は食中毒に十分注意してください。