ニュートン法
ニュートン法
ニュートン法(ニュートン・ラフソン法)は、方程式を数値計算によって解くための反復的な方法による求根アルゴリズムの1つです。
①根の近くの値 を初期値にします。
② の における接戦を引き、 軸と交わったところを とし、 以下同様の手順で と求めます。
③ を求めるには、
という性質があることを用いて、
と、前の値 を用いて求めることができます。
になったときの の値を求める根とし、そうでないなら②を繰り返します。 は適当な精度を選びます。
プログラム
#include <iostream> #include <math.h> using namespace std; #define f(x) ((x)*(x)-2) #define g(x) (2*(x)) #define EPS 1e-09 #define LIMIT 50 int main(void) { double x=2.0,dx; int k; for(k=1;k<=LIMIT;k++){ dx = x; x=x-f(x)/g(x); if (fabs(x-dx)<fabs(dx)*EPS){ cout << "x=" << x << endl; break; } } if (k>LIMIT) cout << "収束しない" << endl; }
実行結果
x=1.41421
数値積分
台形則による定積分
関数 の定積分 を台形則により求めてみます。
関数 の定積分を微小区間に分割して近似値として求める方法を数値積分といいます。
図に示すように, , 区間を 個の台形に分割し, 各台形の面積を合計すると,
となります。
プログラム
#include <iostream> #include <math.h> using namespace std; #define f(x) (sqrt(4-(x)*(x))) /* 被積分関数 */ int main(void){ int k; double a,b,n,h,x,s,sum; cout << "Integration interval A,B ? " << endl; cin >> a >> b ; n = 1000; h=(b-a)/n; x=a; s=0; for(k=1;k<=n-1;k++){ x=x+h; s=s+f(x); } sum = h*((f(a)+f(b))/2+s); cout << " /" << b << endl; cout << " | sqrt(4-x*x) dx = " << sum << endl; cout << " /" << a << endl; return 0; }
実行結果
Integration interval A,B ? 0 2 /2 | sqrt(4-x*x) dx = 3.14156 /0
テイラー展開② - 三角関数
をテイラー展開してみる
をテイラー展開すると以下のようになります。
x の値は ~ の範囲に収まるように計算します。
プログラム
一例(C++)を示します。
#include <iostream> #include <iomanip> #include <math.h> using namespace std; double tailercos(double); int main() { double x,rd=3.14159/180; cout << setw(5) << "x" << setw(14) << "tailercos(x)" << setw(14) << "exp(x)" << endl; for (x=0;x<=180;x=x+10){ cout << setw(5) << x << setw(14) << tailercos(x) << setw(14) << exp(x) << endl; } return 0; } double tailercos(double x){ double EPS=1e-09; double s=1.0,e=1.0,d=1.0; int k; x = fmod(x,2*3.14159265358979); for(k=1;k<=200;k=k+2){ d=s; e=-e*x*x/(k*(k+1)); s=s+e; if(fabs(s-d)<EPS*fabs(d)) return (s); } return (9999.0); }
実行結果
x tailercos(x) cos(x) 0.0 1 1 10.0 0.984808 0.984808 20.0 0.939693 0.939693 30.0 0.866026 0.866026 40.0 0.766045 0.766045 50.0 0.642788 0.642788 60.0 0.500001 0.500001 70.0 0.342021 0.342021 80.0 0.173649 0.173649 90.0 1.32679e-006 1.32679e-006 100.0 -0.173647 -0.173647 110.0 -0.342019 -0.342019 120.0 -0.499998 -0.499998 130.0 -0.642786 -0.642786 140.0 -0.766043 -0.766043 150.0 -0.866024 -0.866024 160.0 -0.939692 -0.939692 170.0 -0.984807 -0.984807 180.0 -1 -1
テイラー展開① - 指数関数
テイラー展開
数値解析などのアルゴリズムを勉強したいと思いましたので何か書いていきます。今回はテイラー展開について。
をテイラー展開してみる
をテイラー展開すると以下のようになります。
この式は無限級数で展開しているため, 実際にプログラムに落とし込むためには有限回で打ち切る必要があります。 条件は, 項までの和を , 項までの和を としたとき,
となります。 を打ち切り誤差, を相対打ち切り誤差というそうです。
今回は とします。
プログラム
一例を示します。(C++)
#include <iostream> #include <math.h> #include <iomanip> using namespace std; double tailerexp(double); int main(void){ double x; cout << setw(5) << "x" << setw(14) << "tailerexp(x)" << setw(14) << "exp(x)" << endl; for(x=0;x<=40;x=x+10){ cout << setw(5) << x << setw(14) << tailerexp(x) << setw(14) << exp(x) << endl; } return 0; } double tailerexp(double x){ double EPS=1e-09; double s=1.0,e=1.0,d=1.0; int k; for(k=1;k<=200;k++){ d=s; e=e*x/k; s=s+e; if (fabs(s-d)<EPS*fabs(d)) return(s); } return (0.0); }
実行結果
x tailerexp(x) exp(x) 0 1 1 10 22026.5 22026.5 20 4.85165e+08 4.85165e+08 30 1.06865e+13 1.06865e+13 40 2.35385e+17 2.35385e+17
単軸ロボットアームのサーボ制御設計 - 外乱オブザーバ
外乱
以前DCモータのサーボ制御(位置制御)について少しやっていました。 qiita.com
位置制御系では, 制御プラントが積分器を有しているため, I制御なくとも指令に追従しました。 しかし外乱に対しては有効ではなく, 偏差を生じます。
"外乱 - 通信系などに外から加わる不要な信号。雑音あるいは妨害ともいう。"
今回は制御系におけるものですね。 以下の図に制御入力に定常的な外乱(電圧オフセット)を加えた時の応答を示します。 このように, 偏差を確認することができます。
外乱オブザーバ
外乱オブザーバは, フィードバックループ内に外乱が発生する状況で, 外乱を推定して制御入力にフィードバックをかけることで, 比較的に簡易に外乱を除去し安定化することができます。補償器のひとつです。
今回は作ったサーボ系で動作を確かめてみようと思います。
外乱オブザーバの原理
今回の制御ループで外乱を有するときのブロック図を以下に示します。 このときの出力 は,
となります。コントローラによって制御された入力が外乱によって変化してしまいます。
上の例では, に一定値を入力しているので, オフセットの電圧がモータドライバに印加され, 定常偏差が発生します。
コントローラ側から生成される制御量から外乱を推定して, 制御入力に外乱 を差し引いておくことで外乱の影響を除去することができます。
この場合の出力 は,
となり, 適切に制御されます。(外乱があらかじめわかっているのなら, フィードフォワードで差し引けば外乱を除去できます。)
本質はどう外乱を推定するかにあります。
出力は外乱を受けた後の出力となるため, ここから推定することができそうです。出力 の式は,
ここで制御プラント が逆システム が存在すれば,
( 今回はとなります )
となり, 推定外乱 を求めることができます。
ブロック図で表すと以下のように表現することができます。
オレンジ部分がオブザーバの表現になります。 コントローラによって生成される制御入力は, 推定がイランと外乱を受けて となるので, 出力 は,
となり, 推定外乱 は,
ここで とその逆システム の積 とすると,
が成立します。 このような原理で外乱を除去します。
プログラム上で制御対象の伝達関数 , またその逆関数 を利用して制御する方式をモデル規範型制御とも呼んだりするそうです。
外乱オブザーバの実用上の運用
今回の制御プラントは逆システム が得られます。運用上で注意すべきことがあり, フィルタの設計になります。逆システムの式、 には微分項が含まれています。
微分動作は基本的に不安定な要素のため, これを避けるためにプロパーな関数にします。具体的な操作としては分子多項式の次数が分母多項式の次数以下にするためローパスフィルタを使います。
今回私は, Butterworthフィルタを選定しました。
カットオフ周波数 となります。
推定外乱にフィルタをかけることも多いそうですが, ループ内に微分器が残ってしまうパスが残ってしまうので, ローパスフィルタを分散して次のように設定します。
こうすることで出力項は以下のように変形され, プロパーな項となります。
このように分母次数と分子の次数が等しくなり, は (なんとか) 動作します。
MATLAB/Simulinkでシミュレーション・実行する際に, 逆システム(を離散化したもの)をそのままの伝達関数として扱うことはできません。(プロパーでない(非因果モデルに使用できません)とでます)
よってこのような方法を取っています。
動作検証
あらかじめシステム同定を行い, パラメータを取得しておきます。
ゲインの計算とオブザーバ(とフィルタ)の設定と実行します。
% pos_pd_mbd.m s = tf('s'); % Initialize & load data close all clear all load sim_param load model_data % PD design by pole placement omega_n = 10; zeta = 0.65; p1 = (-zeta + j*sqrt(1-zeta^2))*omega_n; p2 = (-zeta - j*sqrt(1-zeta^2))*omega_n; % Set PD parameters Kp = p1*p2*T/K; Kd = -((p1+p2)*T + 1)/K; Ki = 0; % observer setting omega_c = 7; % cutoff frequency Fs = omega_c^2 / (s^2 + sqrt(2)*omega_c*s+omega_c^2); Fsd = c2d(Fs,ts,'zoh'); [numfsd,denfsd] = tfdata(Fsd,'v'); % discrete filter invP = s*(T*s + 1)/K; % inverse model Est = Fs * invP % estimation Estd = c2d(Est,ts,'zoh'); [numestd,denestd] =tfdata(Estd,'v'); % discrete model % Experiment r = 60; r_cyc = 4; dist = 2; % 0 or 2 for dist test % dist = 0; Ncyc = 4; tfinal = r_cyc*Ncyc; open_system('pos_pid_mbd_sl_observer') open_system('pos_pid_mbd_sl_observer/Scope') sim('pos_pid_mbd_sl_observer')
ゲインのパラメータは以下のようになります。
Simulinkでもオブザーバ適用時のブロックを更新しました。以下に示します。
次に動作した結果を示します。
外乱発生して1秒後には収束していることが分かるかと思います。
また理想応答と少しずれが起きていることが分かります。(摩擦などの非線形の要素にかなり苦しめられました。)
動いているときの様子はこんな感じです。
外乱を検知して, 補正をかけようとしているような動きとなります。 今回は以上です。ここまで読んでくださった方, 本当にありがとうございます。次回があったら続きます。
参考
Hamachi さんのブログ