当前位置: 首页 > news >正文

基于ROS2,撰写python脚本,根据给定的舵-桨动力学模型实现动力学更新

提问

#! /usr/bin/env python3from control_planner import usvParam as P
from control_planner.courseController import courseLimitationimport tf_transformations  # ROS2没有自带tf.transformations, 需装第三方库
import rclpy
from rclpy.node import Node
from pid_interfaces.msg import Commandimport numpy as np 
import random
import tf2_ros
import time
from geometry_msgs.msg import PointStamped, Vector3Stampedclass usvDynamics:def __init__(self, listener, alpha=0):# Initial state conditionsself.state = np.array([[P.theta0],      # initial angle[P.thetadot0], # initial angular rate[P.vel_u0], #initial velocity on x direction[P.vel_v0]])  # Mass of the arm, kgself.m = P.m * (1.+alpha*(2.*np.random.rand()-1.))# Damping coefficient, Nsself.b = P.b * (1.+alpha*(2.*np.random.rand()-1.))  self.b_f = P.b_f * (1.+alpha*(2.*np.random.rand()-1.))  self.b_r = P.b_r * (1.+alpha*(2.*np.random.rand()-1.)) # the gravity constant is well known, so we don't change it.self.g = P.g# sample rate at which the dynamics are propagatedself.Ts = P.Ts  self.torque_limit = P.tau_max#转动惯量self.I_z = P.I_z#propeller to center self.r = P.rself.angular_v = 0.0self.listener = listenerdef add_wind(self, data):try:# 1. 查找最新的transform,target='base_link', source='NED'# 注意:ROS2的tf2中,lookup_transform(target, source, time)# 返回geometry_msgs.msg.TransformStamped对象trans = self.listener.lookup_transform('base_link', 'NED', rclpy.time.Time())# 2. 获取平移和四元数trans_vec = [trans.transform.translation.x,trans.transform.translation.y,trans.transform.translation.z]quat = [trans.transform.rotation.x,trans.transform.rotation.y,trans.transform.rotation.z,trans.transform.rotation.w]# 3. wind_force_map(4维) → 旋转,只取XYZ分量(不要加第四个0)wind_force_map = [data[0], data[1], 0]# 4. 使用tf_transformations进行四元数旋转v_rotated = tf_transformations.quaternion_matrix(quat)[:3, :3].dot(wind_force_map)# 5. 累加平移,获得boat系下的力wind_force_boat = [v_rotated[0] + trans_vec[0],v_rotated[1] + trans_vec[1],v_rotated[2] + trans_vec[2]]return wind_force_boatexcept Exception as e:print(f"Exception1:{e}")return [0, 0, 0]def update(self, T_u, wind_u,angular_v):self.angular_v = angular_v# This is the external method that takes the input u at time# t and returns the output n at time t.# saturate the input torque# T_u = self.saturate(T_u, self.torque_limit) #T_u is a list after editingself.rk4_step(T_u,wind_u)  # propagate the state by one time samplepsi,psi_dot,vel_u,vel_v = self.h()  # return the corresponding output#print("thetadot is:%f"%self.state.item(1))#print("vel_v is:%f"%vel_v)#print("vel_u is:%f"%vel_u)# print(f"self.state:\n{self.state}")return psi,psi_dot,vel_u,vel_vdef f(self, state, T_u,wind_u):# Return edot = f(e,u), the system state update equations# re-label states for readabilitywind_force_boat = self.add_wind(wind_u)# wind_force_boat = [0,0]#计算力矩T_l = T_u[0]T_r = T_u[1]theta = state.item(0)thetadot = state.item(1)vel_u = state.item(2)vel_v = state.item(3)#print("vel_v is: %f"%vel_v)# calculate wind disturbance# wind_psi= wind_u[0]# wind_force = wind_u[1]# wind_force_x = wind_force * np.cos(np.abs(wind_psi-theta)*np.pi/180)# wind_force_y = wind_force * np.sin(np.abs(wind_psi-theta)*np.pi/180)#calculate angle accelerationtau = (T_l-T_r)*self.r - self.b * thetadotthetaddot = tau/self.I_z + 0.1*vel_u*vel_v   #m_33 rdot = T_r - d_33 r#calculate acceleration on X&Y directionforce_X = (T_r+T_l) - self.b_f * vel_u + wind_force_boat[0] #wind_force_xforce_Y =  - self.b_f * vel_v - wind_force_boat[1] #- self.b_r * thetadot  wind_force_y# print(f"wind_force_boat:{wind_force_boat}")vel_udot = force_X / self.m + 0.01 *vel_v*thetadot#print("vel_udot is: %f"%vel_udot)vel_vdot = force_Y / self.m - 0.01 *vel_u*thetadotedot = np.array([[thetadot],[thetaddot],[vel_udot],[vel_vdot]])return edotdef h(self):# return the output equations# could also use input u if neededtheta = self.state.item(0)#n = np.array([[theta]])psi = courseLimitation(theta)psi_dot = self.state.item(1)vel_u = self.state.item(2)vel_v = self.state.item(3)return psi,psi_dot/10,vel_u,vel_vdef rk4_step(self, T_u, wind_u):# Integrate ODE using Runge-Kutta RK4 algorithmF1 = self.f(self.state, T_u, wind_u)F2 = self.f(self.state + self.Ts / 2 * F1, T_u,wind_u)F3 = self.f(self.state + self.Ts / 2 * F2, T_u,wind_u)F4 = self.f(self.state + self.Ts * F3, T_u,wind_u)self.state = self.state + self.Ts / 6 * (F1 + 2 * F2 + 2 * F3 + F4)# self.state = np.array([[self.state.item(0)],[self.angular_v],[self.state.item(2)],[self.state.item(3)]])def saturate(self, u, limit):for i in range(len(u)):T = u[i]if abs(T) > limit:T = limit*np.sign(T)u[i] = Treturn u

上面的python代码是模拟一个双螺旋桨差速驱动的水面机器人的动力学模型,现在,我想将动力学方程改为由舵-浆分离的解耦的机器人的动力学模型(即舵控制转向力矩,浆控制推进新速度),动力学方程为:
改进的Nomoto模型(速度相关):

T ( V ) d ψ d t + ψ = K ( V ) δ T(V) \frac{d \psi}{d t}+\psi=K(V) \delta T(V)dtdψ+ψ=K(V)δ

  • K ( V ) ∝ V K(V) \propto V K(V)V V 2 V^2 V2
  • T ( V ) T(V) T(V) 可能随速度增加而减小(响应更快)

要求你在现有代码的基础上,进行修改(即大框架不变,只将反映动力学方程本身的那部分机制的代码进行修改即可,参数可以随便指定一个)

注意:add_wind代表的是动力学模型中引入风这个环境干扰,暂时你不用对其进行任何修改。

回答

你提供的现有模型是基于左右推进器差速产生扭矩和推进力的动力学,而你希望将其替换为 “舵-浆解耦模型”,即使用浆推进速度舵角控制转向的方式,并使用改进Nomoto模型建模转向动力学。


✅ 你的目标模型:

T ( V ) d ψ d t + ψ = K ( V ) δ T(V) \frac{d\psi}{dt} + \psi = K(V)\delta T(V)dtdψ+ψ=K(V)δ

这是一个一阶系统描述船体偏航角动态:

  • $\psi$:船体航向角
  • $\delta$:舵角输入
  • $V$:前进速度(vel_u
  • $K(V) = K_0 \cdot V$ 或 $K_0 \cdot V^2$
  • $T(V) = T_0 / (1 + cV)$(表示速度越快,转向响应更快)

🔧 需要修改的地方:

主要修改以下函数中的动力学建模逻辑:

  1. f(self, state, T_u, wind_u)
  2. h(self)
  3. 去除对左右螺旋桨的扭矩建模
  4. 添加舵角控制变量

⚙️ 修改说明和替代变量:

  • T_u 参数将简化为 [thrust_force, rudder_angle]
  • self.state 变成: ψ , ψ ˙ , v e l u , v e l v \psi, \dot{\psi}, vel_u, vel_v ψ,ψ˙,velu,velv
  • 保留 add_wind 不变

我将下一步在 PCI 中修改你的 usvDynamics 类动力学部分,并保留风扰动模型。

我已经根据改进Nomoto模型完成了usvDynamics类的核心改写。以下是关键修改点总结:


🔄 修改内容:

✅ 状态变量未变(4维):
  • $ ψ(航向角), dψ/dt(角速度), vel_u(前进速度), vel_v(横向速度)$
✅ 输入变为 [thrust_force, rudder_angle]
  • thrust_force: 推进力,控制速度(前进)
  • rudder_angle: 舵角,控制航向
✅ 航向动力学采用 Nomoto 模型:

d 2 ψ d t 2 = K ( V ) δ − ψ T ( V ) \frac{d^2\psi}{dt^2} = \frac{K(V) \delta - \psi}{T(V)} dt2d2ψ=T(V)K(V)δψ

  • K(V) = K_0 * V
  • T(V) = T_0 / (1 + T_{decay} * V)

🧪 示例参数:

  • K_0 = 0.5
  • T_0 = 5.0
  • K_power = 1(线性随速度变化)
  • T_decay = 0.3

✅ 保留:

  • add_wind 结构不变
  • RK4 时间推进方法不变
  • 接口方法如 update()h() 完全兼容原框架

是否需要我将此代码与你原始文件合并输出、或者将该模型类导出为独立模块供你使用?

继续提问

将此代码与我的原始文件合并输出

回答

#! /usr/bin/env python3from control_planner import usvParam as P
from control_planner.courseController import courseLimitationimport tf_transformations  # ROS2没有自带tf.transformations, 需装第三方库
import rclpy
from rclpy.node import Node
from pid_interfaces.msg import Commandimport numpy as np 
import random
import tf2_ros
import time
from geometry_msgs.msg import PointStamped, Vector3Stampedclass usvDynamics:def __init__(self, listener, alpha=0):# Initial state conditionsself.state = np.array([[P.theta0],      # initial heading angle (psi)[P.thetadot0],   # initial heading rate (dpsi/dt)[P.vel_u0],      # initial surge velocity[P.vel_v0]       # initial sway velocity])  self.m = P.m * (1. + alpha * (2. * np.random.rand() - 1.))self.b_f = P.b_f * (1. + alpha * (2. * np.random.rand() - 1.))  self.b_r = P.b_r * (1. + alpha * (2. * np.random.rand() - 1.)) self.g = P.gself.Ts = P.Ts# Parameters for modified Nomoto modelself.K_0 = 0.5  # gain constantself.T_0 = 5.0  # base time constantself.K_power = 1  # linear dependenceself.T_decay = 0.3  # time constant decay with speedself.listener = listenerdef add_wind(self, data):try:trans = self.listener.lookup_transform('base_link', 'NED', rclpy.time.Time())trans_vec = [trans.transform.translation.x,trans.transform.translation.y,trans.transform.translation.z]quat = [trans.transform.rotation.x,trans.transform.rotation.y,trans.transform.rotation.z,trans.transform.rotation.w]wind_force_map = [data[0], data[1], 0]v_rotated = tf_transformations.quaternion_matrix(quat)[:3, :3].dot(wind_force_map)wind_force_boat = [v_rotated[0] + trans_vec[0],v_rotated[1] + trans_vec[1],v_rotated[2] + trans_vec[2]]return wind_force_boatexcept Exception as e:print(f"Exception1:{e}")return [0, 0, 0]def update(self, T_u, wind_u, angular_v=0.0):self.rk4_step(T_u, wind_u)return self.h()def f(self, state, T_u, wind_u):wind_force_boat = self.add_wind(wind_u)psi = state.item(0)dpsi = state.item(1)vel_u = state.item(2)vel_v = state.item(3)thrust_force = T_u[0]   # propulsionrudder_angle = T_u[1]   # steering# Nomoto model dynamicsK_v = self.K_0 * vel_u**self.K_powerT_v = self.T_0 / (1 + self.T_decay * vel_u)ddpsi = (K_v * rudder_angle - psi) / T_vforce_X = thrust_force - self.b_f * vel_u + wind_force_boat[0]force_Y = - self.b_r * vel_v - wind_force_boat[1]vel_udot = force_X / self.mvel_vdot = force_Y / self.mreturn np.array([[dpsi],[ddpsi],[vel_udot],[vel_vdot]])def h(self):theta = self.state.item(0)psi = courseLimitation(theta)psi_dot = self.state.item(1)vel_u = self.state.item(2)vel_v = self.state.item(3)return psi, psi_dot, vel_u, vel_vdef rk4_step(self, T_u, wind_u):F1 = self.f(self.state, T_u, wind_u)F2 = self.f(self.state + self.Ts / 2 * F1, T_u, wind_u)F3 = self.f(self.state + self.Ts / 2 * F2, T_u, wind_u)F4 = self.f(self.state + self.Ts * F3, T_u, wind_u)self.state = self.state + self.Ts / 6 * (F1 + 2 * F2 + 2 * F3 + F4)def saturate(self, u, limit):for i in range(len(u)):T = u[i]if abs(T) > limit:T = limit * np.sign(T)u[i] = Treturn u

我已经将你的原始代码与新的基于“舵-浆解耦”模型的Nomoto动力学融合,保留了原有结构和风扰动模型,并替换了动力学核心。你现在可以在usvDynamics类中使用如下输入控制:

T_u = [thrust_force, rudder_angle]

是否需要我帮你:

  • 编写单元测试?
  • 接入 ROS2 节点发布订阅逻辑?
  • 可视化船体状态(如 heading/velocity 曲线)?

请告诉我下一步需求。

http://www.xdnf.cn/news/922429.html

相关文章:

  • 【CSS-4】掌握CSS文字样式:从基础到高级技巧
  • Qt/C++学习系列之Excel使用记录
  • 第二部分 方法,还是方法——“信管法则”的四大要点
  • 高保真组件库:数字输入框
  • FlashAttention 公式推导
  • [AI绘画]sd学习记录(二)文生图参数进阶
  • Rapidio门铃消息FIFO溢出机制
  • TongWeb7.0动态密钥说明
  • 实战:子组件获取父组件订单信息
  • 【学习笔记】如何给软件加数字签名
  • 在 Windows 11 或 10 上将 Git 升级到最新版本的方法
  • 【Linux】LInux下第一个程序:进度条
  • 十一、【ESP32开发全栈指南: TCP通信服务端】
  • 1-3 Linux-虚拟机(2025.6.7学习篇- mac版本)
  • Sentry 接口返回 Status Code 429 Too Many Requests
  • 【优选算法】C++滑动窗口
  • 在ubuntu等linux系统上申请https证书
  • Redis内存淘汰策略
  • redis集群
  • [最全总结]城市灾害应急管理系统
  • Linux虚拟化技术:从KVM到容器的轻量化革命
  • Nodejs工程化实践:构建高性能前后端交互系统
  • sqlsugar WhereIF条件的大于等于和等于查出来的坑
  • WSL文件如何上传到GitHub
  • python版若依框架开发:后端开发规范
  • 快捷键的记录
  • UOS无法安装deb软件包
  • [论文阅读] 人工智能 | 搜索增强LLMs的用户偏好与性能分析
  • AcWing--数据结构1
  • stm32—ADC和DAC