61章:HMC5883Lコンパスで3Dサンプルを動かす

    作成2015.11.26

     HMC5883Lコンパスで3Dサンプルを動かすスケッチについて検討します。

  1. 購入先と価格
    *購入先:アマゾン
    *品名:HMC5883L 使用 3軸 デジタル・コンパス モジュール (3-5V 動作)
    *価格:¥448(配送を含む)安いです!!(説明書の付属はありません。)


  2. 使用方法の参照アドレス(1)
    HMC5883L三軸デジタルコンパスモジュール
    http://wiki.androciti.com/
    *Arduinoとの接続図が参考になります。


  3. 使用方法の参照アドレス(2)
    HMC5883L使用 デジタルコンパスモジュール 3軸地磁気センサ DIP化キット
    http://akizukidenshi.com/catalog/g/gK-09705/
    *評価用デモソフトウェア(Arduino スケッチサンプル)はライブラリーを使用していないため、I2C通信仕様の詳細がわかりやすいです。


  4. HMC5883L評価回路図
     HMC5883L評価回路図を以下に示します。





  5. HMC5883L評価回路外観
     HMC5883L評価回路外観を以下に示します。





  6. 必要ファイルの入手方法
     必要ファイルの入手方法はいろいろありそうな製品ですが、以下の方法で設定しました。
    (1)メニュー「スケッチ」_「Include Library」_「Manage Libraries」を選択します。



    (2))「Library Manager」のサーチ欄にHMCと入力すると検索できます。



    (3)Installボタンを押します。
    (4)C:\Users\ユーザ名\Documents\Arduino\libraries\Adafruit_HMC5883_Unifiedフォルダーが自動作成されます。
    (5)Arduino\libraries\Adafruit_HMC5883_Unified\examples\magsensor\magsensor.inoをダブルクリックで起動して、検証ボタンを押すとAdafruit_Sensor.hが無いというエラーが表示されます。
    (6)https://github.com/adafruit/Adafruit_Sensor/blob/master/Adafruit_Sensor.hのアドレスにアクセスします。
    (7)Rawボタンを右クリックして、対象をファイルに保存をを選択します。



    (8)C:\Users\ユーザ名\Documents\Arduino\libraries\Adafruit_HMC5883_UnifiedフォルダーにAdafruit_Sensor.hをいれます。
    (9)検証ボタンを押すと正常終了します。


  7. Arduinoスケッチ
     Arduinoスケッチは以下となります。
    //HMC5883L 使用 3軸 デジタル・コンパス モジュール (3-5V 動作)
    #include < Wire.h>
    #include < Adafruit_Sensor.h>
    #include < Adafruit_HMC5883_U.h>
    
    Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
    void setup(void) 
    {
      Serial.begin(9600);
      while (!Serial) {}
      if(!mag.begin())
      {
        //Serial.println("Ooops, no HMC5883 detected ... Check your wiring!");
        while(1);
      }
      sensor_t sensor;
      while (Serial.available() <= 0)
      {
        Serial.println("0,0,0");   // send an initial string
        delay(300);
      }
    }
    
    void loop(void) 
    {
      /* Get a new sensor event */ 
      sensors_event_t event; 
      mag.getEvent(&event);
      if (Serial.available() > 0)
      {
        float x=event.magnetic.x;
        float y=event.magnetic.y;
        float z=event.magnetic.z;
        float r=sqrt(x*x+y*y+z*z)-73.0;//-73.0はZオフセット補正
        
        float heading_y = atan2(z,x) + 1.43;//1.43はオフセット補正
        float heading_z = atan2(y,x) -1.36;//-1.36はオフセット補正
        if(heading_y < -PI)heading_y += 2*PI;
        if(heading_z < -PI)heading_z += 2*PI;
        if(heading_y > PI)heading_y -= 2*PI;
        if(heading_z > PI)heading_z -= 2*PI;
        Serial.print(heading_y);
        Serial.print(",");
        Serial.print(heading_z);
        Serial.print(",");
        Serial.print(r);
        Serial.print("\r\n");
        delay(100);
      }
    }
    



  8. HMC5883Lコンパス測定値の変換処理
    (1)HMC5883Lコンパス測定値はx,y,z方向の磁界の強さで与えられます。
    (2)表示しやすいように垂直z軸回りの回転角(heading_z)、水平y軸回り回転角(heading_y)と磁界の強さの絶対値(r)に変換します。
    (3)変換式は以下の通りです。
    float r=sqrt(x*x+y*y+z*z)-73.0;//-73.0はZオフセット補正
    float heading_y = atan2(z,x) + 1.43;//1.43はオフセット補正
    float heading_z = atan2(y,x) -1.36;//-1.36はオフセット補正
    (4)地磁気の方向は北極に対して約10度程度傾いています。これがheading_zの補正値で+0.22程度の値となります。
    (5)水平y軸回り回転角(heading_y)も50度程度傾いています。補正値は0.9程度となります。
    (6)サンプルプログラムの補正値が大きくずれていますが、最初は推定通りの補正値となりました。
    (7)誤って、HMC5883Lコンパスに接近させた後、残留磁気が残ってしまい補正値が大幅にずれてしまいました。
    (8)HMC5883Lコンパスは取扱いに注意が必要です。


  9. Arduinoスケッチ_テキストファイル
     Arduinoスケッチ_テキストファイルは以下から参照できます。
    Arduinoスケッチ_テキストファイルにいく



  10. Processingのスケッチ
     Processingのスケッチを以下に示します。
    import processing.serial.*;
     Serial myPort;        // The serial port
    
    float My=0;
    float Mz=0;
    float gZ=0;
     void setup ()
     {
       size(740, 660, P3D);
       println(Serial.list());
       myPort = new Serial(this, Serial.list()[4], 9600);
       myPort.bufferUntil('\n');
     }
     
     void draw ()
     {
       background(0, 256, 256);
          
        lights();
        translate(width / 2, height / 2);
        //rotateY(map(mouseX, 0, width, -PI/2, PI/2));
        //rotateX(map(mouseY, 0, height, PI/2, -PI/2));
        rotateX(My);
        rotateY(Mz);
        
        noStroke();
        fill(240, 240, 230);
        translate(0,0,50*gZ);
      
        OB_A2();//Z軸回転体(胴体)
        OB_A3();//Z軸回転体(胴体先端)
        OB2();//主翼
        OB3();//水平尾翼
        OB4();//垂直尾翼
        OB_A4();//Z軸回転体(エンジン)
     }
     
    void serialEvent(Serial myPort)
    {
      
       String myString = myPort.readStringUntil('\n');
       myString = trim(myString);
       float sensors[] = float(split(myString, ','));
       for (int sensorNum = 0; sensorNum < sensors.length; sensorNum++) 
       {
          print(sensors[sensorNum] + "\t");
        }
        println();
        My=-sensors[0];
        Mz=sensors[1];
        gZ=sensors[2];
            
        myPort.write("A"); 
     }
     
     
     
     void OB_A4()//Z軸回転体(エンジン)
    {
      int sides=16;//分割数
      int Sn=6;//面数
      float Pz[]={0,-1,-50,-60,-70,-70};//z座標
      float R[]={15,20,20,15,15,1};//回転物半径
     
      int i,j;
      float angleIncrement = TWO_PI/sides;
      pushMatrix();
      translate(60, 36.3,-145);
      for(j=0;j<Sn-1;j++)
      {
        float angle = 0;
        beginShape(QUAD_STRIP);
        for (i = 0; i < sides + 1; ++i)
        {
          vertex(R[j]*cos(angle), R[j]*sin(angle), Pz[j]);
          vertex(R[j+1]*cos(angle), R[j+1]*sin(angle), Pz[j+1]);
          angle += angleIncrement;
        }
        endShape(CLOSE);
      }
      popMatrix();
      
      pushMatrix();
      translate(-60, 36.3,-145);
      for(j=0;j<Sn-1;j++)
      {
        float angle = 0;
        beginShape(QUAD_STRIP);
        for (i = 0; i < sides + 1; ++i)
        {
          vertex(R[j]*cos(angle), R[j]*sin(angle), Pz[j]);
          vertex(R[j+1]*cos(angle), R[j+1]*sin(angle), Pz[j+1]);
          angle += angleIncrement;
        }
        endShape(CLOSE);
      }
      popMatrix();
    }
    
    void OB4()//垂直尾翼
    {
      float Px[]={0,0,3,-3,0,0,2,-2};//ポイントx
      float Py[]={0,0,0,0,-80,-80,-80,-80};//ポイントy
      float Pz[]={25,-25,0,0,0,-25,-15,-15};//ポイントz
      int Sn=10;//面数
      int S1[]={0,0,1,1,0,0,1,1,4,5};//面ポイント1
      int S2[]={2,4,2,5,3,4,3,5,6,6};//面ポイント2
      int S3[]={6,6,5,6,7,7,7,7,7,7};//面ポイント3
      int i;
      
      pushMatrix();
      translate(0, 0,-395);
      for(i=0;i<Sn;i++)
      {
        beginShape();
        vertex(Px[S1[i]], Py[S1[i]],Pz[S1[i]]);
        vertex(Px[S2[i]], Py[S2[i]],Pz[S2[i]]);
        vertex(Px[S3[i]], Py[S3[i]],Pz[S3[i]]);
        endShape(CLOSE);
      }
      popMatrix();
    }
    
    void OB3()//水平尾翼
    {
      float Px[]={0,0,0,100,100,100};//ポイントx
      float Py[]={3,3,-3,2,2,-2};//ポイントy
      float Pz[]={15,-15,0,-5,-15,-10};//ポイントz
      int Sn=7;//面数
      int S1[]={0,0,0,0,1,1,3};//面ポイント1
      int S2[]={1,3,2,3,2,4,4};//面ポイント2
      int S3[]={4,4,5,5,5,5,5};//面ポイント3
      int i;
      
      pushMatrix();
      translate(0, 0,-415);
      for(i=0;i<Sn;i++)
      {
        beginShape();
        vertex(Px[S1[i]], Py[S1[i]],Pz[S1[i]]);
        vertex(Px[S2[i]], Py[S2[i]],Pz[S2[i]]);
        vertex(Px[S3[i]], Py[S3[i]],Pz[S3[i]]);
        endShape(CLOSE);
      }
      for(i=0;i<Sn;i++)
      {
        beginShape();
        vertex(-Px[S1[i]], Py[S1[i]],Pz[S1[i]]);
        vertex(-Px[S2[i]], Py[S2[i]],Pz[S2[i]]);
        vertex(-Px[S3[i]], Py[S3[i]],Pz[S3[i]]);
        endShape(CLOSE);
      }
      popMatrix();
    }
    
    void OB2()//主翼
    {
      float Px[]={10,10,10,220,220,220};//ポイントx
      float Py[]={20,20,10,15,15,10};//ポイントy
      float Pz[]={40,-40,0,10,-10,0};//ポイントz
      int Sn=7;//面数
      int S1[]={0,0,0,0,1,1,3};//面ポイント1
      int S2[]={1,3,2,3,2,4,4};//面ポイント2
      int S3[]={4,4,5,5,5,5,5};//面ポイント3
      int i;
      
      pushMatrix();
      translate(0, 0,-200);
      for(i=0;i<Sn;i++)
      {
        beginShape();
        vertex(Px[S1[i]], Py[S1[i]],Pz[S1[i]]);
        vertex(Px[S2[i]], Py[S2[i]],Pz[S2[i]]);
        vertex(Px[S3[i]], Py[S3[i]],Pz[S3[i]]);
        endShape(CLOSE);
      }
      for(i=0;i<Sn;i++)
      {
        beginShape();
        vertex(-Px[S1[i]], Py[S1[i]],Pz[S1[i]]);
        vertex(-Px[S2[i]], Py[S2[i]],Pz[S2[i]]);
        vertex(-Px[S3[i]], Py[S3[i]],Pz[S3[i]]);
        endShape(CLOSE);
      }
      popMatrix();
    }
    
    void OB_A3()//Z軸回転体(胴体先端)
    {
      int sides=16;//分割数
      int Sn=8;//面数
      float Pz[]={0,-3.2,-11.7,-21.8,-31.9,-45.3,-70.2,-129.4};//z座標
      float R[]={0.1,3.7,7.4,9.6,11.8,13.8,17.4,17.7};//回転物半径
      
      int i,j;
      float angleIncrement = TWO_PI/sides;
      pushMatrix();
      translate(0, 8.5);
      for(j=0;j<Sn-1;j++)
      {
        float angle = 0;
        beginShape(QUAD_STRIP);
        for (i = 0; i < sides + 1; ++i)
        {
          vertex(R[j]*cos(angle), R[j]*sin(angle), Pz[j]);
          vertex(R[j+1]*cos(angle), R[j+1]*sin(angle), Pz[j+1]);
          angle += angleIncrement;
        }
        endShape(CLOSE);
      }
      popMatrix();
    }
    
    void OB_A2()//Z軸回転体(胴体)
    {
      int sides=16;//分割数
      int Sn=13;//面数
      float Pz[]={-40,-46.9,-68.4,-88.2,-108.8,-129.4,-319.4,-346.2,-373,-391.2,-409.4,-433,-440};//z座標
      float R[]={0.1,7.8,15.4,19.5,22.3,25,25,22.3,18.7,16,13.3,8.3,0.1};//回転物半径
     
      int i,j;
      float angleIncrement = TWO_PI/sides;
      for(j=0;j<Sn-1;j++)
      {
        float angle = 0;
        beginShape(QUAD_STRIP);
        for (i = 0; i < sides + 1; ++i)
        {
          fill(240, 240, 230);
          if(j==1){fill(20, 20, 40);}
          if(j==5 && i==0){fill(20, 20, 40);}
          if(j==5 && i==8){fill(20, 20, 40);}
          if(j==5 && i==16){fill(20, 20, 40);}
          vertex(R[j]*cos(angle), R[j]*sin(angle), Pz[j]);
          vertex(R[j+1]*cos(angle), R[j+1]*sin(angle), Pz[j+1]);
          angle += angleIncrement;
        }
        endShape(CLOSE);
      }
    }
    


  11. Processingスケッチ_テキストファイル
     Processingスケッチ_テキストファイルは以下から参照できます。
    Processingスケッチ_テキストファイルにいく



  12. 動作テスト
    (1)HMC5883L評価回路図をします。
    (2)Arduino UNOのUSBを接続します。
    (3)ArduinoIDEを起動して、Arduinoスケッチを書き込みます。
    (4)ProcessingIDEを起動して、Processingスケッチをペーストします。
    (5)ProcessingのRunボタンを押します。
    (6)Arduino\libraries\Adafruit_HMC5883_Unified\examples\magsensor\magsensor.inoをダブルクリックで起動します。
    (7)実行ウインドウにジェト機の3D画像が表示されます。
    (8)鉄片をセンサーを近づけると以下のように画像が変化します。(磁石を近づけると壊れます。)
    (9)以下の結果が表示されます。











  13. HMC5883Lコンパスで3Dサンプルを動かすのまとめ
    (1)HMC5883L 使用 3軸 デジタル・コンパス モジュール (3-5V 動作)で3Dサンプル(sketch_3D_MyJet)を動かすことができました。
    (2)デジタル・コンパス モジュールの測定値はxyz方向の値ですが、表示用としては垂直z軸回りの回転角(heading_z)、水平y軸回り回転角(heading_y)と磁界の強さの絶対値(r)に変換しました。
    (3)新品のセンサーは、北極に対しての地磁気の方向誤差と水平角度誤差がほぼ推定値となりました。
    (4)デジタル・コンパス モジュールに鉄片を近づけると磁界が変化して、3Dサンプル(sketch_3D_MyJet)が変化します。
    (5)誤って、磁石を近づけると、残留磁気が残ってしまい補正値が大幅にずれてしまいました。
    (6)HMC5883Lコンパスは取扱いに注意が必要です。




62章:MPU-60503軸ジャイロスコープで3Dサンプルを動かすに行く。

トップページに戻る。