Commit 5ddb0728 authored by Ibrahim's avatar Ibrahim
Browse files

Controller running pos/att at different frequencies

parent cf843db1
%% Cell type:markdown id:d001f534 tags:
#### Notebook setup
%% Cell type:code id:be76bd53 tags:
``` python
%pip install -e .
```
%% Cell type:code id:5a776232 tags:
``` python
%reload_ext autoreload
%autoreload 2
%matplotlib inline
import time
import warnings
import os, sys
from copy import deepcopy
from types import SimpleNamespace
import matplotlib.pyplot as plt
import gym
import numpy as np
from tqdm.auto import tqdm, trange
from multirotor.helpers import control_allocation_matrix, DataLog
from multirotor.vehicle import MotorParams, VehicleParams, PropellerParams, SimulationParams, BatteryParams
from multirotor.controller import (
PosController, VelController,
AttController, RateController,
AltController, AltRateController,
Controller
)
from multirotor.simulation import Multirotor, Propeller, Motor, Battery
from multirotor.coords import body_to_inertial, inertial_to_body, direction_cosine_matrix, angular_to_euler_rate
from multirotor.env import SpeedsMultirotorEnv as LocalOctorotor
from multirotor.trajectories import Trajectory, GuidedTrajectory
from multirotor.visualize import plot_datalog
```
%% Cell type:code id:421b284b tags:
``` python
# Plotting/display parameters
# https://stackoverflow.com/a/21009774/4591810
float_formatter = "{:.3f}".format
np.set_printoptions(formatter={'float_kind':float_formatter})
SMALL_SIZE = 16
MEDIUM_SIZE = 16
BIGGER_SIZE = 20
plt.rc('font', size=SMALL_SIZE) # controls default text sizes
plt.rc('axes', titlesize=MEDIUM_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=BIGGER_SIZE, titlesize=BIGGER_SIZE) # fontsize of the x and y labels
plt.rc('xtick', labelsize=MEDIUM_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=MEDIUM_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
```
%% Cell type:markdown id:9a31149f tags:
### Parameters
%% Cell type:code id:1ba2a2fe tags:
``` python
# Tarot T18 params
bp = BatteryParams(max_voltage=22.2)
mp = MotorParams(
moment_of_inertia=5e-5,
# resistance=0.27,
resistance=0.081,
k_emf=0.0265,
# k_motor=0.0932,
speed_voltage_scaling=0.0347,
max_current=38.
)
pp = PropellerParams(
moment_of_inertia=1.86e-6,
use_thrust_constant=True,
k_thrust=9.8419e-05, # 18-inch propeller
# k_thrust=5.28847e-05, # 15 inch propeller
k_drag=1.8503e-06, # 18-inch propeller
# k_drag=1.34545e-06, # 15-inch propeller
motor=mp
)
vp = VehicleParams(
propellers=[pp] * 8,
battery=bp,
# angles in 45 deg increments, rotated to align with
# model setup in gazebo sim (not part of this repo)
angles=np.linspace(0, -2*np.pi, num=8, endpoint=False) + 0.375 * np.pi,
distances=np.ones(8) * 0.635,
clockwise=[-1,1,-1,1,-1,1,-1,1],
mass=10.66,
inertia_matrix=np.asarray([
[0.2206, 0, 0],
[0, 0.2206, 0.],
[0, 0, 0.4238]
])
)
sp = SimulationParams(dt=0.001, g=9.81)
```
%% Cell type:markdown id:17f3b34d tags:
### Multirotor
%% Cell type:markdown id:0505e4f6 tags:
#### Motor
%% Cell type:code id:b3128c10 tags:
``` python
%matplotlib inline
# Plot motor speeds as a function of time and input voltage signal
plt.figure(figsize=(8,8))
motor = Motor(mp, sp)
for signal in [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]:
speeds = []
motor.reset()
for i in range(200):
speeds.append(motor.step(signal))
plt.plot(speeds, label='%dV' % signal)
plt.legend(ncol=2)
plt.ylabel('Speed rad/s')
plt.xlabel('Time /ms')
```
%% Cell type:code id:42ebfb97 tags:
``` python
from multirotor.helpers import learn_speed_voltage_scaling
def make_motor_fn(params, sp):
from copy import deepcopy
params = deepcopy(params)
params.speed_voltage_scaling = 1.
def motor_step(signal):
m = Motor(params, sp)
for i in range(100):
s = m.step(signal)
return s
return motor_step
print('Voltage = %.5f * speed' % (learn_speed_voltage_scaling(make_motor_fn(mp, sp))))
```
%% Cell type:markdown id:6fb2f5c9 tags:
#### Propeller
%% Cell type:code id:1669687b tags:
``` python
%matplotlib inline
# Plot propeller speed by numerically solving the thrust equation,
# *if* accurate propeller measurements are given in params
pp_ = deepcopy(pp)
pp_.use_thrust_constant = False # Set to true to just use k_thrust
prop = Propeller(pp_, sp)
plt.figure(figsize=(8,8))
speeds = np.linspace(0, 600, num=100)
for a in np.linspace(0, 10, 10, endpoint=False):
thrusts = []
for s in speeds:
thrusts.append(prop.thrust(s, np.asarray([0, 0, a])))
plt.plot(speeds, thrusts, label='%.1f m/s' % a)
plt.xlabel('Speed rad/s')
plt.ylabel('Thrust /N')
plt.title('Thrust with airspeed')
plt.legend(ncol=2)
```
%% Cell type:markdown id:e7bb489d tags:
#### Vehicle
%% Cell type:code id:ecc23e14 tags:
``` python
# Combine propeller/motor/vehicle to get vehicle.
# Take off simulation
m = Multirotor(vp, sp)
log = DataLog(vehicle=m) # convenient logging class
m.reset()
m.state *= 0 # set to zero, reset() sets random values
action = m.allocate_control(
thrust=m.weight * 1.1,
torques=np.asarray([0, 0, 0])
)
for i in range(500):
m.step_speeds(action)
log.log()
log.done_logging()
plt.plot(log.z)
```
%% Cell type:markdown id:2c6535f4 tags:
### PID Controller
%% Cell type:code id:33984887 tags:
``` python
# From PID parameters file
def get_controller(m: Multirotor):
def get_controller(m: Multirotor, max_velocity=5., max_acceleration=3.):
assert m.simulation.dt <= 0.1, 'Simulation time step too large.'
pos = PosController(
1.0, 0., 0., 1., dt=m.simulation.dt, vehicle=m)
1.0, 0., 0., 1., vehicle=m,
max_velocity=max_velocity, max_acceleration=max_acceleration
)
vel = VelController(
2.0, 1.0, 0.5, 1000., dt=m.simulation.dt, vehicle=m)
2.0, 1.0, 0.5, 1000., vehicle=m)
att = AttController(
[2.6875, 4.5, 4.5],
0, 0.,
1., dt=m.simulation.dt, vehicle=m)
1., vehicle=m)
rat = RateController(
[0.1655, 0.1655, 0.5],
[0.135, 0.135, 0.018],
[0.01234, 0.01234, 0.],
[0.5,0.5,0.5], dt=m.simulation.dt, vehicle=m)
[0.5,0.5,0.5], vehicle=m)
alt = AltController(
1, 0, 0,
1, dt=m.simulation.dt, vehicle=m)
1, vehicle=m)
alt_rate = AltRateController(
5, 0, 0,
1, dt=m.simulation.dt, vehicle=m)
1, vehicle=m)
ctrl = Controller(
pos, vel, att, rat, alt, alt_rate
pos, vel, att, rat, alt, alt_rate,
interval_p=0.1, interval_a=0.01, interval_z=0.1
)
return ctrl
```
%% Cell type:code id:d21d73fe tags:
``` python
%matplotlib inline
m = Multirotor(vp, sp)
ctrl = get_controller(m)
log = DataLog(controller=ctrl)
for i in range(100):
action = ctrl.step((1,1,1,0))
log = DataLog(vehicle=m, controller=ctrl)
for i in range(500):
action = ctrl.step((0.01,0.1,1,0))
dynamics = np.zeros(6, m.dtype)
dynamics[2] = action[0]
dynamics[3:] = action[1:]
# no allocation or motor simulation,
# requested dynamics are fulfilled:
m.step_dynamics(dynamics)
log.log()
log.done_logging()
plt.plot(log.actions[:,0], ls=':', label='thrust')
lines = plt.gca().lines
plt.twinx()
for s, axis in zip(log.actions.T[1:], ('x','y','z')):
plt.plot(s, label=axis + '-torque')
plt.legend(handles=plt.gca().lines + lines)
```
%% Cell type:markdown id:cfbc9c25 tags:
#### Attitude Angle Controller
%% Cell type:code id:f012e1f8 tags:
``` python
m = Multirotor(vp, sp)
fz = m.weight
att = get_controller(m).ctrl_a
log = DataLog(vehicle=m, controller=att, other_vars=('err',))
for i in range(5000):
ref = np.asarray([np.pi/18, 0, 0])
# action is prescribed euler rate
action = att.step(ref, m.orientation)
# action = np.clip(action, a_min=-0.1, a_max=0.1)
m.step_dynamics(np.asarray([0, 0, 0, *action]))
log.log(err=att.err_p[0])
log._actions[-1] = action
log.done_logging()
plt.plot(log.roll * 180 / np.pi)
plt.twinx()
plt.plot(log.actions[:,0], ls=':', label='Rate rad/s')
```
%% Cell type:markdown id:fdbd88c1 tags:
#### Attitude Rate Controller
%% Cell type:code id:9cc3b317 tags:
``` python
m = Multirotor(vp, sp)
fz = m.weight
ctrl = get_controller(m)
rat = ctrl.ctrl_r
att = ctrl.ctrl_a
log = DataLog(vehicle=m, controller=rat, other_vars=('err',))
for i in range(5000):
ref = np.asarray([np.pi/18, 0, 0])
rate = att.step(ref, m.orientation)
action = rat.step(rate, m.euler_rate)
action = np.clip(action, a_min=-0.1, a_max=0.1)
m.step_dynamics(np.asarray([0, 0, 0, *action]))
log.log(err=rat.err_p[0])
log._actions[-1] = action
log.done_logging()
plt.plot(log.roll * 180 / np.pi)
plt.twinx()
plt.plot(log.actions[:,0], ls=':')
```
%% Cell type:markdown id:6370aa80 tags:
#### Altitude Controller
%% Cell type:code id:5fcd0d24 tags:
``` python
m = Multirotor(vp, sp)
ctrl = get_controller(m)
alt = ctrl.ctrl_z
alt_rate = ctrl.ctrl_vz
log = DataLog(vehicle=m, controller=alt, other_vars=('thrust',))
for i in range(5000):
ref = np.asarray([1.])
rate = alt.step(ref, m.position[2:])
action = alt_rate.step(rate, m.world_velocity[2:])
action = np.clip(action, a_min=-2*m.weight, a_max=2*m.weight)
m.step_dynamics(np.asarray([0, 0, action[0], 0,0,0]))
log.log(thrust=action)
#log._actions[-1] = action
log.done_logging()
plt.plot(log.actions.squeeze())
plt.twinx()
plt.plot(log.z, ls=':')
```
%% Cell type:markdown id:f4278c17 tags:
#### Position Controller
%% Cell type:code id:91ae8919 tags:
``` python
m = Multirotor(vp, sp)
ctrl = get_controller(m)
pos = ctrl.ctrl_p
vel = ctrl.ctrl_v
rat = ctrl.ctrl_r
att = ctrl.ctrl_a
log = DataLog(vehicle=m, controller=pos, other_vars=('err', 'att_actions'))
for i in range(5000):
ref = np.asarray([1.,0.])
velocity = pos.step(ref, m.position[:2])
angles = vel.step(velocity, m.velocity[:2])[::-1]
rate = att.step(np.asarray([*angles, 0]), m.orientation)
action = rat.step(rate, m.euler_rate)
action = np.clip(action, a_min=-0.1, a_max=0.1)
m.step_dynamics(np.asarray([0, 0, m.weight, *action]))
log.log(err=pos.err_p[0], att_actions=action)
log.done_logging()
plt.plot(log.position[:,0])
plt.plot(log.err)
# plt.plot(log.position[:,1])
plt.twinx()
plt.plot(log.actions[:,0] * 180 / np.pi, ls=':')
plt.plot(log.pitch * 180 / np.pi, ls='-.')
# plt.plot(log.actions[:,0] * 180 / np.pi, ls=':')
```
%% Cell type:markdown id:9eb34672 tags:
### Simulation
%% Cell type:code id:f5f52df4 tags:
``` python
def wind(t, m, nominal=False):
if nominal:
return np.zeros(3)
w_inertial = np.asarray([5 * np.sin(t * 2 * np.pi / 4000), 0, 0])
dcm = direction_cosine_matrix(*m.orientation)
return inertial_to_body(w_inertial, dcm)
```
%% Cell type:code id:d60f1ca5 tags:
``` python
from pyscurve import plot_trajectory
```
%% Cell type:code id:a98de44f tags:
``` python
env = LocalOctorotor(vehicle=Multirotor(vp, sp))
def run_sim(env, traj, steps=60_000, disturbance=None):
ctrl = get_controller(env.vehicle, max_velocity=5.)
waypoints = [[0,50,2], [50,50,2], [50,0,2], [25,-25,2]]
traj = GuidedTrajectory(env.vehicle, waypoints, proximity=2)
# traj = Trajectory(env.vehicle, waypoints, proximity=2, resolution=None)
log = DataLog(env.vehicle, ctrl,
other_vars=('speeds','target', 'alloc_errs', 'att_err',
'rate_target', 'att_target',
'leash', 'currents', 'voltages'))
disturb_force, disturb_torque = 0., 0
for i, (pos, feed_forward_vel) in tqdm(
enumerate(traj), leave=False, total=steps
):
if i==steps: break
# Generate reference for controller
ref = np.asarray([*pos, 0.])
# Get prescribed dynamics for system as thrust and torques
dynamics = ctrl.step(ref, feed_forward_velocity=feed_forward_vel)
thrust, torques = dynamics[0], dynamics[1:]
# Allocate control: Convert dynamics into motor rad/s
action = env.vehicle.allocate_control(thrust, torques)
# get any disturbances
if disturbance is not None:
disturb_force, disturb_torque = disturbance(i, env.vehicle)
# Send speeds to environment
state, *_ = env.step(
action, disturb_forces=disturb_force, disturb_torques=disturb_torque
)
alloc_errs = np.asarray([thrust, *torques]) - env.vehicle.alloc @ action**2
log.log(speeds=action, target=pos, alloc_errs=alloc_errs,
leash=ctrl.ctrl_p.leash,
att_err=ctrl.ctrl_a.err,
att_target = ctrl.ctrl_v.action[::-1],
rate_target=ctrl.ctrl_a.action,
currents=[p.motor.current for p in env.vehicle.propellers],
voltages=[p.motor.voltage for p in env.vehicle.propellers])
ctrl = get_controller(env.vehicle)
if np.any(np.abs(env.vehicle.orientation[:2]) > np.pi/6): break
errs = SimpleNamespace()
errs.pos = SimpleNamespace()
errs.pos.p, errs.pos.i, errs.pos.d = [], [], []
errs.att = SimpleNamespace()
errs.att.p, errs.att.i, errs.att.d = [], [], []
log = DataLog(env.vehicle, ctrl,
other_vars=('speeds','target', 'alloc_errs',
'ctrl_p', 'leash', 'err_p', 'ff_vel',
'currents', 'voltages'))
for i, (pos, feed_forward_vel) in tqdm(enumerate(traj), leave=False, total=60000):
if i==1000: break
# Generate reference for controller