産業機械をパソコンの中でひも解く・ロータリー充填機(前編)―― 気づかないうちに、液の揺れと拍子が合っていた

  • python
  • シミュレーション
  • デジタルツイン
  • メカトロニクス
  • 制御

充填機がパソコンの中で回るアニメーション
瓶に液を詰めるロータリー充填機を、パソコンの中で動かしたものです。この動きは物理を解いた結果から起こしています。

瓶に液体を詰めていく機械があります。テーブルが回って、止まって、注いで、また回る。壊れてから直すのではなく、壊れる前に気づきたい、という話はよく出ますが、いきなり実機で試すのは簡単ではありません。動いている設備を止めて、センサを付けて、わざと不具合を起こす、というわけにはいかないからです。

そこで、パソコンの中に機械を建てて、そこであたりをつけられないかを試しました。題材はロータリー充填機です。私はこの機械を触ったことがありません。テレビで見たことがあるぐらいです。

実機がないので、ここで出す数字が本物の機械と合っているかは確かめられません。そこは最後まで分かりません。ただ、組んだものが自分の思ったとおりに動いているかは確かめられますし、どこから先が実機の仕事なのか、という線も引けます。

話は二本に分けます。この前編で、外から分かる仕様だけを頼りに機械を建てます。後編で、その機械にセンサを付けて、異常が見えるかを試します。

パソコンの中に作ったものは、数珠つなぎです。カムの形からテーブルの動きが出て、テーブルの動きから瓶の中の液の揺れが出る。数値は一つの設定にまとめてあって、出てきた答えを外から分かる仕様と突き合わせて確かめます。ソフトで言えば、外部APIの仕様だけからモックを組んで、危ない入力を自動テストで先に見つけておく、というのに近いことをしています。

外から分かるのは、毎分20本だけ

手元にあるのは、毎分20本という数字と、8つの持ち場を順に回るテーブル、という形だけです。毎分20本なら、1本あたり3秒。この3秒の中で、テーブルは次の持ち場まで回り、止まって、注いで、また回り出します。回る時間と止まる時間は、この方式では半分ずつが自然なので、回すのに1.5秒、止まるのに1.5秒としました。ここまでは割り算だけです。

動きを決めているのは、モータではなくカムの形

持ち場が8つなので、1回に45度回します。問題は、どう回すかです。一定の速さで回して急に止めると、その瞬間に大きな力がかかって、液は跳ねますし、機械にもよくありません。

この手の機械は、モータを細かく制御して回しているわけではありません。カムという部品の形そのものが、動き方を決めています。入力側の軸は一定の速さで回り続けていて、カムの山の形に従って、テーブルが動いて、止まる。制御しているのではなく、形で決まっている。ここは私が最初に取り違えたところで、モータを速度制御する前提で一度組んでしまい、あとから作り直しました。

一定速の入力軸と、動いて止まるテーブル
左の入力軸は一定の速さで回り続けている。右のテーブルが動いて止まるのは、カムの形による。下は、回っている1.5秒と、止まっている1.5秒。

動き出しと止まりが滑らかになるカムの形を選ぶと、45度を1.5秒で回すときの力まで決まります。計算すると、テーブルの縁にかかる横向きの力は重力の数パーセントで、瓶が倒れる心配はありませんでした。使っているモータの出力に対しても、かなり余裕があります。力やトルクの具体的な値は、記事末のリポジトリに置いてあります。

液の揺れは、こちらで決められない

瓶には液を入れます。テーブルが止まったり動き出したりするたびに、中の液は前後に揺れます。水の入ったコップを持って歩くと、水が勝手に行ったり来たりする、あれと同じです。

この揺れには、瓶ごとに決まった速さがあります。大きなコップはゆっくり、小さなコップは速く揺れるように、その速さは容器の内側の寸法と重力だけで決まります。機械をどう作っても、こちらでは変えられません。この瓶だと、1秒間に3.75回、前後に往復する速さでした。内径65mmという瓶の径と、液の深さ、それに重力。この三つを円筒容器の揺れの式に入れて出した値です(式は記事末のリポジトリに)。

しかもこの揺れは、なかなか収まりません。テーブルが止まっている1.5秒を待っても、8割以上が残ったまま、次の割り出しに入ります。

揺れそのものは、液を振り子1本に置き換えて解いています。テーブルが生む加速度が、その振り子を押す。あとは1コマずつ積分するだけです。

# 液の揺れは、振り子1本に置き換えて解く。テーブルの加速度 a が振り子を押す。
# w1 が揺れやすい速さ(この瓶の毎秒3.75回に対応)、zeta が揺れの減りにくさ。
for a in table_accel:                              # テーブルが生む加速度を1コマずつ
    ddphi = -w1**2 * phi - 2*zeta*w1*dphi - a / L   # 振り子の運動方程式
    dphi += ddphi * dt
    phi  += dphi * dt                               # 液面の傾きを更新

この w1zeta は瓶と液で決まっていて、機械の側からは動かせません。だから、機械の拍子をうっかりこの揺れやすい速さに合わせてしまうと、困ったことになります。

気づかないうちに、拍子が合っていた

機械のほうにも拍子があります。止まって、動いて、また止まる。これを何秒ごとに繰り返すか、です。最初、私はこれを2.4秒にしていました。毎分25本の計算です。

ところが、この機械の拍子が、瓶の中の液の揺れやすい速さと、たまたま同じでした。合っていると、なぜまずいのか。ブランコを思い浮かべてください。でたらめな間隔で押しても大きくは揺れませんが、ブランコが戻ってくるのに合わせて押すと、一回ごとに大きく振れていきます。瓶の中でも同じことが起きます。テーブルが止まるたびに、まだ揺れている液を、ちょうど良い拍子でもう一度押してしまう。揺れはほとんど減らないので、止まるたびに積み上がっていきます。

数字で言うと、2.4秒の動きに含まれる9番目の成分が、液の揺れやすい3.75回とぴったり一致していました。細かい理屈は抜きでも、押す拍子と揺れる拍子が合うと大きくなる、という一点です。

タクトの高調波と、液の揺れやすい帯
赤い帯が液の揺れやすいところ。タクト2.4秒では、動きの9番目の成分がその帯にすっぽり入る。3.0秒にすると、いちばん近い成分でも帯から外れる。

揺れが大きくなると、瓶の中の液は前後に強く振られます。この瓶は縁まで余裕があってあふれはしませんでしたが、余裕の少ない容器なら、こぼれや中身の乱れに近づきます。しかもこの2.4秒は、私が深く考えずに置いた数字で、狙ってそこに合わせたわけではありません。

タクト2.4秒と3.0秒での液の揺れ方の違い
左はタクト2.4秒。止まるたびに揺れが積み上がっていく。右はタクト3.0秒にずらしたもの。同じ回数だけ止めても、揺れは大きくならない。液面の傾きは誇張していません。

その後、別の理由でタクトを3.0秒に変えました。止まる時間の余裕が足りなかったからです。変えたあとに揺れやすい速さと並べ直してみたら、いちばん近い拍子は十分に離れていて、危ないところをたまたま通り過ぎていました。

外から分かる仕様だけで機械を組むと、こういうことが起きます。しかも、自分では気づけません。液の揺れやすい周期を計算していなかったら、最後まで気づかなかったと思います。いまはこれを試験に入れてあって、次に誰かがタクトの数字を触ったら、そこで引っかかるようにしてあります。

実機がなくても、確かめられること

機械はひととおり組めました。ただ、組んだものが本当に正しいのかは、また別の問題です。実機があれば測って比べればいいのですが、その相手がいません。

それでも、やりようはあります。同じ量を、まったく違う二通りのやり方で出して、突き合わせるのです。二つの出どころが違うので、どちらかを間違えていれば答えは食い違います。やってみると、細かい差を除いてよく一致しました。

この突き合わせでも一度間違えています。最初は、二つの力の大きさだけを比べていました。

# 最初はこう書いていた。二つの力の「大きさ」だけを比べている
err = abs(norm(force_a) - norm(force_b))
# これだと向きが真逆でも大きさは同じなので、err はゼロのまま通ってしまう

# 直したあと。前後・左右の成分ごとに引く。符号や向きの取り違えで落ちる
err = max(abs(force_a - force_b))

試しに符号をひっくり返しても、差は動きませんでした。WEBで言えば、200 OK だけ見て、レスポンスの中身(符号や値)を確かめていないテストのようなものです。あとで成分ごとに比べる形に直して、ようやく役目を果たすようになりました。確かめる仕組みそのものが働いているかも、確かめないといけない。ここで一度つまずきました。この突き合わせの中身や、そのほかの試験は、リポジトリに置いてあります。

前編で分かったこと

外から分かる仕様だけでも、機械はここまで建てられました。そして、深く考えずに置いた一つの数字が、液の揺れと拍子を合わせていた、という危ないところまで、机の上で見つけられました。

後編では、この機械にセンサを付けて回します。付ければ見える、とは限りませんでした。速い振動を遅いセンサで見るとありもしない山が現れ、狙った故障がまるで信号に出ないこともある。センサを買って配線する前に、机の上で「見える/見えない」をどこまで言えるか。そこを試します。

コードと詳しい計算は GitHub に置いてあります。

https://github.com/logicia32/rotary-filler-labhttps://github.com/logicia32/rotary-filler-lab

この記事は Zenn に初出したものを加筆・補足したものです ── Zenn の元記事を見る