l_shape_tracker.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2020, Robobrain.
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 met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Willow Garage, Inc. nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /* Author: Konstantinos Konstantinidis */
31 
32 #include "l_shape_tracker.hpp"
33 
34 static inline double normalize_angle_positive(double angle){
35  //Normalizes the angle to be 0 to 2*M_PI.
36  //It takes and returns radians.
37  return fmod(fmod(angle, 2.0*M_PI) + 2.0*M_PI, 2.0*M_PI);
38 }
39 static inline double normalize_angle(double angle){
40  //Normalizes the angle to be -M_PI circle to +M_PI circle
41  //It takes and returns radians.
42  double a = normalize_angle_positive(angle);
43  if (a > M_PI)
44  a -= 2.0 *M_PI;
45  return a;
46 }
47 
48 static inline double shortest_angular_distance(double from, double to){
49  //Given 2 angles, this returns the shortest angular difference.
50  //The inputs and outputs are radians.
51  //The result would always be -pi <= result <= pi.
52  //Adding the result to "from" results to "to".
53  return normalize_angle(to-from);
54 }
55 
56 LshapeTracker::LshapeTracker(){}//Creates a blank estimator
57 
58 LshapeTracker::LshapeTracker(const double& x_corner, const double& y_corner, const double& L1, const double& L2, const double& theta, const double& dt){
59 
60  // Initialization of Dynamic Kalman Filter
61  int n = 6; // Number of states
62  int m = 2; // Number of measurements
63  MatrixXd A(n, n); // System dynamics matrix
64  MatrixXd C(m, n); // Output matrix
65  MatrixXd Q(n, n); // Process noise covariance
66  MatrixXd R(m, m); // Measurement noise covariance
67  MatrixXd P(n, n); // Estimate error covariance
68 
69  //A << 1, 0,dt, 0,
70  //0, 1, 0,dt,
71  //0, 0, 1, 0,
72  //0, 0, 0, 1;
73  //
74  double ddt = dt*dt/2;
75  A << 1, 0,dt, 0, ddt, 0,
76  0, 1, 0,dt, 0, ddt,
77  0, 0, 1, 0, dt, 0,
78  0, 0, 0, 1, 0, dt,
79  0, 0, 0, 0, 1, 0,
80  0, 0, 0, 0, 0, 1;
81 
82  C << 1, 0, 0, 0, 0, 0,
83  0, 1, 0, 0, 0, 0;
84 
85  Q << 1, 0, 0, 0, 0, 0,
86  0, 1, 0, 0, 0, 0,
87  0, 0,10, 0, 0, 0,
88  0, 0, 0,10, 0, 0,
89  0, 0, 0, 0,20, 0,
90  0, 0, 0, 0, 0,20;
91  R.setIdentity();
92  R *=10;
93  R *= 0.1;
94  P.setIdentity() * 0.1;
95 
96  KalmanFilter dynamic_kalman_filter(dt, A, C, Q, R, P);
97  this->dynamic_kf = dynamic_kalman_filter;
98 
99  VectorXd x0_dynamic(n);
100  x0_dynamic << x_corner, y_corner, 0, 0, 0, 0;
101  dynamic_kf.init(0,x0_dynamic);
102 
103  // Initialization of Shape Kalman Filter
104  n = 4; // Number of states
105  m = 3; // Number of measurements
106  MatrixXd As(n, n); // System dynamics matrix
107  MatrixXd Cs(m, n); // Output matrix
108  MatrixXd Qs(n, n); // Process noise covariance
109  MatrixXd Rs(m, m); // Measurement noise covariance
110  MatrixXd Ps(n, n); // Estimate error covariance
111 
112  As<< 1, 0, 0, 0,
113  0, 1, 0, 0,
114  0, 0, 1,dt,
115  0, 0, 0, 1;
116 
117  Cs<< 1, 0, 0, 0,
118  0, 1, 0, 0,
119  0, 0, 1, 0;
120 
121  Qs<< 0.04, 0, 0, 0,
122  0, 0.04, 0, 0,
123  0, 0, dt, pow(dt,2)/2,
124  0, 0, 0,dt;
125  Ps.setIdentity();
126 
127  KalmanFilter shape_kalman_filter(dt, As, Cs, Qs, Rs, Ps);
128  this->shape_kf = shape_kalman_filter;
129 
130  VectorXd x0_shape(n);
131  double L1init=0.1;
132  double L2init=0.1;
133  if(L1>L1init){L1init = L1;}
134  if(L2>L2init){L2init = L2;}
135  x0_shape << L1init, L2init, theta, 0;
136  shape_kf.init(0,x0_shape);
137 
138 
139  L1_old = L1;
140  L2_old = L2;
141  old_thetaL1 = theta;
142  x_old = x_corner;
143  y_old = y_corner;
144 
145 }
146 
147 void LshapeTracker::update(const double& thetaL1, const double& x_corner, const double& y_corner, const double& L1, const double& L2, const double& dt, const int cluster_size) {
148 
149  current_size = cluster_size;
150  detectCornerPointSwitch(old_thetaL1, thetaL1, dt);
151 
152  double norm = normalize_angle(shape_kf.state()(2));
153  double distance = shortest_angular_distance(norm, thetaL1);
154  double theta = distance + shape_kf.state()(2) ;
155 
156  // Update Dynamic Kalman Filter
157  Vector2d y;
158  y << x_corner, y_corner;
159  dynamic_kf.update(y, dt);
160 
161  // Update Shape Kalman Filter
162  Vector3d shape_measurements;
163  double L1max, L2max;
164  L2max = L2;
165  L1max = L1;
166  shape_kf.R<< pow(L1,-2), 0, 0,
167  0, pow(L2,-2), 0,
168  0, 0, 0.5;
169  shape_measurements << L1max, L2max, theta;
170  shape_kf.update(shape_measurements, dt);
171 
172  L1_old = L1;
173  L2_old = L2;
174  old_thetaL1 = thetaL1;
175  x_old = x_corner;
176  y_old = y_corner;
177 
178 }
179 
180 void LshapeTracker::detectCornerPointSwitchMahalanobis(const double& from, const double& to, const double L1, const double L2, const double x_corner, const double y_corner){
181  // The purpose of this function is to detect potential corner point switches.
182  // For this purpose it calculates the Mahalanobis distance between the previous
183  // and the current measurements and based on the lowest distance it decides if
184  // the corner point changed or not.
185 
186  double x_new = x_corner;
187  double y_new = y_corner;
188  double theta_new = to;
189  double theta_corner = from;
190  double x_c = dynamic_kf.state()(0);
191  double y_c = dynamic_kf.state()(1);
192  double L1_box = shape_kf.state()(0);
193  double L2_box = shape_kf.state()(1);
194 
195  double x_corner_L1= x_c + L1_box*cos(theta_corner);
196  double y_corner_L1= y_c + L1_box*sin(theta_corner);
197  double theta_corner_L1 = normalize_angle(theta_corner + pi/2);
198 
199  double x_corner_L2 = x_c + L2_box*sin(theta_corner);
200  double y_corner_L2 = y_c - L2_box*cos(theta_corner);
201  double theta_corner_L2 = normalize_angle(theta_corner + pi/2);
202  ROS_DEBUG_STREAM("simple: "<<theta_corner-theta_new<<", findTurn: "<<findTurn(theta_new,theta_corner));
203 
204  Eigen::Matrix<double, 5, 5> C;
205  C.setZero();
206  C(0,0) = dynamic_kf.P(0,0);
207  C(0,1) = dynamic_kf.P(0,1);
208  C(1,0) = dynamic_kf.P(1,0);
209  C(1,1) = dynamic_kf.P(1,1);
210  C(2,2) = shape_kf.P(0,0);
211  C(2,3) = shape_kf.P(0,1);
212  C(3,2) = shape_kf.P(1,0);
213  C(3,3) = shape_kf.P(1,1);
214  C(4,4) = shape_kf.P(2,2);
215 
216  Eigen::Matrix<double, 5, 1> means;
217  means(0) = x_c - x_new;
218  means(1) = y_c - y_new;
219  means(2) = L1-L1_box;
220  means(3) = L2-L2_box;
221  means(4) = findTurn(theta_new,theta_corner);
222  std::vector<double> mdistances;
223  mdistances.push_back(means.transpose()*C.inverse()*means);
224 
225  means(0) = x_corner_L1 - x_new;
226  means(1) = y_corner_L1 - y_new;
227  means(2) = L1-L2_box;
228  means(3) = L2-L1_box;
229  means(4) = findTurn(theta_new,theta_corner_L1);
230  mdistances.push_back(means.transpose()*C.inverse()*means);
231 
232  means(0) = x_corner_L2 - x_new;
233  means(1) = y_corner_L2 - y_new;
234  means(2) = L1-L2_box;
235  means(3) = L2-L1_box;
236  means(4) = findTurn(theta_new,theta_corner_L2);
237  mdistances.push_back(means.transpose()*C.inverse()*means);
238 
239  int minElementIndex = std::min_element(mdistances.begin(),mdistances.end()) - mdistances.begin();
240 
241  if(minElementIndex == 2 && abs(mdistances[0]-mdistances[2])>0.1 && current_size>1){
243  }
244  else if(minElementIndex == 1 && abs(mdistances[0]-mdistances[1])>0.1 && current_size>1){
245  this->ClockwisePointSwitch();
246  }
247 
248 
249  std::vector<double> distances;
250  double euclidean;
251  euclidean = sqrt(pow(x_c-x_new,2) + pow(y_c-y_new,2) + pow(findTurn(theta_new,theta_corner),2) + pow(L1-L1_box,2) + pow(L2-L2_box,2));
252  distances.push_back(euclidean);
253  euclidean = sqrt(pow(x_corner_L1-x_new,2) + pow(y_corner_L1-y_new,2) + pow(findTurn(theta_new,theta_corner_L1),2)+ pow(L1-L2_box,2) + pow(L2-L1_box,2));
254  distances.push_back(euclidean);
255  euclidean = sqrt(pow(x_corner_L2-x_new,2) + pow(y_corner_L2-y_new,2) + pow(findTurn(theta_new,theta_corner_L2),2)+ pow(L1-L2_box,2) + pow(L2-L1_box,2));
256  distances.push_back(euclidean);
257 
258 }
259 
260  void LshapeTracker::BoxModel(double& x, double& y,double& vx, double& vy,double& theta, double& psi, double& omega, double& L1, double& L2, double& length, double& width){
261  L1 = shape_kf.state()(0);
262  L2 = shape_kf.state()(1);
263  theta = shape_kf.state()(2);
264  //Equations 30 of "L-Shape Model Switching-Based precise motion tracking of moving vehicles"
265  double ex = (L1 * cos(theta) + L2 * sin(theta)) /2;
266  double ey = (L1 * sin(theta) - L2 * cos(theta)) /2;
267 
268  omega = shape_kf.state()(3);
269  x = dynamic_kf.state()(0) + ex;
270  y = dynamic_kf.state()(1) + ey;
271 
272  //Equations 31 of "L-Shape Model Switching-Based precise motion tracking of moving vehicles"
273  //TODO test the complete equation also
274  vx = dynamic_kf.state()(2);
275  vy = dynamic_kf.state()(3);
276 
277  findOrientation(psi, length, width);
278 }
279 
280 
281 double LshapeTracker::findTurn(const double& new_angle, const double& old_angle){
282  //https://math.stackexchange.com/questions/1366869/calculating-rotation-direction-between-two-angles
283  double theta_pro = new_angle - old_angle;
284  double turn = 0;
285  if(-M_PI<=theta_pro && theta_pro <= M_PI){
286  turn = theta_pro;}
287  else if(theta_pro > M_PI){
288  turn = theta_pro - 2*M_PI;}
289  else if(theta_pro < -M_PI){
290  turn = theta_pro + 2*M_PI;}
291  return turn;
292 }
293 
294 void LshapeTracker::detectCornerPointSwitch(const double& from, const double& to, const double dt){
295  //Corner Point Switch Detection
296  double turn = findTurn(from, to);
297  if(turn <-0.8){
299  }
300  else if(turn > 0.6){
301  this->ClockwisePointSwitch();
302  }
303 }
304 
305 void LshapeTracker::findOrientation(double& psi, double& length, double& width){
306  //This function finds the orientation of a moving object, when given an L-shape orientation
307 
308  std::vector<double> angles;
309  double angle_norm = normalize_angle(shape_kf.state()(2));
310  angles.push_back(angle_norm);
311  angles.push_back(angle_norm + pi);
312  angles.push_back(angle_norm + pi/2);
313  angles.push_back(angle_norm + 3*pi/2);
314 
315  double vsp = atan2(dynamic_kf.state()(3),dynamic_kf.state()(2));
316  double min = 1.56;
317  double distance;
318  double orientation;
319  int pos;
320  for (unsigned int i = 0; i < 4; ++i) {
321  distance = abs(shortest_angular_distance(vsp,angles[i]));
322  if (distance < min){
323  min = distance;
324  orientation = normalize_angle(angles[i]);
325  pos = i;
326  }
327  }
328  if(pos ==0 || pos==1){
329  length = shape_kf.state()(0);
330  width = shape_kf.state()(1);
331  }
332  else{
333  length = shape_kf.state()(1);
334  width = shape_kf.state()(0);
335  }
336 
337  psi = normalize_angle(orientation);
338 
339 }
340 
341 
343  // Equation 17
344 
345  Vector6d new_dynamic_states = dynamic_kf.state();
346  Vector4d new_shape_states = shape_kf.state();
347 
348  double L1 = shape_kf.state()(0);
349  double L2 = shape_kf.state()(1);
350 
351  //x = x + L1 * cos(theta);
352  new_dynamic_states(0) += L1 * cos(shape_kf.state()(2));
353  //y = y + L1 * sin(theta);
354  new_dynamic_states(1) += L1 * sin(shape_kf.state()(2));
355  //vx = vx - L1 * omega * sin(theta);
356  new_dynamic_states(2) -= L1 * shape_kf.state()(3) * sin(shape_kf.state()(2));
357  //vy = vy + L1 * omega * cos(theta);
358  new_dynamic_states(3) += L1 * shape_kf.state()(3) * cos(shape_kf.state()(2));
359  //ax = ax - L1 * omega^2 * cos(theta);
360  new_dynamic_states(4) -= L1 * pow(shape_kf.state()(3),2) * cos(shape_kf.state()(2));
361  //ay = ay - L1 * omega^2 * sin(theta);
362  new_dynamic_states(5) -= L1 * pow(shape_kf.state()(3),2) * sin(shape_kf.state()(2));
363 
364 
365  //L1 = L2
366  new_shape_states(0) = L2;
367  //L2 = L1
368  new_shape_states(1) = L1;
369 
370  new_shape_states(2) = shape_kf.state()(2) - pi / 2;
371 
372  dynamic_kf.changeStates(new_dynamic_states);
373  shape_kf.changeStates(new_shape_states);
374 
375 }
376 
378  // Equation 17
379 
380  Vector6d new_dynamic_states = dynamic_kf.state();
381  Vector4d new_shape_states = shape_kf.state();
382 
383  double L1 = shape_kf.state()(0);
384  double L2 = shape_kf.state()(1);
385 
386  //x = x + L2 * sin(theta);
387  new_dynamic_states(0) += L2 * sin(shape_kf.state()(2));
388  //y = y - L2 * cos(theta);
389  new_dynamic_states(1) -= L2 * cos(shape_kf.state()(2));
390  //vx = vx + L2 * omega * cos(theta);
391  new_dynamic_states(2) += L2 * shape_kf.state()(3) * cos(shape_kf.state()(2));
392  //vy = vy + L2 * omega * sin(theta);
393  new_dynamic_states(3) += L2 * shape_kf.state()(3) * sin(shape_kf.state()(2));
394  //ax = ax - L2 * omega^2 * cos(theta);
395  new_dynamic_states(4) = dynamic_kf.state()(4) - L2 * pow(shape_kf.state()(3),2) * sin(shape_kf.state()(2));
396  //ay = ay - L2 * omega^2 * sin(theta);
397  new_dynamic_states(5) = dynamic_kf.state()(5) + L2 * pow(shape_kf.state()(3),2) * cos(shape_kf.state()(2));
398 
399  //L1 = L2
400  new_shape_states(0) = L2;
401  //L2 = L1
402  new_shape_states(1) = L1;
403 
404  new_shape_states(2) = shape_kf.state()(2) + pi / 2;
405 
406  dynamic_kf.changeStates(new_dynamic_states);
407  shape_kf.changeStates(new_shape_states);
408 }
409 
410 
void detectCornerPointSwitch(const double &from, const double &to, const double dt)
static double shortest_angular_distance(double from, double to)
void ClockwisePointSwitch()
static double normalize_angle(double angle)
double min(double a, double b)
void init()
Definition: kalman.cpp:38
INLINE Rall1d< T, V, S > sqrt(const Rall1d< T, V, S > &arg)
KalmanFilter shape_kf
#define ROS_DEBUG_STREAM(args)
const double pi
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
void CounterClockwisePointSwitch()
double findTurn(const double &new_angle, const double &old_angle)
void BoxModel(double &x, double &y, double &vx, double &vy, double &theta, double &psi, double &omega, double &L1, double &L2, double &length, double &width)
INLINE Rall1d< T, V, S > abs(const Rall1d< T, V, S > &x)
KalmanFilter dynamic_kf
static double normalize_angle_positive(double angle)
void update(const double &thetaL1, const double &x_corner, const double &y_corner, const double &L1, const double &L2, const double &dt, const int cluster_size)
INLINE Rall1d< T, V, S > atan2(const Rall1d< T, V, S > &y, const Rall1d< T, V, S > &x)
Eigen::VectorXd state()
Definition: kalman.hpp:73
void changeStates(const Eigen::VectorXd &new_states)
Definition: kalman.cpp:62
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Eigen::MatrixXd P
Definition: kalman.hpp:28
void update(const Eigen::VectorXd &y)
Definition: kalman.cpp:46
void detectCornerPointSwitchMahalanobis(const double &from, const double &to, const double dt)
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
void findOrientation(double &psi, double &length, double &width)
Finds orientations of tracked object.
Eigen::MatrixXd R
Definition: kalman.hpp:28
Eigen::Matrix< double, 6, 1 > Vector6d


datmo
Author(s): Kostas Konstantinidis
autogenerated on Tue May 2 2023 02:58:06