在嵌入式設備中用多項式快速計算三角函數和方根

来源:https://www.cnblogs.com/milton/p/18049551
-Advertisement-
Play Games

在 MCS-51, Cortex M0, M3 之類的晶元上編程時, 能使用的資源是非常有限, 通常只有兩位數KB的Flash, 個位數KB的RAM. 如果要使用三角函數和開方就要引入 math.h, 會消耗掉10KB以上的Flash空間. 在很多情況下受硬體資源限制無法使用 math.h, 這時候... ...


慣性感測器的傾角計算要用到三角函數.

在 MCS-51, Cortex M0, M3 之類的晶元上編程時, 能使用的資源是非常有限, 通常只有兩位數KB的Flash, 個位數KB的RAM. 如果要使用三角函數和開方就要引入 math.h, 會消耗掉10KB以上的Flash空間. 在很多情況下受硬體資源限制無法使用 math.h, 這時候使用簡化的方法進行三角函數和開方運算就非常有意義, OlliW's Bastelseiten在2014年的一篇文章里, 提供了幾個實用的計算方法. 下麵介紹其計算方法和代碼實現.

快速正弦餘弦(Sin, Cos)計算

將角度 \(x \in [0, \frac{\pi}{2}]\)通過下麵的式子轉換到 $ \alpha \in [-\frac{1}{2}, \frac{1}{2}]$ 區間

\[\alpha = \frac{2}{\pi} x - \frac{1}{2} \]

於是, 對應 \(\alpha\) 的多項式近似計算為

\[\sin\alpha = a_0 - b_1\alpha + a_2\alpha^2 - b_3\alpha^3 + a_4\alpha^4 - b_5\alpha^5 + a_6\alpha^6 \\ \cos\alpha = a_0 + b_1\alpha + a_2\alpha^2 + b_3\alpha^3 + a_4\alpha^4 + b_5\alpha^5 + a_6\alpha^6 \]

如果將上面的符號固定項和變化項分成\(A\)\(B\)兩部分

\[A = a_0 + a_2\alpha^2 + a_4\alpha^4 + a_6\alpha^6 \\ B = b_1\alpha + b_3\alpha^3 + b_5\alpha^5 \]

\(\sin\alpha\)\(\cos\alpha\) 可以通過 A 和 B 的值表達

\[\sin\alpha = A - B \\ \cos\alpha = A + B \]

對應的各項繫數值

\(a_0 = 0.707106781187 \\ a_2 = -0.872348075361 \\ a_4 = 0.179251759526 \\ a_6 = -0.0142718282624 \\ \\ b_1 = -1.110670322264 \\ b_3 = 0.4561589075945 \\ b_5 = -0.0539104694791\)

使用上面的計算方式, 結果絕對誤差小於\(6.5 \times 10^{-6}\), 並且 \(\cos^2 x + \sin^2 x\) 不會超過 1. 計算過程只需要7次乘法和7次加法.

C語言實現

const   float coeff[7] = {
  // a0 ~ a6           b1 ~ b5
   0.707106781187,  -1.110670322264,
  -0.872348075361,   0.4561589075945,
   0.179251759526,  -0.0539104694791,
  -0.0142718282624
};

/**
 * @param alpha: value between 0 and 0.5
*/
void sincos_normalized(float alpha, float *sin, float *cos)
{
  int i;
  float alpha_exp = 1.0, part_a = 0, part_b = 0;

  for (i = 0; i < 7; i++)
  {
     if (i % 2 == 0)
     {
        part_a = part_a + (coeff[i] * alpha_exp);
     }
     else
     {
        part_b = part_b + (coeff[i] * alpha_exp);
     }
     alpha_exp = alpha_exp * alpha;
  }
  *sin = part_a - part_b;
  *cos = part_a + part_b;
}

float calculate(float degree_in)
{
  int quadrant, multi;
  float degree = degree_in, alpha, cos, sin, c, s;

  multi = (int)(degree / 90.0);
  degree = degree - (multi * 90.0);
  alpha = (degree / 90) - 0.5;
  sincos_normalized(alpha, &s, &c);
  multi = multi % 4;
  if (multi == 0)
  {
    sin = s;
    cos = c;
  }
  else if (multi == 1)
  {
    sin = c;
    cos = -s;
  }
  else if (multi == 2)
  {
    sin = -s;
    cos = -c;
  }
  else if (multi == 3)
  {
    sin = -c;
    cos = s;
  }
  printf("d_in:%5.0f d:%5.0f a:%10.5f  sin:%10.5f  cos:%10.5f\r\n", degree_in, degree, alpha, sin, cos);
}

計算的結果和 math.h 的 sin cos 函數對比, 數值幾乎一樣, 僅在個別數值的小數點後第五位會有\(\pm1\)的差異.

平方根倒數計算

對於1附近的數值, 平方根倒數可以使用牛頓迭代法計算, 實際上非常簡單,因為它只涉及加法和乘法,而不涉及除法, 對於 \(x \in [0.6, 1.4]\), 計算式為

\[y_0 = 1 \\ y_{n+1} = y_n (1.5 - 0.5 x {y_n}^2) \\ \]

計算兩次牛頓迭代需要3次乘法, 而二階泰勒級數只需要2次, 但是牛頓迭代法精度更高, 甚至比三階泰勒級數的精度更高. 如果執行三次牛頓迭代則需要6次乘法, 在\(0.6 < x < 1.4\)的範圍內結果精度優於\(1 \times 10^{-4}\), 註意\(x\)的取值範圍, 因為近似是以1為中心展開的, 所以離1越遠差異越大, 在這個範圍之外例如\(x = 0.5\)的時候, 三次迭代就達不到這個精度. 在實際應用中, 可以將要計算的數值提一個繫數轉換到 \(x \in [0.6, 1.4]\)區間進行計算.

C語言實現

float inverse_sqrt(int interates, float x)
{
  float y;

  y = 1.5 - (x / 2);
  while (interates--)
  {
    y = y * (1.5 - 0.5 * x * y * y);
  }
  return y;
}

// 使用 0.5 ~ 2.1 之間的數字測試, 分別迭代5次
int main(int argc, char *const argv[])
{
  int i, j;
  for (i = 0; i < 17; i++)
  {
    printf("%4.1f ", i*0.1 + 0.5);
    for (j = 0; j < 5; j++)
    {
      printf("%11.9f ", inverse_sqrt(j, i*0.1 + 0.5));
    }
    printf("\r\n");
  }
  return 0;
}

快速反正弦(Arcsin)計算

原文最終選擇的是多項式近似, 避免了取絕對值等複雜處理, 只是在 \(x = \pm 1\) 附近的絕對精度較差, 輸出值規範化為 \(\pi\),即定義 \(\arcsin(x) = \pi \times asin(x)\). 計算式為

\[asin(x) = \frac{x}{2} \times \frac{a_0 + a_2x^2 + a_4x^4 + a_6x^6}{b_0 + b_2x^2 + b_4x^4 + b_6x^6} \]

對應的繫數數值為
\(a_0 = 0.318309886 \\ a_2 = -0.5182875 \\ a_4 = 0.222375 \\ a_6 = -0.016850156 \\ \\ b_0 = 0.5 \\ b_2 = -0.89745875 \\ b_4 = 0.46138125 \\ b_6 = -0.058377188\)

\(|x|<0.75\)時, 絕對誤差小於 \(1 \times 10^{-5}\), 當 \(|x|<0.91\)時, 絕對誤差小於 \(4.2 \times 10^{-5}\), 在 \(x \approx 0.997\)時, 最大誤差為 \(0.011\).

C語言實現

const float coeffa[4] = {
  // a0 ~ a6
   0.318309886,
  -0.5182875,
   0.222375,
  -0.016850156
};

const float coeffb[4] = {
  0.5,
  -0.89745875,
  0.46138125,
  -0.058377188
};

const float pi = 3.14159265358979;

float arcsin(float x)
{
  int i;
  float x2 = 1, a = 0, b = 0;

  for (i = 0; i < 4; i ++)
  {
    a = a + coeffa[i] * x2;
    b = b + coeffb[i] * x2;
    x2 = x2 * x * x;
  }
  return (x * pi / 2) * (a / b);
}

int main(int argc, char *const argv[])
{
  int i;
  float x, alpha, expect;
  for (i = 0; i < 20; i++)
  {
    x = 0.01 + (i * 0.05);
    alpha = arcsin(x);
    expect= asin(x);
    printf("x:%4.2f  a:%9.6f e:%9.6f\r\n", x, alpha, expect);
  }
}

計算結果, 最右側一列為 math.h 的 asin() 函數, 用於對比

x:0.01  a: 0.010000 e: 0.010000
x:0.06  a: 0.060036 e: 0.060036
x:0.11  a: 0.110223 e: 0.110223
x:0.16  a: 0.160691 e: 0.160691
x:0.21  a: 0.211575 e: 0.211575
x:0.26  a: 0.263022 e: 0.263022
x:0.31  a: 0.315193 e: 0.315193
x:0.36  a: 0.368268 e: 0.368268
x:0.41  a: 0.422454 e: 0.422454
x:0.46  a: 0.477996 e: 0.477995
x:0.51  a: 0.535185 e: 0.535185
x:0.56  a: 0.594386 e: 0.594386
x:0.61  a: 0.656060 e: 0.656061
x:0.66  a: 0.720815 e: 0.720819
x:0.71  a: 0.789485 e: 0.789498
x:0.76  a: 0.863278 e: 0.863313
x:0.81  a: 0.944073 e: 0.944152
x:0.86  a: 1.035139 e: 1.035270
x:0.91  a: 1.143404 e: 1.143284
x:0.96  a: 1.291451 e: 1.287002

將0.9附近細分一下

x:0.90  a: 1.119760 e: 1.119769
x:0.91  a: 1.143404 e: 1.143284
x:0.92  a: 1.168431 e: 1.168081
x:0.93  a: 1.195150 e: 1.194413
x:0.94  a: 1.224027 e: 1.222630
x:0.95  a: 1.255752 e: 1.253236
x:0.96  a: 1.291451 e: 1.287002
x:0.97  a: 1.333107 e: 1.325231
x:0.98  a: 1.384628 e: 1.370462
x:0.99  a: 1.455034 e: 1.429257

\(0 < x < 0.6\)區間的精度最高, 在\(0.6 < x < 0.9\)區間結果數值偏小, 在\(0.9 < x < 1\)區間結果數值偏大. 越接近1精度越差, 實際使用時在大於\(0.97\)時需要做一些補償.

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述:在WPF中,通過EventHandler可實現基礎和高級的UI更新方式。基礎用法涉及在類中定義事件,併在UI中訂閱以執行更新操作。高級用法藉助Dispatcher類,確保在非UI線程上執行操作後,通過UI線程更新界面。這兩種方法提供了靈活而可靠的UI更新機制。 在WPF(Windows Pre ...
  • 是在MVVM中用來傳遞消息的一種方式。它是在MVVMLight框架中提供的一個實現了IMessenger介面的類,可以用來在ViewModel之間、ViewModel和View之間傳遞消息。 Send 接受一個泛型參數,表示要發送的消息內容。 Register 方法用於註冊某個對象接收消息。 pub ...
  • 在本篇教程中,我們學習瞭如何在 Taurus.MVC WebMVC 中進行數據綁定操作。我們還學習瞭如何使用 ${屬性名稱} CMS 語法來綁定頁面上的元素與 Model 中的屬性。通過這些步驟,我們成功實現了一個簡單的數據綁定示例。 ...
  • 我們在《SqlSugar開發框架》中,Winform界面開發部分往往也用到了自定義的用戶控制項,對應一些特殊的界面或者常用到的一些局部界面內容,我們可以使用自定義的用戶控制項來提高界面的統一性,同時也增強了使用的便利性。如我們Winform界面中用到的分頁控制項、附件顯示內容、以及一些公司、部門、菜單的下... ...
  • 在C#中使用SQL Server實現事務的ACID(原子性、一致性、隔離性、持久性)屬性和使用資料庫鎖(悲觀鎖和樂觀鎖)時,你可以通過ADO.NET的SqlConnection和SqlTransaction類來實現。下麵是一些示例和概念說明。 實現ACID事務 ACID屬性是事務處理的四個基本特征, ...
  • 實驗介紹: apache本身只能發佈靜態網站,而添加了php模塊就可以發佈動態網站 一:下載php 進入php官方網址https://www.php.net/ 點擊進入windows版本 下載thread safe(線程安全版),點擊zip 二:安裝php模塊 將php解壓到一個文件夾 複製php中 ...
  • CentOS 設置系統時間與網路時間同步 一、Linux的時間分為(兩種) System Clock(系統時間) 指當前Linux Kernel中的時間 Real Time Clock (硬體時間,簡稱RTC) 主板上有電池供電的時間 二、查看系統時間的命令 系統時間指令:# date 設置系統時間 ...
  • 實驗介紹: apache(阿帕奇)是最流行的web伺服器端軟體 一:下載apache伺服器 1進入官網https://httpd.apache.org/download.cgi 選擇最新版本 2選擇windows進行下載 3繼續點擊 4有64位的和32位的進行選擇 二:安裝apache伺服器 1可以 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...