不同数值微分方法的精度讨论

验证数值微分的精度


  1. 来源
    在进行姿态解算的时候,有一个环节是“通过角度求角速率”,用到离散点求微分的知识,也就是“数值微分”。有人说离散点直接差分就好了,但对于对精度有一定要求的时候,直接差分可以胜任吗?

  2. 验证方法
    身边恰好有一本《工程数学基础教程》(天津大学出版社,P316),上面介绍的数值微分方法,包括插值型求导公式、两点数值微分公式和三点数值微分公式。插值型求导方法需要事先知道所有的插值点,才能写出公式,不能达到实时性的要求。
    插值型求导公式:
    n次Lagrange插值多项式
    两点数值微分公式,以一阶差商代替导数,其实就是我们所说的差分公式,截断误差为O(h)(与h同阶):
    两点数值微分公式
    三点数值微分公式,是为提高精度对两点数值微分公式进行的改进,三点对应二次差值多项式,公式为:
    这里写图片描述
    这里写图片描述
    这里写图片描述
    当f(x)的三阶导在[x0,x2]上有界时,三点的截断误差均为O(h^2)。

  3. MATLAB验证精度
    插值型求导方法毫无实时性,不再讨论;
    两点数值微分实时性最好,可以在获取采样点的同时进行数值微分求解;
    三点数值微分实时性稍差,通过公式我们发现求x1点处的微分值,需要用到x2点值。这样的话,数值微分的解算比采样少了一个节拍。
    下面通过MATLAB进行精度验证两点数值微分和三点数值微分方法的精度,验证思路是:
    a. 首先确定一个多项式,对多项式进行取点作为我们的模拟采样点,多项式进行求导作为数值微分结果对照的标准;

%设定一个多项式,并求出导数形式
syms x;
y = (x-1) * (x-2) * (x-3) * (x-4);
dy = diff(y);
h = 1/4;
%得出多项式函数值
fx = zeros(1,20); 
for i = 1:20
    x = i * h;
    fx(i) = eval(y);
end
%得出各点导数值
k1 = zeros(1,20); 
for i = 1:20
    x = i * h;
    k1(i) = eval(dy);
end

b. 直接差分(两点数值微分公式)求各点微分值;

%直接离散点差分得到
k2 = zeros(1,20); 
for i = 1:20
    if i == 20      %最后一点不用管
        k2(i) = k2(i-1);
    else
        k2(i) = (fx(i+1) - fx(i)) / h;
    end
end

c. 用三点数值微分公式进行求各点微分值;

%三点数值微分
k3 = zeros(1,20); 
for i = 1:20
    if (i == 1 )
        k3(i) = (-3*fx(i) + 4*fx(i+1) - fx(i+2)) / (2 * h);
    else if ( i==20)
        k3(i) = (fx(i-2) - 4*fx(i-1) + 3*fx(i)) / (2 * h);
        else
            k3(i) = (-fx(i-1) + fx(i+1)) / (2 * h);
        end
    end
end

d. 不同方法的微分值与多项式求导值进行比较,找出最精确方法。
精度分析方法很简单,直接画出导数和各种数值微分方法结果图就好了。也可以用不同方法得到的微分值和直接导数值进行卷积比较数值微分精度。

%画图比较
plot(k1,'g');hold on;
plot(k2,'r');plot(k3,'b');
axis([0 19 -40 40]);
legend('直接求导','两点数值微分','三点数值微分');

最后效果图:
不同数值微分方法的精度讨论_第1张图片
从图片中可以明显看出来,三点数值微分几乎和原导数重合,效果要比两点数值微分好很多!

e. 看到红色曲线或许会想到,两点数值微分的红色曲线往右平移,也就是把两点数值微分的值赋给后者点而不是前者点,这样就会使得微分曲线与直接求导曲线重合。真的是这样吗?修改“直接求离散点差分”:

%直接离散点差分得到
k2 = zeros(1,20); 
for i = 1:20
    if i == 1     %这个点不用管
        k2(i) = 0;
    else
        k2(i) = (fx(i) - fx(i-1)) / h;
    end
end

得到新的数值微分结果图:
不同数值微分方法的精度讨论_第2张图片
往右边偏了……求平均值获取可以得到比较精确的中间点导数,但是我们不需要中间点的导数。

4.针对三点数值微分公式的实时性改进
观察三点数值微分公式里面,第三个公式,发现求谋点微分值,只需要用到前面的两个点和当前时刻点的采样值,满足实时性要求。所以,不妨把第三个公式作为求数值微分的主要公式:
这里写图片描述
修改代码为:

%对实时性有要求
k4 = zeros(1,20);
for i = 1:20
    if (i == 1)
        k4(i) = (-3*fx(i) + 4*fx(i+1) - fx(i+2)) / (2 * h);  %其实可以随便取
    else if (i == 2)
        k4(i) = (-fx(i-1) + fx(i+1)) / (2 * h);   %其实可以随便取
        else
            k4(i) = (fx(i-2) - 4*fx(i-1) + 3*fx(i)) / (2 * h);
        end
    end
end

效果图:
不同数值微分方法的精度讨论_第3张图片

看着效果还不错,放大看看:
不同数值微分方法的精度讨论_第4张图片

要是感觉肉眼看不太专业,可以求不同曲线的相关系数,看“两点”、“三点”、“改进”的曲线与“直接求导”曲线接近程度:

%“两点”与直接求导的结果接近程度
k12 = corrcoef(k1,k2);
k13 = corrcoef(k1,k3);    
k14 = corrcoef(k1,k4); 
k12 =
    1.0000    0.7489
    0.7489    1.0000
k13 =
    1.0000    0.9997
    0.9997    1.0000
k14 =
    1.0000    0.9996
    0.9996    1.0000

矩阵的非对角线表示相关系数,分别是0.7489,0.9997,0.9996 。说明了改进的方法既满足实时性要求,又保留了很高的求解精度。

5.后记
由于刚开始学习MATLAB,程序使用的都是最初级的语句,想法也很简单,主要是为了留个云笔记,也欢迎大家纠正错误或提建议。
很简单的内容,花了一整上午和小下午。第一次使用CSDN博客,纪念一下:2017/6/20。

你可能感兴趣的:(matlab算法)