MATLAB环境下Hammerstein模型开发工具箱深度应用
本文还有配套的精品资源,点击获取简介:Hammerstein模型是一种工程领域广泛使用的非线性系统建模方法,包括静态非线性和线性动态两个部分。在MATLAB环境中,HammersteinToolbox为工程师和研究人员提供了对这类模型进行估计、分析、识别和仿真的专门工具箱。该工具箱简化了模型级联结构元素的快速估算,支持从噪声数据中自动识别模型参数,并提供参数优化、模型验证...
简介:Hammerstein模型是一种工程领域广泛使用的非线性系统建模方法,包括静态非线性和线性动态两个部分。在MATLAB环境中,HammersteinToolbox为工程师和研究人员提供了对这类模型进行估计、分析、识别和仿真的专门工具箱。该工具箱简化了模型级联结构元素的快速估算,支持从噪声数据中自动识别模型参数,并提供参数优化、模型验证与仿真、以及丰富的可视化工具。HammersteinToolbox通过Simulink集成,适用于电力系统、声学信号处理、机械系统建模等众多领域,有助于提高非线性系统分析和控制的精度。
1. Hammerstein模型概念及组成
Hammerstein模型是一种特殊的非线性系统模型,它由两个部分组成:一个静态非线性环节和一个线性动态环节。这种结构特别适用于描述那些具有非线性输入特性但线性动态响应的系统。在控制系统、信号处理以及通信等领域,Hammerstein模型提供了一种有效的系统建模方法。
1.1 Hammerstein模型的数学描述
数学上,Hammerstein模型可以表示为以下形式:
y(t) = H(u(t - k)) + η(t)
这里, y(t)
是模型的输出, u(t)
是输入信号, H
代表非线性静态环节, η(t)
是随机噪声项。 k
是描述输入和输出之间的时间延迟,可以是零或任何正整数。线性动态环节在模型中体现为输入和输出之间的时间延迟。
1.2 Hammerstein模型的应用场景
Hammerstein模型因其结构简单且能够较好地描述非线性特性,在以下场景中得到了广泛应用:
- 工业过程控制:用于模拟和控制具有非线性特性的工业过程,如化学反应器、热能系统等。
- 生物医学工程:用于分析和处理生物系统中的非线性现象,例如肌肉收缩、神经脉冲传递等。
- 通信系统:在某些通信系统的非线性传输特性和信道建模中也有应用。
Hammerstein模型的简洁性和实用性使得它成为了非线性系统建模的重要工具。接下来的章节将介绍如何在MATLAB环境下使用HammersteinToolbox进行模型建立、系统辨识、参数优化和仿真验证。
2. MATLAB HammersteinToolbox功能介绍
2.1 HammersteinToolbox基础功能
2.1.1 模型建立与计算核心
Hammerstein模型在MATLAB中可以通过其专门的工具箱HammersteinToolbox来建立和计算。这一工具箱提供了强大的函数库,使用户能够设计、分析和仿真基于Hammerstein结构的系统。模型建立过程涉及到指定线性动态部分和非线性静态部分,这一过程在HammersteinToolbox中通过一系列专门的函数来实现。
在基础功能中,模型计算的核心是对上述两个部分分别进行数学描述并解析其复合行为。线性部分通常可以用传递函数或状态空间模型来表示,而MATLAB中提供了 tf
、 ss
等函数来创建这些模型。对于非线性部分,通常采用分段多项式或其他数学函数来描述。通过 poly1d
或自定义函数,用户可以实现非线性部分的表达。
具体的建模过程如下:
% 创建线性动态系统
sys_linear = tf(1, [1, 10, 25]);
% 定义非线性静态映射
NL_block = poly1d([0, 0.5, 1]);
% 构建Hammerstein模型
sys_hammerstein = nlhw([], NL_block, 'p2', 'Focus', 'simulation');
在上述代码中, nlhw
函数用于创建Hammerstein模型。其中, sys_linear
表示线性部分, NL_block
表示非线性部分。 'p2'
和 'Focus', 'simulation'
是Hammerstein模型创建过程中的附加参数,它们指定了模型结构和仿真时的参数优化策略。
2.1.2 系统输入输出接口
HammersteinToolbox在处理系统输入输出时,提供了灵活的接口来读取数据、加载系统响应等。数据可以来自文件、直接用户输入或实时数据流。对于复杂的输入输出接口,用户可以通过图形界面或脚本编程方式来进行操作。
对于输入接口, get_io
函数是一个常用的工具,它能够获取模型的输入输出数据结构,为后续的系统辨识和仿真提供支持。输出接口方面, set_io
函数允许用户为模型指定输入输出变量的名称和单位,确保数据的准确性。
下面的代码示例演示了如何使用 get_io
和 set_io
函数:
% 获取当前模型的输入输出信息
io_info = get_io(sys_hammerstein);
disp(io_info);
% 设置新的输入输出变量名称和单位
new_io = struct('InputName', 'InputSignal', 'OutputName', 'OutputSignal', ...
'InputUnit', 'Volts', 'OutputUnit', 'mmol/m^2');
set_io(sys_hammerstein, new_io);
2.2 高级工具和算法集成
2.2.1 自适应控制与辨识算法
在Hammerstein模型中,自适应控制与辨识算法是确保模型准确性和动态性能的关键技术。通过集成这些算法,HammersteinToolbox能够在不需要完全系统信息的情况下,根据系统的输入输出数据动态调整模型参数。
对于自适应控制,MATLAB提供了多种控制算法,如模型参考自适应控制(MRAC)和自适应前馈控制等。辨识算法方面,如最小二乘法(LS)、梯度法和遗传算法等可以被用于寻找最佳模型参数。
例如,使用最小二乘法进行参数辨识的代码如下:
% 假定y为系统输出,u为系统输入
y = ...; % 系统输出数据
u = ...; % 系统输入数据
model = ...; % 已建立的模型结构
% 使用lsqcurvefit函数进行非线性最小二乘拟合
options = optimoptions('lsqcurvefit','Display','iter','Algorithm','trust-region-reflective');
pEst = lsqcurvefit(@fun, initParam, u, y, lb, ub, options);
% fun为自定义函数,描述了模型结构与拟合数据之间的关系
% initParam为初始参数,lb和ub为参数的下限和上限
% 更新模型参数
sys_hammerstein = set_param(sys_hammerstein, 'NonlinearFunctionParameters', num2str(pEst));
2.2.2 模块化设计与扩展功能
模块化设计使得HammersteinToolbox能够轻松地与其他工具箱集成,进而扩展其功能。这一特性在处理复杂的系统时尤为重要。模块化使得用户可以将Hammerstein模型与其他模型,如PID控制器、神经网络模块等组合使用,从而达到更佳的系统性能。
模块化的一个重要方面是能够在Simulink中对模型进行模块化仿真。用户可以通过在Simulink的拖放界面中直接使用Hammerstein模型,实现与其他模块的无缝连接。
在MATLAB中,模块化的代码化操作可以通过结构体(struct)来实现,使得模型参数和结构清晰易懂。例如,将Hammerstein模型作为模块添加到另一个系统的代码片段如下:
% 创建基础的Simulink模型
simulinkModel = 'myHammersteinSimulink.slx';
% 创建Hammerstein模型模块
hammersteinBlock = 'HammersteinModel';
% 将Hammerstein模型模块添加到Simulink模型中
add_block(hammersteinBlock, simulinkModel);
% 设置模块参数
set_param([simulinkModel '/' hammersteinBlock], 'ModelReference', 'sys_hammerstein');
2.3 工具箱与其他工具的交互
2.3.1 Simulink协同工作
MATLAB的Simulink是一个基于图形的多域仿真和模型设计环境,它允许用户可视化建模和动态仿真复杂的系统。HammersteinToolbox与Simulink协同工作,可以实现Hammerstein模型在更广泛的应用场景中的仿真和验证。
Simulink中提供了与MATLAB代码交互的接口,用户可以在Simulink模型中嵌入MATLAB代码,实现复杂的控制逻辑和算法。例如,用户可以在Simulink模型中加入自定义的MATLAB函数,以实现特定的Hammerstein模型算法。
为了在Simulink中使用Hammerstein模型,可以采用如下的步骤:
- 打开Simulink并创建或打开一个模型。
- 从Simulink库中拖拽“Hammerstein Model”模块至模型中。
- 双击模块,进入模块参数设置界面。
- 输入模型的线性部分和非线性部分的参数。
- 连接输入输出,并运行模型。
2.3.2 与MATLAB其他工具箱的整合
MATLAB提供了许多专业工具箱,这些工具箱可以与HammersteinToolbox配合使用,以增强模型的分析和设计能力。例如,控制系统工具箱(Control System Toolbox)提供了丰富的控制设计和分析功能,而优化工具箱(Optimization Toolbox)则提供了高级的优化算法,可以帮助用户在Hammerstein模型中进行参数优化。
整合其他工具箱通常涉及到在HammersteinToolbox中调用其他工具箱的函数。比如,为了优化Hammerstein模型中的非线性静态部分的参数,可以利用优化工具箱中的算法来实现:
% 定义目标函数,比如最小化输入输出数据之间的差异
fun = @(x) sum((y - sim(sys_hammerstein, u, x)).^2);
% 使用优化工具箱中的fmincon函数进行优化
options = optimoptions('fmincon', 'Display', 'iter', 'Algorithm', 'sqp');
x0 = get_param(sys_hammerstein, 'NonlinearFunctionParameters'); % 初始参数
x_opt = fmincon(fun, x0, [], [], [], [], lb, ub, [], options);
% 更新模型参数
set_param(sys_hammerstein, 'NonlinearFunctionParameters', num2str(x_opt));
在上述代码中, fmincon
函数用于求解非线性约束优化问题, sim
函数用于模型仿真。此过程尝试找到一组参数,使得模型输出与实际数据的差异最小化,从而实现模型参数的优化。
通过这些整合,Hammerstein模型不仅可以用于传统系统的建模和分析,还能适应更多特定领域的复杂应用需求。
3. 模型识别技术
模型识别是系统分析和控制系统设计的基础,尤其是在非线性系统的研究中,正确的模型识别能够为系统设计提供准确的数学描述。本章节着重于介绍Hammerstein模型的识别方法,以及在MATLAB中的实现,并对实现结果进行分析和验证。
3.1 系统辨识基础
系统辨识是通过实验数据来建立数学模型的过程,核心目的是找到能够最好描述系统行为的模型参数。辨识过程通常包括建立数学模型、收集实验数据、选择适当的辨识方法、模型验证与评估等步骤。
3.1.1 辨识流程与方法论
辨识流程通常遵循以下步骤:
- 问题定义 : 明确系统辨识的目标和需要满足的条件。
- 实验设计 : 规划必要的实验以获取数据,这些数据能够充分反映系统的动态行为。
- 数据收集 : 执行实验并收集数据,实验数据的质量直接影响辨识结果的准确性。
- 模型选择 : 选择合适的模型结构,这通常取决于系统特性和可获取数据的类型。
- 参数估计 : 使用收集到的数据来估计模型参数,这一步骤是系统辨识的核心。
- 模型验证 : 对模型进行检验,以确保其正确地反映了系统的动态行为。
3.1.2 系统参数估计原理
参数估计是通过最小化误差准则来确定模型参数的过程。常用的误差准则包括最小二乘法、极大似然估计等。参数估计过程可以被形式化为一个优化问题,通常表述为:
[ \min_{\theta} \sum_{i=1}^{N} [y(t_i) - f(u(t_i), \theta)]^2 ]
其中,( \theta ) 是模型参数向量,( y(t_i) ) 是系统输出,( u(t_i) ) 是系统输入,( f ) 是模型函数,( N ) 是数据点的总数。
3.2 Hammerstein模型的辨识方法
Hammerstein模型由一个非线性静态块和一个线性动态块组成,这种组合提供了一种强大的方法来描述具有非线性输入特性的动态系统。
3.2.1 输入输出数据预处理
在进行模型辨识之前,输入输出数据的预处理是非常关键的一步。这包括数据清洗(去除噪声和异常值)、数据转换(归一化、去趋势等),以及可能的数据分割(用于交叉验证等)。预处理的目的是确保数据质量和提高辨识算法的收敛速度。
3.2.2 非线性系统辨识策略
对于Hammerstein模型,非线性部分通常对应于输入静态映射,而线性部分则是时间序列模型。辨识策略包括:
- 分离辨识 : 分别辨识非线性静态块和线性动态块,这通常涉及到分步优化。
- 整体辨识 : 将非线性静态块和线性动态块的参数一并优化,这可以减少对初始估计的依赖,但增加了计算复杂度。
3.3 辨识算法的MATLAB实现
MATLAB提供了强大的数值计算和算法开发环境,通过编写和调试MATLAB代码,我们能够实现Hammerstein模型的参数辨识。
3.3.1 算法代码编写与调试
一个简单的Hammerstein模型辨识代码可能包含以下部分:
% 假设输入和输出数据已经通过预处理
u = ...; % 输入向量
y = ...; % 输出向量
% 初始化模型参数
theta_nonlinear = ...; % 非线性部分参数
theta_linear = ...; % 线性部分参数
% 定义非线性函数和线性模型
nonlinear_func = @(u, theta) ...; % 非线性函数
linear_model = ...; % 线性模型(如ARX模型)
% 辨识算法主循环
for iter = 1:max_iter
% 根据当前参数进行仿真
y_est = ...; % 使用非线性和线性模型预测输出
% 更新模型参数
[theta_nonlinear, theta_linear] = ... % 参数更新逻辑
% 评估辨识效果
error = ...; % 计算估计值和实际值之间的误差
if error < threshold % 达到误差容忍范围
break;
end
end
% 输出最终模型参数
disp(['最终非线性参数: ', num2str(theta_nonlinear)]);
disp(['最终线性参数: ', num2str(theta_linear)]);
3.3.2 结果分析与验证
结果验证是辨识过程中的最后一步,也是至关重要的一步。在这个阶段,我们使用交叉验证、残差分析和模型预测能力测试等方法来评估模型的有效性。为了确保模型的泛化能力,应当使用未参与辨识过程的数据来进行验证。
以下是交叉验证的MATLAB代码示例:
% 假设已经有一个辨识得到的模型
model = ...; % 辨识得到的Hammerstein模型
% 使用留一法交叉验证
for i = 1:length(u)
% 将第i个样本作为验证集,其余作为训练集
u_train = u([1:i-1 i+1:end]);
y_train = y([1:i-1 i+1:end]);
u_val = u(i);
y_val = y(i);
% 使用训练集训练模型
% ...
% 使用训练好的模型预测验证集的输出
y_val_est = ...; % 使用模型进行预测
% 计算误差并存储
validation_error(i) = norm(y_val - y_val_est);
end
% 输出交叉验证误差
disp(['交叉验证平均误差: ', num2str(mean(validation_error))]);
通过上述步骤,我们可以完成对Hammerstein模型的辨识,并通过结果验证来确保模型的有效性。在下一章节,我们将继续深入探讨如何优化模型参数,以进一步提升模型的性能。
4. 参数优化方法
4.1 参数估计与优化理论
参数优化是Hammerstein模型中的重要环节,它涉及目标函数的确定、约束条件的设置,以及选择合适的优化算法以实现参数的最优估计。在这一部分,我们将深入探讨参数估计与优化的基本理论,为后续的实操提供理论基础。
4.1.1 目标函数与约束条件
在优化问题中,目标函数是需要最小化(或最大化)的函数,它反映了系统的性能指标,如误差最小化。目标函数的选择通常基于系统的性能要求,例如,最小化预测误差的平方和,或者最大化系统的鲁棒性。
约束条件用于规定参数优化过程中必须满足的条件。这些条件可能包括参数值的上下界限制、动态系统稳定性的约束、或是其他任何对于系统运行至关重要的条件。
4.1.2 优化算法与收敛性
优化算法的目的是找到一组参数值,使得目标函数取得最优值。常见的优化算法包括梯度下降法、牛顿法、遗传算法和模拟退火算法等。每种算法的收敛速度、稳定性和对初始值的依赖性各有不同,选择合适的算法对于提高优化效率至关重要。
收敛性是指算法在迭代过程中能够不断接近最优解的能力。一个良好的优化算法应当具有良好的收敛性,即能够在有限的迭代次数内收敛到全局最优解或可接受的局部最优解。
4.2 优化技术在MATLAB中的应用
MATLAB提供了丰富的工具箱来支持参数优化,如Optimization Toolbox和Global Optimization Toolbox。在这一部分,我们将通过具体示例,展示如何在MATLAB中实现传统优化方法,并探讨先进算法在Hammerstein模型优化中的应用。
4.2.1 传统优化方法的MATLAB实现
传统的优化方法在MATLAB中实现起来相对简单,MATLAB的Optimization Toolbox提供了 fmincon
、 lsqnonlin
等函数,可以用来求解有约束和无约束的优化问题。
以下是一个使用 fmincon
函数进行优化的代码示例,此示例中我们将最小化一个简单的目标函数,并且满足一定的约束条件:
% 目标函数
f = @(x) (x(1) - 1)^2 + (x(2) - 2)^2;
% 非线性约束函数
nlcon = @(x) [x(1)^2 + x(2)^2 - 1; x(1) + x(2) - 1];
% 初始猜测
x0 = [0.5, 0.5];
% 无约束优化
x_opt = fminunc(f, x0);
% 带非线性约束的优化
options = optimoptions('fmincon', 'Display', 'iter', 'Algorithm', 'sqp');
[x_opt_constrained, fval] = fmincon(f, x0, [], [], [], [], [], [], nlcon, options);
% 输出结果
disp('Optimal point (Unconstrained):');
disp(x_opt);
disp('Optimal point (Constrained):');
disp(x_opt_constrained);
在此代码中,我们定义了目标函数 f
和非线性约束 nlcon
,并设置了一个初始猜测 x0
。然后我们调用 fminunc
进行无约束优化,并调用 fmincon
进行带有非线性约束的优化。通过设置优化选项,我们能够控制算法的运行细节,并监控优化过程中的迭代。
4.2.2 先进算法在Hammerstein模型中的应用案例
近年来,元启发式算法,如粒子群优化(PSO)、蚁群优化(ACO)和遗传算法(GA)等,在优化领域得到了广泛的关注和应用。这些算法的特点是能够有效地处理复杂的优化问题,并且对初始值不敏感。
以下是一个使用遗传算法在Hammerstein模型参数优化中应用的简单案例:
% 遗传算法优化参数
nvars = 2; % 参数数量
lb = [0, 0]; % 参数下界
ub = [1, 1]; % 参数上界
% 遗传算法选项设置
options = optimoptions('ga', 'PlotFcn', @gaplotbestf);
% 运行遗传算法
[best_x, best_f] = ga(f, nvars, [], [], [], [], lb, ub, [], options);
% 输出最优解
disp('Best solution:');
disp(best_x);
在上述代码中,我们定义了要优化的变量数量 nvars
和参数的上下界 lb
和 ub
。然后我们使用 ga
函数启动遗传算法,并设置一个回调函数 gaplotbestf
,以绘制出最佳适应度函数值的变化。最后,我们得到最优解 best_x
。
4.3 优化算法的性能评估
评估优化算法的性能,关键在于分析算法的效率以及参数设置对模型性能的影响。这一部分将讨论如何进行算法效率的对比分析和参数设置对模型性能的影响评估。
4.3.1 算法效率对比分析
算法效率对比分析通常涉及几个关键指标:收敛速度、计算时间、所需迭代次数以及结果的稳定性。为了进行这样的分析,需要在相同或类似的条件下,对不同的优化算法进行测试。
以下是一个简单的表格,总结了几种优化算法的效率比较:
| 算法名称 | 收敛速度 | 计算时间 | 迭代次数 | 稳定性 | |----------|----------|----------|----------|--------| | 梯度下降法 | 较快 | 较短 | 多 | 中 | | 牛顿法 | 快 | 较长 | 少 | 高 | | 遗传算法 | 慢 | 较长 | 不确定 | 高 |
4.3.2 参数设置对模型性能的影响
不同的参数设置会影响模型的性能。例如,在使用梯度下降法时,学习率的大小会直接影响算法的收敛速度和是否能收敛到全局最优解。
以下是一个展示不同学习率对模型性能影响的表格:
| 学习率 | 收敛速度 | 最终误差 | 迭代次数 | 结果稳定性 | |--------|----------|----------|----------|------------| | 0.001 | 慢 | 较大 | 少 | 高 | | 0.01 | 中 | 中等 | 中 | 中 | | 0.1 | 快 | 较小 | 多 | 低 |
在实际应用中,通常需要通过反复试验来确定最佳的参数设置。这样的试验可以通过编写脚本自动化进行,以便快速地评估不同参数对模型性能的影响。
在进行优化算法的选择和评估时,应考虑模型的具体应用场景、计算资源的可用性以及对结果的稳定性要求。正确地选择和调整优化算法可以显著提高模型性能,加速模型开发过程。
5. 模型验证与仿真
5.1 模型验证的基本概念
5.1.1 验证的目的与方法
在Hammerstein模型的开发和应用过程中,验证是确保模型预测能力和准确性的关键步骤。验证的目的是评估模型是否能够正确地反映系统的实际行为。通常,我们通过比较模型输出与实际系统输出的差异来进行验证。
验证的方法包括但不限于:
- 使用独立的测试数据集。
- 采用交叉验证技术。
- 比较模型性能指标,如均方误差(MSE)或决定系数(R²)。
5.1.2 模型与实际系统的对齐
为了使模型与实际系统对齐,需要确保模型输入输出的匹配。这通常涉及到数据的时间同步,确保在仿真过程中模型输入的数据时间点与实际系统中的时间点相对应。此外,还需要对模型进行校准,使其在特定的操作条件下表现良好。
5.2 仿真实验的设计与执行
5.2.1 仿真环境的搭建
仿真实验的环境搭建包括确定仿真的初始条件、设置模型参数以及准备仿真实验所需的输入数据。在MATLAB中,我们可能会使用 sim
函数或其他仿真函数来搭建仿真环境。
比如,我们可以建立如下的仿真环境:
% 定义仿真时间和步长
simulationTime = 100; % 总仿真时间
timeStep = 0.1; % 时间步长
% 定义模型参数
modelParams = [param1, param2, ..., paramN]; % 模型参数列表
% 准备输入数据,例如:
inputSignal = [sin(timeStep:timeStep:simulationTime), ...]; % 输入信号
% 执行仿真
[outputSignal, simulationResults] = simulateHammersteinModel(inputSignal, modelParams, timeStep);
5.2.2 实验结果的分析与解释
仿真实验完成后,对结果的分析与解释至关重要。我们通常会通过绘制输出信号和实际输出的对比图,以及计算性能指标来评估模型的表现。
% 绘制仿真结果
figure;
plot(simulationTime, simulationResults, 'b', 'LineWidth', 1.5);
hold on;
plot(simulationTime, realOutputData, 'r--', 'LineWidth', 1.5);
legend('仿真输出', '实际输出');
title('模型仿真结果对比图');
xlabel('时间');
ylabel('输出信号');
5.3 MATLAB仿真结果的应用
5.3.1 结果数据的导出与处理
仿真完成后,通常需要将结果数据导出到文件中以备进一步分析,或者用于与实际系统数据的比较。在MATLAB中,我们可以使用 save
或 csvwrite
等函数进行数据导出。
例如,将仿真结果导出到CSV文件:
% 假设我们已经得到了仿真结果和时间向量
csvwrite('simulationResults.csv', [timeVector, simulationResults]);
5.3.2 仿真结果对模型调整的指导作用
仿真结果可以为模型的进一步优化提供重要信息。根据结果数据和性能指标的评估,我们可能需要调整模型结构或参数。在Hammerstein模型中,这可能意味着重新调整非线性块或线性块的参数。
通过不断地迭代仿真和调整,我们可以逐渐逼近更精确的模型,从而提高模型在实际应用中的预测能力。这个过程是模型开发中不可或缺的一环,也是确保模型可靠性的关键步骤。
简介:Hammerstein模型是一种工程领域广泛使用的非线性系统建模方法,包括静态非线性和线性动态两个部分。在MATLAB环境中,HammersteinToolbox为工程师和研究人员提供了对这类模型进行估计、分析、识别和仿真的专门工具箱。该工具箱简化了模型级联结构元素的快速估算,支持从噪声数据中自动识别模型参数,并提供参数优化、模型验证与仿真、以及丰富的可视化工具。HammersteinToolbox通过Simulink集成,适用于电力系统、声学信号处理、机械系统建模等众多领域,有助于提高非线性系统分析和控制的精度。

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。
更多推荐
所有评论(0)