主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

弹性机翼飞机的模态分析

此示例展示了柔性机翼飞机弯曲模式的计算。机翼的振动响应是在机翼跨度上的多个点收集的。该数据用于辨识系统的动态模型。从辨识模型中提取模态参数。模态参数数据与传感器位置信息相结合,可以直观地看到机翼的各种弯曲模式。此示例需要 Signal Processing Toolbox™。

柔性翼飞机

在这个例子中,我们研究了明尼苏达大学无人驾驶飞行器实验室建造的一架小型柔性飞翼飞机收集的数据 [1]。飞机的几何形状如下所示。

飞机机翼在载荷作用下会发生较大的变形。柔性模式频率比机翼刚性更大的普通飞机的柔性模式频率要低。这种灵活的设计降低了材料成本,提高了飞机的灵活性和飞行距离。然而,如果不加以控制,柔性模式可能会导致灾难性的气动弹性不稳定性(颤振)。设计有效的控制律来抑制这些不稳定性需要准确确定机翼的各种弯曲模式。

试验设置

试验的目标是收集飞机在各个位置对外部激励的振动响应。飞机悬挂在木制框架上,重心处有一个弹簧。弹簧具有足够的柔韧性,使得弹簧质量振动的固有频率不会干扰飞机的基本频率。输入力通过位于飞机中心附近的 Unholtz-Dickie Model 20 电动振动器施加。

沿翼展放置二十个 PCB-353B16 加速度计来收集振动响应,如下图所示。

振动器输入命令被指定为形式为 A sin(ω(t)t) 的恒定幅度线性调频输入。啁啾频率随时间线性变化,即 ω(t)=c0+c1t。输入信号覆盖的频率范围是 3–35 Hz。数据由两个加速度计(位于一个 x 位置的前缘加速度计和后缘加速度计)同时收集。因此进行了 10 次试验来收集所有 20 个加速度计响应。加速度计和力传感器测量均以 2000 Hz 的频率进行采样。

数据准备

数据由 10 组双输出/单输入信号表示,每组包含 600K 个采样。该数据可在 MathWorks 支持文件站点上找到。请参阅免责声明。下载数据文件并将数据加载到 MATLAB® 工作区。

url = 'https://www.mathworks.com/supportfiles/ident/flexible-wing-GVT-data/FlexibleWingData.mat';
websave('FlexibleWingData.mat',url);
load FlexibleWingData.mat MeasuredData

变量 MeasuredData 是一个包含字段 Exp1Exp2、...、Exp10 的结构。每个字段都是一个结构,其中字段 y 和 u 包含两个响应和相应的输入力数据。绘制第一个试验的数据。

fs = 2000;                % data sampling frequency
Ts = 1/fs;                % sample time
y = MeasuredData.Exp1.y;  % output data (2 columns, one for each accelerometer)
u = MeasuredData.Exp1.u;  % input force data
t = (0:length(u)-1)' * Ts;
figure
subplot(211)
plot(t,y)
ylabel('Outputs (m/s^2)')
legend('Leading edge','Trailing edge')
subplot(212)
plot(t,u)
ylabel('Input')
xlabel('Time (seconds)')

为了给模型辨识准备数据,数据被打包成 iddata 对象。iddata 对象是 System Identification Toolbox™ 中打包时域数据的标准方式。输入信号被视为带限信号。

ExpNames = fieldnames(MeasuredData);
Data = cell(1, 10);
for k = 1:10
   y =  MeasuredData.(ExpNames{k}).y;
   u =  MeasuredData.(ExpNames{k}).u;
   Data{k} = iddata(y, u, Ts, 'InterSample', 'bl');
end

将数据集对象合并为一个多试验数据对象。

Data = merge(Data{:})
Data =
Time domain data set containing 10 experiments.

Experiment   Samples      Sample Time           
   Exp1      600001            0.0005           
   Exp2      600001            0.0005           
   Exp3      600001            0.0005           
   Exp4      600001            0.0005           
   Exp5      600001            0.0005           
   Exp6      600001            0.0005           
   Exp7      600001            0.0005           
   Exp8      600001            0.0005           
   Exp9      600001            0.0005           
   Exp10     600001            0.0005           
                                                
Outputs      Unit (if specified)                
   y1                                           
   y2                                           
                                                
Inputs       Unit (if specified)                
   u1                                           
                                                

模型辨识

我们希望找到一个频率响应与实际飞机频率响应尽可能接近的动态模型。动态模型将系统输入和输出之间的数学关系封装为微分方程或差分方程。动态模型的例子有传递函数和状态空间模型。在 System Identification Toolbox 中,动态模型被封装为诸如 idtf(用于传递函数)、idpoly(用于 AR、ARMA 模型)和 idss(用于状态空间模型)等对象。可以通过在时域或频域中对测量数据运行估计命令(例如 tfestssest 命令)来创建动态模型。

对于这个例子,我们首先使用 etfe 命令通过经验传递函数估计将测量的时域数据转换为频率响应数据。然后使用估计的 FRF 来辨识飞机振动响应的状态空间模型。可以直接利用时域数据进行动态模型辨识。然而,FRF 形式的数据允许将大型数据集压缩为更少的采样,并更容易地将估计行为调整到相关的频率范围。FRF 由 idfrd 对象封装。

为每个数据试验估计一个双输出/单输入频率响应函数 (FRF)。不使用加窗。使用 24,000 个频率点进行响应计算。

G = cell(1, 10);
N = 24000;
for k = 1:10
   % Convert time-domain data into a FRF using ETFE command
   Data_k = getexp(Data, k);
   G{k} = etfe(Data_k, [], N); % G{k} is an @idfrd object
end

将所有 FRF 串联成一个 20 输出/1 输入的 FRF。

G = cat(1, G{:});     % concatenate outputs of all estimated FRFs
G.OutputName = 'y';   % name outputs 'y1', 'y2', ..., 'y20'
G.InterSample = 'bl';

为了了解估计的频率响应,绘制输出 1 和 15(任意选择)的幅度。放大感兴趣的频率范围 (4–35 Hz)。

opt = bodeoptions;           % plot options
opt.FreqUnits = 'Hz';        % show frequency in Hz
opt.PhaseVisible = 'off';    % do not show phase
OutputNum = [1 15];          % pick outputs 1 and 15 for plotting
clf
bodeplot(G(OutputNum, :), opt)   % plot frequency response
xlim([4 35])
grid on

FRF 显示至少 9 个共振频率。为了进行分析,我们希望重点关注 6-35 Hz 频率跨度,这是飞机最关键的柔性弯曲模式所在。因此将 FRF 降低到该频率区域。

f = G.Frequency/2/pi;           % extract frequency vector in Hz (G stores frequency in rad/s)
Gs = fselect(G, f>6 & f<=32)    % "fselect" selects the FRF in the requested range (6.5 - 35 Hz)
Gs =
IDFRD model.
Contains Frequency Response Data for 20 output(s) and 1 input(s).
Response data is available at 624 frequency points, ranging from 37.96 rad/s to 201.1 rad/s.
 
Sample time: 0.0005 seconds
Output channels: 'y(1)', 'y(2)', 'y(3)', 'y(4)', 'y(5)', 'y(6)', 'y(7)', 'y(8)', 'y(9)', 'y(10)', 'y(11)', 'y(12)', 'y(13)', 'y(14)', 'y(15)', 'y(16)', 'y(17)', 'y(18)', 'y(19)', 'y(20)'
Input channels: 'u1'
Status:        
Estimated model

因此,Gs 包含 20 个测量位置的频率响应测量值。接下来,确定一个状态空间模型来拟合 Gs。子空间估计器 n4sid 提供了快速的非迭代估计。状态空间模型结构配置如下:

  1. 我们估计一个 24 阶连续时间模型。在对各种阶数进行一些试验并检查模型与 FRF 的拟合情况后,找到了该阶数。

  2. 该模型包含一个馈通通项(D 矩阵非零)。这是因为我们将分析限制在 35 Hz,而机翼的带宽明显大于此(响应在 35 Hz 时显著)。

  3. 为了加快计算速度,我们抑制参数协方差的计算。

  4. FRF 幅值在整个频率范围内变化很大。为了确保低振幅与高振幅受到同等重视,我们应用了与响应的平方根成反比的自定义权重。由于有 20 个通道,因此加权向量取从每个通道获得的权重的平均值(这是因为 20 个通道的 FRF 形状相似)。

我们使用 n4sidOptionsn4sid 设置了估计选项。

FRF = squeeze(Gs.ResponseData);
Weighting = mean(1./sqrt(abs(FRF))).';
n4Opt = n4sidOptions('EstimateCovariance',false,...
   'WeightingFilter',Weighting,...
   'OutputWeight',eye(20));
sys1 = n4sid(Gs,24,'Ts',0,'Feedthrough',true,n4Opt);
Fit = sys1.Report.Fit.FitPercent'
Fit = 1×20

   54.2689   55.3738   84.0188   85.7017   81.9259   80.2293   55.7448   40.6773   58.6429   76.2997   83.5370   84.6555   85.9240   84.8780   76.8937   81.0399   74.8321   79.4069   65.0803   76.3328

“拟合度”数字使用归一化均方根误差 (NRMSE) 拟合优度测量显示数据 (Gs) 和模型 (sys1) 频率响应之间的百分比拟合度。接下来绘制最差和最佳拟合情况。

[~,Imin] = min(Fit);
[~,Imax] = max(Fit);
clf
bodeplot(Gs([Imin, Imax],:), sys1([Imin, Imax],:), opt);
xlim([6 32])
title('Worst (top) and best (bottom) fits between data and model')
grid on
legend('Data', 'Model')

通过模型参数的迭代非线性最小二乘细化,可以进一步改善模型 sys1 实现的拟合度。这可以使用 ssest 命令来实现。我们使用 ssestOptions 命令设置了 ssest 的估计选项。这次还估计了参数协方差。

ssOpt = ssestOptions('EstimateCovariance',true,...
   'WeightingFilter',n4Opt.WeightingFilter,...
   'SearchMethod','lm',...     % use Levenberg-Marquardt search method
   'Display','on',...
   'OutputWeight',eye(20));
sys2 = ssest(Gs, sys1, ssOpt);  % estimate state-space model (this takes several minutes)
Fit = sys2.Report.Fit.FitPercent'
Fit = 1×20

   88.8715   88.6587   88.9388   89.8630   87.8597   88.1235   79.7016   82.5930   74.2458   82.6593   90.2663   88.7739   89.9033   89.0571   88.8040   88.2251   86.9307   87.5250   83.2249   82.8676

和以前一样,我们绘制最差和最佳拟合情况。我们还可视化了模型频率响应的 1-sd 置信域。

[~, Imin] = min(Fit);
[~, Imax] = max(Fit);
clf
h = bodeplot(Gs([Imin, Imax],:), sys2([Imin, Imax],:), opt);
showConfidence(h, 1)
xlim([6.5 35])
title('Worst (top) and best (bottom) fits between data and refined model')
grid on
legend('Data', 'Model (sys2)')

改进的状态空间模型 sys2 在 7–20 Hz 区域内能够很好地拟合 FRF。大多数共振位置的不确定性界限都很严格。我们估计了一个 24 阶模型,这意味着系统 sys2 中最多有 12 种振荡模式。使用 modalfit 命令获取这些模式的固有频率(以 Hz 为单位)。

f = Gs.Frequency/2/pi;     % data frequencies (Hz)
fn = modalfit(sys2, f, 12); % natural frequencies (Hz)
disp(fn)
    7.7720
    7.7954
    9.3176
    9.4052
    9.4919
   15.3462
   19.3281
   23.0219
   27.4035
   28.6873
   31.7036
   64.2624

fn 中的值表明两个频率非常接近 7.8 Hz,三个频率接近 9.4 Hz。对这些频率附近的频率响应的检查表明,峰值位置在输出端有一点点偏移。可以通过更好地控制试验过程来消除这些差异,将输入带宽限制在以这些频率为中心的窄范围内进行直接时域辨识,或将单个振荡模式拟合到这些频率周围的频率响应。本例中没有探讨这些替代方案。

模态参数计算

我们现在可以使用模型 sys2 来提取模态参数。对 FRF 的检查表明大约有 10 个模态频率,大致在频率 [5 7 10 15 17 23 27 30] Hz 附近。我们可以使用 modalsd 命令来使这一评估更加具体,该命令可以检查模态参数在底层模型阶数改变时的稳定性。此操作需要很长时间。因此,生成的图表将直接作为图像插入。执行下面注释模块内的代码来重现该图。

FRF = permute(Gs.ResponseData,[3 1 2]);
f = Gs.Frequency/2/pi;
%{
 figure
 pf = modalsd(FRF,f,fs,'MaxModes',20,'FitMethod','lsrf');
%}

检查图表和 pf 值可以得到一份精确的真实固有频率列表:

Freq = [7.8 9.4 15.3 19.3 23.0 27.3 29.2 31.7];

我们使用 Freq 向量中的值作为从模型 sys2 中挑选最主要模式的指南。这是使用 modalfit 命令完成的。

[fn,dr,ms] = modalfit(sys2,f,length(Freq),'PhysFreq',Freq);

fn 是固有频率(以赫兹为单位),dr 是相应的阻尼系数,ms 是列向量形式的归一化残差(每个固有频率一列)。在提取这些模态参数的过程中,仅使用模型的稳定、欠阻尼极点。ms 列仅包含具有正虚部的极点的极点。

模态形状可视化

为了直观地显示各种弯曲模式,我们需要上面提取的模态参数。此外,我们还需要有关测量点位置的信息。矩阵 AccePos 中记录了每个加速度计的这些位置(x-y 值):

AccelPos = [...   % see figure 2
   16.63 18.48;   % nearest right of center
   16.63 24.48;
   27.90 22.22;
   27.90 28.22;
   37.17 25.97;
   37.17 31.97;
   46.44 29.71;
   46.44 35.71;
   55.71 33.46;
   55.71 39.46;  % farthest right
   -16.63 18.48; % nearest left of center
   -16.63 24.18;
   -27.90 22.22;
   -27.90 28.22;
   -37.17 25.97;
   -37.17 31.97;
   -46.44 29.71;
   -46.44 35.71;
   -55.71 33.46;
   -55.71 39.46]; % farthest left

模式形状包含在矩阵 ms 中,其中每一列对应一个频率的形状。通过在传感器坐标上叠加模态振幅并改变模态固有频率的振幅来对模态进行动画处理。动画显示了无阻尼的弯曲。例如,考虑 15.3 Hz 的模式。

AnimateOneMode(3, fn, ms, AccelPos); 

结论

此示例展示了一种基于参数化模型辨识的模态参数估计方法。使用 24 阶状态空间模型,提取了 6~32Hz 频率范围内的 8 个稳定振荡模式。模态信息与加速度计位置的知识相结合,以可视化各种弯曲模式。下图显示了其中一些模式。

参考资料

[1] Gupta, Abhineet, Peter J. Seiler, and Brian P. Danowsky."Ground Vibration Tests on a Flexible Flying Wing Aircraft."AIAA Atmospheric Flight Mechanics Conference, AIAA SciTech Forum.(AIAA 2016-1753).

function AnimateOneMode(ModeNum, fn, ModeShapes, AccelPos)
% Animate one mode.
% ModeNum: Index of the mode.
    
% Reorder the sensor locations for plotting so that a continuous, 
% non-intersecting curve is traced around the body of the aircraft.
PlotOrder = [19:-2:11, 1:2:9, 10:-2:2, 12:2:20, 19];
Fwd = PlotOrder(1:10);
Aft = PlotOrder(20:-1:11); 
x = AccelPos(PlotOrder,1);
y = AccelPos(PlotOrder,2);
xAft = AccelPos(Aft,1);
yAft = AccelPos(Aft,2);
xFwd = AccelPos(Fwd,1);
yFwd = AccelPos(Fwd,2);

wn = fn(ModeNum)*2*pi;  % Mode frequency in rad/sec
T = 1/fn(ModeNum);      % Period of modal oscillation
Np  = 2.25;             % Number of periods to simulate
tmax = Np*T;            % Simulate Np periods
Nt = 100;               % Number of time steps for animation
t = linspace(0,tmax,Nt);
ThisMode = ModeShapes(:,ModeNum)/max(abs(ModeShapes(:,ModeNum))); % normalized for plotting
z0 = ThisMode(PlotOrder);
zFwd = ThisMode(Fwd);
zAft = ThisMode(Aft);

clf
col1 = [1 1 1]*0.5;
xx = reshape([[xAft, xFwd]'; NaN(2,10)],[2 20]);
yy = reshape([[yAft, yFwd]'; NaN(2,10)],[2 20]);
plot3(x,y,0*z0,'-', x,y,0*z0,'.', xx(:), yy(:), zeros(40,1),'--',...
   'Color',col1,'LineWidth',1,'MarkerSize',10,'MarkerEdgeColor',col1);
xlabel('x')
ylabel('y')
zlabel('Amplitude')
ht = max(abs(z0));
axis([-100  100  10  40 -ht ht])
view(5,55)
title(sprintf('Mode %d. FN = %s Hz', ModeNum, num2str(fn(ModeNum),4)));

% Animate by updating z-coordinates.  
hold on
col2 = [0.87 0.5 0];
h1 = plot3(x,y,0*z0,'-', x,y,0*z0,'.', xx(:), yy(:), zeros(40,1),'--',...
   'Color',col2,'LineWidth',1,'MarkerSize',10,'MarkerEdgeColor',col2);
h2 = fill3(x,y,0*z0,col2,'FaceAlpha',0.6);
hold off

for k = 1:Nt
   Rot1 = cos(wn*t(k));
   Rot2 = sin(wn*t(k));
   z_t = real(z0)*Rot1 - imag(z0)*Rot2;
   zAft_t = real(zAft)*Rot1 - imag(zAft)*Rot2;
   zFwd_t = real(zFwd)*Rot1 - imag(zFwd)*Rot2;
   zz = reshape([[zAft_t, zFwd_t]'; NaN(2,10)],[2 20]);
   set(h1(1),'ZData',z_t)
   set(h1(2),'ZData',z_t)
   set(h1(3),'ZData',zz(:))
   h2.Vertices(:,3) = z_t;
   pause(0.1)
end

end