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

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

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

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

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

α = 2 π x ? 1 2 \alpha = \frac{2}{\pi} x - \frac{1}{2} α=π2?x?21?

于是, 對應 α \alpha α 的多項式近似計算為
sin ? α = a 0 ? b 1 α + a 2 α 2 ? b 3 α 3 + a 4 α 4 ? b 5 α 5 + a 6 α 6 cos ? α = a 0 + b 1 α + a 2 α 2 + b 3 α 3 + a 4 α 4 + b 5 α 5 + a 6 α 6 \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 sinα=a0??b1?α+a2?α2?b3?α3+a4?α4?b5?α5+a6?α6cosα=a0?+b1?α+a2?α2+b3?α3+a4?α4+b5?α5+a6?α6
如果將上面的符號固定項和變化項分成 A A A B B B兩部分
A = a 0 + a 2 α 2 + a 4 α 4 + a 6 α 6 B = b 1 α + b 3 α 3 + b 5 α 5 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 A=a0?+a2?α2+a4?α4+a6?α6B=b1?α+b3?α3+b5?α5
sin ? α \sin\alpha sinα cos ? α \cos\alpha cosα 可以通過 A 和 B 的值表達
sin ? α = A ? B cos ? α = A + B \sin\alpha = A - B \\ \cos\alpha = A + B sinα=A?Bcosα=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 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 a0?=0.707106781187a2?=?0.872348075361a4?=0.179251759526a6?=?0.0142718282624b1?=?1.110670322264b3?=0.4561589075945b5?=?0.0539104694791

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

C語言實現

const   float coeff[7] = {// a0 ~ a6           b1 ~ b50.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 函數對比, 數值幾乎一樣, 僅在個別數值的小數點后第五位會有 ± 1 \pm1 ±1的差異.

平方根倒數計算

對于1附近的數值, 平方根倒數可以使用牛頓迭代法計算, 實際上非常簡單,因為它只涉及加法和乘法,而不涉及除法, 對于 x ∈ [ 0.6 , 1.4 ] x \in [0.6, 1.4] x[0.6,1.4], 計算式為
y 0 = 1 y n + 1 = y n ( 1.5 ? 0.5 x y n 2 ) y_0 = 1 \\ y_{n+1} = y_n (1.5 - 0.5 x {y_n}^2) \\ y0?=1yn+1?=yn?(1.5?0.5xyn?2)

計算兩次牛頓迭代需要3次乘法, 而二階泰勒級數只需要2次, 但是牛頓迭代法精度更高, 甚至比三階泰勒級數的精度更高. 如果執行三次牛頓迭代則需要6次乘法, 在 0.6 < x < 1.4 0.6 < x < 1.4 0.6<x<1.4的范圍內結果精度優于 1 × 1 0 ? 4 1 \times 10^{-4} 1×10?4, 注意 x x x的取值范圍, 因為近似是以1為中心展開的, 所以離1越遠差異越大, 在這個范圍之外例如 x = 0.5 x = 0.5 x=0.5的時候, 三次迭代就達不到這個精度. 在實際應用中, 可以將要計算的數值提一個系數轉換到 x ∈ [ 0.6 , 1.4 ] x \in [0.6, 1.4] x[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 = ± 1 x = \pm 1 x=±1 附近的絕對精度較差, 輸出值規范化為 π \pi π,即定義 arcsin ? ( x ) = π × a s i n ( x ) \arcsin(x) = \pi \times asin(x) arcsin(x)=π×asin(x). 計算式為
a s i n ( x ) = x 2 × a 0 + a 2 x 2 + a 4 x 4 + a 6 x 6 b 0 + b 2 x 2 + b 4 x 4 + b 6 x 6 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} asin(x)=2x?×b0?+b2?x2+b4?x4+b6?x6a0?+a2?x2+a4?x4+a6?x6?
對應的系數數值為
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 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 a0?=0.318309886a2?=?0.5182875a4?=0.222375a6?=?0.016850156b0?=0.5b2?=?0.89745875b4?=0.46138125b6?=?0.058377188

∣ x ∣ < 0.75 |x|<0.75 x<0.75時, 絕對誤差小于 1 × 1 0 ? 5 1 \times 10^{-5} 1×10?5, 當 ∣ x ∣ < 0.91 |x|<0.91 x<0.91時, 絕對誤差小于 4.2 × 1 0 ? 5 4.2 \times 10^{-5} 4.2×10?5, 在 x ≈ 0.997 x \approx 0.997 x0.997時, 最大誤差為 0.011 0.011 0.011.

C語言實現

const float coeffa[4] = {// a0 ~ a60.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 < x < 0.6 0<x<0.6區間的精度最高, 在 0.6 < x < 0.9 0.6 < x < 0.9 0.6<x<0.9區間結果數值偏小, 在 0.9 < x < 1 0.9 < x < 1 0.9<x<1區間結果數值偏大. 越接近1精度越差, 實際使用時在大于 0.97 0.97 0.97時需要做一些補償.

參考

  • 用多項式快速計算三角函數等 https://www.olliw.eu/2014/fast-functions/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/716331.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/716331.shtml
英文地址,請注明出處:http://en.pswp.cn/news/716331.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【 10X summary report】怎么看?詳細解讀筆記

報告內容 在開始正式的分析之前&#xff0c;需要查看在對齊和計數過程中生成的任何總結統計信息。下圖是由Cell Ranger工具創建的10X總結報告&#xff0c;在從10X scRNA-seq實驗生成計數矩陣時會生成。 The left half of the report describes sequencing and mapping statist…

賣wordpress網站模板的網站

WP模板牛 http://www.wpniu.com 上面有很多免費wordpress模板資源的網站&#xff0c;除了免費模板&#xff0c;還有付費模板。 My模板(我的模板) http://www.mymoban.com 老牌網站模板資源站&#xff0c;上面有wordpress模板、帝國CMS模板、WooCommerce模板可以直接免費下載…

Linux whois命令教程:查詢域名所有者信息(附案例詳解和注意事項)

Linux whois命令介紹 whois命令是一個用于查詢域名所有者信息的工具。它可以直接從命令行進行查詢&#xff0c;這對于沒有圖形用戶界面的系統或者需要在shell腳本中進行查詢的情況非常有用。 Linux whois命令適用的Linux版本 whois命令在大多數Linux發行版中都可以使用&…

C++之stack

1、stack簡介 stack是實現的一個先進后出&#xff0c;后進先出的容器。它只有一個出口&#xff0c;只能操作最頂端元素。 2、stack庫函數 &#xff08;1&#xff09;push() //向棧壓入一個元素 &#xff08;2&#xff09;pop() //移除棧頂元素 &#xff08;3…

基于springboot+vue的中國陜西民俗網

博主主頁&#xff1a;貓頭鷹源碼 博主簡介&#xff1a;Java領域優質創作者、CSDN博客專家、阿里云專家博主、公司架構師、全網粉絲5萬、專注Java技術領域和畢業設計項目實戰&#xff0c;歡迎高校老師\講師\同行交流合作 ?主要內容&#xff1a;畢業設計(Javaweb項目|小程序|Pyt…

在 Angular 中使用 Renderer2

Renderer2 類 Renderer2 類是 Angular 提供的一個抽象服務&#xff0c;允許在不直接操作 DOM 的情況下操縱應用程序的元素。這是推薦的方法&#xff0c;因為它使得更容易開發可以在沒有 DOM 訪問權限的環境中渲染的應用程序&#xff0c;比如在服務器上、在 Web Worker 中或在原…

Java如何剪切視頻

背景&#xff1a;如何使用Java批量切割視頻 FFmpeg 是一個強大的開源多媒體處理工具&#xff0c;被廣泛應用于音視頻的錄制、轉碼、編輯等方面。它支持幾乎所有主流的音視頻格式&#xff0c;能夠在各種操作系統平臺上運行&#xff0c;包括 Windows、macOS 和 Linux。FFmpeg 提…

nginx,php-fpm

一&#xff0c;Nginx是異步非阻塞多進程&#xff0c;io多路復用 1、master進程&#xff1a;管理進程 master進程主要用來管理worker進程&#xff0c;具體包括如下4個主要功能&#xff1a; &#xff08;1&#xff09;接收來自外界的信號。 &#xff08;2&#xff09;向各worker進…

SAP PP學習筆記04 - BOM2 -通過Serial來做簡單的BOM變式配置,副明細,BOM狀態,BOM明細狀態,項目種類,遞歸BOM

本章繼續講BOM。 本章講通過Serial來做簡單的BOM變式配置。還講了BOM的相關概念&#xff1a;副明細&#xff0c;BOM狀態&#xff0c;BOM明細狀態&#xff0c;項目種類&#xff0c;遞歸BOM 等。 1&#xff0c;通過Serial&#xff08;序列號&#xff09;來做簡單的 VC&#xff0…

spring自定義注解之-ElementType.METHOD方法級注解聲明

自定義注解類型和常用場景 可以參考之前的文章 &#xff1a; ElementType.FIELD字段級注解聲明 如果在項目中&#xff0c;多處地方都需調用到同一個方法進行邏輯處理&#xff0c;且與方法的業務邏輯無關&#xff0c;比如監控&#xff0c;日志等&#xff0c;則可用自定義的方法…

【JavaSE】面向對象——繼承性

繼承性 繼承性的概念 所謂繼承&#xff0c;就是程序猿在保持原有類特性的基礎上進行擴展&#xff0c;增加新功能&#xff0c;這樣的類被稱為派生類或者子類&#xff0c;原有類被稱為超類或者基類。 在對于繼承性概念進行書寫前&#xff0c;我曾查閱許多資料來保證對其表達的…

Some collections -- 2024.3

一、TensorFlow Android (dataset: Mnist) We used TensorFlow to define and train our machine learning model, which can recognize handwritten numbers, called a number classifier model in machine learning terminology. We transform the trained TensorFlow mod…

C++學習第五天(內存管理)

1、內存分布 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd";int* ptr1 (int*)malloc(sizeof(int) * 4);int…

2024.03.01作業

1. 基于UDP的TFTP文件傳輸 #include "test.h"#define SER_IP "192.168.1.104" #define SER_PORT 69 #define IP "192.168.191.128" #define PORT 9999enum mode {TFTP_READ 1,TFTP_WRITE 2,TFTP_DATA 3,TFTP_ACK 4,TFTP_ERR 5 };void get_…

高維中介數據:基于交替方向乘子法(ADMM)的高維度單模態中介模型的參數估計(入門+實操)

全文摘要 用于高維度單模態中介模型的參數估計&#xff0c;采用交替方向乘子法&#xff08;ADMM&#xff09;進行計算。該包提供了確切獨立篩選&#xff08;SIS&#xff09;功能來提高中介效應的敏感性和特異性&#xff0c;并支持Lasso、彈性網絡、路徑Lasso和網絡約束懲罰等不…

npm 鏡像源切換與設置

項目背景 依賴安裝中斷或響應特別慢。 可以看到當前所用的鏡像是 https://registry.npmjs.org 。 切換淘寶鏡像之后總算能夠安裝下來 命令行模式 查看當前鏡像源 # 查看當前鏡像源 npm config get registry 可以看到默認情況下是官方默認全局鏡像 https://registry.npmjs.o…

競爭加劇下,登頂后的瑞幸該做什么?

瑞幸咖啡僅用短短18個月時間從品牌創立到納斯達克上市&#xff0c;刷新全球最快上市記錄。2020年因交易造假事件被勒令退市股價暴跌80%&#xff0c;有人說這個創造了赴美IPO奇跡的“巨嬰”將是下一個倒下的ofo。2023年瑞幸咖啡以逆勢超速增長領跑咖啡賽道有力回應了市場的質疑&…

Vector中的begin和end函數是左閉右開的區間

vector::end() 函數的語法 vector::end(); 參數&#xff1a; none——它什么都不接受。 返回值&#xff1a; iterator– 它返回一個指向向量的 past-the-end 元素的迭代器。 實際上Vector中的begin和end函數是左閉右開的區間。 例&#xff1a; Input: vector<int>…

Java多線程實現發布和訂閱

目錄 簡介 步驟 1: 定義消息類 步驟 2: 創建發布者 步驟 3: 創建訂閱者 步驟 4: 實現發布-訂閱模型 前言-與正文無關 生活遠不止眼前的苦勞與奔波&#xff0c;它還充滿了無數值得我們去體驗和珍惜的美好事物。在這個快節奏的世界中&#xff0c;我們往往容易陷入工作的漩渦…

棋牌室計時計費管理系統的燈控器連接教程

棋牌室計時計費管理系統的燈控器連接教程 一、前言 以下教程以 佳易王棋牌室計時計費管理系統軟件V18.0為例說明 軟件文件下載可以點擊最下方官網卡片——軟件下載——試用版軟件下載 如上圖&#xff0c;計時計費軟件在開始計時的時候&#xff0c;點擊 開始計時 如果連接了…