MATLAB 求散点曲线斜率

三种最常用、最可靠的散点曲线斜率计算方法


一、三种方法对比

方法 适用场景 优点 缺点
1. 中心差分法 点密集、等间距 简单快速 对噪声敏感,端点难处理
2. 移动最小二乘法(MLS) 工程数据首选 抗噪声,平滑 需选择窗口大小
3. Savitzky-Golay滤波 等间距采样数据 精度高,平滑 需等间距

二、方法1:中心差分法

2.1 核心代码

matlab 复制代码
%% 方法1:中心差分法求斜率
clear; clc; close all;

% 生成示例散点数据
x = linspace(0, 10, 50)';
y = 2*x.^2 - 3*x + 1 + 5*randn(size(x)); % 带噪声的二次函数

% 计算斜率(dy/dx)
dy = diff(y) ./ diff(x); % 前向/后向差分
x_mid = (x(1:end-1) + x(2:end)) / 2; % 斜率对应的x坐标

% 中心差分(更准确)
dy_center = zeros(size(y));
dy_center(2:end-1) = (y(3:end) - y(1:end-2)) ./ (x(3:end) - x(1:end-2));
dy_center(1) = (y(2) - y(1)) / (x(2) - x(1)); % 前端
dy_center(end) = (y(end) - y(end-1)) / (x(end) - x(end-1)); % 后端

% 绘图
figure('Position', [100, 100, 1200, 400]);
subplot(1,3,1);
plot(x, y, 'bo-', 'LineWidth', 1.5);
xlabel('x'); ylabel('y'); title('原始散点数据'); grid on;

subplot(1,3,2);
plot(x_mid, dy, 'ro-', 'LineWidth', 1.5);
xlabel('x'); ylabel('dy/dx'); title('前向/后向差分斜率'); grid on;

subplot(1,3,3);
plot(x, dy_center, 'go-', 'LineWidth', 1.5);
xlabel('x'); ylabel('dy/dx'); title('中心差分斜率'); grid on;

sgtitle('方法1:中心差分法', 'FontSize', 14, 'FontWeight', 'bold');

三、方法2:移动最小二乘法

这是最推荐的方法,特别适合带噪声的工程实验数据。

3.1 完整函数 (moving_least_squares_slope.m)

matlab 复制代码
function [x_fit, slope] = moving_least_squares_slope(x, y, window_size)
% 移动最小二乘法求散点斜率
% 输入:
%   x, y: 散点数据
%   window_size: 滑动窗口大小(奇数,如5,7,9...)
% 输出:
%   x_fit: 拟合点x坐标
%   slope: 对应斜率

arguments
    x (:,1) double
    y (:,1) double
    window_size (1,1) double = 7
end

assert(mod(window_size,2)==1, '窗口大小必须为奇数!');

n = length(x);
half_win = floor(window_size/2);
slope = zeros(n,1);
x_fit = x;

% 滑动窗口局部线性拟合
for i = 1:n
    % 确定窗口范围
    start_idx = max(1, i-half_win);
    end_idx = min(n, i+half_win);
    
    % 提取窗口数据
    x_win = x(start_idx:end_idx);
    y_win = y(start_idx:end_idx);
    
    % 局部线性拟合: y = a*x + b
    p = polyfit(x_win, y_win, 1); % p(1)=斜率, p(2)=截距
    
    % 保存当前点的斜率
    slope(i) = p(1);
end
end

3.2 使用示例

matlab 复制代码
%% 方法2:移动最小二乘法(推荐)
clear; clc; close all;

% 带强噪声的数据
x = linspace(0, 10, 100)';
y = 2*x.^2 - 3*x + 1 + 8*randn(size(x)); % 强噪声

% 不同窗口大小的对比
[slope3] = moving_least_squares_slope(x, y, 5);   % 小窗口
[slope2] = moving_least_squares_slope(x, y, 11);  % 中窗口
[slope1] = moving_least_squares_slope(x, y, 21);  % 大窗口

% 理论斜率(用于对比)
y_theory = 4*x - 3; % y=2x²-3x+1的导数

% 绘图
figure('Position', [100, 100, 1200, 400]);
subplot(1,2,1);
plot(x, y, 'b.', 'MarkerSize', 8); hold on;
plot(x, y_theory, 'r--', 'LineWidth', 2);
xlabel('x'); ylabel('y'); title('原始数据与理论曲线'); grid on;

subplot(1,2,2);
plot(x, slope1, 'g-', 'LineWidth', 2, 'DisplayName', '窗口21');
hold on;
plot(x, slope2, 'b-', 'LineWidth', 2, 'DisplayName', '窗口11');
plot(x, slope3, 'm-', 'LineWidth', 2, 'DisplayName', '窗口5');
plot(x, y_theory, 'r--', 'LineWidth', 2, 'DisplayName', '理论斜率');
xlabel('x'); ylabel('dy/dx'); title('移动最小二乘法求斜率'); grid on;
legend('Location', 'best');

sgtitle('方法2:移动最小二乘法(抗噪声最强)', 'FontSize', 14, 'FontWeight', 'bold');

四、方法3:Savitzky-Golay滤波法

如果你的散点是等间距采样 ,这是精度最高的方法。

4.1 代码实现

matlab 复制代码
%% 方法3:Savitzky-Golay滤波求导(等间距数据专用)
clear; clc; close all;

% 生成等间距数据
x = linspace(0, 10, 200)';
y = 2*x.^2 - 3*x + 1 + 3*randn(size(x));

% 使用sgolayfilt求斜率
window_size = 21; % 窗口大小(奇数)
poly_order = 2;   % 多项式阶数

% 方法3.1:使用sgolayfilt(需要Signal Processing Toolbox)
if license('test', 'Signal_Toolbox')
    % 计算SG滤波器系数
    [b, g] = sgolay(poly_order, window_size);
    
    % 计算一阶导数
    dt = x(2) - x(1); % 采样间隔
    dy = zeros(size(y));
    
    for i = 1:window_size
        dy = dy + g(:,i) * y(i:length(y)-window_size+i);
    end
    dy = dy / (factorial(1) * dt);
    
    figure;
    plot(x, dy, 'b-', 'LineWidth', 2); hold on;
    plot(x, 4*x-3, 'r--', 'LineWidth', 2);
    xlabel('x'); ylabel('dy/dx'); title('Savitzky-Golay滤波求导'); grid on;
else
    warning('未安装Signal Processing Toolbox,跳过方法3');
end

五、实战案例:实验数据处理

5.1 完整分析流程

matlab 复制代码
%% 实战案例:处理实验散点数据
clear; clc; close all;

% 1. 加载实验数据(假设是应力-应变曲线)
load strain_stress_data.mat % 替换为你的数据
% 或直接生成示例数据
x = linspace(0, 0.1, 50)'; % 应变
y = 210e9 * x + 1e9*randn(size(x)); % 应力(带噪声)

% 2. 求切线模量(斜率)
[slope, x_fit] = moving_least_squares_slope(x, y, 7);

% 3. 计算关键点斜率
% 初始模量(前10%数据)
init_idx = x <= 0.1*max(x);
E_initial = mean(slope(init_idx));

% 屈服点斜率(假设在30%处)
yield_idx = abs(x - 0.03*max(x)) == min(abs(x - 0.03*max(x)));
E_yield = slope(yield_idx);

% 4. 绘图
figure('Position', [100, 100, 1000, 400]);
subplot(1,2,1);
plot(x, y, 'bo-', 'LineWidth', 1.5);
xlabel('应变'); ylabel('应力 (Pa)'); title('应力-应变曲线'); grid on;

subplot(1,2,2);
plot(x_fit, slope, 'r-', 'LineWidth', 2);
xlabel('应变'); ylabel('切线模量 (Pa)'); title('切线模量分布'); grid on;
hold on;
plot(x(init_idx), E_initial*ones(sum(init_idx),1), 'g--', 'LineWidth', 2, 'DisplayName', sprintf('初始模量: %.2e Pa', E_initial));
plot(x(yield_idx), E_yield, 'mo', 'MarkerSize', 10, 'MarkerFaceColor', 'm', 'DisplayName', sprintf('屈服模量: %.2e Pa', E_yield));
legend('Location', 'best');

fprintf('初始弹性模量: %.2e Pa\n', E_initial);
fprintf('屈服点切线模量: %.2e Pa\n', E_yield);

参考代码 matlab求散点曲线斜率 www.youwenfan.com/contentcsv/80873.html

六、如何选择最佳方法?

你的数据特点 推荐方法 参数设置
点少、无噪声 中心差分法 直接 diff(y)./diff(x)
带噪声、不等间距(最常见) 移动最小二乘法 窗口大小=7~15
等间距、高精度要求 Savitzky-Golay 窗口=21, 阶数=2
需要局部极值斜率 移动最小二乘法 小窗口(5~7)

七、常见问题解决

7.1 斜率出现异常尖刺?

matlab 复制代码
% 解决:增大移动最小二乘法的窗口大小
slope = moving_least_squares_slope(x, y, 15); % 从7改为15

7.2 端点斜率不准?

matlab 复制代码
% 解决:端点用线性外推
slope(1) = (y(2)-y(1))/(x(2)-x(1));
slope(end) = (y(end)-y(end-1))/(x(end)-x(end-1));

7.3 需要特定点的斜率?

matlab 复制代码
% 求x=0.05处的斜率
x_target = 0.05;
[idx, ~] = min(abs(x - x_target));
slope_at_point = slope(idx);

八、封装成通用函数

matlab 复制代码
function slope = scatter_slope(x, y, method, param)
% SCATTER_SLOPE 计算散点曲线斜率
% 用法:
%   slope = scatter_slope(x, y, 'central')  % 中心差分
%   slope = scatter_slope(x, y, 'mls', 11)   % 移动最小二乘,窗口11
%   slope = scatter_slope(x, y, 'sg', [21,2]) % SG滤波,窗口21,阶数2

arguments
    x (:,1) double
    y (:,1) double
    method string = 'mls'
    param double = 11
end

switch lower(method)
    case 'central'
        slope = zeros(size(y));
        slope(2:end-1) = (y(3:end) - y(1:end-2)) ./ (x(3:end) - x(1:end-2));
        slope(1) = (y(2)-y(1))/(x(2)-x(1));
        slope(end) = (y(end)-y(end-1))/(x(end)-x(end-1));
        
    case 'mls'
        slope = moving_least_squares_slope(x, y, param);
        
    case 'sg'
        if license('test', 'Signal_Toolbox')
            [b, g] = sgolay(param(2), param(1));
            dt = x(2) - x(1);
            slope = zeros(size(y));
            for i = 1:param(1)
                slope = slope + g(:,i) * y(i:length(y)-param(1)+i);
            end
            slope = slope / (factorial(1) * dt);
        else
            error('需要Signal Processing Toolbox!');
        end
end
end

直接使用:

matlab 复制代码
slope = scatter_slope(x, y, 'mls', 11); % 一行搞定!

总结:对于大多数工程散点数据,直接使用「移动最小二乘法(窗口大小=11)」是最稳妥的选择。

相关推荐
kaikaile19951 小时前
MATLAB 实现:Koch & Zhao 图像水印算法(DCT域)
开发语言·算法·matlab
love_muming1 小时前
链表每日一练
java·开发语言·数据结构·链表·idea·每日一练
QiLinkOS1 小时前
QiLink开源生态的三维重构:基于时间、空间与社会价值的底层规则创新白皮书
大数据·c++·人工智能·科技·算法·gitee·开源
weixin_446260851 小时前
LLM智能体在社交模拟中的决策行为分析:有限状态与LLM-based策略对比研究
开发语言·php
牛肉在哪里1 小时前
ros2 从零开始28 监听广播C++
开发语言·c++·算法·机器人
乐观勇敢坚强的老彭2 小时前
GESP一级核心算法:循环与条件判断的结合
java·数据结构·算法
noipp2 小时前
推荐题目:洛谷 P1737 [NOI2016] 旷野大计算
linux·数据结构·算法
techdashen2 小时前
Cargo 1.94 开发周期全解析
开发语言·后端·rust
QiLinkOS2 小时前
极客精神与商业思维的融合实践(2)
c语言·c++·人工智能·算法·开源协议