ilqgames
A new real-time solver for large-scale differential games.
skeleton_example.cpp
1 /*
2  * Copyright (c) 2020, The Regents of the University of California (Regents).
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following
14  * disclaimer in the documentation and/or other materials provided
15  * with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  *
33  * Please contact the author(s) of this library if you have any questions.
34  * Authors: David Fridovich-Keil ( dfk@eecs.berkeley.edu )
35  */
36 
37 ///////////////////////////////////////////////////////////////////////////////
38 //
39 // Simple skeleton example intended as a quick-start guide for learning to use
40 // this repository. This file is extensively commented; however, if you do have
41 // any questions please don't hesitate to post an issue on the repository
42 // (https://github.com/HJReachability/ilqgames/issues) or contact the author.
43 //
44 // Note: Throughout this file, if any of the function or class interfaces are
45 // unclear (e.g., which parameters are supposed to be used where), please do
46 // consult the relevant header or auto-generated documentation available at
47 // https://hjreachability.github.io/ilqgames/documentation/html/.
48 //
49 // Steps (some of these are already done in this example but can be modified as
50 // suggested below):
51 // 1. Set the cost weights.
52 // 2. Set the nominal speed for each car.
53 // 3. Add input constraints.
54 // 4. (Advanced) Try other constraints.
55 // 5. (Advanced) Add in a third player.
56 //
57 ///////////////////////////////////////////////////////////////////////////////
58 
59 #include <ilqgames/constraint/polyline2_signed_distance_constraint.h>
60 #include <ilqgames/constraint/single_dimension_constraint.h>
61 #include <ilqgames/cost/extreme_value_cost.h>
62 #include <ilqgames/cost/proximity_cost.h>
63 #include <ilqgames/cost/quadratic_cost.h>
64 #include <ilqgames/cost/quadratic_polyline2_cost.h>
65 #include <ilqgames/dynamics/concatenated_dynamical_system.h>
66 #include <ilqgames/dynamics/single_player_car_5d.h>
67 #include <ilqgames/examples/skeleton_example.h>
68 #include <ilqgames/geometry/draw_shapes.h>
69 #include <ilqgames/geometry/polyline2.h>
70 #include <ilqgames/solver/ilq_solver.h>
71 #include <ilqgames/solver/problem.h>
72 #include <ilqgames/solver/solver_params.h>
73 #include <ilqgames/utils/types.h>
74 
75 #include <math.h>
76 #include <memory>
77 #include <vector>
78 
79 namespace ilqgames {
80 
81 namespace {
82 
83 // Input contraints.
84 static constexpr float kOmegaMax = 1.5; // rad/s
85 static constexpr float kAMax = 4.0; // m/s
86 
87 // Cost weights.
88 // Step 1. Try changing these.
89 static constexpr float kOmegaCostWeight = 25.0;
90 static constexpr float kACostWeight = 15.0;
91 static constexpr float kNominalVCostWeight = 10.0;
92 static constexpr float kLaneCostWeight = 25.0;
93 static constexpr float kProximityCostWeight = 100.0;
94 
95 // Nominal speed.
96 // Step 2. Try changing these.
97 static constexpr float kP1NominalV = 8.0; // m/s
98 static constexpr float kP2NominalV = 8.0; // m/s
99 
100 // Initial state.
101 static constexpr float kP1InitialX = 0.0; // m
102 static constexpr float kP1InitialY = -30.0; // m
103 
104 static constexpr float kP2InitialX = -5.0; // m
105 static constexpr float kP2InitialY = 30.0; // m
106 
107 static constexpr float kP1InitialTheta = M_PI_2; // rad
108 static constexpr float kP2InitialTheta = -M_PI_2; // rad
109 
110 static constexpr float kP1InitialV = 4.0; // m/s
111 static constexpr float kP2InitialV = 3.0; // m/s
112 
113 // State dimensions.
114 using P1 = SinglePlayerCar5D;
115 using P2 = SinglePlayerCar5D;
116 
117 static const Dimension kP1XIdx = P1::kPxIdx;
118 static const Dimension kP1YIdx = P1::kPyIdx;
119 static const Dimension kP1ThetaIdx = P1::kThetaIdx;
120 static const Dimension kP1VIdx = P1::kVIdx;
121 
122 static const Dimension kP2XIdx = P1::kNumXDims + P2::kPxIdx;
123 static const Dimension kP2YIdx = P1::kNumXDims + P2::kPyIdx;
124 static const Dimension kP2ThetaIdx = P1::kNumXDims + P2::kThetaIdx;
125 static const Dimension kP2VIdx = P1::kNumXDims + P2::kVIdx;
126 
127 } // anonymous namespace
128 
129 void SkeletonExample::ConstructDynamics() {
130  // Create dynamics. In this case, we have two cars with decoupled dynamics
131  // (they are only coupled through the cost structure of the game). This is
132  // expressed in the ConcatenatedDynamicalSystem class. Here, each player's
133  // dynamics follow that of a standard 5D bicycle model with inter-axle
134  // distance below.
135  static constexpr float kInterAxleDistance = 4.0; // m
136  dynamics_.reset(new ConcatenatedDynamicalSystem(
137  {std::make_shared<P1>(kInterAxleDistance),
138  std::make_shared<P2>(kInterAxleDistance)}));
139 }
140 
141 void SkeletonExample::ConstructInitialState() {
142  // Set up initial state. Initially, this is zero, but then we override
143  // individual dimensions to match the desired initial conditions above.
144  x0_ = VectorXf::Zero(dynamics_->XDim());
145  x0_(kP1XIdx) = kP1InitialX;
146  x0_(kP1YIdx) = kP1InitialY;
147  x0_(kP1ThetaIdx) = kP1InitialTheta;
148  x0_(kP1VIdx) = kP1InitialV;
149  x0_(kP2XIdx) = kP2InitialX;
150  x0_(kP2YIdx) = kP2InitialY;
151  x0_(kP2ThetaIdx) = kP2InitialTheta;
152  x0_(kP2VIdx) = kP2InitialV;
153 }
154 
155 void SkeletonExample::ConstructPlayerCosts() {
156  // Set up costs for all players. These are containers for holding each
157  // player's constituent cost functions and constraints that hold pointwise in
158  // time and can apply to either state or control (for *any* player).
159  // These costs can also build in regularization on the state or the control,
160  // which essentially boils down to adding a scaled identity matrix to each's
161  // Hessian.
162  player_costs_.emplace_back("P1");
163  player_costs_.emplace_back("P2");
164  auto& p1_cost = player_costs_[0];
165  auto& p2_cost = player_costs_[1];
166 
167  // Quadratic control costs.
168  const auto p1_omega_cost = std::make_shared<QuadraticCost>(
169  kOmegaCostWeight, P1::kOmegaIdx, 0.0, "OmegaCost");
170  const auto p1_a_cost = std::make_shared<QuadraticCost>(
171  kACostWeight, P1::kAIdx, 0.0, "AccelerationCost");
172  p1_cost.AddControlCost(0, p1_omega_cost);
173  p1_cost.AddControlCost(0, p1_a_cost);
174 
175  const auto p2_omega_cost = std::make_shared<QuadraticCost>(
176  kOmegaCostWeight, P2::kOmegaIdx, 0.0, "OmegaCost");
177  const auto p2_a_cost = std::make_shared<QuadraticCost>(
178  kACostWeight, P2::kAIdx, 0.0, "AccelerationCost");
179  p2_cost.AddControlCost(1, p2_omega_cost);
180  p2_cost.AddControlCost(1, p2_a_cost);
181 
182  // Constrain each control input to lie in an interval.
183  // Step 3. Try uncommenting these blocks.
184  // const auto p1_omega_max_constraint =
185  // std::make_shared<SingleDimensionConstraint>(
186  // P1::kOmegaIdx, kOmegaMax, true, "Omega Constraint
187  // (Max)");
188  // const auto p1_omega_min_constraint =
189  // std::make_shared<SingleDimensionConstraint>(
190  // P1::kOmegaIdx, -kOmegaMax, false, "Omega Constraint
191  // (Min)");
192  // const auto p1_a_max_constraint =
193  // std::make_shared<SingleDimensionConstraint>(
194  // P1::kAIdx, kAMax, true, "Acceleration Constraint (Max)");
195  // const auto p1_a_min_constraint =
196  // std::make_shared<SingleDimensionConstraint>(
197  // P1::kAIdx, -kAMax, false, "Acceleration Constraint
198  // (Min)");
199  // p1_cost.AddControlConstraint(0, p1_omega_max_constraint);
200  // p1_cost.AddControlConstraint(0, p1_omega_min_constraint);
201  // p1_cost.AddControlConstraint(0, p1_a_max_constraint);
202  // p1_cost.AddControlConstraint(0, p1_a_min_constraint);
203 
204  // const auto p2_omega_max_constraint =
205  // std::make_shared<SingleDimensionConstraint>(
206  // P2::kOmegaIdx, kOmegaMax, true, "Omega Constraint
207  // (Max)");
208  // const auto p2_omega_min_constraint =
209  // std::make_shared<SingleDimensionConstraint>(
210  // P2::kOmegaIdx, -kOmegaMax, false, "Omega Constraint
211  // (Min)");
212  // const auto p2_a_max_constraint =
213  // std::make_shared<SingleDimensionConstraint>(
214  // P2::kAIdx, kAMax, true, "Acceleration Constraint (Max)");
215  // const auto p2_a_min_constraint =
216  // std::make_shared<SingleDimensionConstraint>(
217  // P2::kAIdx, -kAMax, false, "Acceleration Constraint
218  // (Min)");
219  // p2_cost.AddControlConstraint(1, p2_omega_max_constraint);
220  // p2_cost.AddControlConstraint(1, p2_omega_min_constraint);
221  // p2_cost.AddControlConstraint(1, p2_a_max_constraint);
222  // p2_cost.AddControlConstraint(1, p2_a_min_constraint);
223 
224  // Encourage each player to go a given nominal speed.
225  const auto p1_nominal_v_cost = std::make_shared<QuadraticCost>(
226  kNominalVCostWeight, kP1VIdx, kP1NominalV, "NominalV");
227  p1_cost.AddStateCost(p1_nominal_v_cost);
228 
229  const auto p2_nominal_v_cost = std::make_shared<QuadraticCost>(
230  kNominalVCostWeight, kP2VIdx, kP2NominalV, "NominalV");
231  p2_cost.AddStateCost(p2_nominal_v_cost);
232 
233  // Encourage each player to remain near the lane center. Could also add
234  // constraints to stay in the lane.
235  const Polyline2 lane1(
236  {Point2(kP1InitialX, -1000.0), Point2(kP1InitialX, 1000.0)});
237  const Polyline2 lane2({Point2(kP2InitialX, 1000.0), Point2(kP2InitialX, 5.0),
238  Point2(kP2InitialX + 5.0, 0.0),
239  Point2(kP2InitialX + 1000.0, 0.0)});
240 
241  const std::shared_ptr<QuadraticPolyline2Cost> p1_lane_cost(
242  new QuadraticPolyline2Cost(kLaneCostWeight, lane1, {kP1XIdx, kP1YIdx},
243  "LaneCenter"));
244  p1_cost.AddStateCost(p1_lane_cost);
245 
246  const std::shared_ptr<QuadraticPolyline2Cost> p2_lane_cost(
247  new QuadraticPolyline2Cost(kLaneCostWeight, lane2, {kP2XIdx, kP2YIdx},
248  "LaneCenter"));
249  p2_cost.AddStateCost(p2_lane_cost);
250 
251  // Penalize proximity (could also use a constraint).
252  constexpr float kMinProximity = 6.0; // m
253  const std::shared_ptr<ProximityCost> p1p2_proximity_cost(
254  new ProximityCost(kProximityCostWeight, {kP1XIdx, kP1YIdx},
255  {kP2XIdx, kP2YIdx}, kMinProximity, "Proximity"));
256  p1_cost.AddStateCost(p1p2_proximity_cost);
257  p2_cost.AddStateCost(p1p2_proximity_cost);
258 }
259 
260 inline std::vector<float> SkeletonExample::Xs(const VectorXf& x) const {
261  return {x(kP1XIdx), x(kP2XIdx)};
262 }
263 
264 inline std::vector<float> SkeletonExample::Ys(const VectorXf& x) const {
265  return {x(kP1YIdx), x(kP2YIdx)};
266 }
267 
268 inline std::vector<float> SkeletonExample::Thetas(const VectorXf& x) const {
269  return {x(kP1ThetaIdx), x(kP2ThetaIdx)};
270 }
271 
272 } // namespace ilqgames