-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #35 from CLeARoboticsLab/hmzh/utils-refactor
Create type and structs to abstract dynamics and costs beyond LQ assumptions.
- Loading branch information
Showing
9 changed files
with
196 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Utilities for managing quadratic and nonquadratic dynamics. | ||
abstract type Cost end | ||
|
||
# Every Cost is assumed to have the following functions defined on it: | ||
# - quadraticize_costs(cost, t, x, us) - this function produces a QuadraticCost at time t given the state and controls | ||
# - evaluate(cost, xs, us) - this function evaluates the cost of a trajectory given the states and controls | ||
|
||
# We use this type by making a substruct of it which can then have certain functions defined for it. | ||
abstract type NonQuadraticCost <: Cost end | ||
|
||
# Cost for a single player. | ||
# Form is: x^T_t Q^i x + \sum_j u^{jT}_t R^{ij} u^j_t. | ||
# For simplicity, assuming that Q, R are time-invariant, and that dynamics are | ||
# linear time-invariant, i.e. x_{t+1} = A x_t + \sum_i B^i u^i_t. | ||
mutable struct QuadraticCost <: Cost | ||
Q | ||
Rs | ||
end | ||
QuadraticCost(Q) = QuadraticCost(Q, Dict{Int, Matrix{eltype(Q)}}()) | ||
|
||
# TODO(hamzah) Add better tests for the QuadraticCost struct and associated functions. | ||
|
||
# Method to add R^{ij}s to a Cost struct. | ||
export add_control_cost! | ||
function add_control_cost!(c::QuadraticCost, other_player_idx, Rij) | ||
c.Rs[other_player_idx] = Rij | ||
end | ||
|
||
function quadraticize_costs(cost::QuadraticCost, t, x, us) | ||
return cost | ||
end | ||
|
||
# Evaluate cost on a state/control trajectory. | ||
# - xs[:, time] | ||
# - us[player][:, time] | ||
function evaluate(c::QuadraticCost, xs, us) | ||
horizon = last(size(xs)) | ||
|
||
total = 0.0 | ||
for tt in 1:horizon | ||
total += xs[:, tt]' * c.Q * xs[:, tt] | ||
total += sum(us[jj][:, tt]' * Rij * us[jj][:, tt] for (jj, Rij) in c.Rs) | ||
end | ||
return total | ||
end | ||
|
||
|
||
# TODO: Make the affine cost structure with homogenenized coordinates. | ||
# struct AffineCost <: Cost end | ||
# function quadraticize_costs(cost::AffineCost, t, x, us) | ||
# function evaluate(c::AffineCost, xs, us) | ||
# end | ||
|
||
# Export all the cost types/structs. | ||
export Cost, NonQuadraticCost, QuadraticCost | ||
|
||
# Export all the cost types/structs. | ||
export quadraticize_costs, evaluate | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Utilities for managing linear and nonlinear dynamics. | ||
|
||
# Every Dynamics is assumed to have the following functions defined on it: | ||
# - linearize_dynamics(dyn, x, us) - this function linearizes the dynamics given the state and controls. | ||
# - propagate_dynamics(cost, t, x, us) - this function propagates the dynamics to the next timestep. | ||
# Every Dynamics struct must have a sys_info field of type SystemInfo. | ||
abstract type Dynamics end | ||
|
||
# A type that every nonlinear dynamics struct (unique per use case) can inherit from. These need to have the same | ||
# functions as the Dynamics type. | ||
abstract type NonlinearDynamics <: Dynamics end | ||
|
||
# TODO(hamzah) Add better tests for the LinearDynamics struct and associated functions. | ||
struct LinearDynamics <: Dynamics | ||
A # state | ||
Bs # controls | ||
sys_info::SystemInfo | ||
end | ||
# Constructor for linear dynamics that auto-generates the system info. | ||
LinearDynamics(A, Bs) = LinearDynamics(A, Bs, SystemInfo(length(Bs), last(size(A)), [last(size(Bs[i])) for i in 1:length(Bs)])) | ||
|
||
function propagate_dynamics(dyn::LinearDynamics, t, x, us) | ||
N = dyn.sys_info.num_agents | ||
x_next = dyn.A * x | ||
for i in 1:N | ||
ui = reshape(us[i], dyn.sys_info.num_us[i], 1) | ||
x_next += dyn.Bs[i] * ui | ||
end | ||
return x_next | ||
end | ||
|
||
function linearize_dynamics(dyn::LinearDynamics, x, us) | ||
return dyn | ||
end | ||
|
||
# Export the types of dynamics. | ||
export Dynamics, NonlinearDynamics, LinearDynamics | ||
|
||
# Export the functionality each Dynamics requires. | ||
export propagate_dynamics, linearize_dynamics | ||
|
||
|
||
# Dimensionality helpers. | ||
function xdim(dyn::Dynamics) | ||
return dyn.sys_info.num_x | ||
end | ||
|
||
function udim(dyn::Dynamics) | ||
return sum(dyn.sys_info.num_us) | ||
end | ||
|
||
function udim(dyn::Dynamics, player_idx) | ||
return dyn.sys_info.num_us[player_idx] | ||
end | ||
|
||
export xdim, udim | ||
|
||
|
||
# TODO(hamzah) Add better tests for the unroll_feedback, unroll_raw_controls functions. | ||
# TODO(hamzah) Abstract the unroll_feedback, unroll_raw_controls functions to not assume linear feedback P. | ||
|
||
# Function to unroll a set of feedback matrices from an initial condition. | ||
# Output is a sequence of states xs[:, time] and controls us[player][:, time]. | ||
export unroll_feedback | ||
function unroll_feedback(dyn::Dynamics, Ps, x₁) | ||
@assert length(x₁) == xdim(dyn) | ||
|
||
N = length(Ps) | ||
@assert N == dyn.sys_info.num_agents | ||
|
||
horizon = last(size(first(Ps))) | ||
|
||
# Populate state/control trajectory. | ||
xs = zeros(xdim(dyn), horizon) | ||
xs[:, 1] = x₁ | ||
us = [zeros(udim(dyn, ii), horizon) for ii in 1:N] | ||
for tt in 2:horizon | ||
for ii in 1:N | ||
us[ii][:, tt - 1] = -Ps[ii][:, :, tt - 1] * xs[:, tt - 1] | ||
end | ||
|
||
us_prev = [us[i][:, tt-1] for i in 1:N] | ||
xs[:, tt] = propagate_dynamics(dyn, tt, xs[:, tt-1], us_prev) | ||
end | ||
|
||
# Controls at final time. | ||
for ii in 1:N | ||
us[ii][:, horizon] = -Ps[ii][:, :, horizon] * xs[:, horizon] | ||
end | ||
|
||
return xs, us | ||
end | ||
|
||
# As above, but replacing feedback matrices `P` with raw control inputs `u`. | ||
export unroll_raw_controls | ||
function unroll_raw_controls(dyn::Dynamics, us, x₁) | ||
@assert length(x₁) == xdim(dyn) | ||
|
||
N = length(us) | ||
@assert N == dyn.sys_info.num_agents | ||
|
||
horizon = last(size(first(us))) | ||
|
||
# Populate state trajectory. | ||
xs = zeros(xdim(dyn), horizon) | ||
xs[:, 1] = x₁ | ||
us = [zeros(udim(dyn, ii), horizon) for ii in 1:N] | ||
for tt in 2:horizon | ||
us_prev = [us[i][:, tt-1] for i in 1:N] | ||
xs[:, tt] = propagate_dynamics(dyn, tt, xs[:, tt-1], us_prev) | ||
end | ||
|
||
return xs | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,110 +1,10 @@ | ||
# Utilities for Assignment 3. You should not need to modify this file. | ||
|
||
# Cost for a single player. | ||
# Form is: x^T_t Q^i x + \sum_j u^{jT}_t R^{ij} u^j_t. | ||
# For simplicity, assuming that Q, R are time-invariant, and that dynamics are | ||
# linear time-invariant, i.e. x_{t+1} = A x_t + \sum_i B^i u^i_t. | ||
mutable struct Cost | ||
Q | ||
Rs # Nonzero R^{ij} for Pi. | ||
end | ||
|
||
Cost(Q) = Cost(Q, Dict{Int, Matrix{eltype(Q)}}()) | ||
export Cost | ||
|
||
# Method to add R^{ij}s to a Cost struct. | ||
export add_control_cost! | ||
function add_control_cost!(c::Cost, other_player_idx, Rij) | ||
c.Rs[other_player_idx] = Rij | ||
end | ||
|
||
# Evaluate cost on a state/control trajectory. | ||
# - xs[:, time] | ||
# - us[player][:, time] | ||
export evaluate | ||
function evaluate(c::Cost, xs, us) | ||
horizon = last(size(xs)) | ||
|
||
total = 0.0 | ||
for tt in 1:horizon | ||
total += xs[:, tt]' * c.Q * xs[:, tt] | ||
total += sum(us[jj][:, tt]' * Rij * us[jj][:, tt] for (jj, Rij) in c.Rs) | ||
end | ||
|
||
return total | ||
# Utilities | ||
struct SystemInfo | ||
num_agents::Int | ||
num_x::Int | ||
num_us::AbstractArray{Int} | ||
num_v::Int | ||
end | ||
SystemInfo(num_agents, num_x, num_us) = SystemInfo(num_agents, num_x, num_us, 0) | ||
|
||
# Dynamics. | ||
export Dynamics | ||
struct Dynamics | ||
A | ||
Bs | ||
end | ||
|
||
export xdim | ||
function xdim(dyn::Dynamics) | ||
return first(size(dyn.A)) | ||
end | ||
|
||
export udim | ||
function udim(dyn::Dynamics) | ||
return sum(last(size(B)) for B in dyn.Bs) | ||
end | ||
|
||
function udim(dyn::Dynamics, player_idx) | ||
return last(size(dyn.Bs[player_idx])) | ||
end | ||
|
||
# Function to unroll a set of feedback matrices from an initial condition. | ||
# Output is a sequence of states xs[:, time] and controls us[player][:, time]. | ||
export unroll_feedback | ||
function unroll_feedback(dyn::Dynamics, Ps, x₁) | ||
@assert length(x₁) == xdim(dyn) | ||
|
||
N = length(Ps) | ||
@assert N == length(dyn.Bs) | ||
|
||
horizon = last(size(first(Ps))) | ||
|
||
# Populate state/control trajectory. | ||
xs = zeros(xdim(dyn), horizon) | ||
xs[:, 1] = x₁ | ||
us = [zeros(udim(dyn, ii), horizon) for ii in 1:N] | ||
for tt in 2:horizon | ||
for ii in 1:N | ||
us[ii][:, tt - 1] = -Ps[ii][:, :, tt - 1] * xs[:, tt - 1] | ||
end | ||
|
||
xs[:, tt] = dyn.A * xs[:, tt - 1] + sum( | ||
dyn.Bs[ii] * us[ii][:, tt - 1] for ii in 1:N) | ||
end | ||
|
||
# Controls at final time. | ||
for ii in 1:N | ||
us[ii][:, horizon] = -Ps[ii][:, :, horizon] * xs[:, horizon] | ||
end | ||
|
||
return xs, us | ||
end | ||
|
||
# As above, but replacing feedback matrices `P` with raw control inputs `u`. | ||
export unroll_raw_controls | ||
function unroll_raw_controls(dyn::Dynamics, us, x₁) | ||
@assert length(x₁) == xdim(dyn) | ||
|
||
N = length(us) | ||
@assert N == length(dyn.Bs) | ||
|
||
horizon = last(size(first(us))) | ||
|
||
# Populate state trajectory. | ||
xs = zeros(xdim(dyn), horizon) | ||
xs[:, 1] = x₁ | ||
us = [zeros(udim(dyn, ii), horizon) for ii in 1:N] | ||
for tt in 2:horizon | ||
xs[:, tt] = dyn.A * xs[:, tt - 1] + sum( | ||
dyn.Bs[ii] * us[ii][:, tt - 1] for ii in 1:N) | ||
end | ||
|
||
return xs | ||
end | ||
export SystemInfo |
Oops, something went wrong.