Skip to content

Commit e77dd68

Browse files
committed
fin class initial test
1 parent f6a3e4f commit e77dd68

File tree

2 files changed

+112
-1
lines changed

2 files changed

+112
-1
lines changed

src/python_vehicle_simulator/lib/__init__.py

Lines changed: 2 additions & 1 deletion
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 .fin import *
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
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+
cs: control subsystem the fin is going to actuate for
16+
rho: density of fluid (kg/m^3)
17+
18+
Coordinate system: Right-handed, x-forward, y-starboard, z-down
19+
'''
20+
21+
22+
######################### TODO add actuator dynamics
23+
# - saturating
24+
def __init__(
25+
self,
26+
a,
27+
CL,
28+
x,
29+
c = 0,
30+
angle = 0,
31+
control_subsystem='heading',
32+
rho = 1026, # Default density of seawater
33+
):
34+
35+
self.area = a # Fin area (m^2)
36+
self.CL = CL # Coefficient of lift (dimensionless)
37+
self.angle_rad = np.deg2rad(angle) # Convert angle to radians
38+
self.control = control_subsystem
39+
self.rho = rho # Fluid density (kg/m^3)
40+
41+
self.fin_actual = 0.0 #Actual position of the fin (rad)
42+
self.T_delta = 0.1 # fin time constant (s)
43+
self.deltaMax = np.deg2rad(15) # max rudder angle (rad)
44+
45+
# Calculate fin's Center of Pressure (COP) position relative to COM
46+
y = np.cos(self.angle_rad) * c # y-component of COP (m)
47+
z = np.sin(self.angle_rad) * c # z-component of COP (m)
48+
self.R = np.array([x, y, z]) # Location of COP of the fin relative to COM (m)
49+
50+
def velocity_in_rotated_plane(self, nu_r):
51+
"""
52+
Calculate velocity magnitude in a plane rotated around the x-axis.
53+
54+
Parameters:
55+
nu_r (numpy array): Velocity vector [vx, vy, vz] (m/s) in ENU frame
56+
57+
Returns:
58+
float: Magnitude of velocity (m/s) in the rotated plane.
59+
"""
60+
# Extract velocity components
61+
vx, vy, vz = nu_r # m/s
62+
63+
# Rotate y component around x-axis to align with fin plane
64+
vy_rot = np.sqrt((vy * np.sin(self.angle_rad))**2 + (vz * np.cos(self.angle_rad))**2)
65+
66+
# Calculate magnitude in the rotated plane (x, y')
67+
U_plane = np.sqrt(vx**2 + vy_rot**2)
68+
69+
return U_plane # m/s
70+
71+
def torque(self, Ur):
72+
"""
73+
Calculate torque generated by the fin.
74+
75+
Parameters:
76+
Ur (numpy array): Relative velocity [vx, vy, vz, p, q, r]
77+
(m/s for linear, rad/s for angular)
78+
delta (float): Deflection angle of fin in radians
79+
(positive: CW rotation of fin and trailing edge)
80+
81+
Returns:
82+
numpy array: tau vector [Fx, Fy, Fz, Tx, Ty, Tz] (N*m) in body-fixed frame
83+
"""
84+
85+
ur = self.velocity_in_rotated_plane(Ur[:3]) # Use only linear velocities
86+
87+
# Calculate lift force magnitude
88+
f = 0.5 * self.rho * self.area * self.CL * self.fin_actual * ur**2 # N
89+
90+
# Decompose force into y and z components
91+
fy = np.sin(self.angle_rad) * f # N
92+
fz = -np.cos(self.angle_rad) * f # N
93+
94+
F = np.array([0, fy, fz]) # Force vector (N)
95+
# print("Force", F)
96+
# print("Radius:", self.R)
97+
98+
# Calculate torque using cross product of force and moment arm
99+
torque = np.cross(self.R, F) # N*m
100+
return np.append(F, torque)
101+
102+
def actuate(self, sampleTime, command):
103+
104+
delta_dot = (command - self.fin_actual) / self.T_delta
105+
self.fin_actual += sampleTime * delta_dot
106+
107+
if abs(self.fin_actual) >= self.deltaMax:
108+
self.fin_actual = np.sign(self.fin_actual) * self.deltaMax
109+
110+
return self.fin_actual

0 commit comments

Comments
 (0)