磁気式角度センサーの開発

#概要
回転軸に磁石を取り付け，ホールセンサによって軸の回転による磁界の変化を計測し，軸回転角を計測する．

[すでに製品化されているもの](https://go.alps.jp/l/506151/2018-11-09/29t52b)はTMR角度センサーと呼ばれるものを使っている．

TMR角度センサーは少々高額であったため，今回は1個20円もしない，アナログホールセンサを用いて角度センサーを制作していく．
購入したホールセンサーは[ここ](https://ja.aliexpress.com/item/4000918072537.html?spm=a2g0s.9042311.0.0.39684c4daStMvj)：OH49E
![P_20201126_152740.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/a8053c4a-0ddf-8b54-0c54-997ad6c5a3cf.jpeg)

![P_20201126_152811.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/ddcc41ca-c104-b0bd-7813-f51cddbf20fe.jpeg)

写真にあるように，エンコーダー式の角度センサー（分解能1degree）を用いることで，角度とホールセンサ計測値の関係を求める．

最終目標は，0.1degree以上の精度を持つ，角度センサーの開発である．
（追記：最終的には達成できず，未達成課題となってしまった．）

#計測結果
手で軸をぐりぐり回し，計測した結果は次のようになる．
![エンコーダー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/26f5863d-e6bb-76de-8d4a-402d31339c70.png)

![ホールセンサ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/b6a25a60-ab33-aa6e-54d0-8914b335c54b.png)

ホールセンサには，幾分の誤差が存在する．
角度0~359に対応するホールセンサーの数値を平均，最大，最小を分析すると次のようになる．

![hole1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/2979f57d-39a7-6987-c9e6-039d54dbc8c1.png)
![hole2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/ec689b10-c463-4fb0-9ffb-876a986e6b47.png)
ピークを取る角度差は約40°となっている．

min,maxの差をグラフ化すると
![maxmin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/eb1b26bf-b832-4648-e1d3-47c83415b5cf.png)
ホールセンサは(500±120)ぐらいで変動する．最大50程度の変動が発生している．これはとても大きい．このままでは，分解能は5degreeあればよい程度になってしまう．ホールセンサーの計測を複数行い，平均化し精度向上を狙ってみる．ただ，使ってみた感じホールセンサー出力はそこまで，振れないので意味があるかは不明．地磁気の影響が大きい気がするので，装置を万力に固定して行う．

または，センサーをもっと軸に近づけて，センサー出力をより大きく変動させた方がよさそうだ．

#ホールセンサー平均化（4回）
ホールセンサの計測を4回行い，平均した数値をデータとしてみた．
![enc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/78c601a1-3428-5814-9101-b865c160e6bd.png)
![hole.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/fe65232e-eaf1-2fb5-e514-3398f094390f.png)
波形は特に問題なく，まぁまぁきれいな形状が取れている,先ほどより，軸の回転速度が高くなった（手動のため）

![hole11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/5abc4810-38fc-2d1d-360e-12746de15de5.png)
先ほど同様に平均を出すと，かなりmaxmin,に差のある結果になってしまった．
時刻の0合わせは手動で行っていることが由来しているのか？と思ったら，そうだった

![hole111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/c2c0765d-8dce-edb4-28db-991d2850b8c5.png)

![maxmin2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/a4b8b4e5-ea9c-730f-3cde-d7211675e418.png)

max,minの特性はそんなに変わらない．

###結論
ホールセンサの計測回数はそこまで影響がない．時刻の0合わせが非常に大切

#ホールセンサ出力の増大化
ホールセンサの位置をもう少し近く配置する．
やろうと思ったがめんどくさいので，現状のセッティングで角度計測を行ってみる

#プログラム

実験データを線形近似曲線でフィッティングする．元データをそのままプログラムには使えないので，単調増加するようにするため．holeの値によって，角度推定値は2択に絞られる．ホールセンサが二つあるため，一つにできる．

![kinji.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/932c750a-8e38-297e-cae2-22d1cfd905a4.png)
まず，多項式近似で曲線を滑らかにする

得られた数値をプログラムで，Arduinoプログラムにする

```C
float serch_hole(float hole1,float hole2,int *pos_hole1){
    if(a1 <= hole1 && hole1 < a2){
       *pos_hole1 = angle
    }
    //-----a1,a2,angleを変えて以上を繰り返す
}
```
ここでのif文はholeの値が10以上増減した時に生成する．

問題は，これでは片方の数値しか得られないことである．二回条件に引っ掛からないと抜けないようにするのがいいかもしれない．

```C
float serch_hole(float hole1,float hole2){
    int pos_hole1a;
    int pos_hole1b;
    int count = 0;
    if(a1 <= hole1 && hole1 < a2){
       if(count == 0){
           pos_hole1a = angle1 +(hole1-a1)/(a2-a1)*(angle_a2 - angle_a1); //内分して得られる数値
           count = 1;
       }else{
           pos_hole1b = angle1 +(hole1-a1)/(a2-a1)*(angle_a2 - angle_a1); //内分して得られる数値
           count = 2;
       }
    }
    //-----a1,a2,angleを変えて以上を繰り返す
    //-----hole2についても同様に計算し2つ数値を得る．
   if(count == 2){
        //4つのうち，差が最も小さい2つを探す．　とりあえず，その二つの平均を計算
        angle1 =
   }
}
```

手入力で，数値を入れるのはきついので，pythonでプログラムを生成する．

###やってみた結果

* 二回反応したらという条件を入れたが，2回反応しないことがほとんど
* 「if文はholeの値が10以上増減した時に生成する．」としたが，これだと最大値以上がカバーされない．

これまでのlook-up-table式＋2択式（単調増加ではないため）はちょっとめんどくさすぎる

###対策
大幅に機能を減少させ．
![hole1_new.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/4bbd625e-8106-4bf4-4dfe-784099137ed6.png)
変化が大きく，230~280degreeの間で考えてみる．

![補完.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/bc1addee-eee1-4e8b-71b0-44995aed1821.png)
4次式で補間した結果がこちら．

これに基づいてプログラムした結果
![result2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/431e5137-35aa-c3c3-c352-5505e912d1e0.png)
まぁまぁ差のある結果になってしまった．難しい．これ以上は深入りしたくないので，とりあえず完結とする．

#プログラム
###Arduino

```C
#include <MsTimer2.h>
#include <TimerOne.h>

volatile boolean flag = 0;
volatile int count = 0;
volatile int hole1 = 0;
volatile int hole2 = 0;
void printer() {
  flag = true;
  count++;
  if (count >= 5) {
    count = 0;
  }
}

void setup() {
  Serial.begin(250000);
  pinMode(A0, INPUT);
  pinMode(A1, INPUT);
  Timer1.initialize(2000); //10ms
  Timer1.attachInterrupt(printer);

}

void loop() {
  if (flag == true) {
    flag = false;
    if (count <= 3) {
      hole1 += analogRead(A0);
      hole2 += analogRead(A1);
    } else {
      float angle;
      hole1 = hole1 / 4;
      hole2 = hole2 / 4;
      angle = serch_hole(hole1, hole2);
      Serial.println(angle);
      hole1 = 0;
      hole2 = 0;
    }
  }
}

float serch_hole(float hole1, float hole2) {
  float position;
  
  if ( 527.623221181479 <= hole1 && hole1 < 527.623221181479 ) {
    position =  151  + (hole1 - 527.623221181479 ) / ( 0.0 ) * ( 0 );
  } else if ( 527.623221181479 <= hole1 && hole1 < 528.113072737562 ) {
    position =  151  + (hole1 - 527.623221181479 ) / ( 0.4898515560829537 ) * ( 1 );
  } else if ( 528.113072737562 <= hole1 && hole1 < 528.764937868486 ) {
    position =  152  + (hole1 - 528.113072737562 ) / ( 0.6518651309239658 ) * ( 1 );
  } else if ( 528.764937868486 <= hole1 && hole1 < 529.573248940607 ) {
    position =  153  + (hole1 - 528.764937868486 ) / ( 0.8083110721210005 ) * ( 1 );
  } else if ( 529.573248940607 <= hole1 && hole1 < 530.532438320288 ) {
    position =  154  + (hole1 - 529.573248940607 ) / ( 0.9591893796809927 ) * ( 1 );
  } else if ( 530.532438320288 <= hole1 && hole1 < 531.636938373875 ) {
    position =  155  + (hole1 - 530.532438320288 ) / ( 1.104500053587003 ) * ( 1 );
  } else if ( 531.636938373875 <= hole1 && hole1 < 532.881181467732 ) {
    position =  156  + (hole1 - 531.636938373875 ) / ( 1.2442430938569942 ) * ( 1 );
  } else if ( 532.881181467732 <= hole1 && hole1 < 534.259599968214 ) {
    position =  157  + (hole1 - 532.881181467732 ) / ( 1.3784185004820984 ) * ( 1 );
  } else if ( 534.259599968214 <= hole1 && hole1 < 535.766626241675 ) {
    position =  158  + (hole1 - 534.259599968214 ) / ( 1.5070262734609514 ) * ( 1 );
  } else if ( 535.766626241675 <= hole1 && hole1 < 537.396692654478 ) {
    position =  159  + (hole1 - 535.766626241675 ) / ( 1.6300664128029894 ) * ( 1 );
  } else if ( 537.396692654478 <= hole1 && hole1 < 539.144231572973 ) {
    position =  160  + (hole1 - 537.396692654478 ) / ( 1.7475389184950245 ) * ( 1 );
  } else if ( 539.144231572973 <= hole1 && hole1 < 541.00367536352 ) {
    position =  161  + (hole1 - 539.144231572973 ) / ( 1.8594437905469476 ) * ( 1 );
  } else if ( 541.00367536352 <= hole1 && hole1 < 542.969456392475 ) {
    position =  162  + (hole1 - 541.00367536352 ) / ( 1.965781028955007 ) * ( 1 );
  } else if ( 542.969456392475 <= hole1 && hole1 < 545.036007026196 ) {
    position =  163  + (hole1 - 542.969456392475 ) / ( 2.0665506337210218 ) * ( 1 );
  } else if ( 545.036007026196 <= hole1 && hole1 < 547.197759631035 ) {
    position =  164  + (hole1 - 545.036007026196 ) / ( 2.1617526048389664 ) * ( 1 );
  } else if ( 547.197759631035 <= hole1 && hole1 < 549.449146573355 ) {
    position =  165  + (hole1 - 547.197759631035 ) / ( 2.251386942320096 ) * ( 1 );
  } else if ( 549.449146573355 <= hole1 && hole1 < 551.784600219509 ) {
    position =  166  + (hole1 - 549.449146573355 ) / ( 2.335453646153951 ) * ( 1 );
  } else if ( 551.784600219509 <= hole1 && hole1 < 554.198552935855 ) {
    position =  167  + (hole1 - 551.784600219509 ) / ( 2.413952716345989 ) * ( 1 );
  } else if ( 554.198552935855 <= hole1 && hole1 < 556.685437088749 ) {
    position =  168  + (hole1 - 554.198552935855 ) / ( 2.4868841528940493 ) * ( 1 );
  } else if ( 556.685437088749 <= hole1 && hole1 < 559.239685044546 ) {
    position =  169  + (hole1 - 556.685437088749 ) / ( 2.5542479557969955 ) * ( 1 );
  } else if ( 559.239685044546 <= hole1 && hole1 < 561.855729169605 ) {
    position =  170  + (hole1 - 559.239685044546 ) / ( 2.61604412505892 ) * ( 1 );
  } else if ( 561.855729169605 <= hole1 && hole1 < 564.528001830284 ) {
    position =  171  + (hole1 - 561.855729169605 ) / ( 2.6722726606790275 ) * ( 1 );
  } else if ( 564.528001830284 <= hole1 && hole1 < 567.25093539294 ) {
    position =  172  + (hole1 - 564.528001830284 ) / ( 2.722933562656067 ) * ( 1 );
  } else if ( 567.25093539294 <= hole1 && hole1 < 570.018962223921 ) {
    position =  173  + (hole1 - 567.25093539294 ) / ( 2.7680268309809435 ) * ( 1 );
  } else if ( 570.018962223921 <= hole1 && hole1 < 572.826514689594 ) {
    position =  174  + (hole1 - 570.018962223921 ) / ( 2.807552465672984 ) * ( 1 );
  } else if ( 572.826514689594 <= hole1 && hole1 < 575.66802515631 ) {
    position =  175  + (hole1 - 572.826514689594 ) / ( 2.841510466716045 ) * ( 1 );
  } else if ( 575.66802515631 <= hole1 && hole1 < 578.537925990431 ) {
    position =  176  + (hole1 - 575.66802515631 ) / ( 2.8699008341209264 ) * ( 1 );
  } else if ( 578.537925990431 <= hole1 && hole1 < 581.430649558308 ) {
    position =  177  + (hole1 - 578.537925990431 ) / ( 2.8927235678770558 ) * ( 1 );
  } else if ( 581.430649558308 <= hole1 && hole1 < 584.340628226299 ) {
    position =  178  + (hole1 - 581.430649558308 ) / ( 2.9099786679910267 ) * ( 1 );
  } else if ( 584.340628226299 <= hole1 && hole1 < 587.262294360762 ) {
    position =  179  + (hole1 - 584.340628226299 ) / ( 2.921666134462953 ) * ( 1 );
  } else if ( 587.262294360762 <= hole1 && hole1 < 590.190080328053 ) {
    position =  180  + (hole1 - 587.262294360762 ) / ( 2.9277859672910154 ) * ( 1 );
  } else if ( 590.190080328053 <= hole1 && hole1 < 593.118418494531 ) {
    position =  181  + (hole1 - 590.190080328053 ) / ( 2.9283381664780563 ) * ( 1 );
  } else if ( 593.118418494531 <= hole1 && hole1 < 596.041741226551 ) {
    position =  182  + (hole1 - 593.118418494531 ) / ( 2.923322732019983 ) * ( 1 );
  } else if ( 596.041741226551 <= hole1 && hole1 < 598.954480890465 ) {
    position =  183  + (hole1 - 596.041741226551 ) / ( 2.9127396639139533 ) * ( 1 );
  } else if ( 598.954480890465 <= hole1 && hole1 < 601.851069852633 ) {
    position =  184  + (hole1 - 598.954480890465 ) / ( 2.896588962168039 ) * ( 1 );
  } else if ( 601.851069852633 <= hole1 && hole1 < 604.725940479419 ) {
    position =  185  + (hole1 - 601.851069852633 ) / ( 2.8748706267859916 ) * ( 1 );
  } else if ( 604.725940479419 <= hole1 && hole1 < 607.573525137172 ) {
    position =  186  + (hole1 - 604.725940479419 ) / ( 2.847584657753032 ) * ( 1 );
  } else if ( 607.573525137172 <= hole1 && hole1 < 610.388256192249 ) {
    position =  187  + (hole1 - 607.573525137172 ) / ( 2.8147310550768907 ) * ( 1 );
  } else if ( 610.388256192249 <= hole1 && hole1 < 613.164566011006 ) {
    position =  188  + (hole1 - 610.388256192249 ) / ( 2.7763098187569994 ) * ( 1 );
  } else if ( 613.164566011006 <= hole1 && hole1 < 615.896886959798 ) {
    position =  189  + (hole1 - 613.164566011006 ) / ( 2.7323209487921076 ) * ( 1 );
  } else if ( 615.896886959798 <= hole1 && hole1 < 618.579651404993 ) {
    position =  190  + (hole1 - 615.896886959798 ) / ( 2.682764445194948 ) * ( 1 );
  } else if ( 618.579651404993 <= hole1 && hole1 < 621.207291712935 ) {
    position =  191  + (hole1 - 618.579651404993 ) / ( 2.627640307941988 ) * ( 1 );
  } else if ( 621.207291712935 <= hole1 && hole1 < 623.774240249984 ) {
    position =  192  + (hole1 - 621.207291712935 ) / ( 2.5669485370490293 ) * ( 1 );
  } else if ( 623.774240249984 <= hole1 && hole1 < 626.274929382502 ) {
    position =  193  + (hole1 - 623.774240249984 ) / ( 2.500689132518005 ) * ( 1 );
  } else if ( 626.274929382502 <= hole1 && hole1 < 628.703791476839 ) {
    position =  194  + (hole1 - 626.274929382502 ) / ( 2.428862094336978 ) * ( 1 );
  } else if ( 628.703791476839 <= hole1 && hole1 < 631.055258899352 ) {
    position =  195  + (hole1 - 628.703791476839 ) / ( 2.3514674225129966 ) * ( 1 );
  } else if ( 631.055258899352 <= hole1 && hole1 < 633.323764016406 ) {
    position =  196  + (hole1 - 631.055258899352 ) / ( 2.268505117054019 ) * ( 1 );
  } else if ( 633.323764016406 <= hole1 && hole1 < 635.50373919435 ) {
    position =  197  + (hole1 - 633.323764016406 ) / ( 2.1799751779440157 ) * ( 1 );
  } else if ( 635.50373919435 <= hole1 && hole1 < 637.58961679954 ) {
    position =  198  + (hole1 - 635.50373919435 ) / ( 2.0858776051899213 ) * ( 1 );
  } else if ( 637.58961679954 <= hole1 && hole1 < 639.575829198338 ) {
    position =  199  + (hole1 - 637.58961679954 ) / ( 1.9862123987981022 ) * ( 1 );
  } else if ( 639.575829198338 <= hole1 && hole1 < 641.456808757093 ) {
    position =  200  + (hole1 - 639.575829198338 ) / ( 1.880979558754916 ) * ( 1 );
  } else if ( 641.456808757093 <= hole1 && hole1 < 643.22698784217 ) {
    position =  201  + (hole1 - 641.456808757093 ) / ( 1.7701790850770749 ) * ( 1 );
  }else{

    
    return 0;
  }
  return position;
}
```
###python
Arduinoのif文を並べるためのコード

```python
import csv

path = 'hole1_hokan.txt'

f=open(path)
#s = f.readlines()
#print(type(s))

reader = csv.reader(f,delimiter='\t')


count = 0

"""
void serch_hole(float hole1){
    float position=0;
    if(a1 <= hole1 && hole1 < a2){
        position = angle1 +(hole1-a1)/(a2-a1)*(angle_a2 - angle_a1); //内分して得られる数値          
    }else 
    //同様に並べる

}

"""


for row in reader:
    #print( int(row[0]),'test' ,float(row[2]))
    val2 = float(row[2])
    angle2 = int(row[0])
    if count == 0:
        count =1;
        angle1 = int(row[0])
        val1 = float(row[2])
        next       
    
    count = count + 1
    print("}else if(",val1,"<= hole1 && hole1 <",val2,"){")
    print("position = ",angle1," +(hole1-",val1,")/(",val2-val1,")*(",angle2-angle1,"); ")

    angle1 = angle2
    val1 = val2
f.close()
```



![P_20201126_152740.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/e54f503d-4f00-e23b-f7ca-5f436a129d25.jpeg)