Skip to content

Commit 2480ac5

Browse files
Merge pull request #16 from BradenMeyers/thruster_class
Thruster and Fin Class
2 parents f1cd1ac + 5de483f commit 2480ac5

File tree

6 files changed

+620
-2
lines changed

6 files changed

+620
-2
lines changed

src/python_vehicle_simulator/lib/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@
66
from .plotTimeSeries import *
77
from .guidance import *
88
from .control import *
9-
from .models import *
9+
from .models import *
10+
from .actuator import *
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import numpy as np
2+
3+
class fin:
4+
'''
5+
Represents a fin for hydrodynamic calculations.
6+
7+
INPUTS:
8+
a: fin area (m^2)
9+
CL: coefficient of lift (dimensionless)
10+
x: x distance (m) from center of vehicle (negative for behind COM)
11+
c: radius (m) from COP on the fin to the COM in the YZ plane
12+
angle: offset of fin angle around x axis (deg) starting from positive y
13+
0 deg: fin on left side looking from front
14+
90 deg: fin on bottom
15+
rho: density of fluid (kg/m^3)
16+
17+
Coordinate system: Right-handed, x-forward, y-starboard, z-down
18+
'''
19+
20+
def __init__(
21+
self,
22+
a,
23+
CL,
24+
x,
25+
c = 0,
26+
angle = 0,
27+
rho = 1026, # Default density of seawater
28+
):
29+
30+
self.area = a # Fin area (m^2)
31+
self.CL = CL # Coefficient of lift (dimensionless)
32+
self.angle_rad = np.deg2rad(angle)
33+
self.rho = rho # Fluid density (kg/m^3)
34+
35+
self.u_actual_fin = 0.0 #Actual position of the fin (rad)
36+
self.T_delta = 0.1 # fin time constant (s)
37+
self.deltaMax = np.deg2rad(15) # max rudder angle (rad)
38+
39+
# Calculate fin's Center of Pressure (COP) position relative to COB
40+
y = np.cos(self.angle_rad) * c # y-component of COP (m)
41+
z = np.sin(self.angle_rad) * c # z-component of COP (m)
42+
self.R = np.array([x, y, z]) # Location of COP of the fin relative to COB (m)
43+
44+
def velocity_in_rotated_plane(self, nu_r):
45+
"""
46+
Calculate velocity magnitude in a plane rotated around the x-axis.
47+
48+
Parameters:
49+
nu_r (numpy array): Velocity vector [vx, vy, vz] (m/s) in ENU frame
50+
51+
Returns:
52+
float: Magnitude of velocity (m/s) in the rotated plane.
53+
"""
54+
# Extract velocity components
55+
vx, vy, vz = nu_r # m/s
56+
57+
# Rotate y component around x-axis to align with fin plane
58+
vy_rot = np.sqrt((vy * np.sin(self.angle_rad))**2 + (vz * np.cos(self.angle_rad))**2)
59+
60+
# Calculate magnitude in the rotated plane (x, y')
61+
U_plane = np.sqrt(vx**2 + vy_rot**2)
62+
63+
return U_plane # m/s
64+
65+
def tau(self, nu_r, nu):
66+
"""
67+
Calculate force vector generated by the fin.
68+
69+
Parameters:
70+
nu_r (numpy array): Relative velocity [vx, vy, vz, p, q, r]
71+
(m/s for linear, rad/s for angular)
72+
nu (numpy array): Velocity [vx, vy, vz, p, q, r]
73+
(m/s for linear, rad/s for angular)
74+
75+
Returns:
76+
numpy array: tau vector [Fx, Fy, Fz, Tx, Ty, Tz] (N) and (N*m) in body-fixed frame
77+
"""
78+
79+
ur = self.velocity_in_rotated_plane(nu_r[:3]) # Calulate relative velocity in plane of the fin
80+
81+
# Calculate lift force magnitude
82+
f = 0.5 * self.rho * self.area * self.CL * self.u_actual_fin * ur**2 # N
83+
84+
# Decompose force into y and z components
85+
fy = np.sin(self.angle_rad) * f # N
86+
fz = -np.cos(self.angle_rad) * f # N
87+
88+
F = np.array([0, fy, fz]) # Force vector (N)
89+
90+
# Calculate torque using cross product of force and moment arm
91+
torque = np.cross(self.R, F) # N*m
92+
return np.append(F, torque)
93+
94+
def actuate(self, sampleTime, command):
95+
# Actuator dynamics
96+
delta_dot = (command - self.u_actual_fin) / self.T_delta
97+
self.u_actual_fin += sampleTime * delta_dot # Euler integration
98+
99+
# Amplitude Saturation
100+
if abs(self.u_actual_fin) >= self.deltaMax:
101+
self.u_actual_fin = np.sign(self.u_actual_fin) * self.deltaMax
102+
103+
return self.u_actual_fin
104+
105+
106+
107+
108+
109+
class thruster:
110+
'''
111+
Represents a thruster for hydrodynamic calculations.
112+
113+
INPUTS:
114+
rho: density of fluid (kg/m^3)
115+
116+
Coordinate system: Right-handed, x-forward, y-starboard, z-down
117+
'''
118+
def __init__(self, rho):
119+
# Actuator dynamics
120+
self.nMax = 1525 # max propeller revolution (rpm)
121+
self.T_n = 0.1 # propeller time constant (s)
122+
self.u_actual_n = 0.0 # actual rpm of the thruster
123+
self.rho = rho
124+
125+
def tau(self, nu_r, nu):
126+
"""
127+
Calculate force vector generated by the fin.
128+
129+
Parameters:
130+
nu_r (numpy array): Relative velocity [vx, vy, vz, p, q, r]
131+
(m/s for linear, rad/s for angular)
132+
nu (numpy array): Velocity [vx, vy, vz, p, q, r]
133+
(m/s for linear, rad/s for angular)
134+
135+
Returns:
136+
numpy array: tau vector [Fx, Fy, Fz, Tx, Ty, Tz] (N) and (N*m) in body-fixed frame
137+
"""
138+
U = np.sqrt(nu[0]**2 + nu[1]**2 + nu[2]**2) # vehicle speed
139+
140+
# Commands and actual control signals
141+
n = self.u_actual_n # actual propeller revolution (rpm)
142+
143+
# Amplitude saturation of the control signals
144+
if abs(n) >= self.nMax:
145+
n = np.sign(n) * self.nMax
146+
147+
# Propeller coeffs. KT and KQ are computed as a function of advance no.
148+
# Ja = Va/(n*D_prop) where Va = (1-w)*U = 0.944 * U; Allen et al. (2000)
149+
D_prop = 0.14 # propeller diameter corresponding to 5.5 inches
150+
t_prop = 0.1 # thrust deduction number
151+
n_rps = n / 60 # propeller revolution (rps)
152+
Va = 0.944 * U # advance speed (m/s)
153+
154+
# Ja_max = 0.944 * 2.5 / (0.14 * 1525/60) = 0.6632
155+
Ja_max = 0.6632
156+
157+
# Single-screw propeller with 3 blades and blade-area ratio = 0.718.
158+
# Coffes. are computed using the Matlab MSS toolbox:
159+
# >> [KT_0, KQ_0] = wageningen(0,1,0.718,3)
160+
KT_0 = 0.4566
161+
KQ_0 = 0.0700
162+
# >> [KT_max, KQ_max] = wageningen(0.6632,1,0.718,3)
163+
KT_max = 0.1798
164+
KQ_max = 0.0312
165+
166+
# Propeller thrust and propeller-induced roll moment
167+
# Linear approximations for positive Ja values
168+
# KT ~= KT_0 + (KT_max-KT_0)/Ja_max * Ja
169+
# KQ ~= KQ_0 + (KQ_max-KQ_0)/Ja_max * Ja
170+
171+
if n_rps > 0: # forward thrust
172+
173+
X_prop = self.rho * pow(D_prop,4) * (
174+
KT_0 * abs(n_rps) * n_rps + (KT_max-KT_0)/Ja_max *
175+
(Va/D_prop) * abs(n_rps) )
176+
K_prop = self.rho * pow(D_prop,5) * (
177+
KQ_0 * abs(n_rps) * n_rps + (KQ_max-KQ_0)/Ja_max *
178+
(Va/D_prop) * abs(n_rps) )
179+
180+
else: # reverse thrust (braking)
181+
182+
X_prop = self.rho * pow(D_prop,4) * KT_0 * abs(n_rps) * n_rps
183+
K_prop = self.rho * pow(D_prop,5) * KQ_0 * abs(n_rps) * n_rps
184+
185+
# Thrust force vector
186+
# K_Prop scaled down by a factor of 10 to match exp. results
187+
tau_thrust = np.array([(1-t_prop) * X_prop, 0, 0, K_prop / 10, 0, 0], float)
188+
return tau_thrust
189+
190+
def actuate(self, sampleTime ,command):
191+
# Actuator dynamics
192+
n_dot = (command - self.u_actual_n) / self.T_n
193+
194+
self.u_actual_n += sampleTime * n_dot
195+
196+
return self.u_actual_n
197+

src/python_vehicle_simulator/lib/mainLoop.py

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def printSimInfo():
3939
print('7 - Offshore supply vessel: controlled by tunnel thrusters and main propellers, L = 76.2 m')
4040
print('8 - Tanker: rudder-controlled ship model including shallow water effects, L = 304.8 m')
4141
print('9 - Remus 100: AUV controlled by stern planes, a tail rudder and a propeller, L = 1.6 m')
42+
print('10 - Torpedo: AUV controlled by configurable fins and a propeller, L = 1.6 m')
4243
print('---------------------------------------------------------------------------------------')
4344

4445
###############################################################################

src/python_vehicle_simulator/main.py

+2
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
supply('DPcontrol',x_d,y_d,psi_d,V_c,beta_c)
4242
tanker('headingAutopilot',psi_d,V_c,beta_c,depth)
4343
remus100('depthHeadingAutopilot',z_d,psi_d,V_c,beta_c)
44+
torpedo('depthHeadingAutopilot',z_d,psi_d,V_c,beta_c)
4445
4546
Call constructors without arguments to test step inputs, e.g. DSRV(), otter(), etc.
4647
"""
@@ -57,6 +58,7 @@
5758
case '7': vehicle = supply('DPcontrol',4.0,4.0,50.0,0.5,20.0)
5859
case '8': vehicle = tanker('headingAutopilot',-20,0.5,150,20,80)
5960
case '9': vehicle = remus100('depthHeadingAutopilot',30,50,1525,0.5,170)
61+
case '10': vehicle = torpedo('depthHeadingAutopilot',30,50,1525,0.5,170)
6062
case _: print('Error: Not a valid simulator option'), sys.exit()
6163

6264
printVehicleinfo(vehicle, sampleTime, N)

src/python_vehicle_simulator/vehicles/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@
99
from .shipClarke83 import *
1010
from .supply import *
1111
from .tanker import *
12-
from .remus100 import *
12+
from .remus100 import *
13+
from .torpedo import *

0 commit comments

Comments
 (0)