iLQGames.jl

Rapidly designing and solving differential games in Julia.

View the Project on GitHub lassepe/iLQGames.jl

iLQGames.jl

CI codecov License

iLQGames.jl is a framework for rapidly designing and solving nonlinear general-sum differential games, built around iterative linear-quadratic game approximations.

A brief introduction to this framework and benchmarks against a C++ implementation can be found in this short workshop manuscript. Finally, this paper demonstrates the flexibility and performance of iLQGames.jl by combining it with a particle-filter scheme to reason about uncertainty in differential games in real-time.

Watch the video

Installation

Within the Julia REPL run:

using Pkg
Pkg.add(PackageSpec(url="https://github.com/lassepe/iLQGames.jl"))

Example

Here is an example of two players controlling a single 4D-unicycle. Player-1 controls the steering, Player-2 controls the acceleration.

1. Describe Dynamics

We define a Unicycle as a subtype of our ControlSystem type and implement the differential equation by overloading dx for our type.

using iLQGames
import iLQGames: dx

# parameters: number of states, number of inputs, sampling time, horizon
nx, nu, ΔT, game_horizon = 4, 2, 0.1, 200

# setup the dynamics
struct Unicycle <: ControlSystem{ΔT,nx,nu} end
# the differential equation of a uncycle with state: (px, py, phi, v)
dx(cs::Unicycle, x, u, t) = SVector(x[4]cos(x[3]), x[4]sin(x[3]), u[1], u[2])
dynamics = Unicycle()

2. Setup Costs

To setup the costs encoding each players objectives, we can derive a custom subtype from PlayerCost, or, as done here, simply hand the cost function as a lambda function to the FunctionPlayerCost.


# player-1 wants the unicycle to stay close to the origin,
# player-2 wants to keep close to 1 m/s
costs = (FunctionPlayerCost((g, x, u, t) -> (x[1]^2 + x[2]^2 + u[1]^2)),
         FunctionPlayerCost((g, x, u, t) -> ((x[4] - 1)^2 + u[2]^2)))
# indices of inputs that each player controls
player_inputs = (SVector(1), SVector(2))

3. Solve the Game

With this information we can construct the game…

g = GeneralGame(game_horizon, player_inputs, dynamics, costs)

…and solve it for some initial conditions x0. Automatic differentiation will save us from having to specify how to compute LQ approximations of the system.

# get a solver, choose initial conditions and solve (in about 9 ms with automatic differentiation)
solver = iLQSolver(g)
x0 = SVector(1, 1, 0, 0.5)
converged, trajectory, strategies = solve(g, solver, x0)

Finally, we can visualize the path of the unicycle as follows (x- and y-position):

# for visualization we need to state which state indices correspond to px and py
position_indices = tuple(SVector(1,2))
# Note: you can use the `plot_traj` call without @animated to get a non-animated plot instead.
@animated(plot_traj(trajectory, g, [:red, :green], player_inputs),
          1:game_horizon, "minimal_example.gif")

At the equilibrium solution, Player-2 accelerates to reach the desired speed. Player-1 steers the unicycle in a figure-8 to stay close to the origin.: