1use nalgebra as na;
2extern crate rand;
3use core::fmt;
4use na::allocator::Allocator;
5use na::base::DefaultAllocator;
6use na::core::dimension::{Dim, DimMin, DimName};
7use std::error::Error;
8
9#[derive(Clone, Debug)]
10pub struct LQRController<T, S, C>
18where
19 T: na::RealField,
20 S: Dim + DimName + DimMin<S>, C: Dim + DimName + DimMin<C>, DefaultAllocator:
23 Allocator<T, S, S> + Allocator<T, C, C> + Allocator<T, S, C> + Allocator<T, C, S>,
24{
25 pub q: Option<na::OMatrix<T, S, S>>,
27 pub r: Option<na::OMatrix<T, C, C>>,
29 pub k: Option<na::OMatrix<T, C, S>>,
31
32 ki: T,
35 integral_error: T,
37}
38
39impl<T, S, C> LQRController<T, S, C>
40where
41 T: na::RealField + Copy,
42 S: Dim + DimName + DimMin<S>,
43 C: Dim + DimName + DimMin<C>,
44 DefaultAllocator:
45 Allocator<T, S, S> + Allocator<T, C, C> + Allocator<T, S, C> + Allocator<T, C, S>,
46 rand::distributions::Standard: rand::distributions::Distribution<T>,
47{
48 pub fn new() -> Result<LQRController<T, S, C>, &'static str> {
50 Ok(LQRController {
51 q: None,
52 r: None,
53 k: None,
54 ki: T::zero(),
55 integral_error: T::zero(),
56 })
57 }
58
59 pub fn setup_i_controller(&mut self, ki: T) -> Result<(), Box<dyn Error>> {
61 if ki != T::zero() {
62 self.ki = ki;
63 } else {
64 Err("You tried setting ki to 0.0 which is not allowed")?;
65 }
66 Ok(())
67 }
68
69 pub fn compute_gain(
82 &mut self,
83 a: &na::OMatrix<T, S, S>,
84 b: &na::OMatrix<T, S, C>,
85 q: &na::OMatrix<T, S, S>,
86 r: &na::OMatrix<T, C, C>,
87 epsilon: T,
88 ) -> Result<na::OMatrix<T, C, S>, &'static str> {
89 let mut a_k = a.clone(); let mut g_k = b.clone()
91 * r.clone().try_inverse().expect("Couldn't compute inverse")
92 * b.clone().transpose();
93
94 let mut h_k_1 = q.clone();
95 let mut h_k = na::OMatrix::<T, S, S>::from_fn(|_, _| rand::random());
96
97 while {
98 let error = (h_k_1.clone() - h_k).norm() / h_k_1.norm();
99 h_k = h_k_1.clone();
100 error >= epsilon
101 } {
102 let temp = (na::OMatrix::<T, S, S>::identity() + &g_k * &h_k)
103 .try_inverse()
104 .expect("Couldn't compute inverse");
105 let a_k_1 = &a_k * &temp * &a_k;
106 let g_k_1 = &g_k + &a_k * &temp * &g_k * &a_k.transpose();
107 h_k_1 = &h_k + &a_k.transpose() * &h_k * &temp * &a_k;
108 a_k = a_k_1;
109 g_k = g_k_1;
110 }
111
112 self.k =
114 Some(r.clone().try_inverse().expect("Couldn't compute inverse") * b.transpose() * h_k);
115 return Ok(self.k.clone().unwrap());
116 }
117
118 pub fn compute_optimal_controls(
130 &mut self,
131 current_state: &na::OVector<T, S>,
132 desired_state: &na::OVector<T, S>,
133 ) -> Result<na::OVector<T, C>, &'static str>
134 where
135 T: na::RealField,
136 DefaultAllocator: Allocator<T, S> + Allocator<T, C> + Allocator<T, C, S>,
137 {
138 let error = desired_state - current_state;
139 let y_error = error[1];
140 if y_error.abs() < T::from_f64(1e-2).unwrap() {
141 self.integral_error = T::zero();
142 } else {
143 self.integral_error += y_error * self.ki.clone();
144 }
145
146 let mut controls = &self.k.clone().unwrap() * error;
147 controls[0] -= self.integral_error.clone();
148
149 Ok(controls)
150 }
151}
152
153impl<T, S, C> fmt::Display for LQRController<T, S, C>
154where
155 T: na::RealField,
156 S: Dim + DimName + DimMin<S>,
157 C: Dim + DimName + DimMin<C>,
158 DefaultAllocator:
159 Allocator<T, S, S> + Allocator<T, C, C> + Allocator<T, S, C> + Allocator<T, C, S>,
160 rand::distributions::Standard: rand::distributions::Distribution<T>,
161{
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 write!(
164 f,
165 "LQR controller
166 Q: {:?}
167 R: {:?}
168 ki: {:}",
169 self.q, self.r, self.ki,
170 )
171 }
172}
173
174pub struct ClosestIndexError;
175
176pub fn find_closest_indices<T, S, N>(
182 current_state: &na::OVector<T, S>,
183 trajectory: &na::OMatrix<T, S, N>,
184) -> Result<((usize, T), (usize, T)), ClosestIndexError>
185where
186 T: na::RealField + PartialOrd + Copy,
187 S: Dim + DimName + DimMin<S>, N: Dim + DimName, DefaultAllocator: Allocator<T, S>
190 + Allocator<T, S, N>
191 + Allocator<T, na::Const<1_usize>, N>
192 + nalgebra::allocator::Allocator<T, N>,
193{
194 if trajectory.ncols() < 2 {
195 return Err(ClosestIndexError);
196 }
197
198 let mut state = Vec::with_capacity(current_state.len());
199 for i in current_state.iter() {
200 state.push(i.clone());
201 }
202 let state = vec![state; trajectory.ncols()].concat();
203
204 let current_state_broadcasted = na::OMatrix::<T, S, N>::from_vec(state);
205 let errors = trajectory - current_state_broadcasted;
206 let errors = errors.map(|x| x.powi(2));
207 let errors = errors.row_sum_tr();
208 let (idx, error) = errors.argmin();
209
210 let (lower, upper) = if idx == 0 {
212 ((0, error.sqrt()), (0, error.sqrt()))
213 } else if idx == errors.len() - 1 {
214 (
215 (errors.len() - 1, error.sqrt()),
216 (errors.len() - 1, error.sqrt()),
217 )
218 } else {
219 if errors[idx + 1] > errors[idx - 1] {
220 ((idx, error.sqrt()), (idx + 1, errors[idx + 1].sqrt()))
221 } else {
222 ((idx - 1, errors[idx - 1].sqrt()), (idx, error.sqrt()))
223 }
224 };
225
226 if lower.0 != 0 && upper.0 != 0 && lower.0 > upper.0 {
228 return Err(ClosestIndexError);
230 }
231
232 Ok((lower, upper))
233}
234
235pub fn compute_target<T, S, N>(
237 trajectory: &na::OMatrix<T, S, N>,
238 lower: (usize, T),
239 upper: (usize, T),
240) -> na::OVector<T, S>
241where
242 T: na::RealField + Copy,
243 S: Dim + DimName + DimMin<S>, N: Dim + DimName, DefaultAllocator: Allocator<T, S> + Allocator<T, S, N>,
246{
247 trajectory
248 .column(lower.0)
249 .component_mul(&na::OVector::from_element(upper.1 / (lower.1 + upper.1)))
250 + trajectory
251 .column(upper.0)
252 .component_mul(&na::OVector::from_element(lower.1 / (lower.1 + upper.1)))
253}