飽和曲線


  1. 飽和曲線

    ある一定の値まで、近づく最初は急激に目的値に近づきますが、近づくにつれ、変化が遅くなるような曲線をここでは飽和曲線と呼びます。例えば、放置した温水の温度の変化も飽和曲線になります。ここでは、電子回路の例を紹介します。

  2. 回路

    1. コンデンサ

      コンデンサは、小容量の電荷(電気のモト)を蓄積する素子で、導体の板の間にごく薄い絶縁層を挟み込んで作成します。コンデンサの容量をC、電圧Vをかけたとき、蓄積した電荷量を Q とすると、 Q = C・V の関係になります。C または V を大きくすると、蓄積できる電荷量が増大します。よく利用される容量は、10-6 程度の大きさです。

    2. 抵抗とコンデンサの直列接続

      図のように電池に抵抗とコンデンサを直列に接続した場合の回路の動作を調べましょう。流れる電流はコンデンサにかかる電圧に比例します。コンデンサに電荷が蓄積されるとコンデンサ自身の電圧が上がります。したがって、回路に流れる電流は時間とともに減少します。


    3. 解析解

      接続した最初の時刻(t=0)ではコンデンサCに蓄積した電荷は0ですから、コンデンサCの両端の電圧は0です。したがって、流れる電流は R/V となります。
      しかし、電流が流れると、コンデンサに電荷が蓄積され、それとともにコンデンサの両端の電圧が増加します。コンデンサの両端の電圧をv(t)とします。((t)は時間のともに変換することを意味しています)。すると流れる電流は
        V = R・i(t) + v(t)
      となります。コンデンサに蓄積された電荷をq(t)とすると、
       q(t) = C・v(t)
      の関係があります。また、i(t) = dq(t)/dt ですから、
       V = RC ・dv(t)/dt + v(t)
      となります。これは、q(t) に関する定数係数の1次微分方程式で、解が知られており
       v(t) = V (1-e-(t/RC))
      となります。これが先の微分方程式を満足することは
       eax の微分は a・eax であることから確認できます。また、t=0のとき、v(0)=0、tが十分大きいとき、v(t)=V となります。

    4. 数値解

      微分方程式の解を求めることは、一般的にはむづかしい問題です。先の微分方程式から、数値計算で回路の動作をシミュレーションすることも出来ます。 
       V = RC ・dv(t)/dt + v(t)
      から、
       dv(t) = (1/(RC))・(V-v(t))dt
      です。これは、dt 時刻経過すると、 (1/(RC))・(V-v(t))dt だけ電圧が変化することを示します。したがって、dt時間経過後の電圧v(t+dt)は
        v(t+dt) = v(t) + (1/(RC))・(V-v(t))dt
      となります。t=0 で v(t)=0 から出発し、短い時間dt経過後の電圧を計算していけば、数値計算が可能です。つまり、v(0)=v0 として
       v1 = v0 + (1/(RC))・(V-v0)dt
      で v0 から dt 時間後のv1 を計算します。どうように、v1 からさらに dt 時刻後のv2 を求めます。
       v2 = v1 + (1/(RC))・(V-v1)dtこの計算法をオイラー法と呼ぶことがあります。
      この計算は、プログラム言語では
        v = v + (1/(RC))・(V-v)dt
      となります。右辺の v はv(t)、左辺のv は v(t+dt) に対応します。この計算法をオイラー法と呼ぶことがあります。
    5. 計算誤差

       オイラー法では、dt の間は 「v の値は一定で変化しない」ことが前提です。実際には、dt の間に v が変化しますから、計算誤差が発生します。dt を小さくすれば、計算誤差を抑えることが出来ます。同じ dt で誤差を少なく計算する方法にルンゲクッタ法があります。詳しくはこちらを参照してください。

    6. 飽和曲線

       解析的に求めた式、または、数値解を計算すると、次のような曲線になります。これは、ある一定目標まで、限りなく近づいてく曲線になり、飽和型の曲線となります。

        

  3. プログラム

    1. 画面のレイアウト

       コンデンサ、抵抗、電圧、をパラメータとして設定します。計算ボタンを押すと、電源投入後3秒間のコンデンサの両端の電圧を計算し、表示します。実際の配置は、実行画面を参照して下さい。

    2. 変数

      double C,R,V,v,t;
       コンデンサーの容量:C,抵抗の値:R,電源の電圧:v
       コンデンサーの電圧:v、時刻:t 、を記録します
      int px,py,osx=30,osy=220,i;
       px,py:時刻pxと電圧をpyの座標、
       osx,psyはグラフの原点の位置

       
    3. メソッド

      1. paint()
        グラフの軸と目盛りの表示、および、最初の3秒間v(t)の計算を行います。
        Javaでは、eの値は Math.E で取得できます。また、aのx乗は Math.pow(a,x)で計算できます。したがって、V (1-e-(t/RC))は
         V*(1.0-Math.pow(Math.E,-t/(R*C)))
        で計算できます。
      2. Euler()
        dt=0.01 として、オイラー法の繰返し計算で、電圧変化を計算します。ここで、dtを大ききすると、計算誤差が発生します。vcの値は、dt時間の間に変化します。dtの値は、「この時間間隔の間vcは変化しない」、程度に小さくする必要があります。

        for(t=0.0;t<3.0;t=t+dt){
          vc = vc + (1/(C*R))*(V-vc)*dt;
         }

    4. ソース
      package crtrj;
      
      import java.awt.*;
      import java.awt.event.*;
      import java.applet.*;
      
      public class Applet1 extends Applet {
        private boolean isStandalone = false;
        private TextField textField1 = new TextField();
        private TextField textField2 = new TextField();
        private Label label1 = new Label();
        private Label label2 = new Label();
        private Label label3 = new Label();
        private TextField textField3 = new TextField();
        private Button button1 = new Button();
        //引数値の取得
        public String getParameter(String key, String def) {
          return isStandalone ? System.getProperty(key, def) :
            (getParameter(key) != null ? getParameter(key) : def);
        }
      
        //アプレットのビルド
        public Applet1() {
        }
        //アプレットの初期化
        public void init() {
          try {
            jbInit();
          }
          catch(Exception e) {
            e.printStackTrace();
          }
        }
        //コンポーネントの初期化
        private void jbInit() throws Exception {
          textField1.setText("0.005");
          textField1.setBounds(new Rectangle(41, 249, 49, 21));
          this.setLayout(null);
          textField2.setText("100");
          textField2.setBounds(new Rectangle(140, 251, 45, 19));
          label1.setText("C");
          label1.setBounds(new Rectangle(12, 250, 26, 21));
          label2.setText("R");
          label2.setBounds(new Rectangle(110, 253, 25, 17));
          label3.setText("E");
          label3.setBounds(new Rectangle(216, 249, 26, 21));
          textField3.setText("1.5");
          textField3.setBounds(new Rectangle(245, 248, 44, 23));
          button1.setLabel("計算");
          button1.setBounds(new Rectangle(310, 247, 69, 23));
          button1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
              button1_actionPerformed(e);
            }
          });
          this.add(textField1, null);
          this.add(label1, null);
          this.add(label3, null);
          this.add(textField3, null);
          this.add(label2, null);
          this.add(button1, null);
          this.add(textField2, null);
        }
        //アプレットの情報取得
        public String getAppletInfo() {
          return "アプレット情報";
        }
      
        //引数情報の取得
        public String[][] getParameterInfo() {
          return null;
        }
      
        public void paint(Graphics g){
          double C,R,V,v,t;
          int px,py,osx=30,osy=220,i;
          int ox=osx,oy=osy;
      
          //パラメータを読み込む
          C=Double.parseDouble(textField1.getText());
          R=Double.parseDouble(textField2.getText());
          V=Double.parseDouble(textField3.getText());
          //C=0.005;
          //R=100;
          //V=1.5;
      
          //x軸と目盛りを表示
          g.drawLine(osx,oy,osx+300,oy);
          for(i=0;i<=300;i=i+1){
            if(i % 20==0) g.drawLine(i+osx,oy,i+osx,oy-3);
            if(i % 100==0) {
              g.drawLine(i+osx,oy,i+osx,oy-7);
              g.drawString(Integer.toString(i/100),i+osx-3,oy+12);
            }
          }
           //y軸と目盛りを表示
          g.drawLine(osx,oy,osx,oy-200);
          for(i=0;i<=200;i=i+1){
            if(i % 20==0) g.drawLine(osx,oy-i,osx+3,oy-i);
            if(i % 100==0) {
              g.drawLine(osx,oy-i,osx+7,oy-i);
              g.drawString(Integer.toString(i/100),osx-12,oy-i+3);
            }
          }
          //式の表示
          //理論式の表示
          for(t=0.0;t<3.0;t=t+0.05){
            v=V*(1.0-Math.pow(Math.E,-t/(R*C)));
            px=(int)(t*100)+osx;
            py=osy-(int)(v*100);
            g.drawLine(ox,oy,px,py);
            ox=px;oy=py;
            //System.out.println("t:"+t+" v:"+v);
          }
          //数値計算での表示
          Euler( g,C,R,V);
        }
      
        void Euler(Graphics g,double C,double R,double V){
          //数値解析をする
          int px,py,osx=30,osy=220;
          int ox=osx,oy=220;
          double t;
          g.setColor(new Color(100,100,255));
          double vc=0.0,dt=0.01;
          ox=osx;oy=200;
      
          for(t=0.0;t<3.0;t=t+dt){
           //dt後の値を求める
            vc = vc + (1/(C*R))*(V-vc)*dt;
           //表示
            px=(int)(t*100)+osx;
            py=osy-(int)(vc*100);
            g.drawLine(ox,oy,px,py);
            ox=px;oy=py;
            //System.out.println("t:"+t+" v:"+vc);
          }
        }
      
        void button1_actionPerformed(ActionEvent e) {
          repaint();
        }
      
      }

    5. 実行

      コンデンサ、抵抗、電圧を設定して、計算ボタンを押します。黒色は解析的に解いた関数の計算値、青色は数値計算した値です。電圧を2以上にすると、曲線は画面の外に出ます。Cを小さくすると、急激に電圧が立ち上がり、大きくすると直線的にゆっくり上昇します。 このアプレットは古い版のVM(Javaの実行プログラム)では、少数入力の不具合で表示が出来ません。Java2以降のVMが必要です。

        

      変化が大きいと、下の例のように黒の理論曲線と青の数値計算がずれてきます。dtを小さくすれば、このずれは減少します。
        

    6. ダウンロード

      このプロジェクトをダウンロードできます。次の行をクリックして、crtrj.exeファイルを適当なフォルダに保存します。
      ダウンロード開始

      このファイルは自己解凍型の圧縮ファイルです。