【Nomoto 船舶模型】
【Nomoto 船舶模型】
- 1. Nomoto 船舶模型简介
- 2. 来源及发展历程
- 3. 构建 一阶模型Nomoto 船舶模型
- 3.1 C++ 实现
- 3.2 Python 实现
- 3.3 说明
- 5. 参数辨识方法
- 5.1 基于最小二乘法的参数辨识
- 5.2 数学推导
- 5.3 Python 实现
- 5.4 说明
- 4. 结论
- 参考文献
1. Nomoto 船舶模型简介
Nomoto 模型是由日本学者 T. Nomoto 于上世纪50年代提出的一种用于描述船舶操纵运动的数学模型。它最初是为了解决船舶在操舵时的动态响应问题,并且因其简单性和实用性而被广泛应用于航海工程和船舶自动控制领域。Nomoto 模型通过简化实际的水动力特性来预测船舶在舵角输入下的响应,适用于初步分析和控制系统设计。
参考学习:https://github.com/martinlarsalbert/blog/blob/master/_notebooks/2020-08-25-nomoto_model.ipynb
2. 来源及发展历程
Nomoto 模型的发展经历了多个阶段,从最初的一阶模型扩展到二阶甚至更高阶模型,以更准确地描述船舶的动态行为。尽管存在更复杂的模型(如 Abkowitz 整体模型、MMG 分离型模型、Fossen 的矩阵向量形式模型),Nomoto 模型由于其简洁性仍然在许多应用中具有重要地位。
3. 构建 一阶模型Nomoto 船舶模型
Nomoto 模型通常用以下微分方程表示:
τ 1 d ψ d t + ψ = K δ \tau_1 \frac{d\psi}{dt} + \psi = K \delta τ1dtdψ+ψ=Kδ
其中:
- ψ \psi ψ 是船舶的航向角(偏航角),单位为弧度。
- δ \delta δ是舵角,单位为弧度。
- K K K 是比例增益,表征舵角对航向角变化的影响程度。
- τ 1 \tau_1 τ1 是时间常数,代表系统的时间响应特性。
3.1 C++ 实现
下面是一个简单的C++代码示例,用于模拟Nomoto模型的行为:
#include <iostream>
#include <cmath>
class NomotoModel {
public:
NomotoModel(double tau, double K) : tau_(tau), K_(K), psi_(0.0), dpsi_dt_(0.0) {}
// 更新状态
void update(double delta, double dt) {
// 计算导数
double d2psi_dt2 = (-1.0 / tau_) * dpsi_dt_ + (K_ / tau_) * delta;
// 使用欧拉法更新状态
dpsi_dt_ += d2psi_dt2 * dt;
psi_ += dpsi_dt_ * dt;
// 确保航向角在 [-pi, pi] 范围内
while (psi_ > M_PI) psi_ -= 2 * M_PI;
while (psi_ < -M_PI) psi_ += 2 * M_PI;
}
// 获取当前航向角
double getHeading() const { return psi_; }
private:
double tau_, K_; // 模型参数
double psi_; // 当前航向角
double dpsi_dt_; // 当前航向角速度
};
int main() {
// 定义模型参数
double tau = 5.0; // 时间常数
double K = 0.05; // 比例增益
// 创建 Nomoto 模型实例
NomotoModel model(tau, K);
// 模拟时间步长和总时间
double dt = 0.1; // 时间步长 (秒)
double totalTime = 30.0; // 总模拟时间 (秒)
// 设置初始舵角
double delta = M_PI / 6; // 初始舵角为 30 度
for (double t = 0; t <= totalTime; t += dt) {
// 更新模型状态
model.update(delta, dt);
// 输出当前时间和航向角
std::cout << "Time: " << t << "s, Heading: "
<< model.getHeading() * 180.0 / M_PI << " degrees" << std::endl;
}
return 0;
}
3.2 Python 实现
下面是等效的Python代码实现:
import math
class NomotoModel:
def __init__(self, tau, K):
self.tau_ = tau
self.K_ = K
self.psi_ = 0.0
self.dpsi_dt_ = 0.0
# 更新状态
def update(self, delta, dt):
# 计算导数
d2psi_dt2 = (-1.0 / self.tau_) * self.dpsi_dt_ + (self.K_ / self.tau_) * delta
# 使用欧拉法更新状态
self.dpsi_dt_ += d2psi_dt2 * dt
self.psi_ += self.dpsi_dt_ * dt
# 确保航向角在 [-pi, pi] 范围内
while self.psi_ > math.pi:
self.psi_ -= 2 * math.pi
while self.psi_ < -math.pi:
self.psi_ += 2 * math.pi
# 获取当前航向角
def get_heading(self):
return self.psi_
if __name__ == "__main__":
# 定义模型参数
tau = 5.0 # 时间常数
K = 0.05 # 比例增益
# 创建 Nomoto 模型实例
model = NomotoModel(tau, K)
# 模拟时间步长和总时间
dt = 0.1 # 时间步长 (秒)
total_time = 30.0 # 总模拟时间 (秒)
# 设置初始舵角
delta = math.pi / 6 # 初始舵角为 30 度
for t in range(int(total_time / dt)):
current_time = t * dt
# 更新模型状态
model.update(delta, dt)
# 输出当前时间和航向角
print(f"Time: {current_time:.1f}s, Heading: {math.degrees(model.get_heading()):.2f} degrees")
3.3 说明
-
参数解释:
tau
:时间常数,反映了系统的惯性特性。K
:比例增益,决定了舵角对航向角变化的影响程度。
-
数值积分方法:
- 在上述实现中,使用了简单的欧拉法进行数值积分。对于更精确的仿真,可以考虑使用更高阶的数值积分方法,如龙格-库塔法(Runge-Kutta method)。
-
角度范围限制:
- 为了确保航向角保持在合理的范围内,使用了
while
循环将角度限制在 ([-π, π]) 之间。
Nomoto 模型参数辨识是指通过实验数据或仿真结果来确定模型中的未知参数,如时间常数 (\tau) 和比例增益 (K)。对于 Nomoto 一阶模型:
- 为了确保航向角保持在合理的范围内,使用了
τ d ψ d t + ψ = K δ \tau \frac{d\psi}{dt} + \psi = K \delta τdtdψ+ψ=Kδ
其中:
- KaTeX parse error: Undefined control sequence: \ps at position 1: \̲p̲s̲是船舶的航向角(偏航角)。
- δ \delta δ是舵角。
- KaTeX parse error: Undefined control sequence: \K at position 1: \̲K̲是比例增益,表征舵角对航向角变化的影响程度。
- τ \tau τ 是时间常数,代表系统的时间响应特性。
5. 参数辨识方法
实现参数辨识通常需要以下步骤:
- 实验设计:进行操舵实验,记录船舶在不同舵角下的航向角随时间的变化情况。
- 数据预处理:清洗和整理实验数据,确保其适用于后续分析。
- 选择辨识方法:根据问题的具体情况选择合适的参数辨识方法,常用的有最小二乘法、递推最小二乘法、极大似然估计等。
- 参数优化:利用选定的方法估计模型参数,使模型预测值与实际观测值之间的误差最小化。
下面介绍一种基于最小二乘法的简单参数辨识方法,并提供相应的 Python 实现示例。
5.1 基于最小二乘法的参数辨识
假设我们已经有一组实验数据,包含时间序列 t
、舵角序列 delta
和航向角序列 psi
。我们的目标是找到最优的 (\tau) 和 (K),使得模型预测值与实际观测值之间的误差平方和最小。
5.2 数学推导
首先将 Nomoto 模型重写为离散形式:
ψ k + 1 = ( 1 − Δ t τ ) ψ k + Δ t τ K δ k \psi_{k+1} = (1 - \frac{\Delta t}{\tau}) \psi_k + \frac{\Delta t}{\tau} K \delta_k ψk+1=(1−τΔt)ψk+τΔtKδk
其中 Δ t \Delta t Δt 是采样间隔, k k k 表示第 k k k 个采样点。
定义误差函数 E E E 为:
E ( τ , K ) = ∑ k = 1 N ( ψ k model − ψ k data ) 2 E(\tau, K) = \sum_{k=1}^{N} (\psi_k^{\text{model}} - \psi_k^{\text{data}})^2 E(τ,K)=k=1∑N(ψkmodel−ψkdata)2
其中 ψ k model \psi_k^{\text{model}} ψkmodel是模型预测的航向角, ψ k data \psi_k^{\text{data}} ψkdata 是实验数据中的航向角。
我们的目标是最小化 E ( τ , K ) E(\tau, K) E(τ,K)。
5.3 Python 实现
以下是使用 Python 和 SciPy 库进行参数辨识的示例代码:
import numpy as np
from scipy.optimize import minimize
import matplotlib.pyplot as plt
# 定义模型函数
def nomoto_model(params, delta, dt):
tau, K = params
psi_model = [0] # 初始航向角设为0
for k in range(1, len(delta)):
psi_next = (1 - dt / tau) * psi_model[-1] + (dt / tau) * K * delta[k]
psi_model.append(psi_next)
return np.array(psi_model)
# 定义误差函数
def error_function(params, delta, dt, psi_data):
psi_model = nomoto_model(params, delta, dt)
return np.sum((psi_model - psi_data) ** 2)
# 生成模拟数据(用于测试)
np.random.seed(0)
time = np.linspace(0, 30, 300)
dt = time[1] - time[0]
delta = np.zeros_like(time)
delta[50:100] = np.radians(10) # 设置舵角为10度
delta[150:200] = np.radians(-10) # 设置舵角为-10度
# 真实参数
true_tau = 5.0
true_K = 0.05
# 使用真实参数生成模拟的航向角数据
psi_true = nomoto_model([true_tau, true_K], delta, dt)
psi_data = psi_true + np.random.normal(0, 0.01, size=psi_true.shape) # 添加噪声
# 参数辨识
initial_guess = [1.0, 0.1] # 初始猜测值
result = minimize(error_function, initial_guess, args=(delta, dt, psi_data), method='L-BFGS-B')
estimated_tau, estimated_K = result.x
print(f"Estimated parameters: tau = {estimated_tau:.3f}, K = {estimated_K:.3f}")
# 绘制结果
plt.figure(figsize=(10, 6))
plt.plot(time, np.degrees(psi_data), label="Noisy Data", linestyle='--')
plt.plot(time, np.degrees(nomoto_model([true_tau, true_K], delta, dt)), label="True Model")
plt.plot(time, np.degrees(nomoto_model(result.x, delta, dt)), label="Estimated Model")
plt.xlabel("Time (s)")
plt.ylabel("Heading Angle (degrees)")
plt.legend()
plt.show()
5.4 说明
-
数据生成:为了测试辨识算法的效果,我们首先生成了一组带有噪声的模拟数据。这里使用了真实的参数 (\tau) 和 (K) 来生成模拟的航向角数据,并添加了一些随机噪声以模拟实际情况。
-
误差函数:定义了一个误差函数
error_function
,它计算模型预测值与实验数据之间的误差平方和。 -
参数辨识:使用 SciPy 的
minimize
函数来最小化误差函数,从而估计出最佳的 (\tau) 和 (K) 值。这里选择了 L-BFGS-B 方法作为优化算法,因为它适合处理有界的参数空间。 -
结果展示:最后,绘制了原始数据、真实模型输出以及辨识得到的模型输出,以便直观地比较它们之间的差异。
上述方法是一个简单的实现,适用于初步的参数辨识任务。在实际应用中,可能需要考虑以下几个方面进行改进:
- 更复杂的模型:如果需要更高的精度,可以考虑使用更高阶的 Nomoto 模型或其他更为复杂的船舶操纵模型。
- 鲁棒性:针对不同的噪声水平和数据质量,优化算法的选择和参数初始化可能会有所不同。
- 实时辨识:在某些应用场景下,可能需要在线实时地更新模型参数,这时可以考虑使用递推最小二乘法等在线学习算法。
4. 结论
Nomoto 模型提供了一种简便的方法来描述船舶的操纵行为,适用于初步分析和控制系统设计。尽管它有一定的局限性,但在很多情况下已经足够有效。对于更精确的模拟和控制需求,可以考虑使用更复杂的模型,如 MMG 分离型模型或 Fossen 的矩阵向量形式模型。
通过【Nomoto 船舶模型C++与python实现】学习,您应该能够用Nomoto 船舶模型描述船舶的操纵行为。从而实现对外部世界进行感知,充分认识这个有机与无机的环境,科学地合理地进行创作和发挥效益,然后为人类社会发展贡献一点微薄之力。🤣🤣🤣
- 我会持续更新对应专栏博客,非常期待你的三连!!!🎉🎉🎉
- 如果鹏鹏有哪里说的不妥,还请大佬多多评论指教!!!👍👍👍
- 下面有我的🐧🐧🐧群推广,欢迎志同道合的朋友们加入,期待与你的思维碰撞😘😘😘
参考文献
- Nomoto, K., Taguchi, K., Honda, K., & Hirano, S. (1957). On the Steering Qualities of Ships. International Shipbuilding Progress, 4(35), 354-370.
- Fossen, T. I. (2011). Handbook of Marine Craft Hydrodynamics and Motion Control. John Wiley & Sons.