ジョイスティックマウスのカーソルを滑らかに動かしたい

スポンサーリンク
スポンサーリンク

ジョイスティックマウス

滑らかにしたい。したいので調べたことを書いてく。

スポンサーリンク

ジョイスティックの傾き

左から静止状態、X軸が0に変わるギリギリの位置、止まるまで目一杯動かした時の位置。意識的には右の位置で0に変わってるように思うけど、実際は真ん中の位置で0になる。22°ぐらいで0、でも39°ぐらいまでは動く。この差が理想と現実の差を生んでるように思う。ストッパーをつけるとか、値を伸張するとか?ストッパーは3Dプリンタとか使わないとだし、値は伸長したところで22°ー39°間は値が動かなくなるので意味無い気もする。

スポンサーリンク

ジョイスティックの可変抵抗の特性

ボリューム | マルツオンライン

可変抵抗には特性があるそうで、ABCカーブの違いがあるみたい。じゃあこのスティックに使われている可変抵抗の特性はなんじゃらほいと思ったけど、調べるとしたら分解して抵抗を調べるか、角度ごとに値のサンプルを取ってカーブの近似を求めるとかしないといけないと思うのですが、正直できる気がしないし気が引けるのでやりたくない。ここはBカーブであるとの前提のもとに動いてはどうか。どうなんでしょうか。

スポンサーリンク

C++のint-long型の端数処理

map()内部で除算があり、analogRead()はint型で、map()はlong型なのでどちらも整数型だから端数が出たときの丸め方で数値がずれるんじゃないかと思った。実験してみる。

void setup() {
  Serial.begin(9600);
  delay(1000);
  for (int i = 0 ; i <= 1023; i++) {
    Serial.print(map(i, 0, 1023, -3, 3));
    Serial.println();
  }
}

void loop() {
}

こんなコードを書きましてー、出てきた数字をエクセルさんで計算した結果と比較してみる。長いので変化した部分だけ。

-3≦out≦3の場合

rawmap
(excel)
map
(arduino)
roundup
(excel)
rounddown
(excel)
0-3-3-3-3
1-2.994134897-3-3-2
2-2.988269795-3-3-2
169-2.008797654-3-3-2
170-2.002932551-3-3-2
171-1.997067449-2-2-1
172-1.991202346-2-2-1
339-1.011730205-2-2-1
340-1.005865103-2-2-1
341-1-1-1-1
342-0.994134897-1-10
343-0.988269795-1-10
510-0.008797654-1-10
511-0.002932551-1-10
5120.002932551010
5130.008797654010
6800.988269795010
6810.994134897010
6821111
6831.005865103121
6841.011730205121
8511.991202346121
8521.997067449121
8532.002932551232
8542.008797654232
10212.988269795232
10222.994134897232
10233333

-12≦out≦12の場合

グラフの引用は上と同じとこだけ。

rawmap
(excel)
map
(arduino)
roundup
(excel)
rounddown
(excel)
0-12-12-12-12
1-11.97653959-12-12-11
2-11.95307918-12-12-11
169-8.035190616-9-9-8
170-8.011730205-9-9-8
171-7.988269795-8-8-7
172-7.964809384-8-8-7
339-4.046920821-5-5-4
340-4.023460411-5-5-4
341-4-4-4-4
342-3.976539589-4-4-3
343-3.953079179-4-4-3
510-0.035190616-1-10
511-0.011730205-1-10
5120.011730205010
5130.035190616010
6803.953079179343
6813.976539589343
6824444
6834.023460411454
6844.046920821454
8517.964809384787
8527.988269795787
8538.011730205898
8548.035190616898
102111.95307918111211
102211.97653959111211
102312121212

うーん。エクセルも癖があって、はっきりとは言えないけど。負の数の場合、エクセルでいうROUNDUPで正の数だとROUNDDOWNと一致する。ただ、Arduinoの方がきれいに階段状になっていて、これはエクセルがアレなのかな?でもまぁ、510-513あたりを見るとほんの少しだけど、Arduinoのmap()は正側に値が振れているような気がする。感覚的には、ROUNDDOWNが近いかなぁと思った。map()のリファレンスには

The map() function uses integer math so will not generate fractions, when the math might indicate that it should do so. Fractional remainders are truncated, and are not rounded or averaged.

Notes & Warnings

As previously mentioned, the map() function uses integer math. So fractions might get suppressed due to this. For example, fractions like 3/2, 4/3, 5/4 will all be returned as 1 from the map() function, despite their different actual values. So if your project requires precise calculations (e.g. voltage accurate to 3 decimal places), please consider avoiding map() and implementing the calculations manually in your code yourself.

ていうことなので、自分でやれってことかぁ。うーんうーん。

スポンサーリンク

直線のグラフを曲線に変換

ハードウェアの電圧からAnalogRead()で数値を取得するところの特性は分からないものの、取得した数値は0-1023で変化する直線のグラフになる。で、等速直線運動っていうのは人間の感覚と合ってないらしい。

AutoExe:貴島ゼミナール

最初は徐々に速度が上がっていって、時間が経つと速度の上がり方が急になる、みたいな。そういうのが人間の感覚に合ってるみたい。だから、0-1023の値をそういう感じに変化させることができれば滑らか感を得られるのではと思った。で、そういう変換、直線のグラフにカーブをかけるような変換はどういうのがあるんだろうと調べてみた。イメージとしては

こういう感じ。考え方合ってんのか不安になってきた。どうなんだろ。

y=x^3

y=x^3 - Wolfram|Alpha
Wolfram|Alphaは,あらゆる職種,あらゆる教育レベルの,できるだけ広い範囲の人々に,専門家レベルの知識と機能をお届けします.

x^3のグラフがそれっぽい。とりあえずエクセルで0-1023に合うように試してみる。

  1. 0-1023を、-1から1に変換する。
    • =((A2-0)/(1023-0))*(1-(-1))+(-1)
  2. 1の値を3乗する。
    • =POWER(B2,3)
  3. 2の値を0-1023に変換する。
    • =((C2-(-1))/(1-(-1)))*(1023-0)

するとこんな感じのグラフになった。

よさげな気がする。3のとこでMouse.moveで使う範囲に変換してもいいかな。

シグモイド関数

探してたらシグモイド関数というのを見つけた。

シグモイド関数 - Wikipedia

ここの標準シグモイド関数っていうのがそれっぽい。けど0.5付近で急になるのじゃなくてそこは潰れてほしい。てことは2つ重ねりゃいいんじゃね?

CC BY-SA 3.0, Link

こんな感じに。やってみよー。

ゲインが1の場合(a=1)

グラフ見るとxが-6から6の区間で、yが0-1でいい感じに変化してるみたいなのでそんな感じにする。

  1. 0-511・512-1023の2区間に分けて、それぞれ(-6)-6に変換
    • =((A2-0)/(511-0))*(6-(-6))+(-6)
    • =((A514-512)/(1023-512))*(6-(-6))+(-6)
  2. 1-1をシグモイド関数で変換して、(-1)-0の区間にするため-1する。1-2は変換するだけ。
    • =((TANH(B2/2)+1)/2)-1
    • =(TANH(B515/2)+1)/2
  3. 目的の区間に変換。ここでは0-1。
    • =(C2-(-1))/(1-(-1))

でけた。

ゲインが5の場合(a=5)

ゲインを5にすると、xが(-1)-1の時に、yが0-1になるっぽく、きれいなのでこっちでもやってみる。上記の流れをちょっと変えるだけ。

  1. (-1)-1に変換
    • =((A2-0)/(511-0))*(1-(-1))+(-1)
    • =((A514-512)/(1023-512))*(1-(-1))+(-1)
  2. シグモイド関数で変換
    • =((TANH((B2*5)/2)+1)/2)-1
    • =(TANH((B514*5)/2)+1)/2
  3. 0-1の区間に変換
    • =(C514-(-1))/(1-(-1))

できた。

違い

gif動画

a=1とa=5の場合だと曲がりがちょっとだけ違う。どうがいいんだろな。

これさ

数式を1行にまとめると

=(((((TANH(((((A2-0)/(511-0))*(1-(-1))+(-1))*5)/2)+1)/2)-1)-(-1))/(1-(-1)))*(1-0)

こんな長くなる。どうやってコード書けばいいんだ。

コードにしてみる

と言っててもどうにもならんので、愚直にコードに起こしてみる。最終的にMouse.moveで使う値になるように。Arduinoのリファレンスに即して-12から12の範囲になるようにしたい。

y=x^3

double tmp = 0;
signed char result = 0;

double mapf(float in , float in_min, float in_max, float out_min, float out_max) {
  return ((in - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min;
}

void setup() {
  Serial.begin(9600);
  delay(1000);

  for (int i = 0 ; i <= 1023; i++) {

    tmp = mapf(i, 0, 1023, -1, 1);
    tmp = pow(tmp, 3);
    tmp = mapf(tmp, -1, 1, -12, 12);
    result = (signed char) tmp;

    Serial.println(result);
  }
}

void loop() {
}

こんなコードを書きまして、結果が

おおう、だいぶ中央が潰れてしまった。これはどうじゃろ…、ちょっとtmp = pow(tmp, 3)のとこをtmp = pow(tmp, 3) + tmpにしてみる。

こんな感じかなぁ…。

シグモイド関数

#include <math.h>

double tmp = 0;
signed char result = 0;

double mapf(float in , float in_min, float in_max, float out_min, float out_max) {
  return ((in - in_min) / (in_max - in_min)) * (out_max - out_min) + out_min;
}

void setup() {
  Mouse.begin();
  Serial.begin(9600);
  delay(1000);

  for (int i = 0 ; i <= 1023; i++) {
    if ( i <= 511) {
      tmp = mapf(i, 0, 511, -6, 6);
      tmp = ((tanh(tmp / 2) + 1) / 2) - 1;
      
    } else if ( i >= 512) {
      tmp = mapf(i, 512, 1023, -6, 6);
      tmp = (tanh(tmp / 2) + 1) / 2;
      
    }

    tmp = mapf(tmp, -1, 1, -12, 12);
    result = (signed char) tmp;

    Serial.println(result);
  }
}

void loop() {
}

ハイパボリックタンジェントという舌噛みそうな関数を使うために#include <math.h>する。あとは条件分岐して書いてみた。結果は

うーん、これも潰れてる気がするし、変化する部分は直線的になってるような気がする。とりあえずゲイン5でもやってみた結果が

若干変わった…ゲイン10でもやってみよ。0-1023からの変換は(-0.4)-4にしてみた。

なんか何がいいのか分かんねーな…。実際使ってみて、触り心地を確かめてみないと机上の空論だなぁ。

速度を計ってみる

いっぱい計算してるので、速度がちょっと気になる。計算が遅かったらジョイスティックを動かした後に、遅れてカーソルが動くとかになりそうだし。ただ基準が分からんよな。0-1023を100回繰り返した。

  • y=x^3+xの場合
    • 結果 millis()では0。micros()で300µs
  • シグモイド関数の場合
    • 結果 28,813ms

全然ちゃうなぁ。y=x^3+xの方がいいかもなぁ…。

コメント

タイトルとURLをコピーしました