Source code for suboptimumg.vehicle.suspension.suspension

from abc import ABC, abstractmethod

from ..models import VehicleModel


[docs] class Suspension(ABC): def __init__(self, vehicle_model: VehicleModel): self.vehicle_model = vehicle_model
[docs] @staticmethod @abstractmethod def lateral_weight_transfer(self, *args, **kwargs): pass
[docs] @staticmethod @abstractmethod def longitudinal_weight_transfer(self, *args, **kwargs): pass
[docs] class SimpleSuspension(Suspension): def __init__(self, vehicle_model: VehicleModel, **kwargs): super().__init__(vehicle_model=vehicle_model) self.params = self.vehicle_model.sus
[docs] def lateral_weight_transfer( self, axle_track: float, m_front: float, m_rear: float, cg_height: float, lat_acc_x: float, **kwargs, ) -> tuple[float, float]: """ Calculate lateral weight transfer using simple suspension model. Parameters ---------- axle_track : float Track width of the axle (m) m_front : float Mass on the front axle (kg) m_rear : float Mass on the rear axle (kg) cg_height : float Height of the center of gravity (m) lat_acc_x : float Lateral acceleration of the vehicle (m/s^2); positive means left turn **kwargs : dict Additional keyword arguments (unused) Returns ------- tuple[float, float] Lateral weight transfer (N) across specific axle, (left, right) Notes ----- Formula used: .. math:: |\\Delta F_{y,sus}| = \\frac{m \\cdot a_x \\cdot h_{CG}}{t} where :math:`\\Delta F_{y,sus}` is change in normal force on the suspension (N) """ # TODO: Standardize naming to avoid x/y coordinates and use long/lat instead. # Also, x in general should be long and y should be lat # TODO: add plausibility checks to inputed values mass = m_front + m_rear delta_f_y_total = (mass * lat_acc_x * cg_height) / axle_track left_delta = -delta_f_y_total right_delta = delta_f_y_total return (left_delta, right_delta)
[docs] def longitudinal_weight_transfer( self, wheelbase: float, total_mass: float, cg_height: float, long_acc_z: float, **kwargs, ) -> tuple[float, float]: """ Calculate longitudinal weight transfer using simple suspension model. Parameters ---------- wheelbase : float Wheelbase of the vehicle (m) total_mass : float Total mass of the vehicle (kg) cg_height : float Height of the center of gravity (m) long_acc_z : float Longitudinal acceleration of the vehicle (m/s^2); positive means go faster **kwargs : dict Additional keyword arguments (unused) Returns ------- tuple[float, float] Longitudinal weight transfer (N) (front, rear) Notes ----- Formula used: .. math:: |\\Delta F_{y,sus}| = \\frac{m \\cdot a_z \\cdot h_{CG}}{L} where :math:`\\Delta F_{y,sus}` is change in normal force on the suspension (N) """ # TODO: add plausibility checks to inputed values delta_f_y_total = (total_mass * long_acc_z * cg_height) / wheelbase front_delta = -delta_f_y_total rear_delta = delta_f_y_total return (front_delta, rear_delta)
[docs] class ComplexSuspension(Suspension): """ https://kktse.github.io/jekyll/update/2021/05/12/simplied-lateral-load-transfer-analysis.html Front & Rear Roll Stiffness includes: - Corner Spring/Torsion Bar Stiffness (Spring Rate, Motion Ratio, etc.) - Anti-roll bar stiffness (ARB Setting, Motion Ratio, etc.) """ def __init__( self, vehicle_model: VehicleModel, ): super().__init__(vehicle_model=vehicle_model) self.params = self.vehicle_model.sus # Hypothetical parameters for complex longitudinal weight transfer, right now unimplemented # self.k_theta_f = front_pitch_stiffness_k # self.k_theta_r = rear_pitch_stiffness_k # self.PC_h = pitch_center_height
[docs] def lateral_weight_transfer( self, axle: str, axle_track: float, m_front: float, m_rear: float, cg_height: float, lat_acc_x: float, **kwargs, ) -> tuple[float, float]: """ Calculate lateral weight transfer using complex suspension model. Parameters ---------- axle : str The position of the suspension (either 'front' or 'rear') axle_track : float The track width of the axle (m) m_front : float The mass on the front axle (kg) m_rear : float The mass on the rear axle (kg) cg_height : float The height of the center of gravity of the vehicle (m) lat_acc_x : float The lateral acceleration of the vehicle (m/s^2) **kwargs : dict Additional keyword arguments (unused) Returns ------- tuple[float, float] Force deltas due to lateral weight transfer (N) (left, right) Notes ----- This model is based on the paper referenced in the class docstring. Formula: .. math:: |\\Delta F_{y,axle}| = \\frac{m_{axle} \\cdot h_{RC,axle}}{t_{axle}} \\cdot a_x + \\frac{k_{\\phi,axle}}{k_{\\phi,axle} + k_{\\phi,opp}} \\cdot \\frac{m_{axle} \\cdot (h_{CG} - h_{RC,axle}) + m_{opp} \\cdot (h_{CG} - h_{RC,opp})}{t_{axle}} \\cdot a_x Where: - :math:`\\Delta F_{y,axle}` : change in normal force on the axle (N) - :math:`m_{axle}` : mass on the axle (kg) - :math:`h_{RC,axle}` : roll center height of the axle (m) - :math:`t_{axle}` : track width of the axle (m) - :math:`a_x` : lateral acceleration of the vehicle (m/s²) - :math:`k_{\\phi,axle}` : roll stiffness of the axle (Nm/rad) - :math:`h_{CG}` : height of the center of gravity (m) """ # Determine axle-specific parameters if axle == "front": h_RC_axle, h_RC_opp = ( self.params.front_roll_center_height, self.params.rear_roll_center_height, ) k_phi, k_phi_opp = ( self.params.front_roll_stiffness_k, self.params.rear_roll_stiffness_k, ) m_axle, m_axle_opp = m_front, m_rear elif axle == "rear": h_RC_axle, h_RC_opp = ( self.params.rear_roll_center_height, self.params.front_roll_center_height, ) k_phi, k_phi_opp = ( self.params.rear_roll_stiffness_k, self.params.front_roll_stiffness_k, ) m_axle, m_axle_opp = m_rear, m_front else: raise ValueError("Axle must be 'front' or 'rear'") # Calculate geometric load transfer geometric_load_transfer = (m_axle * h_RC_axle * lat_acc_x) / axle_track # Calculate roll stiffness ratio roll_stiffness_ratio = k_phi / (k_phi + k_phi_opp) # Calculate elastic load transfer elastic_load_transfer = roll_stiffness_ratio * ( (m_axle * (cg_height - h_RC_axle) + m_axle_opp * (cg_height - h_RC_opp)) * lat_acc_x / axle_track ) # Calculate total lateral weight transfer total_lateral_weight_transfer = geometric_load_transfer + elastic_load_transfer left_delta = -total_lateral_weight_transfer right_delta = total_lateral_weight_transfer return (left_delta, right_delta)
[docs] def longitudinal_weight_transfer( self, wheelbase: float, total_mass: float, cg_height: float, long_acc_z: float, **kwargs, ) -> tuple[float, float]: """ Calculate longitudinal weight transfer using complex suspension model. Parameters ---------- wheelbase : float Wheelbase of the vehicle (m) total_mass : float Total mass of the car (kg) cg_height : float Height of the center of gravity (m) long_acc_z : float Longitudinal acceleration of the vehicle (m/s^2) **kwargs : dict Additional keyword arguments (unused) Returns ------- tuple[float, float] Force deltas due to longitudinal weight transfer (N) (front, rear) Notes ----- This model was derived by Vedansh Goenka based on the paper referenced in the class docstring. Currently uses simplified model pending full implementation with pitch stiffness parameters. Formula: .. math:: \\Delta F_{y,axle} = \\frac{m \\cdot h_{PC}}{L} \\cdot a_z + \\frac{k_{\\theta,axle}}{k_{\\theta,axle} + k_{\\theta,opp}} \\cdot \\frac{m \\cdot (h_{CG} - h_{PC})}{L} \\cdot a_z Where: - :math:`\\Delta F_{y,axle}` : change in normal force on the axle (N) - :math:`m` : mass on the axle (kg) - :math:`h_{PC}` : pitch center height (m) - :math:`L` : wheelbase of the vehicle (m) - :math:`a_z` : longitudinal acceleration of the vehicle (m/s²) - :math:`k_{\\theta,axle}` : pitch stiffness of the axle (Nm/rad) - :math:`h_{CG}` : height of the center of gravity (m) """ # Right now, complex longitudinal weight transfer is lacking parameters delta_f_y_total = (total_mass * long_acc_z * cg_height) / wheelbase front_delta = -delta_f_y_total rear_delta = delta_f_y_total return (front_delta, rear_delta)
# # Calculate geometric load transfer # geometric_load_transfer = (total_mass * self.PC_h * long_acc_z) / wheelbase # # Calculate elastic load transfer # elastic_load_transfer = ( # total_mass * (cg_height - self.PC_h) * long_acc_z # ) / wheelbase # # Calculate total pitch stiffness # total_pitch_stiffness = self.k_theta_f + self.k_theta_r # # Calculate elastic load transfer distribution # front_elastic_ratio = self.k_theta_f / total_pitch_stiffness # rear_elastic_ratio = self.k_theta_r / total_pitch_stiffness # # Calculate total load transfer for each axle # front_delta = -geometric_load_transfer - ( # front_elastic_ratio * elastic_load_transfer # ) # rear_delta = geometric_load_transfer + ( # rear_elastic_ratio * elastic_load_transfer # ) # return (front_delta, rear_delta) # class Suspension: # def __init__( # self, # front_roll_k: float, # rear_roll_k: float, # front_z: float, # rear_z: float, # front_h: float, # rear_h: float, # torsion_k: float, # front_d: float, # rear_d: float, # front_m_sprung: float, # rear_m_sprung: float, # front_m_unsprung: float, # rear_m_unsprung: float, # **kwargs, # ): # self.torsion_k = torsion_k # torsion stiffness (TODO: check units) # # Front Suspension # self.front_roll_k = front_roll_k # front roll stiffness (TODO: check units) # self.front_z = front_z # front vertical stiffness (TODO: check units) # self.front_h = front_h # front roll center height (m) # self.front_d = front_d # front damping coefficient (TODO: check units) # self.front_m_sprung = front_m_sprung # front sprung mass (kg) # self.front_m_unsprung = front_m_unsprung # front unsprung mass (kg) # # Rear Suspension # self.rear_roll_k = rear_roll_k # rear roll stiffness (TODO: check units) # self.rear_z = rear_z # rear vertical stiffness (TODO: check units) # self.rear_h = rear_h # rear roll center height (m) # self.rear_d = rear_d # rear damping coefficient (TODO: check units) # self.rear_m_sprung = rear_m_sprung # rear sprung mass (kg) # self.rear_m_unsprung = rear_m_unsprung # rear unsprung mass (kg) # def lat_wt_sus(self, axle, lat_acc, axle_track): # """ # Calculates the lateral weight transfer using a suspension model # Args: # axle (str): The position of the suspension (either 'front' or 'rear'). # lat_acc (float): The lateral acceleration of the vehicle. # axle_track (float): The front or rear track width of the vehicle. # Returns: # float: Lateral weight transfer (N) # """ # if axle == "front": # roll_k = self.front_roll_k # z = self.front_z # h = self.front_h # d = self.front_d # m_sprung = self.front_m_sprung # m_unsprung = self.front_m_unsprung # opp_roll_k = self.rear_roll_k # opp_d = self.rear_d # opp_m_sprung = self.rear_m_sprung # elif axle == "rear": # roll_k = self.rear_roll_k # z = self.rear_z # h = self.rear_h # d = self.rear_d # m_sprung = self.rear_m_sprung # m_unsprung = self.rear_m_unsprung # opp_roll_k = self.front_roll_k # opp_d = self.front_d # opp_m_sprung = self.front_m_sprung # else: # raise ValueError("Position must be 'front' or 'rear'") # lat_wt_sus = ( # lat_acc # / axle_track # * ( # (roll_k * d * m_sprung) # / ( # roll_k # + (opp_roll_k * self.torsion_k / (opp_roll_k + self.torsion_k)) # ) # + ( # (roll_k * self.torsion_k / (roll_k + self.torsion_k)) # * opp_d # * opp_m_sprung # ) # / ((roll_k * self.torsion_k / (roll_k + self.torsion_k)) + opp_roll_k) # + z * m_sprung # + h * m_unsprung # ) # ) # return lat_wt_sus