utiasDSL / safe-control-gym

PyBullet CartPole and Quadrotor environments—with CasADi symbolic a priori dynamics—for learning-based control and RL
https://www.dynsyslab.org/safe-robot-learning/
MIT License
560 stars 123 forks source link

Regarding dynamics disturbance in Quadrotor 3D #137

Closed SapanaChaudhary closed 9 months ago

SapanaChaudhary commented 9 months ago

Hello!

I want to be able to induce disturbances in quadrotor dynamics. Do the quadrotor environments have dynamics disturbance by default as suggested by the line below?

passive_disturb = 'dynamics' in self.disturbances

adamhall commented 9 months ago

Hi!

Thanks for taking a look at our repo. For me to help you better, what exactly do you mean by "disturbance in quadrotor dynamics"? Do you want to change the mass properties of the quadrotor, apply an "external force" to the quadrotor, or do you want to add noise to the observations?

SapanaChaudhary commented 9 months ago

Thanks for the quick response!

I want to be able to apply external force to the quadrotor, and add noise to the observations.

adamhall commented 9 months ago

This should be doable. Basically, when specify disturbances, you can either apply then to observation, action or dynamics. Disturbances on

Each disturbance must be selected from a list of possible disturbance types

DISTURBANCE_TYPES = {'impulse': ImpulseDisturbance,
                     'step': StepDisturbance,
                     'uniform': UniformNoise,
                     'white_noise': WhiteNoise,
                     'periodic': PeriodicNoise,
                     }

Then each of these disturbance types have specific arguments. To add these to your environment, you must add them in your environment YAML file.

For example, say you want white_noise with a standard deviation 0.01 added to all states of your observations, and an external step disturbance of 2 N that acts on the quadrotor 2D quadrotor in the X direction after 10 seconds of flying, you would add the following to your YAML file, under task_config:

task_config:
  disturbances:
    observation:
      - disturbance_func: white_noise
        std: 0.01
    dynamics:
      - distrubance_func: step
        mask: [1 0 0 0 0 0]              # mask to only affect the x-axis
        magnitude: 2.0
        offset: 10.0

To see the options for the various disturbance types, check out the classes in safe_control_gym/envs/disturbances.py

Also note that the dynamics disturbances are not well debugged, so if you run into any issues post them here and we can try to figure them out.

SapanaChaudhary commented 9 months ago

Thank you, this is very helpful!

The suggested mask ([1 0 0 0 0 0] under dynamics in the example provided) doesn't seem to work for 2D and 3D quadrotors. I get the following Assertion error while running execute_rl_controller.py on a 3D quadrotor with dynamics disturbances from the example above.

self.dim = 3 and len(self.mask) = 1, leading to the error.

File "/home/../safe-control-gym/examples/rl/../../safe_control_gym/experiments/execute_rl_controller.py", line 147, in <module>
    func(config)
  File "/home/../safe-control-gym/examples/rl/../../safe_control_gym/experiments/execute_rl_controller.py", line 33, in train
    control_agent = make(config.algo,
  File "/home/../safe-control-gym/safe_control_gym/utils/registration.py", line 124, in make
    return registry.make(idx, *args, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/utils/registration.py", line 87, in make
    obj = spec.make(*args, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/utils/registration.py", line 68, in make
    obj = cls(*args, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/controllers/ppo/ppo.py", line 45, in __init__
    self.env = make_vec_envs(env_func, None, self.rollout_batch_size, self.num_workers, seed)
  File "/home/../safe-control-gym/safe_control_gym/envs/env_wrappers/vectorized_env/__init__.py", line 66, in make_vec_envs
    return DummyVecEnv(env_fns)
  File "/home/../safe-control-gym/safe_control_gym/envs/env_wrappers/vectorized_env/dummy_vec_env.py", line 18, in __init__
    self.envs = [fn() for fn in env_fns]
  File "/home/../safe-control-gym/safe_control_gym/envs/env_wrappers/vectorized_env/dummy_vec_env.py", line 18, in <listcomp>
    self.envs = [fn() for fn in env_fns]
  File "/home/../safe-control-gym/safe_control_gym/envs/env_wrappers/vectorized_env/__init__.py", line 35, in _thunk
    env = env_func(seed=e_seed, **env_config)
  File "/home/../safe-control-gym/safe_control_gym/utils/registration.py", line 124, in make
    return registry.make(idx, *args, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/utils/registration.py", line 87, in make
    obj = spec.make(*args, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/utils/registration.py", line 68, in make
    obj = cls(*args, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/envs/gym_pybullet_drones/quadrotor.py", line 203, in __init__
    super().__init__(init_state=init_state, inertial_prop=inertial_prop, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/envs/gym_pybullet_drones/base_aviary.py", line 137, in __init__
    super().__init__(gui=gui, verbose=verbose, **kwargs)
  File "/home/../safe-control-gym/safe_control_gym/envs/benchmark_env.py", line 195, in __init__
    self._setup_disturbances()
  File "/home/../safe-control-gym/safe_control_gym/envs/gym_pybullet_drones/quadrotor.py", line 708, in _setup_disturbances
    super()._setup_disturbances()
  File "/home/../safe-control-gym/safe_control_gym/envs/benchmark_env.py", line 316, in _setup_disturbances
    self.disturbances[mode] = create_disturbance_list(disturb_specs, mode_shared_args, self)
  File "/home/../safe-control-gym/safe_control_gym/envs/disturbances.py", line 301, in create_disturbance_list
    disturb = disturb_cls(env, **shared_args, **cfg)
  File "/home/../safe-control-gym/safe_control_gym/envs/disturbances.py", line 140, in __init__
    super().__init__(env, dim, mask)
  File "/home/../safe-control-gym/safe_control_gym/envs/disturbances.py", line 19, in __init__
    assert self.dim == len(self.mask)
AssertionError
adamhall commented 9 months ago

Sorry, I misled you! I have not personally used this feature much. For the dynamics disturbances, we only apply forces. For the 1D quad, mask should have 1 dimension, for 2D quad, 2 dimensions, and 3D quad only 3 dimensions. I also forgot to add commas. Thus, you should add (for the 3D quad)

task_config:
  disturbances:
    observation:
      - disturbance_func: white_noise
        std: 0.01
    dynamics:
      - distrubance_func: step
        mask: [1, 0, 0]              # mask to only affect the x-axis, and don't forget commas
        magnitude: 2.0
        offset: 10.0

For the PID example, I confirmed that adding examples/pid/config_overrides/quadrotor_3D/quadrotor_3D_tracking.yaml

  disturbances:
    dynamics:
    - disturbance_func: step
      mask: [1, 0, 0]
      magnitude: 0.05
      step_offset: 0.5

to examples/pid/config_overrides/quadrotor_3D/quadrotor_3D_tracking.yaml works when running examples/pid/pid_experiment.sh.

Also, note that if no value is given to step_offset, then a random one is chosen, but there is a bug here which I am going to add a PR for (numpy Generators don't have the method randint but required integers).

Let me know if this works for you.

adamhall commented 9 months ago

Hi @SapanaChaudhary,

Just wondering if you had a chance to try this so we can close this issue.

SapanaChaudhary commented 9 months ago

Yes, it works now. Sorry, I missed replying earlier. Thanks a lot!