2015年9月23日 星期三

Arduino 數學函數測試

Arduino 的大部分應用都只用到簡單的四則運算, 其中比較值得注意的是做整數的除法時, 其結果若為浮點數, 必須將其宣告為 float, 而且運算元必須至少有一個是以浮點數表示, 例如下面的的指令將得到 0.00 而非 0.50 :

float a=1/2;    //a=0.00

欲得到正確的運算結果 0.50 必須這樣 :

float a=1.0/2;    //至少一個運算元是用浮點數表示
float a=1/2.0;    //至少一個運算元是用浮點數表示
float a=(float)1/2;    //被除數強制轉型為浮點數

複雜的數學運算例如三角函數, 指數或對數等超越函數就要用到 Arduino C 語言內建的數學函數, 參考官方文件中的 Math 與 Trigonometry 部分 :

https://www.arduino.cc/en/Reference/HomePage

Arduino 官方文件中的數學函數列表 (含三角函數) :

 數學函數 說明
 abs(x) 傳回 x 的絕對值
 min(x,y) 傳回 x 與 y 兩數中值較小者
 max(x,y) 傳回 x 與 y 兩數中值較大者
 constrain(x,min,max) 以 max, min 為上下限, 傳回 x 值
 pow(x,y) 傳回 x 的 y 次方
 sqrt(x)  傳回 x 的平方根
 floor(x) 傳回不大於 x 的最大整數
 ceil(x) 傳回不小於 x 的最小整數
 sin(x) 傳回 x 的正弦值
 cos(x) 傳回 x 的餘弦值
 tan(x) 傳回 x 的正切值
 random(max)  傳回 0~(max-1) 間的隨機數
 random(min,max) 傳回 min~(max-1) 間的隨機數
 randomSeed(x) 啟始一個假隨機數產生器, x 為整數

除了官網的所列的數學函數外, 其實 C 語言的其他數學函數也可以用, 例如自然對數 log, 常用對數 log10, 雙曲函數 sinh, cosh, tanh 等 (但 fmod 不行), 參考 :

C 教材:數學函式庫

 其他數學函數 說明
 fabs(x) 傳回浮點數 x 的絕對值
 asin(x) 傳回 x 的反正弦值 sin-1 
 acos(x) 傳回 x 的反餘弦值 cos-1
 atan(x)  傳回 x 的反正切值 tan-1
 sinh(x)  傳回 x 的雙曲正弦值 (ex-e-x)/2
 cosh(x) 傳回 x 的雙曲餘弦值 (ex+e-x)/2
 tanh(x)  傳回 x 的雙曲正切值 sinh(x)/cosh(x)
 exp(x) 傳回 x 的自然指數值 ex
 log(x)  傳回 x 的自然對數值 ln(x)
 log10(x)  傳回 x 的常用對數值 log10(x)
 ldexp(x,n) 傳回 x 與 2 的 n 次方乘積 x*2n

另外, AVR 晶片系列也定義了一些常用的數學常數 (Macro, 巨集), 都可以在 Arduino 程式中使用, 參考 :

# http://www.nongnu.org/avr-libc/user-manual/group__avr__math.html
http://wiki.secondlife.com/wiki/RAD_TO_DEG
http://wiki.secondlife.com/wiki/DEG_TO_RAD

數學常數 說明
 M_E  e=2.7182818284590452354 (自然指數)
 M_LOG2E log2e=1.4426950408889634074 (自然指數以 2 為底之對數)
 M_LOG10E log10e=0.43429448190325182765 (自然指數的常用對數)
 M_LN2 ln2=0.69314718055994530942 (2 的自然對數)
 M_LN10 ln10=2.30258509299404568402 (10 的自然對數)
 M_PI PI=3.14159265358979323846 (圓周率)
 M_PI_2 PI/2=1.57079632679489661923 (二分之一的圓周率)
 M_PI_4 PI/4=0.78539816339744830962 (四分之一的圓周率)
 M_1_PI 1/PI=0.31830988618379067154 (圓周率的倒數)
 M_2_PI 2/PI=0.63661977236758134308 (兩倍的圓周率倒數)
 M_2_SQRTPI 2/sqrt(PI)=1.12837916709551257390 (2 除以圓周率開平方)
 M_SQRT2 sqrt(2)=1.41421356237309504880 (2 開平方)
 M_SQRT1_2 1/sqrt(2)=0.70710678118654752440 (2 開平方的倒數)
 DEG_TO_RAD 角度*PI/180=0.0174532925199432958 (角度轉弧度常數)
 RAD_TO_DEG 弧度*180/PI=57.2957795130823208768 (弧度轉角度常數)
 NAN 非數字=nan
 INFINITY 無限大=inf

測試範例 (使用 Nano) :

void setup() {
  Serial.begin(9600);
  Serial.println(abs(-1));  //輸出 1
  Serial.println(abs(-1.235));  //輸出 1.23
  Serial.println(fabs(-1.235));  //輸出 1.23
  Serial.println(min(-1,-3));  //輸出 -3
  Serial.println(max(-1,1));  //輸出 1
  Serial.println(constrain(101,100,200));  //輸出 100
  Serial.println(constrain(150,100,200));  //輸出 150
  Serial.println(constrain(201,100,200));  //輸出 200
  Serial.println(constrain(201,100,200));  //輸出 200
  Serial.println(pow(2,3));  //輸出 8.00
  Serial.println(sqrt(2));  //輸出 1.41
  Serial.println(floor(1.9));  //輸出 1.00
  Serial.println(floor(-1.1));  //輸出 -2.00
  Serial.println(ceil(1.9));  //輸出 2.00
  Serial.println(ceil(-1.1));  //輸出 -1.00
  float deg=30;
  float rad=deg*PI/180;
  Serial.println(sin(rad));  //輸出 0.50
  Serial.println(sin(deg*DEG_TO_RAD));  //輸出 0.50
  Serial.println(cos(rad));  //輸出 0.87
  Serial.println(cos(deg*DEG_TO_RAD));  //輸出 0.87
  Serial.println(tan(deg*DEG_TO_RAD));  //輸出 0.58
  //轉回角度
  Serial.println(asin(0.5)*RAD_TO_DEG);  //輸出 30.00
  Serial.println(acos(0.87)*RAD_TO_DEG);  //輸出 29.54
  Serial.println(atan(0.58)*RAD_TO_DEG);  //輸出 30.11
  Serial.println(atan2(5.8,10)*RAD_TO_DEG);  //輸出 30.11
  Serial.println(sinh(1));  //輸出 1.18
  Serial.println(cosh(1));  //輸出 1.54
  Serial.println(tanh(1));  //輸出 0.76
  Serial.println(exp(1));  //輸出 2.72
  Serial.println(log(2));  //輸出 0.69
  Serial.println(log10(100));  //輸出 2.00
  Serial.println(ldexp(2,3));  //輸出 16.00
  Serial.println(M_E,19);  //輸出 2.7182817459106445312
  Serial.println(M_LOG2E,19);  //輸出 1.4426950454711914062
  Serial.println(M_LOG10E,19);  //輸出 0.4342945098876953125
  Serial.println(M_LN2,19);  //輸出 0.6931471824645996093
  Serial.println(M_LN10,19);  //輸出 2.3025851249694824218
  Serial.println(M_PI,19);  //輸出 3.1415927410125732421
  Serial.println(M_PI_2,19);  //輸出 1.5707963943481445312
  Serial.println(M_PI_4,19);  //輸出 0.7853981971740722656
  Serial.println(M_1_PI,19);  //輸出 0.3183098793029785156
  Serial.println(M_2_PI,19);  //輸出 0.6366197586059570312
  Serial.println(M_2_SQRTPI,19);  //輸出 1.1283792257308959960
  Serial.println(M_SQRT2,19);  //輸出 1.4142135620117187500
  Serial.println(M_SQRT1_2,19);  //輸出 0.7071067810058593750
  Serial.println(DEG_TO_RAD,19);  //輸出 0.0174532914161682128
  Serial.println(RAD_TO_DEG,19);  //輸出 57.2957801818847656250
  Serial.println(NAN);  //輸出 nan
  Serial.println(INFINITY);  //輸出 inf
  Serial.println(random(10));  //輸出 0~10 間之隨機數
  Serial.println(random(100,200));  //輸出 100~200 間之隨機數
  randomSeed(1023);
  }
void loop() {
  Serial.println(random(10));  //輸出 0~10 間之隨機數
  delay(1000);
  }

結果似乎在小數點後第五位就有誤差了 (雖然不需要這麼精確), 即使是常數也是如此, 不知是何原因?

關於超越函數詳參 :

維基 : 三角函數
維基 : 指數函數
維基 : 對數
維基 : 自然對數
維基 : 雙曲函數
維基 : 反雙曲函數


沒有留言:

張貼留言