RustでPID制御を書いてみる。オイラー法で制御モデルを解く
RustでPID制御を書いていってみようと思います、、、が、その前に、制御をするということは制御されるもの(制御モデル)があるわけで、まずはその制御モデルを書いていくことから始めていきます。(なので実際にPID制御のコードを書くのはもう少し先です)
前回も書かせていただきましたが、制御のコードを書いていくにあたり、南裕樹様が書かれた『Pythonによる制御工学入門』を参考にさせていただきます。(とても良い本です!)
制御モデルとするのは上記の本の第五章でPID制御の対象として使われている垂直駆動アームです。
この垂直アームが入力によってどんな挙動が分かるようにしたいので、オイラー法により数値計算的に解き、各時点でどのようになっているか、状態を出力するようにします。
なお、数値計算の詳細、他の手法(ベルレ法やルンゲクッタ法)やRustの文法についてはここでは解説しませんのでご了承ください(全てを解説してたらいつまでたっても進めないので、、、)
struct Plant { h: f64, u: f64, y: f64, v: f64, } impl Plant { fn new(h: f64) -> Plant { return Plant { h, u: 0.0, y: 0.0, v: 0.0, }; } fn get_state(&self) -> (f64, f64) { return (self.y, self.v); } fn set_input(&mut self, val: f64) { self.u = val; return; } fn step(&mut self) { // 「Pythonによる制御工学入門」p.60, 式(3.6)を変形すると // dx/dt = v // dv/dt = (1/J) * (u - (mu * v) - (M * g * l * y)) // となる。これらを用いてオイラー法により微小時間後の状態を求める // なお、数値は上記本に記載されているものを用いている let dv_dt = (self.u - (0.015 * self.v) - (0.5 * 9.81 * 0.2 * self.y)) / 0.01; self.y = self.y + (self.v * self.h); self.v = self.v + (dv_dt * self.h); return; } } fn main() { let t_max: f64 = 2.0; // tの最大値 let points: u64 = 1_000_000; // 計算する点数。精度を確保するために多くしている let mut t: f64 = 0.0; let h = t_max / (points as f64); // 刻み幅 let mut plant = Plant::new(h); plant.set_input(30.0); // とりあえず適当な値を入力として設定する for i in 0..points { // 制御対象の状態を取得する let state = plant.get_state(); // 1000点ごとに出力する if i % 1000 == 0 { println!("(t_x), {}, {}", t, state.0); //println!("(t_x_v), {}, {}, {}", t, state.0, state.1); } // 次の状態を計算する plant.step(); t += h; } return; }
上のコードでは、Plant
構造体+メソッドが今回の制御モデルである垂直駆動アームの、いわゆるクラスになっています。
そしてforループの中でplant.step()を実行することによる制御モデルの状態を微小時間だけ変化させ続けています。
さて、出力された結果をグラフにしてみると下のようなグラフになります。(結構振動してますねぇ)
今回はここまでです。
制御モデルの挙動(数値)を出力しましたが、毎度数値からグラフを作るのも面倒なので、次はRustのグラフ描画ライブラリを使ってグラフを作ることをしてみたいと思います。