-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcop_ctrl.cxx
More file actions
216 lines (170 loc) · 6.32 KB
/
cop_ctrl.cxx
File metadata and controls
216 lines (170 loc) · 6.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include <iostream>
#include <cmath>
#include "Hermite.h"
#include <Eigen/Dense>
// Solve the equation:
// m z y_ddot - m y g = -T_x(t)
// where z is constant and T_x(t) is piecewise linear.
// The center-of-pressure p_x is defined by:
// p_y = T_x/F_z = T_x/(m*g)
// *** System parameters ***
const double sys_m = 1.0;
const double sys_g = 9.81;
const double sys_z = 1.0;
const unsigned int N_INTERVALS = 6;
// Time interval
const double h = 0.5;
const double t_end = h * N_INTERVALS;
// Reference torques T_x
const double T_x_ref[N_INTERVALS+1] = { 0.0, 0.1, 1.0, 1.1, 2.0, 2.1, 3.0};
double calc_T_x(double t)
{
int i = floor(t / h);
if(i < 0) i = 0;
if(i > N_INTERVALS-1) i = N_INTERVALS-1;
const double t_i = i*h;
const double t_i1 = (i+1)*h;
return ((t-t_i)*T_x_ref[i+1] + (t_i1-t)*T_x_ref[i])/h;
}
// Approximate solution
Hermite bvp_solver()
{
const size_t n_pts = 31 + 2;
const double dx = 0.1;
Eigen::MatrixXd A(n_pts, n_pts); A.setZero();
Eigen::MatrixXd R(n_pts, n_pts); R.setZero();
for(size_t i=1; i<(n_pts-1); i++) {
A(i, i-1) = 1./dx;
A(i, i) = 4./dx;
A(i, i+1) = 1./dx;
R(i, i+1) = 3./(dx*dx);
R(i, i-1) = -3./(dx*dx);
}
A(0, 0) = 2./dx;
A(0, 1) = 1./dx;
R(0, 0) = -3./(dx*dx);
R(0, 1) = 3./(dx*dx);
A(n_pts-1, n_pts-2) = 1./dx;
A(n_pts-1, n_pts-1) = 2./dx;
R(n_pts-1, n_pts-2) = -3./(dx*dx);
R(n_pts-1, n_pts-1) = 3./(dx*dx);
// Matrix relating k to the control points
Eigen::MatrixXd K = A.colPivHouseholderQr().solve(R);
// Matrix relating the second derivative of y to the control points
Eigen::MatrixXd B(n_pts, n_pts);
for(size_t i=0; i<(n_pts-1); i++) {
for(size_t j=0; j<n_pts; j++) {
B(i, j) = -4./dx * K(i, j) - 2./dx * K(i+1, j);
}
B(i, i) += -6./(dx*dx);
B(i, i+1) += 6./(dx*dx);
}
// Enforce the ODE at the inner control points
Eigen::MatrixXd D(n_pts, n_pts); D.setZero();
Eigen::VectorXd Y(n_pts); Y.setZero();
for(size_t i=1; i<(n_pts-1); i++) {
for(size_t j=0; j<n_pts; j++) {
D(i,j) = sys_m*sys_z*B(i, j);
}
D(i, i) -= sys_m*sys_g;
Y(i) = -calc_T_x(i*dx - dx);
}
// Enforce the boundary points (y = y_cop)
const double y_0 = 0.;
const double y_end = calc_T_x(t_end)/(sys_m*sys_g);
D(0, 1) = 1.;
D(n_pts-1, n_pts-2) = 1.;
Y(0) = y_0;
Y(n_pts-1) = y_end;
Eigen::VectorXd y(n_pts);
y = D.colPivHouseholderQr().solve(Y);
Hermite h;
h.setData(n_pts, -dx, dx, y.data(), (Eigen::Matrix<double, n_pts, 1>(K*y)).data());
return h;
}
// Analytical solution for piecewise-linear T_x
// The analytical solution is given by:
// y_i = a_i*exp(sqrt(alpha)*t) + b_i*exp(-sqrt(alpha)*t) + T_x(t)
// (typo in thesis?)
Eigen::VectorXd pwl_solver()
{
const double alpha = sys_g/sys_z;
const double sqrt_alpha = sqrt(alpha);
// FIXME: using a dense matrix here is very inefficient.
Eigen::MatrixXd Q(2*N_INTERVALS, 2*N_INTERVALS);
Q.setZero();
Eigen::VectorXd r(2*N_INTERVALS);
r.setZero();
// Left boundary condition
// y_0 = a_0 + b_0 + T_x_0/(m*g)
const double y_0 = 0.;
Q(0, 0) = 1.;
Q(0, 1) = 1.;
r(0) = y_0 - T_x_ref[0]/(sys_m*sys_g);
// Internal smoothness conditions
for(unsigned int i=0; i<(N_INTERVALS-1); i++) {
const double t_i1 = h*(i+1);
// Smooth value
// a_i*exp(sqrt(alpha)*t_{i+1}) + b_i*exp(-sqrt(alpha)*t_{i+1})
// - a_{i+1}*exp(sqrt(alpha)*t_{i+1}) - b_{i+1}*exp(-sqrt(alpha)*t_{i+1}) = 0
Q(2*i+1, 2*i ) = exp(sqrt_alpha*t_i1);
Q(2*i+1, 2*i+1) = exp(-sqrt_alpha*t_i1);
Q(2*i+1, 2*i+2) = -exp(sqrt_alpha*t_i1);
Q(2*i+1, 2*i+3) = -exp(-sqrt_alpha*t_i1);
// Smooth first derivative
// a_i*exp(sqrt(alpha)*t_{i+1}) - b_i*exp(-sqrt(alpha)*t_{i+1}) + (dT_i/dt)/(m*g)
// - a_{i+1}*exp(sqrt(alpha)*t_{i+1}) + b_{i+1}*exp(-sqrt(alpha)*t_{i+1}) - (dT_{i+1}/dt)/(m*g) = 0
Q(2*i+2, 2*i ) = exp(sqrt_alpha*t_i1);
Q(2*i+2, 2*i+1) = -exp(-sqrt_alpha*t_i1);
Q(2*i+2, 2*i+2) = -exp(sqrt_alpha*t_i1);
Q(2*i+2, 2*i+3) = exp(-sqrt_alpha*t_i1);
r(2*i+2) = -(T_x_ref[i+1]-T_x_ref[i])/(h*sys_m*sys_g*sqrt_alpha)
+ (T_x_ref[i+2]-T_x_ref[i+1])/(h*sys_m*sys_g*sqrt_alpha);
}
// Right boundary condition
// y_N = a_{N-1} exp(sqrt(alpha)*t_N) + b_{N-1}*exp(-sqrt(alpha)*t_N) + T_N/(m*g)
const double y_end = T_x_ref[N_INTERVALS]/(sys_m*sys_g);
Q(2*N_INTERVALS-1, 2*N_INTERVALS-2) = exp(sqrt_alpha*t_end);
Q(2*N_INTERVALS-1, 2*N_INTERVALS-1) = exp(-sqrt_alpha*t_end);
r(2*N_INTERVALS-1) = y_end - T_x_ref[N_INTERVALS]/(sys_m*sys_g);
// Solve equations
Eigen::VectorXd params(2*N_INTERVALS);
params = Q.colPivHouseholderQr().solve(r);
return params;
}
double eval_y_cog_ana(double t, const Eigen::VectorXd& params)
{
int i = floor(t / h);
if(i < 0) i = 0;
if(i > N_INTERVALS-1) i = N_INTERVALS-1;
const double alpha = sys_g/sys_z;
const double sqrt_alpha = sqrt(alpha);
const double a_i = params[2*i];
const double b_i = params[2*i+1];
const double T_x = calc_T_x(t);
return a_i*exp(sqrt_alpha*t) + b_i*exp(-sqrt_alpha*t) + T_x/(sys_m*sys_g);
}
int main()
{
// Analytical solution
Eigen::VectorXd params = pwl_solver();
// Approximate solution
Hermite y_cog_approx = bvp_solver();
std::cout << "#:1:y_cop" << std::endl;
std::cout << "#:2:y_cog_ana" << std::endl;
std::cout << "#:3:y_cog_approx" << std::endl;
std::cout << "#:4:y_cop_approx" << std::endl;
for(unsigned int step=0; step<=300; step++) {
const double t = step/100.;
const double T_x = calc_T_x(t);
const double y_cop = T_x/(sys_m*sys_g);
const double y_cop_approx = -(sys_m*sys_z*y_cog_approx.eval_d2(t) - sys_m*sys_g*y_cog_approx.eval(t))/(sys_m * sys_g);
std::cout << t << " ";
std::cout << y_cop << " ";
std::cout << eval_y_cog_ana(t, params) << " ";
std::cout << y_cog_approx.eval(t) << " ";
std::cout << y_cop_approx << " ";
std::cout << std::endl;
}
return 0;
}