Processingで物理 フックの法則と単振動

バネやゴムに引っ張られて振動する動きをProcessingで作ってみます。

減衰の無い振動

バネやゴムに固定した物体を引っ張るとバネの長さに比例した力が加わります。バネの自然長から伸びた長さをLとすると働く力は次のようになります。

F=- k \times L

この式をフックの法則と呼びます。kはバネ定数と言われる定数でバネの強さによって変わります。重力を考えない空間で物体をバネに繋いだモデルを考えます。上のサンプルのようなものです。サンプル内では上下に赤い線を描いてますが、これがゴムみたいなものと考えてください。上下で固定していますが、中央で固定してあるのと同じと考えてください。(描画の都合でこのようにしています)

x軸方向で、画面中央の座標をcx、物体(画面に描画した円)の座標をxとします。バネの長さは

L= cx - x

となります。フックの法則により力FはLに比例するので次のようになります。

F = -k \times L

運動方程式は

m \times a = -k \times L

a(加速度)をdT時間での変化とすると

m \frac{v_2 - v_1}{dT} = -k \times L

となります。v2は変化後の速度。v1は力が加わる前の速度です。この式でdT=1、m=1とすると式は次のようになります。

v_2 = v_1 + k \times L

上の式ではv2とv1が出てきます。コンピュータプログラム的にはv2は変化後(演算後)、v1は演算前と考えられます。(右辺で計算したものを左辺に代入する)
v2とv1をvと書いて、速度変化の式は次のようなコードになります。

v = k * L +v;

速度vと座標xの関係は次のようになります。

  x = x + v;

以上のことからコード全文は次のようになります。

float v, F, x, y;
float cx,k;

void setup() {
  size(800, 200);
  x = 100;
  y = 100;
  v = 0;
  F=0;
  cx = 400;
  k = 0.002;
}

void draw() {
  background(0);
  float L = cx - x;
  
  v = k * L +v;
  x = x + v;

  ellipse(x, y, 20, 20);
}

上記のサンプルは減衰無しの振動になるので永遠に同じ振幅で振動を続けます。

減衰振動

次に振動の幅が徐々に小さくなる減衰振動を作ってみます。速度に比例する抵抗がある場合の運動方程式は次の式になります。rは抵抗の強さを表す定数です。

m \times a = -k \times L - v \times r

この式をProcessingのコードにいれてみます。コードの全文は次の通りです。

float v, F, x, y;
float cx,k;

void setup() {
  size(800, 200);
  x = 100;
  y = 100;
  v = 0;
  F=0;
  cx = 400;
  k = 0.002;
}

void draw() {
  background(0);
  float L = cx - x;
  float r = 0.005;
  
  F = k * L - v * r;//速度に比例した抵抗
  
  v = F +v;
  x = x + v;

  ellipse(x, y, 20, 20);
}

drawの中にあるrで減衰の強さが決まります。適当に値を変えながら試してみてください。

ドラッグで振れ幅を決める

いままでのサンプルでは振れ幅はコードの中で決めていました。これでは面白くないのでドラッグで振れ幅を変えられるようにしてみます。
マウスの操作になるので
mousePressed
mouseReleased
mouseDragged
の3つを使います。
mousePressedはマウスボタンが押された時にどこで押されたのかをチェックします。物体の中心(x,y)とマウスクリック座標の距離を調べ円の中に入っていれば物体の上でマウスボタンが押されたと判断し、変数drag をtrueにします。
mouseDraggedはdragがtrueの時にxをmouseXにします。drawの中でxを使い描画をしているのでマウスドラッグで円が移動することになります。
mouseReleasedはマウスボタンが上がった時の処理です。これでドラッグが終わりになるので変数dragをfalseにします。
drawの中ではdragがfalseの時には運動方程式に従い計算を行います。trueの時(ドラッグ中)では計算をせずに描画だけを行います。コードの全文は次の通りになります。

float v, F, x, y;
float cx, k;
int stageW;
boolean drag = false;

void setup() {
  size(400, 200);
  stageW = width;
  x = stageW/2;
  y = 100;
  v = 0;
  F=0;
  cx = stageW/2;
  k = 0.002;
  x = 0;
}

void draw() {
  background(0);
  
  if (drag == false) {
    float L = cx - x;

    v = k * L +v;
    x = x + v;
    v = v * 0.99;
    if (abs(v)< 0.001) x = random(100);
  }
  stroke(255,0,0);
  line(stageW/2,20,x,y);
  line(stageW/2,180,x,y);
  noStroke();
  fill(255);
  ellipse(x, y, 20, 20);
}

void mousePressed() {
  float px = mouseX;
  float py = mouseY;
  float dx = px - x;
  float dy = py - y;
  float r = sqrt(dx*dx + dy*dy);

  if (r < 15) {
    drag = true;
  }
}


void mouseReleased(){
  drag = false;
}


void mouseDragged() {
 if (drag){
    println("move");
    x = mouseX;
  }
}

これでマウスで物体を引っぱって放すとビヨ〜ん振動するような動きが作れます。