情報学部 菅沼ホーム Java 目次 基礎技術目次 索引

速度と加速度(その2)

  1. 摩擦

      一般的に,物体が運動するときは摩擦friction )が存在し,外部から何らかの力が加わらない限り,等速直線運動を永久に続けるようなことはありません.ここでは,摩擦について考えてみます.摩擦には様々な物が存在しますが,その一つに速度に比例する摩擦があります.例えば,先の章で述べた自由落下運動に速度に比例した摩擦(摩擦係数:k )を加えると,物体の質量を m,重力の加速度を g / t2 としたとき,その微分方程式は以下のようになります.

    上の微分方程式を解くと,x 軸方向の位置と速度は,

    となり,また,y 軸方向の位置と速度は以下のようになります.

      以上の結果に基づき,自由落下運動を描画したものが以下に示すプログラムです.このプログラムにおいて,緑の丸は摩擦がない場合,また,赤い丸は摩擦がある場合を表しています.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("摩擦1");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(640, 470);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double x0, y0, x1, x2, y1, y2;
    	double t = 0.0, dt = 0.04, vx0 = 100.0, vy0 = -200.0, g = 98.0, k = 0.2, m = 1.0;
    	Dimension d;
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d = d1;
    		x0 = 0.0;
    		y0 = d.height / 2;
    		x1 = x0;   // 摩擦が無い場合
    		y1 = y0;
    		x2 = x0;   // 摩擦がある場合
    		y2 = y0;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(40);
    			}
    			catch (InterruptedException e) {}
    			if (x1 < d.width && y1 < d.height) {
    				t  += dt;
    				x1  = vx0 * t;
    				y1  = y0 + vy0 * t + 0.5 * g * t * t;
    				x2  = vx0 * (1 - Math.exp(-k * t)) / k;
    				y2  = y0 + m * (vy0 - m * g / k) / k - m * (vy0 - m * g / k) * Math.exp(-k * t / m) / k + m * g * t / k;
    			}
    			else {
    				x1 = x0;
    				y1 = y0;
    				x2 = x0;
    				y2 = y0;
    				t  = 0;
    			};
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    
    		g.setColor(Color.green);
    		g.fillOval((int)x1-20, (int)y1-20, 40, 40);
    		g.setColor(Color.red);
    		g.fillOval((int)x2-20, (int)y2-20, 40, 40);
    	}
    }
    			
      この例の場合は,簡単に微分方程式を解くことができますが,一般的には可能であるとは限りません(勿論,数値的に解くこと可能ですが).しかし,アニメーションやゲームにおいては,それほど正確さが求められるわけではありません.そこで,計算する時間間隔 dt が小さい場合は,前章の「加速度」における (5) 式,(6) 式を使用して,速度や位置を近似的に計算することが可能です.自由落下運動(近似)は,上で述べた方法との大きな違いを見つけることが困難だと思います.基本的に,以下のような方法で近似計算をします.現在の y 軸方向の速度を v1 とすると,y 軸方向の加速度 a は,
    a = -k * v1 / m - g			
    となります.そこで,dt 時間後の y 軸方向の速度 v2 は,
    v2 = v1 + a * dt			
    となります.そこで,現在の y 軸方向の位置を y1 としたとき,dt 時間後の y 軸方向の位置 y2 を以下のようにして計算します.
    y2 = y1 + 0.5 * (v1 + v2) * dt			
      なお,x 軸方向に関しても,加速度計算に g が入らないだけで,同様の方法で計算できます.以下に示すのが,この方法を使用したプログラム Test.java の内容です.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("摩擦2");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(640, 470);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double x0, y0, x1, x2, y1, y2, dt = 0.04;
    	double vx0 = 100.0, vy0 = -200.0, vy, vx_f, vy_f, g = 98.0, k = 0.2, m = 1.0;
    	Dimension d;
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d = d1;
    		x0 = 0.0;
    		y0 = d.height / 2;
    		x1 = x0;   // 摩擦が無い場合
    		y1 = y0;
    		vy = vy0;
    		x2 = x0;   // 摩擦がある場合
    		y2 = y0;
    		vx_f = vx0;
    		vy_f = vy0;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(40);
    			}
    			catch (InterruptedException e) {}
    			if (x1 < d.width && y1 < d.height) {
    					// 摩擦無し
    				x1 += vx0 * dt;
    				double v1 = vy;
    				double v2 = vy + g * dt;
    				y1 += 0.5 * (v1 + v2) * dt;
    				vy = v2;
    					// 摩擦あり
    				v1 = vx_f;
    				v2 = vx_f + (-k * vx_f / m) * dt;
    				x2 += 0.5 * (v1 + v2) * dt;
    				vx_f = v2;
    
    				v1 = vy_f;
    				v2 = vy_f + (-k * vy_f / m + g) * dt;
    				y2 += 0.5 * (v1 + v2) * dt;
    				vy_f = v2;
    			}
    			else {
    				x1   = x0;
    				y1   = y0;
    				x2   = x0;
    				y2   = y0;
    				vy   = vy0;
    				vx_f = vx0;
    				vy_f = vy0;
    			};
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    
    		g.setColor(Color.green);
    		g.fillOval((int)x1-20, (int)y1-20, 40, 40);
    		g.setColor(Color.red);
    		g.fillOval((int)x2-20, (int)y2-20, 40, 40);
    	}
    }
    			
      上に示した例は多少わかりにくかったかもしれませんので,前章で示した速度加速度において示した例に対して,それらに摩擦を加えた結果を以下に示します.この例において,緑が摩擦がない場合,赤が摩擦がある場合を示しています.

      まず,等速直線運動において,摩擦がない場合は前章の結果と同じですが,摩擦がある場合は,速度に比例した摩擦によって加速度 - f1 * v12 ( f1 は摩擦係数)が生じるため,速度が徐々に遅くなり,最後には止まってしまいます.また,等加速度運動における摩擦がある場合は,本来の加速度 a と摩擦による逆向きの加速度 - f2 * v22 によって加速度は 0 に近づき,最終的には等速直線運動になります.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("摩擦3");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(640, 470);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double dt = 0.04;
    	double x11 = 0.0, x12 = 0.0, y1 = 0.0, v11 = 100.0, v12 = 100.0;
    	double x21 = 0.0, x22 = 0.0, y2 = 0.0, v21 = 0.0, v22 = 0.0;
    	double a = 200.0;   // 加速度
    	double f1 = 0.2, f2 = 0.7;   // 摩擦係数
    	Dimension d;
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d  = d1;
    		y1 = d.height / 2 - 100;
    		y2 = d.height / 2 + 100;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(33);
    			}
    			catch (InterruptedException e) {}
    			if (x11 < d.width) {
    							// 摩擦無し
    									// 等速直線運動
    				x11 += v11 * dt;
    									// 等加速度運動
    				double v = v21 + a * dt;   // 加速度 a による速度の変化(加速)
    				x21 += 0.5 * (v21 + v) * dt;   // 加速前と加速後の速度の平均で位置を計算
    				v21  = v;   // 加速前と加速後の速度の平均で位置を計算
    							// 摩擦あり
    									// 等速直線運動+摩擦
    				v    = v12 - f1 * v12 * dt;   // 速度に比例した摩擦による速度の変化(減速)
    				x12 += 0.5 * (v12 + v) * dt;   // 減速前と減速後の速度の平均で位置を計算
    				v12  = v;   // 加速前と加速後の速度の平均で位置を計算
    									// 等加速度運動+摩擦
    				v = v22 + (a - f2 * v22) * dt;   // 本来の加速度と速度に比例した摩擦による速度の変化
    				x22 += 0.5 * (v22 + v) * dt;   // 変化前と変化後の速度の平均で位置を計算
    				v22  = v;   // 加速前と加速後の速度の平均で位置を計算
    			}
    			else {
    				x11 = 0.0;
    				x12 = 0.0;
    				v12 = 100.0;
    				x21 = 0.0;
    				x22 = 0.0;
    				v21 = 0.0;
    				v22 = 0.0;
    			}
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    							// 摩擦無し
    		g.setColor(Color.green);
    		g.fillOval((int)x11-20, (int)y1-20, 40, 40);
    		g.fillOval((int)x21-20, (int)y2-20, 40, 40);
    							// 摩擦あり
    		g.setColor(Color.red);
    		g.fillOval((int)x12-20, (int)y1-20, 40, 40);
    		g.fillOval((int)x22-20, (int)y2-20, 40, 40);
    	}
    }
    			

  2. 周期運動

      ここでは,振り子バネのような周期運動振動periodic motionoscillation )について説明します.右図に示すような剛体振り子に対する微分方程式(θ = 0 周りで線形化したもの)は,m を質量,g を重力加速度,r を棒の長さの半分,k を摩擦係数,I を慣性モーメントとすると,以下のようになります.

    また,p,q を利用して以下のように変形することができます.

    さらに,この微分方程式の解は,条件により,以下のような結果になります.

      以上の結果に基づき,剛体振り子の運動を記述したものが以下に示すプログラムです.このプログラムでは,m = 2 * r = 1 を仮定しています.緑色の棒は摩擦がない場合( k = 0 ),赤い棒は k = 0.5 の場合,青い棒は k = 2.6 の場合を表しています.このように,摩擦がないと永久に振動し続け,k が小さい場合は振動しながら停止し,また,k が大きくなると振動せずに停止位置に向かいます.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("周期運動1");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(440, 670);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double g = 9.8, m = 1.0, r = 0.5, I = 4.0 * r * r / 12.0;
    	double a20 = 20.0 * Math.PI / 180.0;
    	double k1 = 0, k2 = 0.5, k3 = 2.6;
    	double p1 = k1 / (I + m * r * r), p2 = k2 / (I + m * r * r), p3 = k3 / (I + m * r * r);
    	double q = m * r * g / (I + m * r * r);
    	double alpha1 = -0.5 * p1, beta1 = 0.5 * Math.sqrt(4 * q - p1 * p1);
    	double c12 = 0.5 * Math.PI, c11 = a20;
    	double alpha2 = -0.5 * p2, beta2 = 0.5 * Math.sqrt(4 * q - p2 * p2);
    	double c22 = Math.atan(-beta2 / alpha2), c21 = a20 / Math.sin(c22);
    	double m1 = 0.5 * (-p3 + Math.sqrt(p3 * p3 - 4 * q));
    	double m2 = 0.5 * (-p3 - Math.sqrt(p3 * p3 - 4 * q));
    	double c31 = -a20 * m2 / (m1 - m2), c32 = a20 - c31;
    	double len, t = 0, dt = 0.01, x1, x2, x3, y1, y2, y3;
    	Dimension d;
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d = d1;
    		len = d.height - 100;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    
    		double z = Math.sin(a20);
    		x1 = 200 + len * Math.sin(z);
    		y1 = 50 + len * Math.cos(z);
    		x2 = x1;
    		y2 = y1;
    		x3 = x1;
    		y3 = y1;
    
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(40);
    			}
    			catch (InterruptedException e) {}
    
    			t += dt;
    
    			double z = c11 * Math.exp(alpha1 * t) * Math.sin(beta1 * t + c12);
    			x1 = 200 + len * Math.sin(z);
    			y1 = 50 + len * Math.cos(z);
    
    			z = c21 * Math.exp(alpha2 * t) * Math.sin(beta2 * t + c22);
    			x2 = 200 + len * Math.sin(z);
    			y2 = 50 + len * Math.cos(z);
    
    			z = c31 * Math.exp(m1 * t) + c32 * Math.exp(m2 * t);
    			x3 = 200 + len * Math.sin(z);
    			y3 = 50 + len * Math.cos(z);
    
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    							// 軸
    		g.setColor(Color.black);
    		g.fillOval(195, 45, 10, 10);
    							// Graphics2Dの取得
    		Graphics2D g2 = (Graphics2D)g;
    							// 摩擦無し
    		g.setColor(Color.green);
    		g2.setStroke(new BasicStroke(3.0f));
    		g2.drawLine(200, 50, (int)x1, (int)y1);
    							// 摩擦小,k = 0.5
    		g.setColor(Color.red);
    		g2.drawLine(200, 50, (int)x2, (int)y2);
    							// 摩擦大,k = 2.6
    		g.setColor(Color.blue);
    		g2.drawLine(200, 50, (int)x3, (int)y3);
    	}
    }
    			
      次のプログラム( Test.java )は,同じ問題に対し,時間と角度の変化を図示したものです.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("周期運動2");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(840, 670);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double g = 9.8, m = 1.0, r = 0.5, I = 4.0 * r * r / 12.0, a20 = 20.0;
    	double k1 = 0, k2 = 0.5, k3 = 2.6;
    	double p1 = k1 / (I + m * r * r), p2 = k2 / (I + m * r * r), p3 = k3 / (I + m * r * r);
    	double q = m * r * g / (I + m * r * r);
    	double alpha1 = -0.5 * p1, beta1 = 0.5 * Math.sqrt(4 * q - p1 * p1);
    	double c12 = 0.5 * Math.PI, c11 = a20;
    	double alpha2 = -0.5 * p2, beta2 = 0.5 * Math.sqrt(4 * q - p2 * p2);
    	double c22 = Math.atan(-beta2 / alpha2), c21 = a20 / Math.sin(c22);
    	double m1 = 0.5 * (-p3 + Math.sqrt(p3 * p3 - 4 * q));
    	double m2 = 0.5 * (-p3 - Math.sqrt(p3 * p3 - 4 * q));
    	double c31 = -a20 * m2 / (m1 - m2), c32 = a20 - c31;
    	double amp, t = 0, dt = 0.01, x, y1, y2, y3;
    	Dimension d;
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d = d1;
    		amp = (d.height / 2 - 20) / a20;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    
    		x = 0;
    		y1 = d.height / 2 - a20 * amp;
    		y2 = y1;
    		y3 = y1;
    
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(40);
    			}
    			catch (InterruptedException e) {}
    
    			t += dt;
    
    			x = 200 * t;
    			y1 = d.height / 2 - amp * c11 * Math.exp(alpha1 * t) * Math.sin(beta1 * t + c12);
    			y2 = d.height / 2 - amp * c21 * Math.exp(alpha2 * t) * Math.sin(beta2 * t + c22);
    			y3 = d.height / 2 - amp * (c31 * Math.exp(m1 * t) + c32 * Math.exp(m2 * t));
    
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    							// Graphics2Dの取得
    		Graphics2D g2 = (Graphics2D)g;
    							// 軸
    		g.setColor(Color.black);
    		g2.setStroke(new BasicStroke(2.0f));
    		g2.drawLine(0, d.height/2, d.width, d.height/2);
    		g.drawString("時間", d.width-30, d.height/2+15);
    
    		g.setColor(new Color(0xc0, 0xc0, 0xc0));
    		g2.drawLine(0, 20, d.width, 20);
    		g.setColor(Color.black);
    		g.drawString("θ=20度", 5, 15);
    
    		g.setColor(new Color(0xc0, 0xc0, 0xc0));
    		g2.drawLine(0, d.height-20, d.width, d.height-20);
    		g.setColor(Color.black);
    		g.drawString("θ=-20度", 5, d.height-5);
    
    		if (x < d.width) {
    							// 摩擦無し
    			g.setColor(Color.green);
    			g.fillOval((int)x-10, (int)y1-10, 20, 20);
    							// 摩擦小,k = 0.5
    			g.setColor(Color.red);
    			g.fillOval((int)x-10, (int)y2-10, 20, 20);
    							// 摩擦大,k = 2.6
    			g.setColor(Color.blue);
    			g.fillOval((int)x-10, (int)y3-10, 20, 20);
    		}
    		else {
    			t = 0.0;
    			x = 0.0;
    			y1 = d.height / 2 - a20 * amp;
    			y2 = y1;
    			y3 = y1;
    		}
    	}
    }
    			
      次のプログラム( Test.java )は,同じ問題を先に述べた近似的方法で解き,時間と角度の変化を図示(近似)したものです.前の結果と大きくは変わりませんが,摩擦がない場合は,発散していく傾向が見られます(近似的方法の誤差による).
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("周期運動3");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(840, 670);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double g = 9.8, m = 1.0, r = 0.5, I = 4.0 * r * r / 12.0, a20 = 20.0;
    	double k1 = 0, k2 = 0.5, k3 = 2.6;
    	double p1 = k1 / (I + m * r * r), p2 = k2 / (I + m * r * r), p3 = k3 / (I + m * r * r);
    	double q = m * r * g / (I + m * r * r);
    	double alpha1 = -0.5 * p1, beta1 = 0.5 * Math.sqrt(4 * q - p1 * p1);
    	double c12 = 0.5 * Math.PI, c11 = a20;
    	double alpha2 = -0.5 * p2, beta2 = 0.5 * Math.sqrt(4 * q - p2 * p2);
    	double c22 = Math.atan(-beta2 / alpha2), c21 = a20 / Math.sin(c22);
    	double m1 = 0.5 * (-p3 + Math.sqrt(p3 * p3 - 4 * q));
    	double m2 = 0.5 * (-p3 - Math.sqrt(p3 * p3 - 4 * q));
    	double c31 = -a20 * m2 / (m1 - m2), c32 = a20 - c31;
    	double amp, t = 0, dt = 0.01, x, y1, y2, y3, a1, a2, a3, v1, v2, v3, w1, w2, w3;
    	Dimension d;
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d = d1;
    		amp = (d.height / 2 - 20) / a20;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    
    		w1 = a20;
    		v1 = 0;
    		a1 = -p1 * v1 - q * w1;
    		x = 0;
    		y1 = d.height / 2 - w1 * amp;
    
    		w2 = a20;
    		v2 = 0;
    		a2 = -p2 * v2 - q * w2;
    		y2 = d.height / 2 - w2 * amp;
    
    		w3 = a20;
    		v3 = 0;
    		a3 = -p3 * v3 - q * w3;
    		y3 = d.height / 2 - w3 * amp;
    
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(40);
    			}
    			catch (InterruptedException e) {}
    
    			t += dt;
    			x = 200 * t;
    
    			double v = v1 + a1 * dt;   // 加速度から速度
    			w1 += 0.5 * (v + v1) * dt;
    			v1 = v;
    			a1 = -p1 * v1 - q * w1;   // 加速度
    			y1 = d.height / 2 - amp * w1;
    
    			v = v2 + a2 * dt;
    			w2 += 0.5 * (v + v2) * dt;
    			v2 = v;
    			a2 = -p2 * v2 - q * w2;
    			y2 = d.height / 2 - amp * w2;
    
    			v = v3 + a3 * dt;
    			w3 += 0.5 * (v + v3) * dt;
    			v3 = v;
    			a3 = -p3 * v3 - q * w3;
    			y3 = d.height / 2 - amp * w3;
    
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    							// Graphics2Dの取得
    		Graphics2D g2 = (Graphics2D)g;
    							// 軸
    		g.setColor(Color.black);
    		g2.setStroke(new BasicStroke(2.0f));
    		g2.drawLine(0, d.height/2, d.width, d.height/2);
    		g.drawString("時間", d.width-30, d.height/2+15);
    
    		g.setColor(new Color(0xc0, 0xc0, 0xc0));
    		g2.drawLine(0, 20, d.width, 20);
    		g.setColor(Color.black);
    		g.drawString("θ=20度", 5, 15);
    
    		g.setColor(new Color(0xc0, 0xc0, 0xc0));
    		g2.drawLine(0, d.height-20, d.width, d.height-20);
    		g.setColor(Color.black);
    		g.drawString("θ=-20度", 5, d.height-5);
    
    		if (x < d.width) {
    							// 摩擦無し
    			g.setColor(Color.green);
    			g.fillOval((int)x-10, (int)y1-10, 20, 20);
    							// 摩擦小,k = 0.5
    			g.setColor(Color.red);
    			g.fillOval((int)x-10, (int)y2-10, 20, 20);
    							// 摩擦大,k = 2.6
    			g.setColor(Color.blue);
    			g.fillOval((int)x-10, (int)y3-10, 20, 20);
    		}
    		else {
    			t = 0.0;
    			x = 0;
    
    			w1 = a20;
    			v1 = 0;
    			a1 = -p1 * v1 - q * w1;
    			y1 = d.height / 2 - w1 * amp;
    
    			w2 = a20;
    			v2 = 0;
    			a2 = -p2 * v2 - q * w2;
    			y2 = d.height / 2 - w2 * amp;
    
    			w3 = a20;
    			v3 = 0;
    			a3 = -p3 * v3 - q * w3;
    			y3 = d.height / 2 - w3 * amp;
    		}
    	}
    }
    			

  3. もう一つの例

      この例では,前の章で作成したもう一つの例に対し,速度に比例する摩擦を加えています.
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Test {
    	public static void main (String[] args)
    	{
    		Win win = new Win("等速度運動と等加速度運動");
    	}
    }
    
    class Win extends JFrame
    {
    	/******************/
    	/* コンストラクタ */
    	/******************/
    	Win(String name)
    	{
    					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    		super(name);
    					// Windowの大きさ
    		setSize(640, 470);   // +40, +70
    					// ContentPane の取得と MainPanel の追加
    		Dimension d  = getSize();   // Windowの大きさ
    		d.width     -= 40;
    		d.height    -= 70;
    		MainPanel pn = new MainPanel(d);   // MainPanel オブジェクトの生成
    		getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    					// ウィンドウを表示
    		setVisible(true);
    					// イベントアダプタ
    		addWindowListener(new WinEnd());
    	}
    
    	/******************************/
    	/* 上,左,下,右の余白の設定 */
    	/******************************/
    	public Insets getInsets()
    	{
    		return new Insets(50, 20, 20, 20);
    	}
    
    	/************/
    	/* 終了処理 */
    	/************/
    	class WinEnd extends WindowAdapter
    	{
    		public void windowClosing(WindowEvent e) {
    			System.exit(0);
    		}
    	}
    }
    
    class MainPanel extends JPanel implements Runnable
    {
    	boolean state = true;
    	double x0 = 0.0, x1, x2, y1, y2;   // x方向の初期位置と各物体の位置
    	double t = 0.0, dt = 0.04;   // 時間とその刻み幅
    	double v0 = 50.0, v1, v2;   // 初期速度と各物体の速度
    	double a = 30.0;   // 加速度
    	double ang1 = 20.0 * Math.PI / 180.0, ang2 = -20.0 * Math.PI / 180.0;   // 進行方向
    	Dimension d;   // パネルの幅と高さ
    	double k = 0.2;   // 摩擦係数
    	Thread th;
    
    	MainPanel(Dimension d1)
    	{
    		d  = d1;
    		x1 = x0;
    		y1 = d.height / 2;
    		v1 = v0;
    		x2 = x0;
    		y2 = d.height / 2;
    		v2 = v0;
    		setBackground(new Color(238, 255, 238));   // 背景色の設定
    		th = new Thread(this);   // スレッドの生成とスタート
    		th.start();
    	}
    					// 他ページへ移動の際,一時的にスレッドを停止
    	public void stop()
    	{
    		state = false;
    	}
    					// スレッドの実行
    	public void run()
    	{
    		while (state) {
    			try {
    				th.sleep(40);
    			}
    			catch (InterruptedException e) {}
    			if (x2 < d.width) {
    				double t1 = v1;
    				double t2 = v1 - k * v1 * dt;
    				v1  = t2;
    				x1 += 0.5 * (t1 + t2) * dt * Math.cos(ang1);
    				y1 += -0.5 * (t1 + t2) * dt * Math.sin(ang1);
    				t1  = v2;
    				t2  = v2 + (a - k * v2) * dt;
    				v2  = t2;
    				x2 += 0.5 * (t1 + t2) * dt * Math.cos(ang2);
    				y2 += -0.5 * (t1 + t2) * dt * Math.sin(ang2);
    			}
    			else {
    				x1 = x0;
    				v1 = v0;
    				y1 = d.height / 2;
    				x2 = x0;
    				v2 = v0;
    				y2 = d.height / 2;
    				t  = 0;
    			};
    			repaint();
    		}
    	}
    					// 描画
    	public void paintComponent(Graphics g)
    	{
    		super.paintComponent(g);   // 親クラスの描画
    
    		g.setColor(Color.green);
    		g.fillOval((int)x1-20, (int)y1-20, 40, 40);
    		g.setColor(Color.red);
    		g.fillOval((int)x2-20, (int)y2-20, 40, 40);
    	}
    }
    			

情報学部 菅沼ホーム Java 目次 基礎技術目次 索引