自由落下の続き

 自由落下の追加の検討です。
自由落下では、空気抵抗が、速度の二乗に比例する場合、速度に比例する場合、空気の抵抗がない場合等の計算を行っていますが、此処では、速度の二乗に比例して、空気抵抗が発生する場合で、特に、鉛直方向で、下方に初速度をもって投射された場合について、検討します。
此処でのプログラムは、放物線の下向き投射(0~-90°)の計算に利用します、放物線のけいさんで、下向き投射に対する計算をインターネットで見つける事が出来ませんでした。

鉛直 下向き投射の時は、計算を二つに分けて計算します。

 まず、投射速度が、落下時間が無限大の時の落下速度より早いときです。
この時は、減速が働き、落下時間が無限大の時の速度に近づきます。


  次は、初速度(V0)が、落下時間が無限大の時の速度V(t∞)より遅い時です。
この時は、落下と共に加速され、無限大の時の速度V(t∞)に近づきます。
この計算は、停止状態から自然落下した時の計算を利用すれば、落下の計算ができます。


 

 問題は、落下(投射)の初速度(V0)が、がV(t∞)より速い時です。
解析解を出すのが難しかったので、分割計算をすることにしました。

dm: 落下時間∞時抗力       dm = V(t∞) * V(t∞) * k
dv:  抗力
k:: 空気抵抗係数 [kg/m]
m: 質量      [kg]
V: 初速度     [m/s]
mdg: 加速度   [m/s2]
fV:   減速後速度 [m/s]
Δt: 分割時間      [Δs]        Δt  = t / 分割数 
L: 距離      [m]

1. dv = V * V * k
2. mdg = (dv - dm) / m
3. fV = V - mdg × Δt
4. L = L + (V + fV) / 2 * Δt;
5. V = fV

Δtの値を十分に小さくとり、1~5の計算を繰り返せば、目的の値を求める事が出来ます。
実際に計算をしてみると、分割数1000位では、結構誤差が大きく、10万分割位必要となり演算に時間がかかります。
少し改良して、分割数が小さくて済むようにします。
加速度の平均値を計算し、加速度の平均値でΔt後の速度とします。

ndv: 予抗力
nmdg: 予加速度

1. dv = V * V * k
2. mdg = (dv - dm) / m
3. fV = V - mdg × Δt
4. ndv = fV * fV * k
5. nmdg = (ndv - dm) / m
6. mdg = (nmdg + mdg) / 2
7. fV = V - mdg * Δt
8. L = L + (V + fV) / 2 * Δt
9. V = Vf

若干、値が値が大きくなる傾向がありますが、1000分割程度で、4桁の精度を得ることが可能です。

 プログラムには、水平方向で、重力加速度ゼロの計算も、分割計算の検算の為に追加しました。

プログラム

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.Buttons, Vcl.ExtCtrls,
  VclTee.TeeGDIPlus, VCLTee.TeEngine, VCLTee.Series, VCLTee.TeeProcs,
  VCLTee.Chart;

type
  TForm1 = class(TForm)
    GroupBox1: TGroupBox;
    rhoEdit: TLabeledEdit;
    sumEdit: TLabeledEdit;
    KcalcBtn: TBitBtn;
    kEdit: TLabeledEdit;
    CdEdit: TLabeledEdit;
    GroupBox2: TGroupBox;
    mEdit: TLabeledEdit;
    tEdit: TLabeledEdit;
    gEdit: TLabeledEdit;
    vhBtn: TBitBtn;
    V0Edit: TLabeledEdit;
    Memo1: TMemo;
    Chart1: TChart;
    Series1: TLineSeries;
    Series2: TLineSeries;
    Chart2: TChart;
    Series3: TLineSeries;
    Series4: TLineSeries;
    jEdit: TLabeledEdit;
    CheckBox1: TCheckBox;
    procedure KcalcBtnClick(Sender: TObject);
    procedure vhBtnClick(Sender: TObject);
  private
    { Private 宣言 }
    procedure down(v0, t, k, m, g: extended; j: integer);
    procedure downN(v0, t, k, m, g: extended; j: integer);
    procedure Horizontal(v0, t, k, m: extended; j: integer);
    procedure HorizontalN(v0, t, k, m: extended; j: integer);
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
// 空気抗力係数から空気抵抗計算kg/m計算
procedure TForm1.KcalcBtnClick(Sender: TObject);
var
  k   : extended;   // 空気抵抗係数  kg/m
  Cd  : extended;   // 空気抗力係数
  r   : extended;   // 空気密度   kg/m^3
  S   : extended;   // 代表面積   m^2
  ch  : integer;
begin
  val(CdEdit.Text, Cd, ch);
  if ch <> 0 then begin
    Memo1.Text := 'Cd:空気抗力係数に間違いがあります。';
    exit;
  end;
  val(rhoEdit.Text, r, ch);
  if ch <> 0 then begin
    Memo1.Text := 'ρ:空気密度に間違いがあります。';
    exit;
  end;
  val(sumEdit.Text, S, ch);
  if ch <> 0 then begin
    Memo1.Text := 'S:代表面積に間違いがあります。';
    exit;
  end;
  k := Cd * r * s / 2;
  kEdit.Text := floatTostr(k);
end;

// 水平投射 補正有
procedure TForm1.HorizontalN(v0, t, k, m: extended; j: integer);
var
  dv, dt, mdg, fv : extended;
  ndv, nmdg : extended;
  ti : extended;
  l,  v : extended;
  i: Integer;
begin
  series3.Clear;
  series4.Clear;
  l := 0;
  fv := 0;
  v := abs(v0);
  dt := t / j;
  series3.AddXY(0 , v);
  series4.AddXY(0 , l);
  for i := 1 to j do begin
    ti := i * dt;                  // 時間
    dv := v * v * k;               // 抗力
    mdg := dv / m;                 // 加速度
    fv := v - mdg * dt;            // 予速度
    ndv := fv * fv * k;            // 予抗力
    nmdg := ndv / m;               // 予加速度
    mdg := (mdg + nmdg) / 2;       // 平均加速度
    fv := v - mdg * dt;            // 新速度
    if fv < 0 then break;
    l := l + (v + fv) / 2 * dt;    // 距離
    v := fv;
    series3.AddXY(ti , v);
    series4.AddXY(ti , l);
  end;
  if fv < 0 then begin
    memo1.Lines.Append('新減速計算 分割数不足、時間が長い為計算出来ません。');
    exit;
  end;
  memo1.Lines.Append('新速度 ' + floatTostr(v) + ' m/s');
  memo1.Lines.Append('新距離 ' + floatTostr(l) + ' m');
end;

// 水平投射 補正無
procedure TForm1.Horizontal(v0, t, k, m: extended; j: integer);
var
  dv, dt, mdg, fv : extended;
  ti : extended;
  l,  v : extended;
  i: Integer;
begin
  series1.Clear;
  series2.Clear;
  l := 0;
  fv := 0;
  v := abs(v0);
  dt := t / j;
  series1.AddXY(0 , v);
  series2.AddXY(0 , l);
  for i := 1 to j do begin
    ti := i * dt;                  // 時間
    dv := v * v * k;               // 抗力
    mdg := dv / m;                 // 加速度
    fv := v - mdg * dt;            // 速度
    if fv < 0 then break;
    l := l + (v + fv) / 2 * dt;    // 距離
    v := fv;                       // 新速度
    series1.AddXY(ti , v);
    series2.AddXY(ti , l);
  end;
  if fv < 0 then begin
    memo1.Lines.Append('新減速計算 分割数不足、時間が長い為計算出来ません。');
    exit;
  end;
  memo1.Lines.Append('旧速度 ' + floatTostr(v) + ' m/s');
  memo1.Lines.Append('旧距離 ' + floatTostr(l) + ' m');
end;

// 鉛直降下  補正無
procedure TForm1.down(v0, t, k, m, g: extended; j: integer);
var
  vmax, ti : extended;
  h, v: extended;
  dv, dm, dt, mdg, fv : extended;
  i :  integer;
begin
  series1.Clear;
  series2.Clear;
  h := 0;
  dt := t / j;                      // Δt
  v := -v0;
  fv := 0;
  vmax := sqrt(m * g / k);          // t∞速度
  dm := vmax * vmax * k;            // t∞抗力
  series1.AddXY(0 , -v);
  series2.AddXY(0 , -h);
  for i := 1 to j do begin
    ti := i * dt;                   // 時間
    dv := v * v * k;                // 抗力
    mdg := (dv - dm) / m;           // 加速度
    fv := v - mdg * dt;             // 減速
    if fv < 0 then break;
    h := h + (v + fv) / 2 * dt;     // 高さ + 平均速度*時間
    v := fv;                        // 新速度
    series1.AddXY(ti , -v);
    series2.AddXY(ti , -h);
  end;
  if fv < 0 then begin
    memo1.Lines.Append('旧減速計算 分割数不足、時間が長い為計算出来ません。');
    exit;
  end;
  memo1.Lines.Append('旧速度 ' + floatTostr(-v) + ' m/s');
  memo1.Lines.Append('旧高さ ' + floatTostr(-h) + ' m');
end;

// 鉛直降下 補正有
procedure TForm1.downN(v0, t, k, m, g: extended; j: integer);
var
  vmax, ti : extended;
  h, v: extended;
  dv, dm, dt, mdg, fv : extended;
  ndv, nmdg : extended;
  im, itwo : extended;
  i : integer;
begin
  series3.Clear;
  series4.Clear;
  im := 1 / m;
  itwo := 1 / 2;
  h := 0;
  dt := t / j;                      // Δt
  v := -v0;
  fv := 0;
  vmax := sqrt(m * g / k);          // t∞速度
  dm := vmax * vmax * k;            // t∞抗力
  series3.AddXY(0 , -v);
  series4.AddXY(0 , -h);
  for i := 1 to j do begin
    ti := i * dt;
    dv := v * v * k;                // 抗力
    mdg := (dv - dm) * im;          // 加速度
    fv := v - mdg * dt;             // 予減速
    ndv := fv * fv * k;             // 予抗力
    nmdg := (ndv - dm) * im;        // 予加速度
    mdg := (nmdg + mdg) * itwo;     // 平均加速度
    fv := v - mdg * dt;             // 減速
    if fv < 0 then break;
    h := h + (fv + v) * itwo * dt;  // 高さ + 平均速度*時間
    v := fv;                        // 新速度
    series3.AddXY(ti , -v);
    series4.AddXY(ti , -h);
  end;
  if fv < 0 then begin
    memo1.Lines.Append('新減速計算 分割数不足、時間が長い為計算出来ません。');
    exit;
  end;
  memo1.Lines.Append('新速度 ' + floatTostr(-v) + ' m/s');
  memo1.Lines.Append('新高さ ' + floatTostr(-h) + ' m');
end;

// vh計算実行
procedure TForm1.vhBtnClick(Sender: TObject);
var
  k   : extended;   // 空気抵抗係数  kg/m
  m   : extended;   // 質量          kg
  t   : extended;   // 落下時間      sec
  g   : extended;   // 重力加速度    m/sec^2
  v0  : extended;   // 初速度        m/s
  ch  : integer;
  j   : integer;
begin
  Memo1.Clear;
  val(kEdit.Text, k, ch);
  if ch <> 0 then begin
    Memo1.Text := 'k:空気抵抗係数に間違いがあります。';
    exit;
  end;
  if k <= 0 then begin
    Memo1.Text := 'k:空気抵抗係数の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(mEdit.Text, m, ch);
  if ch <> 0 then begin
    Memo1.Text := 'm:質量に間違いがあります。';
    exit;
  end;
  if m <= 0 then begin
    Memo1.Text := 'm:質量の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(tEdit.Text, t, ch);
  if ch <> 0 then begin
    Memo1.Text := 't:時間に間違いがあります。';
    exit;
  end;
  if t <= 0 then begin
    Memo1.Text := 't:時間の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(gEdit.Text, g, ch);
  if ch <> 0 then begin
    Memo1.Text := 'g:加速度に間違いがあります。';
    exit;
  end;
  if g <= 0 then begin
    Memo1.Text := 'g:加速度の値は0(ゼロ)より大きくしてください。';
    exit;
  end;
  val(v0Edit.Text, v0, ch);
  if ch <> 0 then begin
    Memo1.Text := 'V0:初速度に間違いがあります。';
    exit;
  end;
  val(jEdit.Text, j, ch);
  if ch <> 0 then begin
    Memo1.Text := 'j:分割数に間違いがあります。';
    exit;
  end;
  if j <= 2 then begin
    Memo1.Text := 'j:分割数は2より大きくしてください。';
    exit;
  end;
  if j > 10000000 then begin
    Memo1.Text := 'j:分割数が大きすぎます。';
    exit;
  end;
  memo1.Clear;
  if checkbox1.Checked = false then begin
    down(v0, t, k, m, g, j);
    downN(v0, t, k, m, g, j);
  end
  else begin
    Horizontal(v0, t, k, m, j);
    HorizontalN(v0, t, k, m, j);
  end;
end;

end.

  download free_fall_test.zip

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