振動の減衰と強制振動
 振動の減衰と強制振動をルンゲクッタ法で解析します。
 
       
                    減衰    強制
d2x/dt2 = -ω02x - 2γdx/dt + F0cosωt

ルンゲクッタ法については、インターネットで検索して参照して下さい。
非常に詳しく解説が載っています。

振動の計算には、二階常微分を使用しますが、まず一階常微分と二階常微分の簡単なサンプルプログラムを参考として載せます。


unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, VclTee.TeeGDIPlus,
  VCLTee.TeEngine, VCLTee.Series, Vcl.ExtCtrls, VCLTee.TeeProcs, VCLTee.Chart;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Chart1: TChart;
    Series1: TFastLineSeries;
    Series2: TFastLineSeries;
    GroupBox1: TGroupBox;
    RadioButton2: TRadioButton;
    Button1: TButton;
    RadioButton1: TRadioButton;
    GroupBox2: TGroupBox;
    Button2: TButton;
    Label1: TLabel;
    Label2: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{
 4次のルンゲタック法
 一階の常微分方程式
 dy/dx=f(x,y)
}
function f(x, y: double):double;
begin
  result := 1;                            // ダミー
  if Form1.RadioButton2.Checked then result := sin(x) + cos(y);
  if Form1.RadioButton1.Checked then result := 1 - y * y;          // x は無視
end;

procedure sumple1;
var
  i : integer;
  h, x, y, k1, k2, k3, k4: double;
begin
  h := 1;                                 // 分割幅 ダミー
  if Form1.RadioButton2.Checked then begin
    h := pi / 50;                         // 0~π迄微分
    Form1.Chart1.Title.Text.Text := 'sin(x) + cos(y)';
  end;
  if Form1.RadioButton1.Checked then begin
    h := 1 / 50;                          // 0~1迄微分
    Form1.Chart1.Title.Text.Text := '1-y^2';
  end;
  x := 0;
  y := 0;
  i := 0;
  with Form1.memo1 do begin
    clear;
    Form1.Series1.Clear;
    Form1.Series2.Clear;
    Lines.Add('i             x              y');
    Lines.Add(inttostr(i) + '  ' + floatTostr(x) + '  ' + floatTostr(y));
    Form1.Series1.AddXY(x, y);
    for i := 1 to 50 do begin
      k1 := h * f(x, y);
      k2 := h * f(x + h / 2, y + k1 / 2);
      k3 := h * f(x + h / 2, y + k2 / 2);
      k4 := h * f(x + h,     y + k3);
      y  := y + (k1 + 2 * k2 + 2 * k3 + k4) / 6;      // 重み付き平均
      x  := i * h;
      Lines.Add(inttostr(i) + '  ' + floatTostr(x) + '  ' + floatTostr(y));
      Form1.Series1.AddXY(x, y);
    end;
  end;
end;

// 一階常微分実行
procedure TForm1.Button1Click(Sender: TObject);
begin
  sumple1;
end;

{
 二階常微分方程式
 d^2/dx^2=x*dy/dx+y
  dy/dx=y' -------(a)
  dy'/dx=xy'+y ---(b)
}
function df(i:integer; x: double; s: array of double): double;
begin
{
  i=0   dy/dx=y'
  i=1   dy'/dx=xy'+y
}
  result := 0;
  case i of
    0 : result := s[1];
    1 : result := x * s[1] + s[0];
  end;
end;

procedure sumple2;
var
  j, k: integer;
  h, x: double;
  k1, k2, k3, k4, y, ywork: array[0..1] of double;
begin
  k := 20;
  h := 1 / k;
  x := 0;
  y[0] := 1;
  y[1] := 1;
  j := 0;
  with Form1 do begin
    Memo1.clear;
    Chart1.Title.Text.Text := 'dy/dx= y' + #39 + #13#10 + '    dy' + #39 + '/dx=y' + #39 + '+y';
    Series1.Clear;
    Series2.Clear;
    Memo1.Lines.Add('j             x              y[0],             y[1]');
    Memo1.Lines.Add(inttostr(j) + '  ' + floatTostr(x) + '  ' + floatTostr(y[0]) + '  ' + floatTostr(y[1]));
    Series1.addxy(x, y[0]);
    Series2.addxy(x, y[1]);
    for j := 1 to k do begin
      k1[0]    := h * df(0, x, y);
      k1[1]    := h * df(1, x, y);
      ywork[0] := y[0] + k1[0] / 2;
      ywork[1] := y[1] + k1[1] / 2;
      k2[0]    := h * df(0, x + h / 2, ywork);   // k2[0] := h * ywork[1]
      k2[1]    := h * df(1, x + h / 2, ywork);
      ywork[0] := y[0] + k2[0] / 2;
      ywork[1] := y[1] + k2[1] / 2;
      k3[0]    := h * df(0, x + h / 2, ywork);   // k3[0] := h * ywork[1]
      k3[1]    := h * df(1, x + h / 2, ywork);
      ywork[0] := y[0] + k3[0];
      ywork[1] := y[1] + k3[1];
      k4[0]    := h * df(0, x + h, ywork);       // k4[0] := h * ywork[1]
      k4[1]    := h * df(1, x + h, ywork);
      y[0]     := y[0] + (k1[0] + 2 * k2[0] + 2 * k3[0] + k4[0]) / 6;
      y[1]     := y[1] + (k1[1] + 2 * k2[1] + 2 * k3[1] + k4[1]) / 6;
      x        := h * j;
      Memo1.Lines.Add(inttostr(j) + '  ' + floatTostr(x) + '  ' + floatTostr(y[0]) + '  ' + floatTostr(y[1]));
      Series1.addxy(x, y[0]);
      Series2.addxy(x, y[1]);
   end;
  end;
end;

// 二階常微分実行
procedure TForm1.Button2Click(Sender: TObject);
begin
  sumple2;
end;

end.

 左図は、減衰振動と強制振動のプログラムの実行例です。
減衰と強制振動を同時に与えています。
            減衰    強制
 d2x/dt2 = -ω02x - 2γdx/dt + F0cosωt

 質量          Kg
 減衰係数       N・s/m
 ばね定数       N/m
 励振力の振幅    N
 励振力の周波数   Hz
 励振力の開始角   deg (開始する角度によって初期振動が変かします。)
 初期変位       m    (質量Mの最初の位置です)
 初期速度       m/s
 観測開始時間    sec
 観測する時間    sec
 刻み幅        sec (dtの時間です)

  x       (位置、変位)
 dx/dt = v(速度)    位置の変化量を時間で微分すれば速度になります。
  dv/dt = α(加速度)   更に速度の変化を時間で微分すれば加速度になります。


プログラム
// 一自由度系の振動
// 四次のルンゲクッタ法

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, VclTee.TeeGDIPlus, VCLTee.TeEngine,
  VCLTee.Series, VCLTee.TeeProcs, VCLTee.Chart;

type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    LabeledEdit3: TLabeledEdit;
    LabeledEdit4: TLabeledEdit;
    LabeledEdit5: TLabeledEdit;
    LabeledEdit6: TLabeledEdit;
    LabeledEdit7: TLabeledEdit;
    LabeledEdit8: TLabeledEdit;
    LabeledEdit9: TLabeledEdit;
    Button1: TButton;
    GroupBox2: TGroupBox;
    Label1: TLabel;
    Edit1: TEdit;
    Label2: TLabel;
    Edit2: TEdit;
    Chart1: TChart;
    Series1: TFastLineSeries;
    Chart2: TChart;
    Series2: TFastLineSeries;
    Chart3: TChart;
    Series3: TFastLineSeries;
    LabeledEdit10: TLabeledEdit;
    LabeledEdit11: TLabeledEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    function hzcalc: boolean;
    function inputdata: boolean;
    function Func(T: double; X: array of double): double;
    procedure Runge_Kutta(T, Dt: double;var X, Y: array of double);
    procedure calc;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  dblAK, dblAC      : double;
  dblb              : double;
  Omega, OmegQ      : double;

  dblM, dblC, dblK  : double;
  dblF, dblFe       : double;
  dblQ              : double;
  dblX0, dblV0      : double;
  dblT, dblTstt, dblTend, dblDt : double;
  dblX, dblY        : array[0..1] of double;
  Number, Numbstt   : integer;

// 固有周波数 減衰係数の処理
function TForm1.hzcalc: boolean;
var
  Check : integer;
begin
  result := false;
  val(LabeledEdit1.Text, dblM, Check);  // Kg
  if Check <> 0 then begin
    application.MessageBox('質量の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit2.Text, dblC, Check);  // N・s/m
  if Check <> 0 then begin
    application.MessageBox('減衰係数の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit3.Text, dblK, Check);  // N/m
  if Check <> 0 then begin
    application.MessageBox('ばね定数の値に間違いがあります。','注意',0);
    exit;
  end;
  if dblK <= 0 then begin
    application.MessageBox('ばね定数の値がゼロ又は、ゼロ以下です。','注意',0);
    exit;
  end;
  if dblM <= 0 then begin
    application.MessageBox('質量の値がゼロ又は。ゼロ以下ではいけません。','注意',0);
    exit;
  end;
  // 固有周波数                         Hz
  dblF := sqrt(dblK / dblM) / 2 / pi;
  // 減衰係数比 ζ
  dblFe := dblC / 2 / sqrt(dblM * dblK);
  Edit1.Text := floattostr(dblF);
  Edit2.Text := floattostr(dblFe);
  result := True;
end;

// 入力データー処理
function TForm1.inputdata: boolean;
var
  check : integer;
begin
  result := false;
  val(LabeledEdit4.Text, dblF, Check);  // N
  if Check <> 0 then begin
    application.MessageBox('励震力の振幅の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit5.Text, dblFe, Check); // Hz
  if (Check <> 0) or (dblFe < 0) then begin
    application.MessageBox('励震力の周波数の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit10.Text, dblQ, Check); // deg
  if Check <> 0 then begin
    application.MessageBox('励震力の開始角の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit6.Text, dblX0, Check); // m(メートル)
  if Check <> 0 then begin
    application.MessageBox('初期変位の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit7.Text, dblV0, Check); // m/s
  if Check <> 0 then begin
    application.MessageBox('初期速度の値に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit8.Text, dblTend, Check);   // sec
  if (Check <> 0) or (dblTend <= 0) then begin
    application.MessageBox('観測する時間に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit11.Text, dblTstt, Check);  // sec
  if (Check <> 0) or (dblTstt < 0) then begin
    application.MessageBox('観測開始時間に間違いがあります。','注意',0);
    exit;
  end;
  val(LabeledEdit9.Text, dblDt, Check);     // sec
  if (Check <> 0) then begin
    application.MessageBox('刻み幅に間違いがあります。','注意',0);
    exit;
  end;
  if dblDt <= 0 then begin
    application.MessageBox('刻み幅がゼロ 又は、ゼロ以下ではいけません。','注意',0);
    exit;
  end;
  // 励震力の角周波数ω
  Omega := 2 * pi * dblFe;
  // 励震力の開始角
  OmegQ := dblQ / 180 * pi;
  // 計算データー数 - 1
  Number := round((dblTend + dblTstt) / dblDt);
  // 観測開始位置
  Numbstt := round((dblTstt) / dblDt) + 1;
  dblAK   := -dblK / dblM;
  dblAC   := -dblC / dblM;
  dblB    :=  dblF / dblM;

  result := true;
end;

// 加速度を求めます
// 速度分無はルーチン内で処理
function TForm1.Func(T: double; X: array of double): double;
begin
  result := dblAK * X[0] + dblAC * X[1] + dblB * cos(Omega * T + OmegQ);
end;

// ルンゲクッタルーチン
procedure TForm1.Runge_Kutta(T, Dt: double;var X, Y: array of double);
var
  Rk1: array[0..1] of double;
  Rk2: array[0..1] of double;
  Rk3: array[0..1] of double;
  Rk4: array[0..1] of double;
  Acc: array[0..1] of double;
  Dt2: double;
  Th2, Th: double;
begin
  Dt2 := Dt / 2;
  Th2 := T + Dt2;
  Th  := T + Dt;

  Rk1[0] := X[1];
  Rk1[1] := Func(T, X);
  Acc[0] := X[0] + Rk1[0] * Dt2;
  Acc[1] := X[1] + Rk1[1] * Dt2;
  Rk2[0] := Acc[1];
  Rk2[1] := Func(Th2, Acc);
  Acc[0] := X[0] + Rk2[0] * Dt2;
  Acc[1] := X[1] + Rk2[1] * Dt2;
  Rk3[0] := Acc[1];
  Rk3[1] := Func(Th2, Acc);
  Acc[0] := X[0] + Rk3[0] * Dt;
  Acc[1] := X[1] + Rk3[1] * Dt;
  Rk4[0] := Acc[1];
  Rk4[1] := Func(Th, Acc);

  Y[0] := (Rk1[0] + 2 * (Rk2[0] + Rk3[0]) + Rk4[0]) / 6;     // 区間重み付き平均速度
  Y[1] := (Rk1[1] + 2 * (Rk2[1] + Rk3[1]) + Rk4[1]) / 6;     // 区間重み付き平均加速度
  // 変位 + 区間重み付き平均速度 x Δ時間
  X[0] := X[0] + Y[0] * Dt;                                  // 変位
  // 速度 + 区間重み付き平均加速度 x Δ時間
  X[1] := X[1] + Y[1] * Dt;                                  // 速度
end;

// 固有振動の計算
procedure TForm1.calc;
const
  thousand = 1000;
var
  i : integer;
begin
  chart1.LeftAxis.Title.Text := '変位(mm)';
  Series1.Clear;
  Series2.Clear;
  Series3.Clear;
  dblT := 0;                                    // 時間
  dblY[0] := 0;                                 // 区間平均速度
  dblY[1] := 0;                                 // 区間平均加速度
  dblX[0] := dblX0;                             // 変位
  dblX[1] := dblV0;                             // 速度
  for i := 1 to Numbstt - 1 do begin            // 観測開始時間まで進める
    Runge_Kutta(dblT, dblDt, dblX, dblY);
    dblT := i * dblDt;
  end;
  Series1.AddXY(dblT, dblX[0] * thousand);      // 変位
  Series2.AddXY(dblT, dblX[1]);                 // 速度
  for i := Numbstt to Number do begin           // 観測
    Runge_Kutta(dblT, dblDt, dblX, dblY);
    dblT := i * dblDt;
    Series1.AddXY(dblT, dblX[0] * thousand);    // 変位
    Series2.AddXY(dblT, dblX[1]);               // 速度
    Series3.AddXY(dblT - dblDt, dblY[1]);       // 加速度
  end;
  Runge_Kutta(dblT, dblDt, dblX, dblY);
  Series3.AddXY(dblT, dblY[1]);                 // 加速度
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not hzcalc then exit;
  if not inputdata then exit;
  calc;
end;

end.

download Runge_Kutta.zip

  各種プログラム計算例に戻る