Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Iterative Linear Quadratic Regulator

https://travis-ci.org/anassinator/ilqr.svg?branch=master

This is an implementation of the Iterative Linear Quadratic Regulator (iLQR) for non-linear trajectory optimization based on Yuval Tassa's paper.

It is compatible with both Python 2 and 3 and has built-in support for auto-differentiating both the dynamics model and the cost function using Theano.

Install

To install, clone and run:

python setup.py install

You may also install the dependencies with pipenv as follows:

pipenv install

Usage

After installing, import as follows:

fromilqrimportiLQR

You can see the examples directory for Jupyter notebooks to see how common control problems can be solved through iLQR.

Dynamics model

You can set up your own dynamics model by either extending the Dynamics class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffDynamics class for it to be auto-differentiated. Finally, if all you have is a function, you can use the FiniteDiffDynamics class to approximate the derivatives with finite difference approximation.

This section demonstrates how to implement the following dynamics model:

m \dot{v} = F - \alpha v

where m is the object's mass in kg, alpha is the friction coefficient, v is the object's velocity in m/s, \dot{v} is the object's acceleration in m/s^2, and F is the control (or force) you're applying to the object in N.

Automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportAutoDiffDynamicsx=T.dscalar("x") # Position.x_dot=T.dscalar("x_dot") # Velocity.F=T.dscalar("F") # Force.dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.f=T.stack([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
x_inputs= [x, x_dot] # State vector.u_inputs= [F] # Control vector.# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=AutoDiffDynamics(f, x_inputs, u_inputs)

Note: If you want to be able to use the Hessians (f_xx, f_ux, and f_uu), you need to pass the hessians=True argument to the constructor. This will increase compilation time. Note that iLQR does not require second-order derivatives to function.

Batch automatic differentiation

importtheano.tensorasTfromilqr.dynamicsimportBatchAutoDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Batched implementation of the dynamics model. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. Returns: Next state vector [*, state_size]. """x_=x[..., 0]
x_dot=x[..., 1]
F=u[..., 0]
# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/m# Discrete dynamics model definition.returnT.stack([
x_+x_dot*dt,
x_dot+x_dot_dot*dt,
]).T# Compile the dynamics.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.dynamics=BatchAutoDiffDynamics(f, state_size, action_size)

Note: This is a faster version of AutoDiffDynamics that doesn't support Hessians.

Finite difference approximation

fromilqr.dynamicsimportFiniteDiffDynamicsstate_size=2# [position, velocity]action_size=1# [force]dt=0.01# Discrete time-step in seconds.m=1.0# Mass in kg.alpha=0.1# Friction coefficient.deff(x, u, i):
"""Dynamics model function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Next state vector [state_size]. """
[x, x_dot] =x
[F] =u# Acceleration.x_dot_dot=x_dot* (1-alpha*dt/m) +F*dt/mreturnnp.array([
x+x_dot*dt,
x_dot+x_dot_dot*dt,
])
# NOTE: Unlike with AutoDiffDynamics, this is instantaneous, but will not be# as accurate.dynamics=FiniteDiffDynamics(f, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your dynamics model, you can use them as follows:

curr_x=np.array([1.0, 2.0])
curr_u=np.array([0.0])
i=0# This dynamics model is not time-varying, so this doesn't matter.>>>dynamics.f(curr_x, curr_u, i)
... array([ 1.02 , 2.01998])
>>>dynamics.f_x(curr_x, curr_u, i)
... array([[ 1. , 0.01 ],
[ 0. , 1.00999]])
>>>dynamics.f_u(curr_x, curr_u, i)
... array([[ 0. ],
[ 0.0001]])

Comparing the output of the AutoDiffDynamics and the FiniteDiffDynamics models should generally yield consistent results, but the auto-differentiated method will always be more accurate. Generally, the finite difference approximation will be faster unless you're also computing the Hessians: in which case, Theano's compiled derivatives are more optimized.

Cost function

Similarly, you can set up your own cost function by either extending the Cost class and hard-coding it and its partial derivatives. Alternatively, you can write it up as a Theano expression and use the AutoDiffCost class for it to be auto-differentiated. Finally, if all you have are a loss functions, you can use the FiniteDiffCost class to approximate the derivatives with finite difference approximation.

The most common cost function is the quadratic format used by Linear Quadratic Regulators:

(x - x_{goal})^T Q (x - x_{goal}) + (u - u_{goal})^T R (u - u_{goal})

where Q and R are matrices defining your quadratic state error and quadratic control errors and x_{goal} is your target state. For convenience, an implementation of this cost function is made available as the QRCost class.

QRCost class

importnumpyasnpfromilqr.costimportQRCoststate_size=2# [position, velocity]action_size=1# [force]# The coefficients weigh how much your state error is worth to you vs# the size of your controls. You can favor a solution that uses smaller# controls by increasing R's coefficient.Q=100*np.eye(state_size)
R=0.01*np.eye(action_size)
# This is optional if you want your cost to be computed differently at a# terminal state.Q_terminal=np.array([[100.0, 0.0], [0.0, 0.1]])
# State goal is set to a position of 1 m with no velocity.x_goal=np.array([1.0, 0.0])
# NOTE: This is instantaneous and completely accurate.cost=QRCost(Q, R, Q_terminal=Q_terminal, x_goal=x_goal)

Automatic differentiation

importtheano.tensorasTfromilqr.costimportAutoDiffCostx_inputs= [T.dscalar("x"), T.dscalar("x_dot")]
u_inputs= [T.dscalar("F")]
x=T.stack(x_inputs)
u=T.stack(u_inputs)
x_diff=x-x_goall=x_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
l_terminal=x_diff.T.dot(Q_terminal).dot(x_diff)
# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=AutoDiffCost(l, l_terminal, x_inputs, u_inputs)

Batch automatic differentiation

importtheano.tensorasTfromilqr.costimportBatchAutoDiffCostdefcost_function(x, u, i, terminal):
"""Batched implementation of the quadratic cost function. Args: x: State vector [*, state_size]. u: Control vector [*, action_size]. i: Current time step [*, 1]. terminal: Whether to compute the terminal cost. Returns: Instantaneous cost [*]. """Q_=Q_terminalifterminalelseQl=x.dot(Q_).dot(x.T)
ifl.ndim==2:
l=T.diag(l)
ifnotterminal:
l_u=u.dot(R).dot(u.T)
ifl_u.ndim==2:
l_u=T.diag(l_u)
l+=l_ureturnl# Compile the cost.# NOTE: This can be slow as it's computing and compiling the derivatives.# But that's okay since it's only a one-time cost on startup.cost=BatchAutoDiffCost(cost_function, state_size, action_size)

Finite difference approximation

fromilqr.costimportFiniteDiffCostdefl(x, u, i):
"""Instantaneous cost function. Args: x: State vector [state_size]. u: Control vector [action_size]. i: Current time step. Returns: Instantaneous cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q).dot(x_diff) +u.T.dot(R).dot(u)
defl_terminal(x, i):
"""Terminal cost function. Args: x: State vector [state_size]. i: Current time step. Returns: Terminal cost [scalar]. """x_diff=x-x_goalreturnx_diff.T.dot(Q_terminal).dot(x_diff)
# NOTE: Unlike with AutoDiffCost, this is instantaneous, but will not be as# accurate.cost=FiniteDiffCost(l, l_terminal, state_size, action_size)

Note: It is possible you might need to play with the epsilon values (x_eps and u_eps) used when computing the approximation if you run into numerical instability issues.

Usage

Regardless of the method used for constructing your cost function, you can use them as follows:

>>>cost.l(curr_x, curr_u, i)
... 400.0>>>cost.l_x(curr_x, curr_u, i)
... array([ 0., 400.])
>>>cost.l_u(curr_x, curr_u, i)
... array([ 0.])
>>>cost.l_xx(curr_x, curr_u, i)
... array([[ 200., 0.],
[ 0., 200.]])
>>>cost.l_ux(curr_x, curr_u, i)
... array([[ 0., 0.]])
>>>cost.l_uu(curr_x, curr_u, i)
... array([[ 0.02]])

Putting it all together

N=1000# Number of time-steps in trajectory.x0=np.array([0.0, -0.1]) # Initial state.us_init=np.random.uniform(-1, 1, (N, 1)) # Random initial action path.ilqr=iLQR(dynamics, cost, N)
xs, us=ilqr.fit(x0, us_init)

xs and us now hold the optimal state and control trajectory that reaches the desired goal state with minimum cost.

Finally, a RecedingHorizonController is also bundled with this package to use the iLQR controller in Model Predictive Control.

Important notes

To quote from Tassa's paper: "Two important parameters which have a direct impact on performance are the simulation time-step dt and the horizon length N. Since speed is of the essence, the goal is to choose those values which minimize the number of steps in the trajectory, i.e. the largest possible time-step and the shortest possible horizon. The size of dt is limited by our use of Euler integration; beyond some value the simulation becomes unstable. The minimum length of the horizon N is a problem-dependent quantity which must be found by trial-and-error."

Contributing

Contributions are welcome. Simply open an issue or pull request on the matter.

Linting

We use YAPF for all Python formatting needs. You can auto-format your changes with the following command:

yapf --recursive --in-place --parallel .

You may install the linter as follows:

pipenv install --dev

License

See LICENSE.

Credits

This implementation was partially based on Yuval Tassa's MATLABimplementation, and navigator8972's implementation.

About

Iterative Linear Quadratic Regulator with auto-differentiatiable dynamics models

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages