Esempio n. 1
0
    def testTypeChecks(self):
        stdout = test_util.StdoutPatch()

        # Double array.
        with stdout, self.assertRaises(AssertionError):
            mconfig.MakeParams(
                'common.all_params',
                overrides={
                    'control': {
                        'control_output': {
                            'flaps_min':
                            [0] + [0.0 for _ in range(labels.kNumFlaps - 1)]
                        }
                    }
                },
                override_method='derived')
        self.assertRegexpMatches(
            stdout.Read(),
            r'(?s).*control_output\.flaps_min\[0\] must be a float.*')

        # Vec3.
        with stdout, self.assertRaises(AssertionError):
            mconfig.MakeParams('common.all_params',
                               overrides={
                                   'sim': {
                                       'ground_frame_sim': {
                                           'pos_ecef': [1e3, 1, 1e1]
                                       }
                                   }
                               },
                               override_method='derived')
        self.assertRegexpMatches(
            stdout.Read(),
            r'(?s).*ground_frame_sim\.pos_ecef\[1\] must be a float.*')

        # Integer array.
        with stdout, self.assertRaises(AssertionError):
            mconfig.MakeParams('common.all_params',
                               overrides={
                                   'control': {
                                       'fault_detection': {
                                           'gsg': {
                                               'no_update_counts_limit':
                                               [0, 3.14]
                                           }
                                       }
                                   }
                               },
                               override_method='derived')
        self.assertRegexpMatches(
            stdout.Read(),
            (r'(?s).*fault_detection\.gsg\.no_update_counts_limit\[1\]'
             ' must be an int.*'))
Esempio n. 2
0
def main(argv):
    tether_force_model = dynamics.ConstantTetherForceModel(
        np.matrix(np.zeros((3, 1))))

    all_params = mconfig.MakeParams('common.all_params')
    wing = dynamics.Wing(all_params['system'], all_params['sim'],
                         dynamics.SwigAeroModel(), tether_force_model)

    log_file = h5py.File(argv[1])

    sim = log_file['messages']['kAioNodeSimulator']
    sim_telem = sim['kMessageTypeSimTelemetry']['message']
    controller_a = log_file['messages']['kAioNodeControllerA']
    control_telem = controller_a['kMessageTypeControlDebug']['message']
    wing_telem = sim_telem['wing']

    control_idx = np.where(control_telem['flight_mode'] >=
                           control_types.kFlightModeHoverFullLength)[0]
    assert control_idx.size > 0
    sim_idx = np.where(
        sim_telem['time'] > control_telem['time'][control_idx[0]])[0]
    assert sim_idx.size > 0

    for i in range(sim_idx[0], len(sim_telem)):
        _CompareTimeStep(tether_force_model, wing, wing_telem[i])
Esempio n. 3
0
    def testMixRotors(self):
        all_params = mconfig.MakeParams('common.all_params')

        rotors_0 = actuator_util.MixRotors(
            {
                'thrust': 1e4,
                'moment': [0.0, 0.0, 0.0]
            }, {
                'thrust': 1e-3,
                'moment': [1e-4, 1.0, 1.0]
            }, 0.0, [0.0, 0.0, 0.0], control_types.kStackingStateNormal, False,
            all_params['system']['phys']['rho'],
            all_params['system']['rotors'],
            all_params['control']['rotor_control'])
        self.assertTrue(isinstance(rotors_0, np.matrix))
        self.assertEqual(control_types.kNumMotors, rotors_0.shape[0])
        self.assertEqual(1, rotors_0.shape[1])

        rotors_10 = actuator_util.MixRotors(
            {
                'thrust': 1e4,
                'moment': [0.0, 0.0, 0.0]
            }, {
                'thrust': 1e-3,
                'moment': [1e-4, 1.0, 1.0]
            }, 10.0, [0.0, 0.0, 0.0], control_types.kStackingStateNormal,
            False, all_params['system']['phys']['rho'],
            all_params['system']['rotors'],
            all_params['control']['rotor_control'])
        for i in range(rotors_10.shape[0]):
            self.assertGreaterEqual(rotors_10[i, 0], rotors_0[i, 0])
Esempio n. 4
0
 def testMakeParamsIntegerIndexedOverrides(self):
     overrides = {
         'sim': {
             'aero_sim': {
                 'force_coeff_w_scale_factors': {
                     'flap_derivatives': {
                         0: {
                             '1': 22.0
                         },
                         1: [2.0, 3.0, 4.0]
                     }
                 }
             }
         }
     }
     params = mconfig.MakeParams('common.all_params',
                                 overrides=overrides,
                                 override_method='derived')
     factors = params['sim']['aero_sim']['force_coeff_w_scale_factors']
     for i in range(sim_types.kNumFlaps):
         if i == 1:
             self.assertEqual(2.0, factors['flap_derivatives'][i][0])
             self.assertEqual(3.0, factors['flap_derivatives'][i][1])
             self.assertEqual(4.0, factors['flap_derivatives'][i][2])
         else:
             for j in range(3):
                 if i == 0 and j == 1:
                     self.assertEqual(22.0,
                                      factors['flap_derivatives'][i][j])
                 else:
                     self.assertEqual(1.0,
                                      factors['flap_derivatives'][i][j])
Esempio n. 5
0
 def setUp(self):
   all_params = mconfig.MakeParams('common.all_params')
   self._trans_in = trans_in.ControlDesign(all_params['system'],
                                           all_params['control'],
                                           all_params['sim'])
   self._trans_in_frame = self._trans_in._trans_in_frame
   (self._trim_state, self._trim_inputs) = self._trans_in.CalcTrim(
       5.0, np.deg2rad(50.0), False)
Esempio n. 6
0
    def __init__(self):
        iec_params = mconfig.MakeParams(FLAGS.wing_model + '.sim.iec_sim',
                                        None)
        sys_params = mconfig.MakeParams(FLAGS.wing_model + '.system_params')

        v_in = float(iec_params['v_in'])  # Cut-in wind speed.
        v_out = float(iec_params['v_out'])  # Cut-out wind speed.
        self._v_hub = float(iec_params['v_r1'])  # Rated hub speed.

        # alpha is the wind shear exponent defined in IEC 61400-1:2005 on page 24.
        alpha = 0.2

        # Take the hub height to be the tether length * sin(30 deg.) + height of
        # the GSG.
        z_hub = float(sys_params['tether']['length'] *
                      numpy.sin(numpy.pi / 6.0) +
                      sys_params['ground_frame']['ground_z'])

        # Height at which wind_speed is measured.
        # This assumes that the perch z-axis is parallel to the
        # g-coordinate z-axis are aligned.
        z_sensor = float(sys_params['ground_frame']['ground_z'] -
                         sys_params['wind_sensor']['pos_parent'][2])

        # v_hub * shear_correction = wind_speed.
        shear = float((z_sensor / z_hub)**alpha)

        # The *_sensed variables are the speed measured at the ground to
        # arrive at a given speed at the hub assuming the shear model
        # above.
        self._v_in_sensed = v_in * shear
        self._v_out_sensed = v_out * shear
        self._v_hub_sensed = self._v_hub * shear
        self._v_hub_m2_sensed = (self._v_hub -
                                 2.0) * shear  # m2 means minus two.
        self._v_hub_p2_sensed = (self._v_hub +
                                 2.0) * shear  # p2 means plus two.

        self._fault_power_sys_zero = {
            't_start': 20.0,
            't_end': 25.0,
            'component': 'PowerSys/motor_connections[0]',
            'type': sim_types.kSimFaultActuatorZero,
        }
Esempio n. 7
0
    def setUp(self):
        # Default parameters created when MakeParams is run on the
        # all_params.py file without overrides.
        self._default_params = mconfig.MakeParams('common.all_params')

        # Set flight plan to something that is not the default flight
        # plan.  This makes the harmless assumption that there are at
        # least two flight plans 0 and 1.
        if self._default_params['system']['flight_plan'] == 0:
            self._changed_flight_plan = 1
        else:
            self._changed_flight_plan = 0
Esempio n. 8
0
  def setUp(self):
    all_params = mconfig.MakeParams('common.all_params')
    self._sim_params = all_params['sim']
    self._system_params = all_params['system']
    self._dcm_g2control = geometry.AngleToDcm(0.1, 0.2, 0.3)
    motor_model = dynamics.PureForceMomentMotorModel(
        self._system_params['rotors'],
        self._system_params['wing']['center_of_mass_pos'])

    self._wing = dynamics.Wing(
        self._system_params, self._sim_params, VacuumAeroModel(), motor_model,
        dynamics.ConstantTetherForceModel(np.matrix(np.zeros((3, 1)))))
Esempio n. 9
0
def GenerateConfigList(base, override_list):
  """Generate a series of configs based on lists of overrides."""
  override_list = _PreprocessOverrideList(override_list)
  clean_overrides = [o for o in override_list if o.name_def.clean]
  unclean_overrides = [o for o in override_list if not o.name_def.clean]
  if not unclean_overrides:
    base_config = mconfig.MakeParams('common.all_params', copy.deepcopy(base),
                                     override_method='derived')
  for i in range(len(override_list[0].values)):
    if unclean_overrides:
      base_override = copy.deepcopy(base)
      for override in unclean_overrides:
        ApplyOverride(base_override, override.name_def, override.values[i],
                      True)
      config = mconfig.MakeParams('common.all_params', base_override,
                                  override_method='derived')
    else:
      config = copy.deepcopy(base_config)
    for override in clean_overrides:
      ApplyOverride(config, override.name_def, override.values[i])
    config['adjusted_parameters'] = {
        o.name_def.name: o.values[i] for o in override_list}
    yield config
Esempio n. 10
0
def RunFlightChecks(log_file):
  """Run flight checks on an H5 log file."""
  system_params = log_file['parameters']['system_params']
  simulator = log_file['messages']['kAioNodeSimulator']
  sim_telem = simulator['kMessageTypeSimTelemetry']['message']

  controller = log_file['messages']['kAioNodeControllerA']
  control_telem = controller['kMessageTypeControlDebug']['message']

  monitor_params = mconfig.MakeParams('common.monitor.monitor_params')
  CheckOverTension(monitor_params, sim_telem)
  CheckMinAltitude(system_params, sim_telem)
  CheckFlightModeProgression(control_telem)
  CheckCrosswindMinAltitude(system_params, control_telem, sim_telem)
Esempio n. 11
0
    def testAllWingSerials(self):
        # TODO: OktKite. When the Oktoberkite has a valid yaml system_config
        # entry remove the restriction which models are run.
        for wing_serial in _WING_SERIAL_HELPER.Values():
            if (system_types.WingSerialToModel(wing_serial) ==
                    system_types.kWingModelOktoberKite):
                continue
            is_active = mconfig.MakeParams(
                'common.wing_serial_status',
                overrides={'wing_serial': wing_serial},
                override_method='derived')
            if not is_active:
                continue

            config = system_config.SystemConfig.GetSystemConfigBySerial(
                wing_serial)
            self.assertIsNotNone(config)
Esempio n. 12
0
    def _GenerateConfigs(self):
        # Create a JSON file describing the simulation parameters for each
        # wind speed.  We override the flight plan because we are only
        # interested in power production during crosswind flight.  We
        # override the aerodynamic and other simulation parameters to make
        # the most realistic simulation of power production.
        for joystick_throttle in FLAGS.joystick_throttles:
            for v_wind in FLAGS.wind_speeds:
                overrides = {
                    'system': {
                        'flight_plan': sim_types.kFlightPlanStartDownwind
                    },
                    'sim': {
                        'joystick_sim': {
                            'num_updates':
                            1,
                            'updates': [{
                                't_update': 0.0,
                                'type': sim_types.kSimJoystickUpdateThrottle,
                                'enum_value':
                                sim_types.kSimJoystickThrottleManual,
                                'value': float(joystick_throttle),
                            }] * sim_types.MAX_JOYSTICK_UPDATES
                        },
                        # It is necessary to add the perch here so the winch
                        # updates and payout is set correctly.  This helps the
                        # wing hover properly by setting the proper
                        # feed-forward thrust when the simulation begins.
                        'sim_opt': (sim_types.kSimOptImperfectSensors
                                    | sim_types.kSimOptPerch
                                    | sim_types.kSimOptStackedPowerSystem),
                        'phys_sim': {
                            'wind_model': sim_types.kWindModelDrydenTurbulence,
                            'wind_speed': float(v_wind),
                        },
                        'sim_time':
                        FLAGS.sim_time
                    }
                }

                yield mconfig.MakeParams('common.all_params',
                                         overrides,
                                         override_method='derived')
Esempio n. 13
0
def CalcWindVelMwAndMeanWind(wind_vel, wind_source):
    """Converts wind to the mean wind frame, and provides mean speed/direction.

  Args:
    wind_vel: Wind velocity [m/s] in the wind sensor frame or the ground frame,
    depending on wind_source (ws and est, respectively).
    wind_source: 'est' or 'ws'
  Returns:
    (wind_vel_mw, mean_wind_speed, mean_wind_dir), defined as:
      wind_vel_mw: Wind velocity [m/s] in the mean wind frame.
      mean_wind_speed: Mean wind speed [m/s] in the horizontal plane.
      mean_wind_dir: Direction [rad] of the mean wind.
  Raises:
    ValueError: The specified wind source was invalid.
  """

    # Transform from weather station to ground coordinates, if needed
    if wind_source == 'ws':
        system_params = mconfig.MakeParams('m600.system_params')
        assert (
            system_params['gs_model'] == system_types.kGroundStationModelTopHat
        ), ('This script assumes a top hat configuration.')
        dcm_ws2g = np.array(
            system_params['wind_sensor']['dcm_parent2ws']['d']).T
        wind_vel_g = np.dot(dcm_ws2g, wind_vel.T).T
    elif wind_source == 'est':
        wind_vel_g = wind_vel
    else:
        raise ValueError('Invalid value of --wind_source: %s.' % wind_source)

    # Calculate mean wind xy-speed and direction.
    mean_wind_g = np.mean(wind_vel_g, axis=0)
    mean_wind_speed = np.hypot(mean_wind_g[0], mean_wind_g[1])
    mean_wind_dir = np.arctan2(-mean_wind_g[1], -mean_wind_g[0])

    # Rotate wind velocity to the mean wind frame.
    dcm_mw2g = wind_frame.Mat3()
    wind_frame.CalcDcmMwToG(mean_wind_dir, ctypes.byref(dcm_mw2g))
    dcm_mw2g = np.array([row[:] for row in dcm_mw2g.d])
    wind_vel_mw = np.dot(dcm_mw2g.T, wind_vel_g.T).T

    return wind_vel_mw, mean_wind_speed, mean_wind_dir
Esempio n. 14
0
    def testLinearizeMixRotors(self):
        params = mconfig.MakeParams('common.all_params')

        a = actuator_util.LinearizeMixRotors(
            {
                'thrust': 1e4,
                'moment': [0.0, 0.0, 0.0]
            }, params)

        # A pure thrust command results in almost equal rotor speed increase for
        # all the rotors.
        self.assertTrue(all(np.abs(a[:, 0] / a[0, 0] - 1.0) < 5e-2))

        # A pure pitch moment command results in almost equal rotor speed increase
        # for the top rotors, of opposite sign as the rotor speed increase of the
        # lower rotors.
        self.assertTrue(
            all(
                np.abs(a[:, 2] / a[0, 2] -
                       np.matrix([1, 1, 1, 1, -1, -1, -1, -1]).T < 5e-2)))
Esempio n. 15
0
def GenerateConfigPermutations(base, override_params):
  """Generate a series of configs based on permutations of overrides."""
  override_params = _PreprocessOverrideList(override_params)
  clean_overrides = [o for o in override_params if o.name_def.clean]
  unclean_overrides = [o for o in override_params if not o.name_def.clean]
  for unclean_values in itertools.product(
      *[o.values for o in unclean_overrides]):
    base_override = copy.deepcopy(base)
    for override, value in zip(unclean_overrides, unclean_values):
      ApplyOverride(base_override, override.name_def, value, True)
    base_config = mconfig.MakeParams('common.all_params', base_override,
                                     override_method='derived')
    for clean_values in itertools.product(*[o.values for o in clean_overrides]):
      config = copy.deepcopy(base_config)
      for override, value in zip(clean_overrides, clean_values):
        ApplyOverride(config, override.name_def, value)
      adjusted_params = (zip(unclean_overrides, unclean_values)
                         + zip(clean_overrides, clean_values))
      config['adjusted_parameters'] = {
          o.name_def.name: v for o, v in adjusted_params}
      yield config
Esempio n. 16
0
    def testMakeParamsDerivedOverrides(self):
        params = mconfig.MakeParams('common.all_params',
                                    overrides={
                                        'control': {
                                            'hover': {
                                                'path': {
                                                    'reel_azimuth_offset':
                                                    -0.2222,
                                                    'accel_start_elevation':
                                                    0.2222
                                                }
                                            }
                                        },
                                        'sim': {
                                            'ground_frame_sim': {
                                                'pos_ecef': [1e3, 1e2, 1e1]
                                            }
                                        },
                                        'system': {
                                            'flight_plan':
                                            self._changed_flight_plan
                                        }
                                    },
                                    override_method='derived')

        # Check that wind speed is overridden as expected.
        self.assertEqual(params['sim']['ground_frame_sim']['pos_ecef'],
                         [1e3, 1e2, 1e1])

        # Check that multiple parameters in the same module are overridden.
        hover_path_params = params['control']['hover']['path']
        self.assertEqual(hover_path_params['reel_azimuth_offset'], -0.2222)
        self.assertEqual(hover_path_params['accel_start_elevation'], 0.2222)

        # Check that the derived override overrides the main flight plan
        # and the derived flight plan used in the controller.
        self.assertEqual(params['system']['flight_plan'],
                         self._changed_flight_plan)
        self.assertEqual(params['control']['flight_plan'],
                         self._changed_flight_plan)
Esempio n. 17
0
    def testSystemServoFlapRatios(self):
        """Sanity check servo/flap ratios in system config (used for rudder)."""
        servo_config = mconfig.MakeParams(
            'common.all_params')['system']['servos']

        for servo in range(system_types.kNumServos):
            if servo == system_types.kServoR1 or servo == system_types.kServoR2:
                self.assertEqual(
                    servo_config[servo]['linear_servo_to_flap_ratio'], 0.0)
            else:
                self.assertAlmostEqual(
                    servo_config[servo]['linear_servo_to_flap_ratio'],
                    1.0,
                    delta=_FLOAT_DELTA)
                self.assertEqual(
                    servo_config[servo]['nonlinear_servo_to_flap_ratio'], 0.0)

        self.assertAlmostEqual(servo_config[
            system_types.kServoR1]['nonlinear_servo_to_flap_ratio'],
                               servo_config[system_types.kServoR2]
                               ['nonlinear_servo_to_flap_ratio'],
                               delta=_FLOAT_DELTA)
Esempio n. 18
0
def GetParams(wing_model, wing_serial, use_wake_model=True):
    """Returns the set of parameters used for the model.

  Args:
    wing_model: Wing model (e.g. 'm600').
    wing_serial: String giving the desired wing serial number (e.g. '01').
    use_wake_model: Boolean flag that determines whether the advected
        rotor wake is included in the calculation of the local
        apparent wind.

  Returns:
    Parameter structure for the hover model.
  """
    wing_config_name = wing_flag.WingModelToConfigName(wing_model)

    if wing_config_name == 'oktoberkite':
        wing_serial_helper = c_helpers.EnumHelper('WingSerialOktoberKite',
                                                  system_types)
        db_name = 'avl/oktoberkite.avl'
    elif wing_config_name == 'm600':
        wing_serial_helper = c_helpers.EnumHelper('WingSerial', system_types)
        db_name = 'avl/m600_low_tail_no_winglets.avl'
    else:
        assert False, 'Wing model, %s, is not recognized.' % wing_config_name

    mconfig.WING_MODEL = wing_config_name
    system_params = mconfig.MakeParams(
        wing_config_name + '.system_params',
        overrides={'wing_serial': wing_serial_helper.Value(wing_serial)},
        override_method='derived')

    # Thrust level is necessary for the propeller wake model.  It is
    # determined by assuming the kite is supporting the full weight of
    # the tether and has to balance the pitching moment about the
    # C.G. with differential thrust between the top and bottom motor
    # rows:
    #
    #   4.0 * thrust_top + 4.0 * thrust_bot = weight
    #   z_top * thrust_top + z_bot * thrust_bot = 0.0
    total_mass = (system_params['wing']['m'] +
                  (system_params['tether']['linear_density'] *
                   system_params['tether']['length']))
    weight = system_params['phys']['g'] * total_mass

    main_wing_incidence_deg = system_params['wing']['wing_i']

    rotor_pos_z = [
        rotor['pos'][2] - system_params['wing']['center_of_mass_pos'][2]
        for rotor in system_params['rotors']
    ]

    z_bot = np.mean([z for z in rotor_pos_z if z > 0.0])
    z_top = np.mean([z for z in rotor_pos_z if z < 0.0])
    thrust_bot = weight / (4.0 * (1.0 + (-z_bot / z_top)))
    thrust_top = thrust_bot * (-z_bot / z_top)

    rotors = [{
        'pos': rotor['pos'],
        'radius': rotor['D'] / 2.0,
        'thrust': thrust_bot if rotor['pos'][2] > 0.0 else thrust_top
    } for rotor in system_params['rotors']]

    # It is assumed that all rotors have the same pitch angle.
    rotor_pitches = [
        np.arctan2(-rotor['axis'][2], rotor['axis'][0])
        for rotor in system_params['rotors']
    ]
    assert np.all(rotor_pitches == rotor_pitches[0])

    aero_home = os.path.join(makani.HOME, 'analysis/aero/')

    wing = avl_reader.AvlReader(os.path.join(aero_home, db_name))

    # TODO: Extract airfoil information from avl_reader.py.
    if wing_config_name == 'm600':
        # The maximum value of 16.0 degrees for the M600 is because of the airfoil
        # hysteresis going from attached flow to detached flow, which occures during
        # HoverAccel.
        stall_angles_deg = [1.0, 16.0]
    elif wing_config_name == 'oktoberkite':
        # (b/146071300) Currently, there is no understanding of the hysteresis
        # curve. This needs to be understood and modified once available.
        # (b/146061441) Additional ramifications for this choice.
        stall_angles_deg = [1.0, 20.0]
    else:
        assert False, 'Unknown wing model.'

    # (b/146081917) Oktoberkite will utilize the same airfoils as the M600.
    outer_wing_airfoil = airfoil.Airfoil(os.path.join(
        aero_home, 'hover_model/airfoils/oref.json'),
                                         stall_angles_deg=stall_angles_deg)
    inner_wing_airfoil = airfoil.Airfoil(os.path.join(
        aero_home, 'hover_model/airfoils/ref.json'),
                                         stall_angles_deg=stall_angles_deg)
    pylon_airfoil = airfoil.Airfoil(os.path.join(
        aero_home, 'hover_model/airfoils/mp6d.json'),
                                    stall_angles_deg=[-10.0, 10.0])

    airfoils = {
        'Wing (panel 0)':
        outer_wing_airfoil,
        'Wing (panel 1)':
        outer_wing_airfoil,
        'Wing (panel 2)':
        inner_wing_airfoil,
        'Wing (panel 3)':
        inner_wing_airfoil,
        'Wing (panel 4)':
        inner_wing_airfoil,
        'Wing (panel 5)':
        inner_wing_airfoil,
        'Wing (panel 6)':
        inner_wing_airfoil,
        'Wing (panel 7)':
        inner_wing_airfoil,
        'Wing (panel 8)':
        outer_wing_airfoil,
        'Wing (panel 9)':
        outer_wing_airfoil,
        'Pylon 1':
        pylon_airfoil,
        'Pylon 2':
        pylon_airfoil,
        'Pylon 3':
        pylon_airfoil,
        'Pylon 4':
        pylon_airfoil,
        'Horizontal tail':
        airfoil.Airfoil(os.path.join(aero_home,
                                     'hover_model/airfoils/f8.json'),
                        stall_angles_deg=[-10.0, 10.0]),
        'Vertical tail':
        airfoil.Airfoil(os.path.join(
            aero_home, 'hover_model/airfoils/approx_blade_rj8.json'),
                        stall_angles_deg=[-10.0, 10.0])
    }

    # TODO: Extract flap index information from
    # avl_reader.py.
    # The indices of the wing pannel indicate which flap number they are going to
    # be in the controller, i.e. "0" is A1 for the M600 and "7" is the rudder for
    # the M600. An index of -1 means that the panel has no flap section.
    if wing_config_name == 'm600':
        delta_indices = {
            'Wing (panel 0)': 0,
            'Wing (panel 1)': 1,
            'Wing (panel 2)': -1,
            'Wing (panel 3)': 2,
            'Wing (panel 4)': -1,
            'Wing (panel 5)': -1,
            'Wing (panel 6)': 3,
            'Wing (panel 7)': -1,
            'Wing (panel 8)': 4,
            'Wing (panel 9)': 5,
            'Pylon 1': -1,
            'Pylon 2': -1,
            'Pylon 3': -1,
            'Pylon 4': -1,
            'Horizontal tail': 6,
            'Vertical tail': 7
        }
    elif wing_config_name == 'oktoberkite':
        delta_indices = {
            'Wing (panel 0)': 0,
            'Wing (panel 1)': 1,
            'Wing (panel 2)': 2,
            'Wing (panel 3)': -1,
            'Wing (panel 4)': -1,
            'Wing (panel 5)': -1,
            'Wing (panel 6)': -1,
            'Wing (panel 7)': 3,
            'Wing (panel 8)': 4,
            'Wing (panel 9)': 5,
            'Pylon 1': -1,
            'Pylon 2': -1,
            'Pylon 3': -1,
            'Pylon 4': -1,
            'Horizontal tail': 6,
            'Vertical tail': 7
        }

    # TODO: Modify the hover model to accept the
    # avl_reader.py surface dictionary directly.
    hover_model_panels = []
    for surface in wing.properties['surfaces']:
        if surface['name'] in ('Horizontal tail', 'Vertical tail', 'Pylon 1',
                               'Pylon 2', 'Pylon 3', 'Pylon 4'):
            hover_model_panels.append({
                'name':
                surface['name'],
                'area':
                surface['area'],
                'span':
                surface['span'],
                'surface_span':
                surface['span'],
                'chord':
                surface['standard_mean_chord'],
                'aspect_ratio':
                surface['span']**2.0 / surface['area'],
                # The 2.0 is a fudge factor to match the C_Y slope from AVL.
                'cl_weight':
                2.0 if surface['name'][0:5] == 'Pylon' else 1.0,
                'pos_b':
                surface['aerodynamic_center_b'],
                'airfoil':
                airfoils[surface['name']],
                'incidence':
                surface['mean_incidence'],
                # These surfaces are single panel. So there is no relative incidence.
                'relative_incidence':
                0.0,
                'dcm_b2s':
                surface['dcm_b2surface'],
                'delta_index':
                delta_indices[surface['name']]
            })
        elif surface['name'] == 'Wing':
            for i, panel in enumerate(surface['panels']):
                panel_name = surface['name'] + ' (panel %d)' % i
                hover_model_panels.append({
                    'name':
                    panel_name,
                    'area':
                    panel['area'],
                    'span':
                    panel['span'],
                    'surface_span':
                    surface['span'],
                    'chord':
                    panel['standard_mean_chord'],
                    'aspect_ratio':
                    surface['span']**2.0 / surface['area'],
                    'cl_weight':
                    (surface['mean_incidence'] *
                     surface['standard_mean_chord'] /
                     (panel['mean_incidence'] * panel['standard_mean_chord'])),
                    'pos_b':
                    panel['aerodynamic_center_b'],
                    'airfoil':
                    airfoils[panel_name],
                    'incidence':
                    panel['mean_incidence'],
                    'relative_incidence':
                    (panel['mean_incidence'] -
                     np.deg2rad(main_wing_incidence_deg)),
                    'dcm_b2s':
                    surface['dcm_b2surface'],
                    'delta_index':
                    delta_indices[panel_name]
                })
        else:
            assert False

    # Check that we aren't missing any panels.
    assert (len(hover_model_panels) == len(delta_indices)
            and len(hover_model_panels) == len(airfoils))

    return {
        'wing_serial': wing_serial,
        'git_commit': build_info.GetGitSha(),
        'use_wake_model': use_wake_model,
        'rotors': rotors,
        'rotor_pitch': rotor_pitches[0],
        'phys': {
            'rho': 1.1,
            'dynamic_viscosity': 1.789e-5
        },
        'wing': {
            'area': system_params['wing']['A'],
            'b': system_params['wing']['b'],
            'c': system_params['wing']['c'],
            'wing_i': system_params['wing']['wing_i']
        },
        'center_of_mass_pos': system_params['wing']['center_of_mass_pos'],
        'panels': hover_model_panels
    }
Esempio n. 19
0
def main(unused_argv):
    all_params = mconfig.MakeParams('common.all_params')
    trimmer = TrimSelector(all_params['system'], all_params['control'],
                           all_params['sim'])

    trimmer.PrintTrim(*trimmer.CalcTrim())
Esempio n. 20
0
    # Convert a configuration filename to the equivalent module name by
    # remove the beginning "config/" and the trailing ".py", and then
    # replacing "/" with ".".
    module_str = FLAGS.input_file[7:-3].replace('/', '.')

    # Parse overrides command line option.
    if FLAGS.overrides:
        overrides = overrides_util.PreprocessOverrides(
            json.loads(FLAGS.overrides))
    else:
        overrides = {}

    # Make a parameters dict from the configuration files.
    params = mconfig.MakeParams(module_str,
                                overrides=overrides,
                                override_method='derived')

    # Write parameters to the output_file or to stdout.
    if FLAGS.output_file is None:
        print params
    else:
        if FLAGS.type == 'json':
            WriteJsonParams(params, FLAGS.output_file)
        elif FLAGS.type == 'control':
            WriteDefaultControlParams(params, FLAGS.output_file)
        elif FLAGS.type == 'monitor':
            WriteDefaultMonitorParams(params, FLAGS.output_file)
        elif FLAGS.type == 'sim':
            WriteDefaultSimParams(params, FLAGS.output_file)
        elif FLAGS.type == 'system':
Esempio n. 21
0
    def testMakeParamsArrayOverrides(self):
        with self.assertRaises(mconfig.InvalidOverrideException):
            # There are either one or three IMUs so this call should always fail.
            params = mconfig.MakeParams(
                'common.all_params',
                overrides={'sim': {
                    'wing_imus_sim': [{
                        'delay': 0.22
                    }, {}]
                }},
                override_method='derived')

        with self.assertRaises(mconfig.InvalidOverrideException):
            overrides = {
                'sim': {
                    'aero_sim': {
                        'force_coeff_w_scale_factors': {
                            'flap_derivatives': {
                                's': {
                                    '1': 22.0
                                },
                            }
                        }
                    }
                }
            }
            params = mconfig.MakeParams('common.all_params',
                                        overrides=overrides,
                                        override_method='derived')

        with self.assertRaises(mconfig.InvalidOverrideException):
            overrides = {
                'sim': {
                    'aero_sim': {
                        'force_coeff_w_scale_factors': {
                            'flap_derivatives': {
                                0: {
                                    '3': 22.0
                                },
                            }
                        }
                    }
                }
            }
            params = mconfig.MakeParams('common.all_params',
                                        overrides=overrides,
                                        override_method='derived')

        if sim_types.kNumWingImus == 1:
            params = mconfig.MakeParams(
                'common.all_params',
                overrides={'sim': {
                    'wing_imus_sim': [{
                        'delay': 0.22
                    }]
                }},
                override_method='derived')

            # Check that delay is overridden as expected.
            self.assertEqual(params['sim']['wing_imus_sim'][0]['delay'], 0.22)
        else:
            params = mconfig.MakeParams('common.all_params',
                                        overrides={
                                            'sim': {
                                                'wing_imus_sim': [{
                                                    'delay': 0.2
                                                }, {}, {
                                                    'delay': 2.0
                                                }]
                                            }
                                        },
                                        override_method='derived')

            # Check that delays are overridden as expected.
            self.assertEqual(params['sim']['wing_imus_sim'][0]['delay'], 0.2)
            self.assertEqual(params['sim']['wing_imus_sim'][2]['delay'], 2.0)
            # This delay should still have the default value.
            self.assertEqual(
                params['sim']['wing_imus_sim'][1]['delay'],
                self._default_params['sim']['wing_imus_sim'][1]['delay'])
Esempio n. 22
0
    def BuildConfig(self, case_name):
        """Build a config for a specific IEC case.

    Args:
      case_name: Name of the case.

    Returns:
      A config suitable for running a simulation corresponding to the provided
          case.

    Raises:
      ValueError: `case_name` does not correspond to a known case.
    """

        overrides = {
            'sim': {
                'iec_sim': {},
                'phys_sim': {
                    'wind_model': sim_types.kWindModelIec,
                    'wind_shear_exponent': 0.2,
                },
                'sim_opt': (sim_types.kSimOptFaults
                            | sim_types.kSimOptPerch
                            | sim_types.kSimOptStackedPowerSystem
                            | sim_types.kSimOptGroundContact
                            | sim_types.kSimOptImperfectSensors),
                'sim_time':
                150.0,
            },
            'system': {
                'flight_plan': sim_types.kFlightPlanStartDownwind
            }
        }
        iec_sim = overrides['sim']['iec_sim']
        phys_sim = overrides['sim']['phys_sim']

        # Power production
        # DLC 1.1: NTM V_in < V_hub < V_out, Ultimate
        # DLC 1.2: NTM V_in < V_hub < V_out, Fatigue
        if case_name in ('1.1', '1.2'):
            iec_sim['load_case'] = sim_types.kIecCaseNormalTurbulenceModel
            phys_sim['wind_speed'] = self._v_hub

        # DLC 1.3: ETM V_in < V_hub < V_out, Ultimate
        elif case_name == '1.3':
            iec_sim['load_case'] = sim_types.kIecCaseExtremeTurbulenceModel

        # DLC 1.4: ECD V_hub = V_r - 2, V_r, V_r + 2, Ultimate
        elif case_name.startswith('1.4'):
            iec_sim['event_t_start'] = 60.0
            iec_sim['load_case'] = (
                sim_types.kIecCaseExtremeCoherentGustWithDirectionChange)
            if case_name == '1.4b':
                phys_sim['wind_speed'] = self._v_hub_m2_sensed
            elif case_name == '1.4c':
                phys_sim['wind_speed'] = self._v_hub_p2_sensed

        # DLC 1.5: EWS V_in < V_hub < V_out, Ultimate
        elif case_name == '1.5a':
            iec_sim['event_t_start'] = 20.0
            iec_sim['load_case'] = (
                sim_types.kIecCaseExtremeWindShearHorizontal)
        elif case_name == '1.5b':
            iec_sim['event_t_start'] = 20.0
            iec_sim['load_case'] = (sim_types.kIecCaseExtremeWindShearVertical)

        # Power production plus occurrence of fault.
        #
        # I'm not sure how the different types of IEC faults should be interpreted,
        # so I'm assuming rotor-out, servo-out, GSG fails faults.
        #
        # DLC 2.1: NTM V_in < V_hub < V_out, Control system fault, Ultimate
        elif case_name == '2.1':
            iec_sim['load_case'] = sim_types.kIecCaseNormalTurbulenceModel
            phys_sim['wind_speed'] = self._v_hub
            overrides['sim']['faults_sim'] = [self._fault_power_sys_zero]

        # DLC 2.2: NTM V_in < V_hub < V_out, Protection system fault, Ultimate
        elif case_name == '2.2':
            iec_sim['load_case'] = sim_types.kIecCaseNormalTurbulenceModel
            phys_sim['wind_speed'] = self._v_hub
            overrides['sim']['faults_sim'] = [{
                't_start': 20.0,
                't_end': 25.0,
                'component': 'GSG/elevation[0]',
                'type': sim_types.kSimFaultMeasurementRescale,
                'parameters': [0.0]
            }, {
                't_start': 20.0,
                't_end': 25.0,
                'component': 'GSG/elevation[1]',
                'type': sim_types.kSimFaultMeasurementRescale,
                'parameters': [0.0]
            }]

        # DLC 2.3: EOG V_hub = V_r-2, V_r+2, V_out, Electrical fault, Ultimate
        elif case_name.startswith('2.3'):
            iec_sim['event_t_start'] = 20.0
            iec_sim['load_case'] = sim_types.kIecCaseExtremeOperatingGust
            overrides['sim']['faults_sim'] = [self._fault_power_sys_zero]
            if case_name == '2.3b':
                phys_sim['wind_speed'] = self._v_hub_sensed - 2.0
            elif case_name == '2.3c':
                phys_sim['wind_speed'] = self._v_hub_sensed + 2.0

        # DLC 2.4: NTM V_in < V_hub < V_out, Fault, Fatigue
        elif case_name == '2.4':
            iec_sim['load_case'] = sim_types.kIecCaseNormalTurbulenceModel
            phys_sim['wind_speed'] = self._v_hub
            overrides['sim']['faults_sim'] = [{
                't_start':
                150.0,
                't_end':
                170.0,
                'component':
                'Servo[1]',
                'type':
                sim_types.kSimFaultActuatorZero,
            }]

        # Start up
        # DLC 3.1: NWP V_in < V_hub < V_out, Fatigue
        elif case_name == '3.1':
            iec_sim['load_case'] = sim_types.kIecCaseNormalWindProfile

        # DLC 3.2: EOG V_hub = V_in, V_r-2, V_r+2, V_out, Ultimate
        elif case_name.startswith('3.2'):
            iec_sim['event_t_start'] = 10.0
            iec_sim['load_case'] = sim_types.kIecCaseExtremeOperatingGust
            phys_sim['wind_speed'] = self._v_in_sensed
            if case_name == '3.2b':
                phys_sim['wind_speed'] = self._v_hub_m2_sensed
            elif case_name == '3.2c':
                phys_sim['wind_speed'] = self._v_hub_p2_sensed
            elif case_name == '3.2d':
                phys_sim['wind_speed'] = self._v_out_sensed

        # DLC 3.3: EDC V_hub = V_in, V_r-2, V_r+2, V_out, Ultimate
        elif case_name.startswith('3.3'):
            iec_sim['event_t_start'] = 10.0
            iec_sim['load_case'] = sim_types.kIecCaseExtremeDirectionChange
            phys_sim['wind_speed'] = self._v_in_sensed
            if case_name == '3.3b':
                phys_sim['wind_speed'] = self._v_hub_m2_sensed
            elif case_name == '3.3c':
                phys_sim['wind_speed'] = self._v_hub_p2_sensed
            elif case_name == '3.3d':
                phys_sim['wind_speed'] = self._v_out_sensed

        # Normal shut down
        # DLC 4.1: NWP V_in < V_hub < V_out, Fatigue
        elif case_name == '4.1':
            iec_sim['load_case'] = sim_types.kIecCaseNormalWindProfile

        # DLC 4.2: EOG V_hub = V_r-2, V_r+2, V_out, Ultimate
        elif case_name.startswith('4.2'):
            iec_sim['event_t_start'] = 50.0
            iec_sim['load_case'] = sim_types.kIecCaseExtremeOperatingGust
            if case_name == '4.2a':
                phys_sim['wind_speed'] = self._v_hub_m2_sensed
            elif case_name == '4.2b':
                phys_sim['wind_speed'] = self._v_hub_p2_sensed
            elif case_name == '4.2c':
                phys_sim['wind_speed'] = self._v_out_sensed

        else:
            raise ValueError('Unknown IEC Case: ' + case_name)

        # Missing cases:
        #  - Emergency shut down.
        #  - Parked (standing still or idling).
        #  - Parked and fault conditions.
        #  - Transport assembly, maintenance, and repair.

        return mconfig.MakeParams(
            'common.all_params',
            overrides_util.PreprocessOverrides(overrides),
            override_method='derived')
Esempio n. 23
0
# See the License for the specific language governing permissions and
# limitations under the License.
"""Report the various flap limits applied in the system."""

import collections
import math
import os

import makani
from makani.avionics.bootloader import system_config
from makani.avionics.firmware.params import codec
from makani.avionics.network import network_config
from makani.config import mconfig
from makani.control import system_types

_CONFIG = mconfig.MakeParams('common.all_params')
_SERVOS = ['A' + f for f in '124578'] + ['E1', 'E2', 'R1', 'R2']
_FLAPS = _SERVOS[0:6] + ['Elevator', 'Rudder']


def GetSimLimits():
    servo_limits = [
        _CONFIG['sim']['servos_sim'][i]['servo_drive']
        for i in range(len(_SERVOS))
    ]
    return {
        _SERVOS[i]: (servo_limits[i]['ref_model_min_position_limit'],
                     servo_limits[i]['ref_model_max_position_limit'])
        for i in range(len(_SERVOS))
    }
Esempio n. 24
0
  pyplot.ylabel('Thrust error [N]')
  pyplot.title('Thrust fit error')
  pyplot.xlim(rotor_model.omegas[[0, -1]])

  print rotor_database_path
  print '  Max Thrust (v_freestream = 0.0): %g' % (
      rotor_model.CalcMaxThrusts([0.0])[0][0])
  print '  Max Thrust (v_freestream = 40.0): %g' % (
      rotor_model.CalcMaxThrusts([40.0])[0][0])


def main(argv):
  # Parse flags.
  try:
    argv = FLAGS(argv)
  except gflags.FlagsError, e:
    print '\nError: %s\n\nUsage: %s ARGS\n%s' % (e, sys.argv[0], FLAGS)
    sys.exit(1)

  propellers = mconfig.MakeParams('m600.propellers')
  for i in range(system_types.kNumPropVersions):
    PlotSimpleRotorModel(
        FLAGS.air_density,
        propellers[i]['database']['name'],
        FLAGS.power_limit, FLAGS.torque_limit, FLAGS.tip_speed_limit,
        FLAGS.advance_ratio_stall_margin, FLAGS.num_curves)
  pyplot.show()

if __name__ == '__main__':
  main(sys.argv)
Esempio n. 25
0
  pyplot.xlabel('Rotor Speed [rad/s]')
  pyplot.ylabel('Airspeed [m/s]')


def main(argv):
  # Parse flags.
  try:
    argv = FLAGS(argv)
  except gflags.FlagsError, e:
    print '\nError: %s\n\nUsage: %s ARGS\n%s' % (e, sys.argv[0], FLAGS)
    sys.exit(1)

  if len(argv) > 1:
    propeller_paths = argv[1:]
  else:
    propellers = mconfig.MakeParams('prop.propellers')
    propeller_paths = [
        os.path.join(makani.HOME, 'database', propeller['database']['name'])
        for propeller in propellers
    ]

  linked_axes = None
  for i, propeller_path in enumerate(propeller_paths):
    prop = load_database.LoadPropDatabase(propeller_path)

    fig = pyplot.figure(i + 1)
    fig.suptitle(os.path.basename(propeller_path))
    fig.canvas.set_window_title(os.path.basename(propeller_path))
    if linked_axes:
      linked_axes = pyplot.subplot(1, 3, 1, sharex=linked_axes,
                                   sharey=linked_axes)
Esempio n. 26
0
    def __init__(self, **kwargs):
        base_params = mconfig.MakeParams('common.all_params',
                                         overrides=None,
                                         override_method='simple')

        # Time [s] to end simulation.
        if FLAGS.flight_plan == 'TurnKey':
            end_time = 1500.0
        elif FLAGS.flight_plan == 'HighHover':
            end_time = 1000.0
        else:
            assert False, 'Unsupported flight plan: %s' % FLAGS.flight_plan

        flight_plan = 'kFlightPlan' + FLAGS.flight_plan

        # Build the list of tables.

        raw_base_overrides = {
            'system': {
                'flight_plan': flight_plan
            },
            'sim': {
                'phys_sim': {
                    'wind_model': ('kWindModelDatabase' if FLAGS.turbsim_folder
                                   else FLAGS.wind_model)
                },
                'telemetry_sample_period':
                0.0,
                'sim_opt': [
                    'kSimOptExitOnCrash', 'kSimOptFaults',
                    'kSimOptGroundContact', 'kSimOptImperfectSensors',
                    'kSimOptPerch', 'kSimOptPerchContact',
                    'kSimOptStackedPowerSystem'
                ],
                'sim_time':
                end_time
            }
        }

        # Only use sensor imperfections in Monte Carlo simulations.
        if FLAGS.monte_carlo:
            raw_base_overrides['sim']['sim_opt'] += ['kSimOptImperfectSensors']

        if FLAGS.offshore:
            raw_base_overrides['system']['test_site'] = 'kTestSiteNorway'
        else:
            raw_base_overrides['system']['test_site'] = 'kTestSiteParkerRanch'

        # Update raw_base_overrides with flag base_overrides.
        raw_base_overrides = dict_util.UpdateNestedDict(
            raw_base_overrides, json.loads(FLAGS.base_overrides))

        if ('system' in raw_base_overrides
                and 'wing_serial' in raw_base_overrides['system']):
            assert False, (
                'Cannot override wing_serial; '
                'it is set implicitly by flight plan and wing_model.')

        y_ranges = []

        if FLAGS.turbsim_folder:
            # Initialize object for selecting appropriate wind databases for each run.
            turbsim_database_selector = turbsim_util.TurbSimDatabaseSelector(
                FLAGS.turbsim_folder, base_params)

            # TODO: Rather than hardcode the shear reference height,
            # pull from a database/file with properties of each database set.
            # Also think about overriding this value in the base_params.
            x_range = parameter_tables.WindSpeedParameterRange(
                FLAGS.wind_speeds, wind_shear_ref_height_agl=21.0)

            if FLAGS.monte_carlo:
                # TODO: Pull the range of options for these from a
                # database/file with properties of each database set. Would need to be
                # able to query a sheet w/ these parameters; maybe make a descriptive
                # csv or json file in each database folder.
                y_ranges += [
                    parameter_tables.WindDatabaseInitialTimeParameterRange(
                        [30.0, 270.0],
                        distribution={
                            'lower_bound': 30.0,
                            'upper_bound': 270.0,
                            'type': 'uniform'
                        }),
                    parameter_tables.WindDatabaseYOffsetParameterRange(
                        [-100.0, 100.0],
                        distribution={
                            'lower_bound': -100.0,
                            'upper_bound': 100.0,
                            'type': 'uniform'
                        }),
                ]

        else:
            turbsim_database_selector = None

            # Sweep wind speeds.
            # If times for wind speed updates are specified (a 3-by-1 list of time
            # values in seconds), then saturate the mean wind speed to
            # FLAGS.max_hover_mean_wind_speed before the second time entry and after
            # the third time entry.
            # NOTE:
            # - This flag will not affect simulations which use a TurbSim database.
            # - Here, the second value in t_updates corresponds to the approximate
            #   time when the kite enters crosswind, and the third value is the time
            #   when the joystick throttle is updated to
            #   kSimJoystickThrottleReturnToPerch.
            x_range = parameter_tables.WindSpeedParameterRange(
                FLAGS.wind_speeds,
                wind_shear_ref_height_agl=base_params['sim']['phys_sim']
                ['wind_shear_ref_height_agl'],
                t_updates=[0.0, 460.0, 820.0],
                max_wind_speed=FLAGS.max_hover_mean_wind_speed)

            # Sweep environmental properties.
            # Original source for WindElevation is unknown.
            # TODO: Update WindElevation distribution based on Parker Ranch
            # wind measurements.
            # Wind veer range is based on Parker Ranch wind measurements as discussed
            # in http://b/117942530.
            y_ranges += [
                parameter_tables.WindElevationDegParameterRange([-6.0, 6.0],
                                                                distribution={
                                                                    'mean':
                                                                    0.0,
                                                                    'sigma':
                                                                    3.0,
                                                                    'bound':
                                                                    2.0,
                                                                    'type':
                                                                    'normal'
                                                                }),
                parameter_tables.WindVeerDegParameterRange(
                    [-45.0, -21.0, 21.0, 45.0],
                    distribution={
                        'mean': 13.5,
                        'sigma': 7.5,
                        'bound': 3.0,
                        'type': 'normal'
                    }),
            ]

        shear_parameter_range = parameter_tables.WindShearExponentParameterRange(
            FLAGS.wind_shears)
        # Add shear exponents as a sweep if this is not a Monte Carlo run.
        if not FLAGS.monte_carlo:
            y_ranges.append(shear_parameter_range)

        # Sweep mass properties.
        y_ranges += [
            # Center of mass location: maximum 0.05 m error in each dimension for
            # Monte Carlo sweeps, and 0.05 m variation in crosswind parameters
            # sweeps.
            parameter_tables.CenterOfMassOffsetParameterRange(
                0, [-0.05, 0.05],
                distribution={
                    'mean': 0.0,
                    'sigma': 0.025,
                    'bound': 2.0,
                    'type': 'normal'
                },
                body='wing_sim',
                body_name='Wing'),
            parameter_tables.CenterOfMassOffsetParameterRange(
                1, [-0.05, 0.05],
                distribution={
                    'mean': 0.0,
                    'sigma': 0.025,
                    'bound': 2.0,
                    'type': 'normal'
                },
                body='wing_sim',
                body_name='Wing'),
            parameter_tables.CenterOfMassOffsetParameterRange(
                2, [-0.05, 0.05],
                distribution={
                    'mean': 0.0,
                    'sigma': 0.025,
                    'bound': 2.0,
                    'type': 'normal'
                },
                body='wing_sim',
                body_name='Wing'),
            # Mass scaling: 1.4% error for both Monte Carlo and crosswind sweeps.
            parameter_tables.MassScaleParameterRange([0.986, 1.014],
                                                     distribution={
                                                         'mean': 1.0,
                                                         'sigma': 0.014,
                                                         'bound': 2.0,
                                                         'type': 'normal'
                                                     },
                                                     body='wing_sim',
                                                     body_name='Wing'),
            # Inertia scaling: maximum errors for Monte Carlo sweeps of 5% for Ixx,
            # 2% for Iyy and 4.4% for Izz. 10% variation in crosswind sweeps.
            parameter_tables.InertiaScaleParameterRange(0, [0.90, 1.1],
                                                        distribution={
                                                            'mean': 1.0,
                                                            'sigma': 0.025,
                                                            'bound': 2.0,
                                                            'type': 'normal'
                                                        },
                                                        body='wing_sim',
                                                        body_name='Wing'),
            parameter_tables.InertiaScaleParameterRange(1, [0.90, 1.1],
                                                        distribution={
                                                            'mean': 1.0,
                                                            'sigma': 0.01,
                                                            'bound': 2.0,
                                                            'type': 'normal'
                                                        },
                                                        body='wing_sim',
                                                        body_name='Wing'),
            parameter_tables.InertiaScaleParameterRange(2, [0.90, 1.1],
                                                        distribution={
                                                            'mean': 1.0,
                                                            'sigma': 0.022,
                                                            'bound': 2.0,
                                                            'type': 'normal'
                                                        },
                                                        body='wing_sim',
                                                        body_name='Wing'),
        ]

        # Sweep aerodynamics parameters.
        # The following parameters override existing aerodynamic offsets in our
        # config files.
        if FLAGS.wing_model == 'm600':
            y_ranges += [
                # CD offset: maximum 0.050 error.
                # Known offset in CD from RPX-07 glide data analysis is 0.075.
                # Use 0.075 as mean for Monte Carlo sweeps.
                # This is chosen since this will override existing offsets.
                # [0.075 - 0.050, 0.075 + 0.050] variation in crosswind sweeps.
                parameter_tables.AeroSimOffsetParameterRange(
                    'CD', [0.025, 0.125],
                    distribution={
                        'mean': 0.075,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    }),
                # CL offset: maximum 0.3 error for Monte Carlo sweeps,
                # 0.2 variation in crosswind sweeps.
                # Flight data and aero database comparison shows CL is over predicted,
                # thus setting the mean at -0.125 from Monte Carlo sweeps.
                parameter_tables.AeroSimOffsetParameterRange(
                    'CL', [-0.2, 0.2],
                    distribution={
                        'mean': -0.125,
                        'sigma': 0.15,
                        'bound': 2.0,
                        'type': 'normal'
                    }),
            ] + parameter_sweeps.GetAeroDerivativeParameterRanges()
        elif FLAGS.wing_model == 'oktoberkite':
            y_ranges += [
                parameter_tables.AeroSimOffsetParameterRange(
                    'CD', [-0.011, 0.033],
                    distribution={
                        'mean': 0.011,
                        'sigma': 0.011,
                        'bound': 2.0,
                        'type': 'normal'
                    }),
                parameter_tables.AeroSimOffsetParameterRange(
                    'CL', [-0.275, 0.275],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.15,
                        'bound': 2.0,
                        'type': 'normal'
                    }),
            ] + parameter_sweeps.GetAeroDerivativeParameterRanges()
        else:
            assert False, '{} wing_model is not supported.'.format(
                FLAGS.wing_model)

        # Sweep Pitot parameters.
        y_ranges += [
            # Pitot angles offsets: maximum 1 deg offset for Monte Carlo and
            # crosswind sweeps.
            # Pitot Cp offset: maximum 0.01 offset for Monte Carlo and
            # 0.02 variation in crosswind sweeps.
            parameter_tables.PitotPitchDegParameterRange([-1.0, 1.0],
                                                         distribution={
                                                             'mean': 0.0,
                                                             'sigma': 0.5,
                                                             'bound': 2.0,
                                                             'type': 'normal'
                                                         }),
            parameter_tables.PitotYawDegParameterRange([-1.0, 1.0],
                                                       distribution={
                                                           'mean': 0.0,
                                                           'sigma': 0.5,
                                                           'bound': 2.0,
                                                           'type': 'normal'
                                                       }),
            parameter_tables.PitotCpOffsetParameterRange([-0.02, 0.02],
                                                         distribution={
                                                             'mean': 0.0,
                                                             'sigma': 0.01,
                                                             'bound': 2.0,
                                                             'type': 'normal'
                                                         }),
        ]

        # Sweep actuator parameters.
        y_ranges += parameter_sweeps.GetFlapOffsetParameterRanges()

        # Sweep offshore parameters.
        if FLAGS.offshore:
            # Current uncertainties:
            #   - 5% on mass properties.
            #   - 5% on hydrodynamic model parameters.
            #   - 5% on mooring line model parameters.
            #   - 5 deg on yaw equilibrium heading.
            #   - 50 cm on axial mooring line attachment point model.
            #   - 20 cm on orthogonal mooring line attachment point model.
            # TODO: Refine the parameter variations once we have measured
            # data and knowledge of sensitivity.
            # TODO: Add a deterministic offshore crosswind sweep to the
            # nightly (b/137648033).
            y_ranges += [
                # Center of mass offset.
                parameter_tables.CenterOfMassOffsetParameterRange(
                    0, [-0.2, 0.2],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.1,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    body='buoy_sim',
                    body_name='Buoy'),
                parameter_tables.CenterOfMassOffsetParameterRange(
                    1, [-0.2, 0.2],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.1,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    body='buoy_sim',
                    body_name='Buoy'),
                parameter_tables.CenterOfMassOffsetParameterRange(
                    2, [-1.0, 1.0],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.39,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    body='buoy_sim',
                    body_name='Buoy'),

                # Mass scaling.
                parameter_tables.MassScaleParameterRange([0.9, 1.1],
                                                         distribution={
                                                             'mean': 1.0,
                                                             'sigma': 0.025,
                                                             'bound': 2.0,
                                                             'type': 'normal'
                                                         },
                                                         body='buoy_sim',
                                                         body_name='Buoy'),

                # Inertia scaling.
                parameter_tables.InertiaScaleParameterRange(0, [0.9, 1.1],
                                                            distribution={
                                                                'mean': 1.0,
                                                                'sigma': 0.025,
                                                                'bound': 2.0,
                                                                'type':
                                                                'normal'
                                                            },
                                                            body='buoy_sim',
                                                            body_name='Buoy'),
                parameter_tables.InertiaScaleParameterRange(1, [0.9, 1.1],
                                                            distribution={
                                                                'mean': 1.0,
                                                                'sigma': 0.025,
                                                                'bound': 2.0,
                                                                'type':
                                                                'normal'
                                                            },
                                                            body='buoy_sim',
                                                            body_name='Buoy'),
                parameter_tables.InertiaScaleParameterRange(2, [0.9, 1.1],
                                                            distribution={
                                                                'mean': 1.0,
                                                                'sigma': 0.025,
                                                                'bound': 2.0,
                                                                'type':
                                                                'normal'
                                                            },
                                                            body='buoy_sim',
                                                            body_name='Buoy'),

                # Hydrodynamic model uncertainties.
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Torsional Damping X Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='torsional_damping_x_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Torsional Damping Y Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='torsional_damping_y_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Torsional Damping Z Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='torsional_damping_z_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Buoyancy Damping Coeff. Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='buoyancy_damping_coeff_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Added Mass Coeff. Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='Ca_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Effective Heave Diameter Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='Dh_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Added Inertia Coeff. Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='hydrodynamics',
                    variable='ki_scale'),

                # Mooring line model uncertainties.
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Equilibrium Yaw Angle Delta [deg]',
                    np.arange(-180., 180., 30.),
                    distribution={
                        'mean': 0.0,
                        'sigma': 2.5,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='yaw_equilibrium_heading_delta'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Yaw Torsional Stiffness Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='torsional_stiffness_z_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Effective Mooring Line X Attachment Delta [m]',
                    [-0.2, 0.2],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.1,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='mooring_attach_pos_x_delta'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Effective Mooring Line Y Attachment Delta [m]',
                    [-0.2, 0.2],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.1,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='mooring_attach_pos_y_delta'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Effective Mooring Line Z Attachment Delta [m]',
                    [-0.5, 0.5],
                    distribution={
                        'mean': 0.0,
                        'sigma': 0.25,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='mooring_attach_pos_z_delta'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Mooring Line Linear Spring Coeff. Scale [#]',
                    [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='kt0_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Mooring Line Quadratic Spring Coeff. Scale [#]',
                    [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='kt1_scale'),
                parameter_tables.BuoyModelParameterRange(
                    'Buoy Mooring Line Damping Coeff. Scale [#]', [0.9, 1.1],
                    distribution={
                        'mean': 1.0,
                        'sigma': 0.025,
                        'bound': 2.0,
                        'type': 'normal'
                    },
                    category='mooring_lines',
                    variable='ct_scale'),
            ]

            # Sweep environmental properties.
            # Air density at Stavanger test site is 1.220 kg/m^3.
            # Use this as mean value and maximum 5% error
            # in density measurement for Monte Carlo sweeps. For crosswind sweeps,
            # high limit is at sea-level density and low limit is at roughly 2500 m
            # density altitude.
            # NOTE: Ideally, we would like to point the mean value of
            # the air density parameter range to the air_density in the config
            # structure. However, it is a parameter that is derived from the test_site
            # param, which is also overridden at the current level.
            y_ranges += [
                parameter_tables.AirDensityParameterRange([0.976, 1.225],
                                                          distribution={
                                                              'mean': 1.220,
                                                              'sigma': 0.025,
                                                              'bound': 2.0,
                                                              'type': 'normal'
                                                          })
            ]

            # Parameters used to define the 'WaveParameterSelector' class used in
            # Monte Carlo analyses and define the distribution in the
            # parameter_tables. See the WaveParameterSelector and ParameterRange class
            # definitions in parameter_tables.py for more description.
            # Note, for example, that the 'mean' used in a lognormal distribution is
            # the mean of the underlying normal distribution, and can therefore be
            # outside the upper/lower_bounds.
            # Generated from Colab notebook: b/130682761#comment15
            # Source: 'NORA10_5922N_0509E.txt'
            # Data filtered for Jun to Sep, between hours of 6 and 20, and when
            # wind speed at 10 m is between 5 and 15 m/s.'
            # Overwriting the upper_bound of the significant wave height to 3.0 m
            # to account for wave envelope due to max significant wave height for
            # personnel transfers at sea: b/130682761#comment18
            wave_variation_params = {
                'peak_period': {
                    'correlation_fit': {
                        'coefficient': 1.186394,
                        'description': 'TP = coefficient * HS + intercept'
                        ' + (lognorm(mean, sigma) + loc)',
                        'intercept': 5.822409,
                        'lower_bound': 3.923101,
                        'upper_bound': 13.687049,
                    },
                    'description':
                    'Peak wave period of the total sea state, based on total sea '
                    'significant wave height.',
                    'distribution': {
                        'loc': -4.342332,
                        'lower_bound': -2.478684,
                        'mean': 1.384812,
                        'sigma': 0.405295,
                        'type': 'lognormal',
                        'upper_bound': 4.217562,
                    },
                    'label':
                    'Peak wave period variation [s]',
                    'values': [-2.478684, 4.217562],
                },
                'significant_height': {
                    'correlation_fit': {
                        'coefficient':
                        0.012127,
                        'description':
                        'HS = coefficient * speed^2 + intercept + (lognorm(mean, '
                        'sigma) + loc). Note the distribution is closer to '
                        'exponnorm, but lognorm should be close enough and keeps '
                        'things simpler.',
                        'intercept':
                        0.653568,
                        'lower_bound':
                        0.427477,
                        'upper_bound':
                        3.0,
                    },
                    'description':
                    'Significant wave height of the total sea state, '
                    'based on wind speed at 10 m.',
                    'distribution': {
                        'loc': -2.647313,
                        'lower_bound': -0.854369,
                        'mean': 0.953525,
                        'sigma': 0.196548,
                        'type': 'lognormal',
                        'upper_bound': 1.108073,
                    },
                    'label':
                    'Significant wave height variation [m]',
                    'values': [-0.854369, 1.108073],
                },
                'wave_wind_alignment': {
                    'description':
                    'Alignment [deg] of total sea peak wave direction minus wind '
                    'direction at 10 m. Corrected for use of wave heading '
                    'as wave-direction-of-travel.',
                    'distribution': {
                        'bound': 2.000000,
                        'mean': 191.708319,
                        'sigma': 51.891563,
                        'type': 'normal',
                    },
                    'label':
                    'Wave alignment [deg] relative to wind',
                    'values': [87.925192, 295.491445],
                },
                'wind_direction': {
                    'description': 'Measured at 10 m.',
                    'distribution': {
                        'distributions': [{
                            'mean': 170.364074,
                            'sigma': 29.122998,
                            'type': 'vonmises',
                            'units': 'deg',
                        }, {
                            'mean': 335.172882,
                            'sigma': 14.044122,
                            'type': 'vonmises',
                            'units': 'deg',
                        }],
                        'type':
                        'multimodal',
                        'weights': [0.510851, 0.489149],
                    },
                    'label': 'Wind direction [deg]',
                    'values': [170.364074, 335.172882],
                },
            }

            y_ranges += [
                parameter_tables.WindDirectionDegParameterRange(
                    wave_variation_params['wind_direction']['values'],
                    distribution=wave_variation_params['wind_direction']
                    ['distribution'])
            ]
            if FLAGS.randomize_waves:
                del wave_variation_params['wind_direction']
                for param in wave_variation_params:
                    y_ranges += [
                        parameter_tables.CustomParameterRange(
                            wave_variation_params[param]['label'],
                            ['sim', 'sea_sim', 'waves', param],
                            wave_variation_params[param]['values'],
                            wave_variation_params[param]['distribution'])
                    ]
                wave_parameter_selector = parameter_tables.WaveParameterSelector(
                    wave_variation_params)
            else:
                wave_parameter_selector = None
        else:
            wave_parameter_selector = None

            # Sweep environmental properties.
            # Air density at Parker Ranch test site is 1.026 kg/m^3. This is roughly
            # 1800 m density altitude. Use this as mean value and maximum 5% error
            # in density measurement for Monte Carlo sweeps. For crosswind sweeps,
            # high limit is at sea-level density and low limit is at roughly 2500 m
            # density altitude.
            y_ranges += [
                parameter_tables.AirDensityParameterRange([0.976, 1.225],
                                                          distribution={
                                                              'mean': 1.026,
                                                              'sigma': 0.025,
                                                              'bound': 2.0,
                                                              'type': 'normal'
                                                          })
            ]

            # Wind at Parker Ranch test site is nominally from the NorthEast.
            # From the initial analysis of our 2018 Aug-Oct Lidar data at the test
            # site, the mean is roughly at 55 degrees azimuth.
            # From the analysis presented in go/makani-cw09-wind-envelope, the
            # operational envelope for the wind direction was set to [0 - 75] degrees.
            # The normal distribution is clipped 5 degrees past these limits.
            # NOTE: Wind direction is an epistemic random variable that
            # we treat as an aleatory random variable to simplify the Monte Carlo
            # analysis.
            y_ranges += [
                parameter_tables.WindDirectionDegParameterRange(
                    [30, 80],
                    distribution={
                        'mean': 55.0,
                        'sigma': 15.0,
                        'lower_bound': -5.0,
                        'upper_bound': 80.0,
                        'type': 'normal'
                    })
            ]

        base_overrides = overrides_util.PreprocessOverrides(raw_base_overrides)

        # Wipe out existing y_range if only_custom_sweep flag is passed.
        if FLAGS.only_custom_sweep:
            y_ranges = []

        # Parse the custom_sweep flag.
        custom_sweep_entries = json.loads(FLAGS.custom_sweep)

        # Add custom sweep entries to y_ranges.
        for entry in custom_sweep_entries:
            y_ranges += [
                parameter_tables.CustomParameterRange(
                    entry['name'],
                    entry['path'],
                    # Values can be unspecified for monte-carlo usage.
                    entry.get('values', None),
                    # Distribution can be unspecifed for deterministic sweep usage.
                    entry.get('distribution', None))
            ]

        tables = []
        if FLAGS.monte_carlo:
            num_cols = len(x_range.values)

            for shear_exponent in shear_parameter_range.values:
                for value in x_range.values:
                    table_base_overrides = dict_util.MergeNestedDicts(
                        base_overrides, x_range.GetOverrides(value))
                    table_base_overrides = dict_util.MergeNestedDicts(
                        table_base_overrides,
                        shear_parameter_range.GetOverrides(shear_exponent))
                    tables.append(
                        parameter_tables.ParameterRangeMonteCarloTable(
                            'Monte Carlo %s = %g, shear exponent = %g' %
                            (x_range.label, x_range.GetDisplayValue(value),
                             shear_exponent),
                            [FLAGS.monte_carlo_cols, FLAGS.monte_carlo_rows],
                            y_ranges,
                            base_overrides=table_base_overrides,
                            turbsim_database_selector=turbsim_database_selector,
                            wave_parameter_selector=wave_parameter_selector))
            title = FLAGS.flight_plan + ' Monte Carlo'
        else:
            num_cols = 4
            for y_range in y_ranges:
                tables.append(
                    parameter_tables.ParameterRangeTable(
                        y_range.label,
                        x_range,
                        y_range,
                        base_overrides=base_overrides,
                        turbsim_database_selector=turbsim_database_selector))
            title = FLAGS.flight_plan + ' Parameter Sweeps'

        wing_model = wing_flag.FlagToWingModelName(FLAGS.wing_model)
        params = batch_sim_params.CrosswindSweepsParameters(
            flight_plan=flight_plan,
            wing_model=wing_model,
            offshore=FLAGS.offshore)
        super(CrosswindSweepsSimClient,
              self).__init__(FLAGS.output_dir,
                             tables,
                             params.scoring_functions,
                             title=title,
                             columns=num_cols,
                             **kwargs)
Esempio n. 27
0
        wing_serials = [FLAGS.wing_serial_enum]
        if not _IsValidWingSerial(FLAGS.wing_serial_enum, FLAGS.wing_model):
            assert False, (
                'Wing model "%s" does not have serial number %d (%s).' %
                (FLAGS.wing_model, FLAGS.wing_serial_enum,
                 _WING_SERIAL_HELPER.ShortName(FLAGS.wing_serial_enum)))

    controllers = []
    for wing_serial in wing_serials:
        if not _IsValidWingSerial(wing_serial, FLAGS.wing_model):
            continue

        mconfig.WING_MODEL = FLAGS.wing_model

        wing_serial_is_active = mconfig.MakeParams(
            'common.wing_serial_status',
            overrides={'wing_serial': wing_serial},
            override_method='derived')
        if not wing_serial_is_active:
            continue

        print '\nWing serial: %s\n' % (_WING_SERIAL_HELPER.Name(wing_serial))
        overrides = {'system': {'wing_serial': wing_serial}}
        all_params = mconfig.MakeParams('common.all_params',
                                        overrides=overrides,
                                        override_method='derived')
        CheckAeroSimParams(all_params)

        trans_in = ControlDesign(all_params['system'], all_params['control'],
                                 all_params['sim'])

        # TODO: Consider the impact of nonzero wind speed, with updates