Facebook
From Toxic Pheasant, 1 Year ago, written in Python.
Embed
Download Paste or View Raw
Hits: 115
  1. import numpy as np
  2. import math as math
  3. from scipy.integrate import solve_ivp, simps
  4. from scipy.interpolate import interp1d
  5. from scipy.integrate import simps
  6.  
  7. def define_rover_1():   # or define_rover()
  8.     # Initialize Rover dict for testing
  9.     wheel = {'radius':0.30,
  10.              'mass':1}
  11.     speed_reducer = {'type':'reverted',
  12.                      'diam_pinion':0.04,
  13.                      'diam_gear':0.07,
  14.                      'mass':1.5}
  15.     motor = {'torque_stall':170,
  16.              'torque_noload':0,
  17.              'speed_noload':3.80,
  18.              'mass':5.0}
  19.    
  20.        
  21.     chassis = {'mass':659}
  22.     science_payload = {'mass':75}
  23.     power_subsys = {'mass':90}
  24.    
  25.     wheel_assembly = {'wheel':wheel,
  26.                       'speed_reducer':speed_reducer,
  27.                       'motor':motor}
  28.    
  29.     rover = {'wheel_assembly':wheel_assembly,
  30.              'chassis':chassis,
  31.              'science_payload':science_payload,
  32.              'power_subsys':power_subsys}
  33.    
  34.     planet = {'g':3.72}
  35.    
  36.     # return everything we need
  37.     return rover, planet
  38.  
  39. def get_mass(rover):
  40.     """
  41.    Inputs:  rover:  dict      Data structure containing rover parameters
  42.    
  43.    Outputs:     m:  scalar    Rover mass [kg].
  44.    """
  45.    
  46.     # Check that the input is a dict
  47.     if type(rover) != dict:
  48.         raise Exception('Input must be a dict')
  49.    
  50.     # add up mass of chassis, power subsystem, science payload,
  51.     # and components from all six wheel assemblies
  52.     m = rover['chassis']['mass'] \
  53.         + rover['power_subsys']['mass'] \
  54.         + rover['science_payload']['mass'] \
  55.         + 6*rover['wheel_assembly']['motor']['mass'] \
  56.         + 6*rover['wheel_assembly']['speed_reducer']['mass'] \
  57.         + 6*rover['wheel_assembly']['wheel']['mass'] \
  58.    
  59.     return m
  60.  
  61.  
  62. def get_gear_ratio(speed_reducer):
  63.     """
  64.    Inputs:  speed_reducer:  dict      Data dictionary specifying speed
  65.                                        reducer parameters
  66.    Outputs:            Ng:  scalar    Speed ratio from input pinion shaft
  67.                                        to output gear shaft. Unitless.
  68.    """
  69.    
  70.     # Check that 1 input has been given.
  71.     #   IS THIS NECESSARY ANYMORE????
  72.    
  73.     # Check that the input is a dict
  74.     if type(speed_reducer) != dict:
  75.         raise Exception('Input must be a dict')
  76.    
  77.     # Check 'type' field (not case sensitive)
  78.     if speed_reducer['type'].lower() != 'reverted':
  79.         raise Exception('The speed reducer type is not recognized.')
  80.    
  81.     # Main code
  82.     d1 = speed_reducer['diam_pinion']
  83.     d2 = speed_reducer['diam_gear']
  84.    
  85.     Ng = (d2/d1)**2
  86.    
  87.     return Ng
  88.  
  89.  
  90. def tau_dcmotor(omega, motor):
  91.     """
  92.    Inputs:  omega:  numpy array      Motor shaft speed [rad/s]
  93.             motor:  dict             Data dictionary specifying motor parameters
  94.    Outputs:   tau:  numpy array      Torque at motor shaft [Nm].  Return argument
  95.                                      is same size as first input argument.
  96.    """
  97.    
  98.     # Check that 2 inputs have been given
  99.     #   IS THIS NECESSARY ANYMORE????
  100.    
  101.     # Check that the first input is a scalar or a vector
  102.     if (type(omega) != int) and (type(omega) != float) and (not isinstance(omega, np.ndarray)):
  103.         raise Exception ('First input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  104.     elif not isinstance(omega, np.ndarray):
  105.         omega = np.array([omega],dtype=float) # make the scalar a numpy array
  106.     elif len(np.shape(omega)) != 1:
  107.         raise Exception('First input must be a scalar or a vector. Matrices are not allowed.')
  108.  
  109.     # Check that the second input is a dict
  110.     if type(motor) != dict:
  111.         raise Exception('Second input must be a dict')
  112.        
  113.     # Main code
  114.     tau_s    = motor['torque_stall']
  115.     tau_nl   = motor['torque_noload']
  116.     omega_nl = motor['speed_noload']
  117.    
  118.     # initialize
  119.     tau = np.zeros(len(omega),dtype = float)
  120.     for i in range(len(omega)):
  121.         if omega[i] >= 0 and omega[i] <= omega_nl:
  122.             tau[i] = tau_s - (tau_s-tau_nl)/omega_nl *omega[i]
  123.         elif omega[i] < 0:
  124.             tau[i] = tau_s
  125.         elif omega[i] > omega_nl:
  126.             tau[i] = 0
  127.        
  128.     return tau
  129.    
  130.    
  131.  
  132.  
  133. def F_rolling(omega, terrain_angle, rover, planet, Crr):
  134.     """
  135.    Inputs:           omega:  numpy array     Motor shaft speed [rad/s]
  136.              terrain_angle:  numpy array     Array of terrain angles [deg]
  137.                      rover:  dict            Data structure specifying rover
  138.                                              parameters
  139.                    planet:  dict            Data dictionary specifying planetary
  140.                                              parameters
  141.                        Crr:  scalar          Value of rolling resistance coefficient
  142.                                              [-]
  143.    
  144.    Outputs:           Frr:  numpy array     Array of forces [N]
  145.    """
  146.    
  147.     # Check that the first input is a scalar or a vector
  148.     if (type(omega) != int) and (type(omega) != float) and (not isinstance(omega, np.ndarray)):
  149.         raise Exception('First input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  150.     elif not isinstance(omega, np.ndarray):
  151.         omega = np.array([omega],dtype=float) # make the scalar a numpy array
  152.     elif len(np.shape(omega)) != 1:
  153.         raise Exception('First input must be a scalar or a vector. Matrices are not allowed.')
  154.        
  155.     # Check that the second input is a scalar or a vector
  156.     if (type(terrain_angle) != int) and (type(terrain_angle) != float) and (not isinstance(terrain_angle, np.ndarray)):
  157.         raise Exception('Second input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  158.     elif not isinstance(terrain_angle, np.ndarray):
  159.         terrain_angle = np.array([terrain_angle],dtype=float) # make the scalar a numpy array
  160.     elif len(np.shape(terrain_angle)) != 1:
  161.         raise Exception('Second input must be a scalar or a vector. Matrices are not allowed.')
  162.        
  163.     # Check that the first two inputs are of the same size
  164.     if len(omega) != len(terrain_angle):
  165.         raise Exception('First two inputs must be the same size')
  166.    
  167.     # Check that values of the second input are within the feasible range  
  168.     if max([abs(x) for x in terrain_angle]) > 75:    
  169.         raise Exception('All elements of the second input must be between -75 degrees and +75 degrees')
  170.        
  171.     # Check that the third input is a dict
  172.     if type(rover) != dict:
  173.         raise Exception('Third input must be a dict')
  174.        
  175.     # Check that the fourth input is a dict
  176.     if type(planet) != dict:
  177.         raise Exception('Fourth input must be a dict')
  178.        
  179.     # Check that the fifth input is a scalar and positive
  180.     if (type(Crr) != int) and (type(Crr) != float):
  181.         raise Exception('Fifth input must be a scalar')
  182.     if Crr <= 0:
  183.         raise Exception('Fifth input must be a positive number')
  184.        
  185.     # Main Code
  186.     m = get_mass(rover)
  187.     g = planet['g']
  188.     r = rover['wheel_assembly']['wheel']['radius']
  189.     Ng = get_gear_ratio(rover['wheel_assembly']['speed_reducer'])
  190.    
  191.     v_rover = r*omega/Ng
  192.    
  193.     Fn = np.array([m*g*math.cos(math.radians(x)) for x in terrain_angle],dtype=float) # normal force
  194.     Frr_simple = -Crr*Fn # simple rolling resistance
  195.    
  196.     Frr = np.array([math.erf(40*v_rover[ii]) * Frr_simple[ii] for ii in range(len(v_rover))], dtype = float)
  197.    
  198.     return Frr
  199.  
  200.  
  201. def F_gravity(terrain_angle, rover, planet):
  202.     """
  203.    Inputs:  terrain_angle:  numpy array   Array of terrain angles [deg]
  204.                     rover:  dict          Data structure specifying rover
  205.                                            parameters
  206.                    planet:  dict          Data dictionary specifying planetary
  207.                                            parameters
  208.    
  209.    Outputs:           Fgt:  numpy array   Array of forces [N]
  210.    """
  211.    
  212.     # Check that the first input is a scalar or a vector
  213.     if (type(terrain_angle) != int) and (type(terrain_angle) != float) and (not isinstance(terrain_angle, np.ndarray)):
  214.         raise Exception('First input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  215.     elif not isinstance(terrain_angle, np.ndarray):
  216.         terrain_angle = np.array([terrain_angle],dtype=float) # make the scalar a numpy array
  217.     elif len(np.shape(terrain_angle)) != 1:
  218.         raise Exception('First input must be a scalar or a vector. Matrices are not allowed.')
  219.        
  220.     # Check that values of the first input are within the feasible range  
  221.     if max([abs(x) for x in terrain_angle]) > 75:    
  222.         raise Exception('All elements of the first input must be between -75 degrees and +75 degrees')
  223.  
  224.     # Check that the second input is a dict
  225.     if type(rover) != dict:
  226.         raise Exception('Second input must be a dict')
  227.    
  228.     # Check that the third input is a dict
  229.     if type(planet) != dict:
  230.         raise Exception('Third input must be a dict')
  231.        
  232.     # Main Code
  233.     m = get_mass(rover)
  234.     g = planet['g']
  235.    
  236.     Fgt = np.array([-m*g*math.sin(math.radians(x)) for x in terrain_angle], dtype = float)
  237.        
  238.     return Fgt
  239.  
  240.  
  241. def F_drive(omega, rover):
  242.     """
  243.    Inputs:  omega:  numpy array   Array of motor shaft speeds [rad/s]
  244.             rover:  dict          Data dictionary specifying rover parameters
  245.    
  246.    Outputs:    Fd:  numpy array   Array of drive forces [N]
  247.    """
  248.    
  249.     # Check that 2 inputs have been given.
  250.     #   IS THIS NECESSARY ANYMORE????
  251.    
  252.     # Check that the first input is a scalar or a vector
  253.     if (type(omega) != int) and (type(omega) != float) and (not isinstance(omega, np.ndarray)):
  254.         raise Exception('First input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  255.     elif not isinstance(omega, np.ndarray):
  256.         omega = np.array([omega],dtype=float) # make the scalar a numpy array
  257.     elif len(np.shape(omega)) != 1:
  258.         raise Exception('First input must be a scalar or a vector. Matrices are not allowed.')
  259.  
  260.     # Check that the second input is a dict
  261.     if type(rover) != dict:
  262.         raise Exception('Second input must be a dict')
  263.    
  264.     # Main code
  265.     Ng = get_gear_ratio(rover['wheel_assembly']['speed_reducer'])
  266.    
  267.     tau = tau_dcmotor(omega, rover['wheel_assembly']['motor'])
  268.     tau_out = tau*Ng
  269.    
  270.     r = rover['wheel_assembly']['wheel']['radius']
  271.    
  272.     # Drive force for one wheel
  273.     Fd_wheel = tau_out/r
  274.    
  275.     # Drive force for all six wheels
  276.     Fd = 6*Fd_wheel
  277.    
  278.     return Fd
  279.  
  280.  
  281. def F_net(omega, terrain_angle, rover, planet, Crr):
  282.     """
  283.    Inputs:           omega:  list     Motor shaft speed [rad/s]
  284.              terrain_angle:  list     Array of terrain angles [deg]
  285.                      rover:  dict     Data structure specifying rover
  286.                                      parameters
  287.                     planet:  dict     Data dictionary specifying planetary
  288.                                      parameters
  289.                        Crr:  scalar   Value of rolling resistance coefficient
  290.                                      [-]
  291.    
  292.    Outputs:           Fnet:  list     Array of forces [N]
  293.    """
  294.    
  295.     # Check that the first input is a scalar or a vector
  296.     if (type(omega) != int) and (type(omega) != float) and (not isinstance(omega, np.ndarray)):
  297.         raise Exception('First input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  298.     elif not isinstance(omega, np.ndarray):
  299.         omega = np.array([omega],dtype=float) # make the scalar a numpy array
  300.     elif len(np.shape(omega)) != 1:
  301.         raise Exception('First input must be a scalar or a vector. Matrices are not allowed.')
  302.        
  303.     # Check that the second input is a scalar or a vector
  304.     if (type(terrain_angle) != int) and (type(terrain_angle) != float) and (not isinstance(terrain_angle, np.ndarray)):
  305.         raise Exception('Second input must be a scalar or a vector. If input is a vector, it should be defined as a numpy array.')
  306.     elif not isinstance(terrain_angle, np.ndarray):
  307.         terrain_angle = np.array([terrain_angle],dtype=float) # make the scalar a numpy array
  308.     elif len(np.shape(terrain_angle)) != 1:
  309.         raise Exception('Second input must be a scalar or a vector. Matrices are not allowed.')
  310.        
  311.     # Check that the first two inputs are of the same size
  312.     if len(omega) != len(terrain_angle):
  313.         raise Exception('First two inputs must be the same size')
  314.    
  315.     # Check that values of the second input are within the feasible range  
  316.     if max([abs(x) for x in terrain_angle]) > 75:    
  317.         raise Exception('All elements of the second input must be between -75 degrees and +75 degrees')
  318.        
  319.     # Check that the third input is a dict
  320.     if type(rover) != dict:
  321.         raise Exception('Third input must be a dict')
  322.        
  323.     # Check that the fourth input is a dict
  324.     if type(planet) != dict:
  325.         raise Exception('Fourth input must be a dict')
  326.        
  327.     # Check that the fifth input is a scalar and positive
  328.     if (type(Crr) != int) and (type(Crr) != float):
  329.         raise Exception('Fifth input must be a scalar')
  330.     if Crr <= 0:
  331.         raise Exception('Fifth input must be a positive number')
  332.    
  333.     # Main Code
  334.     Fd = F_drive(omega, rover)
  335.     Frr = F_rolling(omega, terrain_angle, rover, planet, Crr)
  336.     Fg = F_gravity(terrain_angle, rover, planet)
  337.    
  338.     Fnet = Fd + Frr + Fg # signs are handled in individual functions
  339.    
  340.     return Fnet
  341.  
  342. def motorW(v, rover):
  343.     '''
  344.    General Description:
  345.    Compute the rotational speed of the motor shaft [rad/s] given the translational velocity of the rover and the rover
  346.    dictionary.This function should be “vectorized” such that if given a vector of rover velocities it returns a vector the same size
  347.    containing the corresponding motor speeds.
  348.    
  349.    Calling Syntax
  350.         w = motorW(v, rover)
  351.    Input Arguments
  352.         v -- 1D numpy array OR scalar float/int -- Rover translational velocity [m/s]
  353.         rover -- dictionary -- Data structure containing rover parameters
  354.    Return Arguments
  355.         w -- 1D numpy array OR scalar float/int -- Motor speed [rad/s]. Return argument should be the same size as the input argument v.
  356.    Additional Specifications and Notes
  357.        ▪ This function should validate (a) that the first input is a scalar or vector and (b) that the second input is a
  358.        dictionary. If any condition fails, call raise Exception().
  359.        ▪ This function should call get_gear_ratio from Phase 1.
  360.        ▪ Be wary of units.
  361.    '''
  362.    
  363.     # Validate input arguments
  364.     assert isinstance(v, (np.ndarray, float, int)), "v must be a scalar or 1D numpy array"
  365.     assert isinstance(rover, dict), "rover must be a dictionary"
  366.    
  367.     # Store frequently used parameters in variables
  368.     radius = rover['wheel_assembly']['wheel']['radius']
  369.     Ng = get_gear_ratio(rover['wheel_assembly']['speed_reducer'])
  370.    
  371.     # Compute motor speed
  372.     w = v / radius * Ng
  373.    
  374.     return w
  375.  
  376. def rover_dynamics(t, y, rover, planet, experiment):
  377.     '''
  378.    rover_dynamics
  379.    General Description
  380.    This function computes the derivative of the state vector (state vector is: [velocity, position]) for the rover given its
  381.    current state. It requires rover and experiment dictionary input parameters. It is intended to be passed to an ODE
  382.    solver.
  383.    '''
  384.  
  385.     # Validates input types and shapes
  386.     if not isinstance(t, (float, int, np.float64, np.int32)):
  387.         raise TypeError("t must be a scalar")
  388.     if not isinstance(y, np.ndarray) or y.shape != (2,):
  389.         raise TypeError("y must be a 1D numpy array of length 2")
  390.     if not isinstance(rover, dict):
  391.         raise TypeError("rover must be a dictionary")
  392.     if not isinstance(planet, dict):
  393.         raise TypeError("planet must be a dictionary")
  394.     if not isinstance(experiment, dict):
  395.         raise TypeError("experiment must be a dictionary")
  396.        
  397.     # Computes terrain angle using cubic spline interpolation
  398.     alpha_fun = interp1d(experiment['alpha_dist'], experiment['alpha_deg'], kind='cubic', fill_value='extrapolate')
  399.     terrain_angle = alpha_fun(y[1])
  400.    
  401.     # Computes motor speed
  402.     w = motorW(y[0], rover)
  403.    
  404.     # Computes net force using previously computed terrain angle
  405.     Fnet = F_net(w, terrain_angle, rover, planet, experiment['Crr'])
  406.    
  407.     # Computes acceleration and velocity
  408.     mrover = get_mass(rover)
  409.     a = Fnet / mrover
  410.     dydt = np.array([a, y[0]])
  411.    
  412.     return dydt
  413. #############################################
  414. # def mechpower(v, rover):
  415. #     '''
  416. #     General Description
  417. #     This function computes the instantaneous mechanical power output by a single DC motor at each point in a given
  418. #     velocity profile.
  419. #     Calling Syntax
  420. #      P = mechpower(v,rover)
  421. #     Input Arguments
  422. #      v 1D numpy array
  423. #     OR scalar float/int
  424. #     Rover velocity data obtained from a simulation [m/s]
  425. #      rover dict Data structure containing rover definition
  426. #     Return Arguments
  427. #      P 1D numpy array
  428. #     OR scalar float/int
  429. #     Instantaneous power output of a single motor corresponding to
  430. #     each element in v [W]. Return argument should be the same size as
  431. #     input v.
  432. #     Additional Specifications and Notes
  433. #     ▪ This function should validate (a) that the first input is a scalar or vector of numerical values and (b) that the
  434. #     second input is a dict. If any condition fails, call raise Exception().
  435. #     ▪ This function should call tau_dcmotor and motorW.
  436. #     ▪ Be wary of units  
  437. #     '''
  438.  
  439. #     '''Computes instantaneous power output by a SINGLE DC motor at each point in a given velocity profile'''
  440. #     # Validate input is a dict
  441. #     if not isinstance(rover, dict):
  442. #         raise Exception("Invalid input: rover must be a dictionary.")
  443.  
  444. #     # Validate input is a scalar or a vector
  445. #     if not isinstance(v, (int, float, np.ndarray)):
  446. #         raise Exception("Invalid input: v must be a scalar or a vector.")
  447. #     elif not isinstance(v, np.ndarray):
  448. #         v = np.array([v], dtype=float)  # Convert scalar to numpy array
  449. #     elif len(v.shape) != 1:
  450. #         raise Exception("Invalid input: v must be a 1D numpy array.")
  451.  
  452. #     # Compute omega and torque using helper functions
  453. #     omega = motorW(v, rover)
  454. #     tau = tau_dcmotor(omega, rover["wheel_assembly"]["motor"])
  455.  
  456. #     # Compute power and return
  457. #     P = omega * tau
  458. #     return P
  459.  
  460.  
  461. # def battenergy(t, v, rover):
  462. #     """
  463. #     General Description
  464. #     This function computes the total electrical energy consumed from the rover battery pack over a simulation profile,
  465. #     defined as time-velocity pairs. This function assumes all 6 motors are driven from the same battery pack (i.e., this
  466. #     function accounts for energy consumed by all motors).
  467. #     This function accounts for the inefficiencies of transforming electrical energy to mechanical energy using a DC
  468. #     motor.
  469. #     In practice, the result given by this function will be a lower bound on energy requirements since it is undesirable to
  470. #     run batteries to zero capacity and other losses exist that are not modeled in this project.
  471. #     Calling Syntax
  472. #      E = battenergy(t,v,rover)
  473. #     Input Arguments
  474. #      t 1D numpy array N-element array of time samples from a rover simulation [s]
  475. #      v 1D numpy array N-element array of rover velocity data from a simulation [m/s]
  476. #      rover dict Data structure containing rover definition
  477. #     Return Arguments
  478. #      E scalar Total electrical energy consumed from the rover battery pack over
  479. #     the input simulation profile. [J]
  480. #     Additional Specifications and Notes
  481. #     ▪ This function should validate (a) that the first two inputs are equal-length vectors of numerical values and (b)
  482. #     that the third input is a dict. If any condition fails, call raise Exception().
  483. #     ▪ This function should call mechpower and tau_dcmotor.
  484. #     ▪ The rover dictionary should contain data points for efficiency as a function of torque as noted in Section 4 and
  485. #     also in Task 5. You will need to interpolate between these data points (as you did in Task 5) to evaluate the
  486. #     efficiency for a given torque.
  487. #     ▪ Be wary of units
  488. #     """
  489.  
  490. #     # Validate inputs
  491. #     if type(rover) != dict or not isinstance(v, np.ndarray) or not isinstance(t, np.ndarray) or np.shape(v) != np.shape(t):
  492. #         raise Exception("Invalid inputs for battenergy function.")
  493.  
  494. #     # Get motor parameters
  495. #     omega = motorW(v, rover)
  496. #     P = mechpower(v, rover)
  497. #     torque = tau_dcmotor(omega, rover['wheel_assembly']['motor'])
  498. #     effcy = interp1d(rover['wheel_assembly']['motor']['effcy_tau'], rover['wheel_assembly']['motor']['effcy'], kind='cubic')
  499.  
  500. #     # Calculate battery power at each point
  501. #     P_batt = P / effcy(torque)
  502.  
  503. #     # Integrate power to find total energy
  504. #     E_OneMotor = simps(P_batt, x=t)
  505. #     E = E_OneMotor * 6  # multiply by number of motors
  506.  
  507. #     return E
  508. def mechpower(v, rover):
  509.    
  510.     power = []
  511.    
  512.     # Exceptions
  513.     if type(rover) is bool:
  514.         raise Exception("V must be scalar or vector input")
  515.     if type(v) != dict:
  516.         raise Exception("Rover must be a dictionary input")
  517.        
  518.     if np.isscalar(v):
  519.         w = motorW(v,rover)
  520.         omega = w
  521.         tau = tau_dcmotor(omega, motor)
  522.         pwr = tau * w
  523.         p = pwr
  524.     else:
  525.         for i in range(len(v)):
  526.             w = motorW(v[i],rover)
  527.             omega = wtau = tau_dcmotor(omega, motor)
  528.             pwr = tau * w
  529.             power.append(pwr)
  530.             p = np.array(power)
  531.     return p
  532.  
  533.  
  534. def battenergy(t, v, rover):
  535.    
  536.     #Exceptions
  537.     if (type(t) is bool):
  538.         raise Exception('t must be a scalar or vector input')
  539.     if type(t) != type(v):
  540.         raise Exception('t and v must be the same type')  
  541.     if type(v) is bool:
  542.         raise Exception('v must be a scalar or vector input')
  543.     if np.size(t) != np.size(v):
  544.         raise Exception('v and t must be the same size')
  545.     if (type(rover) is not dict):
  546.         raise Exception('rover must be a dictionary input')
  547.        
  548.     p = mechpower(v, rover)
  549.     w = motorW(v, rover)
  550.     omega = w
  551.     tau = tau_dcmotor(omega, motor)
  552.     effcy_tau = motor['effcy_tau']
  553.     efccy = motor['effcy']
  554.     effcy_fun = interpolate.interp1d(effcy_tau, effcy, kind='cubic', fill_value='extrapolate')
  555.     bpower = (6*p) / (effcy_fun(tau))
  556.     E = integrate.trapz(bpower,t)
  557.     return E
  558.  
  559. #############################################
  560. def end_of_mission_event(end_event):
  561.     """
  562.    Defines an event that terminates the mission simulation. Mission is over
  563.    when rover reaches a certain distance, has moved for a maximum simulation
  564.    time or has reached a minimum velocity.            
  565.    """
  566.    
  567.     mission_distance = end_event['max_distance']
  568.     mission_max_time = end_event['max_time']
  569.     mission_min_velocity = end_event['min_velocity']
  570.    
  571.     # Assume that y[1] is the distance traveled
  572.     distance_left = lambda t,y: mission_distance - y[1]
  573.     distance_left.terminal = True
  574.    
  575.     time_left = lambda t,y: mission_max_time - t
  576.     time_left.terminal = True
  577.    
  578.     velocity_threshold = lambda t,y: y[0] - mission_min_velocity;
  579.     velocity_threshold.terminal = True
  580.     velocity_threshold.direction = -1
  581.    
  582.     # terminal indicates whether any of the conditions can lead to the
  583.     # termination of the ODE solver. In this case all conditions can terminate
  584.     # the simulation independently.
  585.    
  586.     # direction indicates whether the direction along which the different
  587.     # conditions is reached matter or does not matter. In this case, only
  588.     # the direction in which the velocity treshold is arrived at matters
  589.     # (negative)
  590.    
  591.     events = [distance_left, time_left, velocity_threshold]
  592.    
  593.     return events
  594.  
  595. def experiment1():
  596.    
  597.     experiment = {'time_range' : np.array([0,20000]),
  598.                   'initial_conditions' : np.array([0.3125,0]),
  599.                   'alpha_dist' : np.array([0, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000]),
  600.                   'alpha_deg' : np.array([11.509, 2.032, 7.182, 2.478, \
  601.                                         5.511, 10.981, 5.601, -0.184, \
  602.                                         0.714, 4.151, 4.042]),
  603.                   'Crr' : 0.1}
  604.    
  605.    
  606.     # Below are default values for example only:
  607.     end_event = {'max_distance' : 50,
  608.                  'max_time' : 5000,
  609.                  'min_velocity' : 0.01}
  610.    
  611.     return experiment, end_event
  612.  
  613. def simulate_rover(rover, planet, experiment, end_event):
  614.     '''
  615.    This function simulates the trajectory of a rover on a planet surface.
  616.  
  617.    Inputs:
  618.    rover (dict): A dictionary containing the parameters of the rover.
  619.    planet (dict): A dictionary containing the planet definition.
  620.    experiment (dict): A dictionary containing parameters of the trajectory to be followed by the rover.
  621.    end_event (dict): A dictionary containing the conditions necessary and sufficient to terminate simulation of rover dynamics.
  622.  
  623.    Returns:
  624.    rover (dict): A dictionary containing the parameters of the rover, including updated telemetry information.
  625.  
  626.    '''
  627.     # Check validity of inputs
  628.     if not isinstance(experiment, dict):
  629.         raise TypeError("experiment must be a dictionary")
  630.     if not isinstance(end_event, dict):
  631.         raise TypeError("end_event must be a dictionary")
  632.  
  633.     # Set initial conditions and integrate the differential equations
  634.     t_span = experiment["time_range"]
  635.     y0 = experiment["initial_conditions"]
  636.     function = lambda time, y: rover_dynamics(time, y, rover, planet, experiment)
  637.     solution = solve_ivp(function, t_span=t_span, y0=y0, method='RK45', events=end_of_mission_event(end_event))
  638.  
  639.     # Populate the rover telemetry dictionary
  640.     telemetry = rover.setdefault('telemetry', {})
  641.     telemetry['Time'] = solution.t
  642.     telemetry['completion_time'] = solution.t[-1]
  643.     telemetry['velocity'] = solution.y[0]
  644.     telemetry['position'] = solution.y[1]
  645.     telemetry['distance_traveled'] = solution.y[1][-1]
  646.     telemetry['max_velocity'] = max(telemetry['velocity'])
  647.     telemetry['average_velocity'] = np.mean(telemetry['velocity'])
  648.     telemetry['power'] = mechpower(telemetry['velocity'], rover)
  649.     telemetry['battery_energy'] = battenergy(telemetry['Time'], telemetry['velocity'], rover)
  650.     telemetry['energy_per_distance'] = telemetry['battery_energy'] / telemetry['distance_traveled']
  651.  
  652.     return rover
  653.  

Replies to subfunctions rss

Title Name Language When
Re: subfunctions Gamboge Hamster python 1 Year ago.