ilqgames
A new real-time solver for large-scale differential games.
single_player_flat_unicycle_4d.h
1 /*
2  * Copyright (c) 2019, 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 // Single player dynamics modeling a unicycle. 4 states and 2 control inputs.
40 // State is [x, y, theta, v], control is [omega, a], and dynamics are:
41 // \dot px = v cos theta
42 // \dot py = v sin theta
43 // \dot theta = omega
44 // \dot v = a
45 //
46 // Linear system state xi is laid out as [x, y, vx, vy]:
47 // vx = v * cos(theta)
48 // vy = v * sin(theta)
49 //
50 ///////////////////////////////////////////////////////////////////////////////
51 
52 #ifndef ILQGAMES_DYNAMICS_SINGLE_PLAYER_FLAT_UNICYCLE_4D_H
53 #define ILQGAMES_DYNAMICS_SINGLE_PLAYER_FLAT_UNICYCLE_4D_H
54 
55 #include <ilqgames/dynamics/single_player_flat_system.h>
56 #include <ilqgames/utils/types.h>
57 
58 #include <glog/logging.h>
59 
60 namespace ilqgames {
61 
63  public:
65  SinglePlayerFlatUnicycle4D() : SinglePlayerFlatSystem(kNumXDims, kNumUDims) {}
66 
67  // Compute time derivative of state.
68  VectorXf Evaluate(const VectorXf& x, const VectorXf& u) const;
69 
70  // Discrete time approximation of the underlying linearized system.
71  void LinearizedSystem(Eigen::Ref<MatrixXf> A, Eigen::Ref<MatrixXf> B) const;
72 
73  // Utilities for feedback linearization.
74  MatrixXf InverseDecouplingMatrix(const VectorXf& x) const;
75  VectorXf AffineTerm(const VectorXf& x) const;
76  VectorXf ToLinearSystemState(const VectorXf& x) const;
77  VectorXf FromLinearSystemState(const VectorXf& xi) const;
78  void Partial(const VectorXf& xi, std::vector<VectorXf>* grads,
79  std::vector<MatrixXf>* hesses) const;
80  bool IsLinearSystemStateSingular(const VectorXf& xi) const;
81 
82  // Distance metric between two states.
83  float DistanceBetween(const VectorXf& x0, const VectorXf& x1) const;
84 
85  // Position dimensions.
86  std::vector<Dimension> PositionDimensions() const { return {kPxIdx, kPyIdx}; }
87 
88  // Constexprs for state indices.
89  static const Dimension kNumXDims;
90  static const Dimension kPxIdx;
91  static const Dimension kPyIdx;
92  static const Dimension kThetaIdx;
93  static const Dimension kVIdx;
94  static const Dimension kVxIdx;
95  static const Dimension kVyIdx;
96 
97  // Constexprs for control indices.
98  static const Dimension kNumUDims;
99  static const Dimension kOmegaIdx;
100  static const Dimension kAIdx;
101 }; //\class SinglePlayerFlatUnicycle4D
102 
103 // ----------------------------- IMPLEMENTATION ----------------------------- //
104 
105 inline VectorXf SinglePlayerFlatUnicycle4D::Evaluate(const VectorXf& x,
106  const VectorXf& u) const {
107  VectorXf xdot(xdim_);
108  xdot(kPxIdx) = x(kVIdx) * std::cos(x(kThetaIdx));
109  xdot(kPyIdx) = x(kVIdx) * std::sin(x(kThetaIdx));
110  xdot(kThetaIdx) = u(kOmegaIdx);
111  xdot(kVIdx) = u(kAIdx);
112 
113  return xdot;
114 }
115 
116 inline void SinglePlayerFlatUnicycle4D::LinearizedSystem(
117  Eigen::Ref<MatrixXf> A, Eigen::Ref<MatrixXf> B) const {
118  A(kPxIdx, kVxIdx) += time::kTimeStep;
119  A(kPyIdx, kVyIdx) += time::kTimeStep;
120 
121  B(kVxIdx, 0) = time::kTimeStep;
122  B(kVyIdx, 1) = time::kTimeStep;
123 }
124 
125 inline MatrixXf SinglePlayerFlatUnicycle4D::InverseDecouplingMatrix(
126  const VectorXf& x) const {
127  MatrixXf M_inv(kNumUDims, kNumUDims);
128 
129  const float sin_t = std::sin(x(kThetaIdx));
130  const float cos_t = std::cos(x(kThetaIdx));
131  // HACK! KSmallOffset should realy be 0...
132  const float kSmallOffset = sgn(x(kVIdx) + 0.0000001) * 0.00011;
133 
134  CHECK_GT(std::abs(x(kVIdx) + kSmallOffset), 1e-4);
135 
136  M_inv(0, 0) = cos_t;
137  M_inv(0, 1) = sin_t;
138  M_inv(1, 0) = -sin_t / (x(kVIdx) + kSmallOffset);
139  M_inv(1, 1) = cos_t / (x(kVIdx) + kSmallOffset);
140 
141  return M_inv;
142 }
143 
144 inline VectorXf SinglePlayerFlatUnicycle4D::AffineTerm(
145  const VectorXf& x) const {
146  return VectorXf::Zero(kNumUDims);
147 }
148 
149 inline VectorXf SinglePlayerFlatUnicycle4D::ToLinearSystemState(
150  const VectorXf& x) const {
151  VectorXf xi(kNumXDims);
152 
153  xi(kPxIdx) = x(kPxIdx);
154  xi(kPyIdx) = x(kPyIdx);
155  xi(kVxIdx) = x(kVIdx) * std::cos(x(kThetaIdx));
156  xi(kVyIdx) = x(kVIdx) * std::sin(x(kThetaIdx));
157 
158  return xi;
159 }
160 
161 inline VectorXf SinglePlayerFlatUnicycle4D::FromLinearSystemState(
162  const VectorXf& xi) const {
163  VectorXf x(kNumXDims);
164 
165  x(kPxIdx) = xi(kPxIdx);
166  x(kPyIdx) = xi(kPyIdx);
167  x(kThetaIdx) = std::atan2(xi(kVyIdx), xi(kVxIdx));
168  x(kVIdx) = std::hypot(xi(kVyIdx), xi(kVxIdx));
169 
170  return x;
171 }
172 
173 inline bool SinglePlayerFlatUnicycle4D::IsLinearSystemStateSingular(
174  const VectorXf& xi) const {
175  constexpr float kTolerance = 1e-2;
176  return (std::isnan(xi(kVxIdx)) || std::isnan(xi(kVyIdx))) ||
177  (std::abs(xi(kVxIdx)) < kTolerance &&
178  std::abs(xi(kVyIdx)) < kTolerance);
179 }
180 
181 inline void SinglePlayerFlatUnicycle4D::Partial(
182  const VectorXf& xi, std::vector<VectorXf>* grads,
183  std::vector<MatrixXf>* hesses) const {
184  CHECK_NOTNULL(grads);
185  CHECK_NOTNULL(hesses);
186 
187  if (grads->size() != xi.size())
188  grads->resize(xi.size(), VectorXf::Zero(kNumXDims));
189  else {
190  for (auto& grad : *grads) {
191  DCHECK_EQ(grad.size(), xi.size());
192  grad.setZero();
193  }
194  }
195 
196  if (hesses->size() != xi.size())
197  hesses->resize(xi.size(), MatrixXf::Zero(kNumXDims, kNumXDims));
198  else {
199  for (auto& hess : *hesses) {
200  DCHECK_EQ(hess.rows(), xi.size());
201  DCHECK_EQ(hess.cols(), xi.size());
202  hess.setZero();
203  }
204  }
205 
206  // grads->clear();
207  // grads->resize(xi.size(), VectorXf::Zero(kNumXDims));
208 
209  // hesses->clear();
210  // hesses->resize(xi.size(), MatrixXf::Zero(kNumXDims, kNumXDims));
211 
212  CHECK_GT(std::hypot(xi(kVxIdx), xi(kVyIdx)), 1e-2);
213 
214  const float norm_squared = xi(kVxIdx) * xi(kVxIdx) + xi(kVyIdx) * xi(kVyIdx);
215  const float norm = std::sqrt(norm_squared);
216  const float norm_ss = norm_squared * norm_squared;
217  const float sqrt_norm_sss = std::sqrt(norm_ss * norm_squared);
218 
219  (*grads)[kPxIdx](kPxIdx) = 1.0;
220  (*grads)[kPyIdx](kPyIdx) = 1.0;
221  (*grads)[kThetaIdx](kVxIdx) = -xi(kVyIdx) / norm_squared;
222  (*grads)[kThetaIdx](kVyIdx) = xi(kVxIdx) / norm_squared;
223  (*grads)[kVIdx](kVxIdx) = xi(kVxIdx) / norm;
224  (*grads)[kVIdx](kVyIdx) = xi(kVyIdx) / norm;
225 
226  (*hesses)[kThetaIdx](kVxIdx, kVxIdx) =
227  2.0 * xi(kVxIdx) * xi(kVyIdx) / norm_ss;
228  (*hesses)[kThetaIdx](kVxIdx, kVyIdx) =
229  (xi(kVyIdx) * xi(kVyIdx) - xi(kVxIdx) * xi(kVxIdx)) / norm_ss;
230  (*hesses)[kThetaIdx](kVyIdx, kVxIdx) = (*hesses)[kThetaIdx](kVxIdx, kVyIdx);
231  (*hesses)[kThetaIdx](kVyIdx, kVyIdx) = -(*hesses)[kThetaIdx](kVxIdx, kVxIdx);
232  (*hesses)[kVIdx](kVxIdx, kVxIdx) = (xi(kVyIdx) * xi(kVyIdx)) / sqrt_norm_sss;
233  (*hesses)[kVIdx](kVxIdx, kVyIdx) = (-xi(kVxIdx) * xi(kVyIdx)) / sqrt_norm_sss;
234  (*hesses)[kVIdx](kVyIdx, kVxIdx) = (*hesses)[kVIdx](kVxIdx, kVyIdx);
235  (*hesses)[kVIdx](kVyIdx, kVyIdx) = (xi(kVxIdx) * xi(kVxIdx)) / sqrt_norm_sss;
236 }
237 
238 inline float SinglePlayerFlatUnicycle4D::DistanceBetween(
239  const VectorXf& x0, const VectorXf& x1) const {
240  // Squared distance in position space.
241  const float dx = x0(kPxIdx) - x1(kPxIdx);
242  const float dy = x0(kPyIdx) - x1(kPyIdx);
243  return dx * dx + dy * dy;
244 }
245 
246 } // namespace ilqgames
247 #endif