Repository: HiroIshida/robust-tube-mpc Branch: master Commit: 427a181dd368 Files: 18 Total size: 29.9 KB Directory structure: gitextract_gc5y9v0w/ ├── .gitignore ├── LICENSE.md ├── README.md ├── example/ │ ├── example_MPC.m │ ├── example_dist_inv_set.m │ ├── example_optimalcontrol.m │ └── example_tubeMPC.m ├── note/ │ ├── .gitignore │ └── idea.tex └── src/ ├── ConstraintManager.m ├── DisturbanceLinearSystem.m ├── Graphics.m ├── LinearSystem.m ├── ModelPredictiveControl.m ├── OptimalControler.m ├── TubeModelPredictiveControl.m └── utils/ ├── convert_Poly2Mat.m └── number2string.m ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ old/ *.db *.asv example/results/ pathdef.m /tbxmanager ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2018 Hirokazu Ishida Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Robust Model Predictive Control Using Tube This repository includes examples for the tube model predictive control (tube-MPC)[1] as well as the generic model predictive control (MPC) written in MATLAB. ## Requirement 1) optimization_toolbox (matlab)
2) control_toolbox (matlab)
3) Multi-Parametric Toolbox 3 (open-source and freely available at http://people.ee.ethz.ch/~mpt/3/) ## Feedback, bug reports, contributions If you find this package helpful, giving a "star" to this repositry will be a happy feedback for me! If you find a bug, or have more broader kind of quession about tube MPC,please post that in the [issue page](https://github.com/HiroIshida/robust-tube-mpc/issues). I will try hard to respond to questions via e-mail but, I **strongly recommend** do it in the issue page. It's much easier for me to keep myself on track. ## Usage See `example/example_tubeMPC.m` and `example/example_MPC.m` for the tube-MPC and generic MPC, respectively. Note that every inequality constraint here is expressed as a convex set. For example, the constraints on the state `Xc` is specified as a rectangular, which is constructed with 4 vertexes. When considering a 1-dim input `Uc`, `Uc` will be specified by min and max value (i.e. `u∊[u_min, u_max]`), so it will be constructed by 2 vertexes. For more detail, please see the example codes. ## Short introduction to the tube MPC After running `example/example_tubeMPC.m`, you will get the following figure sequence. ![the gif file](/fig/tube_mpc.gif) Now that you can see that the green nominal trajectory starting from the bottom left of the figure and surrounding a "tube". At each time step, the nominal trajectory (green line) is computed online. Let me give some important details. The red region `Xc` that contains the pink region `Xc-Z` is the state constraint that we give first. However, considering the uncertainty, the tube-MPC designs the nominal trajectory to be located inside `Xc-Z`, which enables to put "tube" around the nominal trajectory such that the tube is also contained in `Xc-Z`. Of course, the input sequence associated with the nominal trajectory is inside of `Uc-KZ`. ## Disturbance invariant set I think one may get stuck at computation of what paper [1] called "disturbance invariant set". The disturbance invariant set is an infinite [Minkowski addition](https://en.wikipedia.org/wiki/Minkowski_addition) `Z = W ⨁ Ak*W ⨁ Ak^2*W...`, where ⨁ denotes Minkowski addition. Because it's an infinite sum of Minkowski addition, computing Z analytically is intractable. In [2], Racovic proposed a method to efficiently compute an outer approximiation of Z, which seems to be heavily used in MPC community. In this repository, computation of Z takes place in the constructor of `DisturbanceLinearSystem` class. To understand how Z guarantee the robustness, running `example/example_dist_inv_set.m` may help you. ## Maximum positively invariant set I used the maximal positively invariant (MPI) set `Xmpi` as the terminal constraint set. (Terminal constraint is usually denoted as Xf in literature). Book [3] explains the concept of the MPI and algorithm well in section 2.4. `Xmpi` is computed in the constructor of `OptimalControler.m`. Note that the MPI set is computed with `Xc` and `Uc` in the normal MPC setting, but in the tube-MPC the MPI set is computed with `Xc⊖Z`and `Uc⊖Z` instead. # Reference [1] Mayne, David Q., María M. Seron, and S. V. Raković. "Robust model predictive control of constrained linear systems with bounded disturbances." Automatica 41.2 (2005): 219-224. [2] Rakovic, Sasa V., et al. "Invariant approximations of the minimal robust positively invariant set." IEEE Transactions on Automatic Control 50.3 (2005): 406-410. [3] Kouvaritakis, Basil, and Mark Cannon. "Model predictive control." Switzerland: Springer International Publishing (2016). ================================================ FILE: example/example_MPC.m ================================================ addpath('../src/') addpath('../src/utils/') %the usage is mostly same as tubeMPC A = [1 1; 0 1]; B = [0.5; 1]; Q = diag([1, 1]); R = 0.1; mysys = LinearSystem(A, B, Q, R); Xc_vertex = [2, -2; 2 2; -10 2; -10 -2]; Uc_vertex = [1; -1]; Xc = Polyhedron(Xc_vertex); Uc = Polyhedron(Uc_vertex); % Unlike tube-MPC, this generic MPC doesn't guarantee the robustness. % In the following simulation you will notice that the generated path almost touch the boundary. % Thus, you can imagine that if some additive disturbance is considered, then the state can % easily be off the boundary, and the succeeding optimization will no longer be feasible. % Please add some noise and do experiments. N_horizon = 5; mpc = ModelPredictiveControl(mysys, Xc, Uc, N_horizon); x = [-7; -2]; savedir_name = 'results'; for i = 1:15 u_next = mpc.solve(x); x = mysys.propagate(x, u_next); % + add some noise here mpc.show_prediction(); pause(0.1); clf; end ================================================ FILE: example/example_dist_inv_set.m ================================================ addpath('../src/') addpath('../src/utils/') % specify your own discrete linear system A = [1 1; 0 1]; B = [0.5; 1]; Q = diag([1, 1]); R = 0.1; % construct a convex set of system noise (2dim here) W_vertex = [0.15, 0.15; 0.15, -0.15; -0.15, -0.15; -0.15, 0.15]; W = Polyhedron(W_vertex); % construct disturbance Linear system % note that disturbance invariant set Z is computed and stored as member variable in the constructor. disturbance_system = DisturbanceLinearSystem(A, B, Q, R, W); % you can see that with any disturbance bounded by W, the state is guaranteed to inside Z x = zeros(2); % initial state for i = 1:50 u = disturbance_system.K * (x - 0); x = disturbance_system.propagate(x, u); % disturbance is considered inside the method clf; Graphics.show_convex(disturbance_system.Z, 'g', 'FaceAlpha', .3); % show Z scatter(x(1, :), x(2, :)); % show particle pause(0.01); end ================================================ FILE: example/example_optimalcontrol.m ================================================ addpath('../src/') addpath('../src/utils/') % make your own discrete linear system A = [1 1; 0 1]; B = [0.5; 1]; Q = diag([1, 1]); R = 0.1; mysys = LinearSystem(A, B, Q, R); % constraints on 2dim state Xc and 1dim input Uc Xc_vertex = [2, -2; 2 2; -10 2; -10 -2]; Uc_vertex = [1; -1]; Xc = Polyhedron(Xc_vertex); Uc = Polyhedron(Uc_vertex); % create a optimal controler % if N_horizon is too small, the path will never reach inside % the maximum positively invariant set (X_MPI) in time step N_horizon, % then the problem becomes infeasible. % OptimalControler.solve method returns optimal path (x_seq) and input (u_seq) N_horizon = 20; optcon = OptimalControler(mysys, Xc, Uc, 20); x_init = [-7; -2]; optcon.add_initial_eq_constraint(x_init); [x_seq, u_seq] = optcon.solve(); ================================================ FILE: example/example_tubeMPC.m ================================================ addpath('../src/') addpath('../src/utils/') % fix random seed rng(0); % make your own discrete linear system with disturbance A = [1 1; 0 1]; B = [0.5; 1]; Q = diag([1, 1]); R = 0.1; W_vertex = [0.15, 0.15; 0.15, -0.15; -0.15, -0.15; -0.15, 0.15]; % construct a convex set of disturbance (2dim here) W = Polyhedron(W_vertex); % construct disturbance Linear system disturbance_system = DisturbanceLinearSystem(A, B, Q, R, W); % constraints on state Xc and input Uc Xc_vertex = [2, -2; 2 2; -10 2; -10 -2]; Uc_vertex = [1; -1]; Xc = Polyhedron(Xc_vertex); Uc = Polyhedron(Uc_vertex); % create a tube_mpc simulater % if N_horizon is too small, the path will never reach inside the robust MPI-set X_mpi_robust in time step N_horizon, then the problem becomes infeasible. N_horizon = 10; w_min = [0; -0.10]; w_max = [0; 0.10]; mpc = TubeModelPredictiveControl(disturbance_system, Xc, Uc, N_horizon); % The robust MPC guidances the path inside the robust MPI-set so that the path will reach the robust MPI-set in N_horizon. x = [-7; -2]; savedir_name = './results/'; mkdir(savedir_name); for i = 1:15 disp(i) u_next = mpc.solve(x); x = disturbance_system.propagate(x, u_next); % additive disturbance is considered inside the method mpc.show_prediction(); filename = strcat(savedir_name, 'tmpc_seq', number2string(i), '.png') saveas(gcf, char(filename)); % removing this line makes the code much faster clf; end ================================================ FILE: note/.gitignore ================================================ *.aux *.dvi *.log *.out ================================================ FILE: note/idea.tex ================================================ \documentclass[15pt]{article} \usepackage{cancel} \usepackage{amsmath} \usepackage{listings} \usepackage{amsthm} \usepackage{amssymb} %symbols for mapping \usepackage{amsfonts} \usepackage{mathptmx} \usepackage{bm} \usepackage{framed} \usepackage{geometry} \geometry{left=20mm,right=20mm,top=20mm,bottom=20mm} \usepackage{hyperref} \usepackage{mathtools} \usepackage{color} \usepackage{comment} \usepackage{hyperref} \newcommand{\argmax}{\mathop{\rm argmax}\limits} \newcommand{\argmin}{\mathop{\rm argmin}\limits} \newcommand{\mymax}{\mathop{\rm max}\limits} \newcommand{\mymin}{\mathop{\rm min}\limits} \newcommand{\np}{\mathcal{N}_p} \definecolor{dkgreen}{rgb}{0,0.6,0} \definecolor{gray}{rgb}{0.5,0.5,0.5} \definecolor{mauve}{rgb}{0.58,0,0.82} \lstset{frame=tb, language=matlab, aboveskip=3mm, belowskip=3mm, showstringspaces=false, columns=flexible, basicstyle={\small\ttfamily}, numbers=none, numberstyle=\tiny\color{gray}, keywordstyle=\color{blue}, commentstyle=\color{dkgreen}, stringstyle=\color{mauve}, breaklines=true, breakatwhitespace=true, tabsize=3 } \hypersetup{ urlcolor=cyan } \begin{document} \begin{center} \textbf{Possible extension to distributed MPC} \end{center} Let just consider $x_p$ and $x_q$ at a particular time step. For simplicity, let me remove the time index and the bar notation. Also, \begin{align} \sum_{q\in \np}(x_q -x_p)^T Q (x_q - x_p) &= \sum_{q \in \np} \left( x_p^T Q x_p + - 2 x_q^T Q x_p + x_q^T Q x_q \right) \\ & = (\#\np) x_p^T Q x_p - 2 \left[ \sum_{q\in \np} x_q^T Q \right] ^T x_p + \sum_{q\in \np} x_q^T Q x_q \end{align} Letting $A = (\#\np) Q$, $b = -2\left[ \sum_{q\in \np} x_q^T Q \right] ^T$ and $c = \sum_{q\in \np} x_q^T Q x_q$, we now have the form of \[ x_p^TAx_p + b x_p + c \] We have $N$ (number of waypoints of trajectory) of this. Note that you can do the same thing for $P$(for terminal cost) to make $A_f, b_f, c_f$. But for simplicity, let us just consider $P=Q$. Let me denote $X:=[x_{p_1}^T, \ldots x_{p_N}^T]^T$ (the path) and input sequence $U:=[u_{p_1}^T, \ldots, u_{p_N}^T]^T$. Also, let me write $S:=[X^T, U^T]^T$. Then the total objective function $J(X, U)$ will be \begin{align} J(X, U) &= \sum_{k\in [N]} \left( x_{p_k}^T A x_{p_k} + b x_{p_k} + c \right) + \sum_{k\in[N]} u_{p_k}^TQu_{p_{k}} \\ &= \left( X^T \tilde{A} X + \tilde{b} X + \tilde{c} \right) + U^T \tilde{R} U \\ &= S^T \mathrm{diag}(\tilde{A}, \tilde{R}) S + [\tilde{b}, \mathrm{zeros}]^T S + \tilde{c}\\ \end{align} wher $\tilde{A} = \mathrm{diag}(A, \ldots, A)$, $\tilde{B} = [b, \ldots, b]$, $\tilde{c}=\sum_{i\in [N]}^{} c$ and $\tilde{R} = \mathrm{diag}(R, \ldots, R)$. In my original implementation, the cost $J$ is expressed by $S^T H S$ and $H$ is constructed by the following: \begin{lstlisting} function H = construct_costfunction(obj) % compute H Q_block = []; R_block = []; for itr=1:obj.N Q_block = blkdiag(Q_block, obj.sys.Q); R_block = blkdiag(R_block, obj.sys.R); end H = blkdiag(Q_block, obj.sys.P, R_block); end \end{lstlisting} So first you should replace Q\_block and R\_block in final line by $\tilde{Q}$ and $\tilde{R}$. As for $obj.sys.P$ you can do the same. Note that in this setting, we have $[\tilde{b}, \mathrm{zeros}]^TS$ and $tilde{c}$. So, just compute then in the constructor when you make `OptimalControler` instance and then pass them to `quadprog` (MATLAB's quadprog accept the form of $x^T A x + b^Tx$). \end{document} ================================================ FILE: src/ConstraintManager.m ================================================ classdef ConstraintManager < handle properties (SetAccess = private) C_equality; C_inequality; end methods (Access = public) function obj = ConstraintManager() obj.C_equality = containers.Map(); obj.C_inequality = containers.Map(); end function add_eq_constraint(obj, key, C_eq1, C_eq2) obj.C_equality(key) = {C_eq1, C_eq2}; end function add_ineq_constraint(obj, key, C_ineq1, C_ineq2) obj.C_inequality(key) = {C_ineq1, C_ineq2}; end function remove_eq_constraint(obj, key) remove(obj.C_equality, key); end function remove_ineq_constraint(obj, key) remove(obj.C_inequality, key); end function [C_eq1, C_eq2] = combine_all_eq_constraints(obj) C_eq_array = values(obj.C_equality); C_eq1 = []; C_eq2 = []; for i = 1:numel(C_eq_array) C_eq1 = [C_eq1; C_eq_array{i}{1}]; C_eq2 = [C_eq2; C_eq_array{i}{2}]; end end function [C_ineq1, C_ineq2] = combine_all_ineq_constraints(obj) C_ineq_array = values(obj.C_inequality); C_ineq1 = []; C_ineq2 = []; for i = 1:numel(C_ineq_array) C_ineq1 = [C_ineq1; C_ineq_array{i}{1}]; C_ineq2 = [C_ineq2; C_ineq_array{i}{2}]; end end end end ================================================ FILE: src/DisturbanceLinearSystem.m ================================================ classdef DisturbanceLinearSystem < LinearSystem properties (SetAccess = private) W % convex set of distrubance Z % disturbance invariant set end methods (Access = public) function obj = DisturbanceLinearSystem(A, B, Q, R, W) obj = obj@LinearSystem(A, B, Q, R); obj.W = W; obj.Z = obj.compute_mrpi_set(1e-4); end function x_new = propagate(obj, x, u) w = obj.pick_random_disturbance(); x_new = propagate@LinearSystem(obj, x, u) + w; end end methods (Access = public) function w = pick_random_disturbance(obj) % pick disturbance form uniform distribution verts = obj.W.V; b_max = max(verts)'; b_min = min(verts)'; % generate random until it will be inside of W while true w = rand(obj.nx, 1) .* (b_max - b_min) + b_min; if obj.W.contains(w) break end end end function Fs = compute_mrpi_set(obj, epsilon) % Computes an invariant approximation of the minimal robust positively % invariant set for % x^{+} = Ax + w with w \in W % according to Algorithm 1 in 'Invariant approximations of % the minimal robust positively invariant set' by Rakovic et al. % Requires a matrix A, a Polytope W, and a tolerance 'epsilon'. [nx,~] = size(obj.Ak); s = 0; alpha = 1000; Ms = 1000; E = eye(nx); it = 0; while(alpha > epsilon/(epsilon + Ms)) s = s+1; alpha = max(obj.W.support(obj.Ak^s*(obj.W.A)')./obj.W.b); mss = zeros(2*nx,1); for i = 1:s mss = mss+obj.W.support([obj.Ak^i, -obj.Ak^i]); end Ms = max(mss); it = it+1; end Fs = obj.W; for i =1:s-1 Fs = Fs+obj.Ak^i*obj.W; end Fs = (1/(1-alpha))*Fs; end end end ================================================ FILE: src/Graphics.m ================================================ classdef Graphics F, G matrix form % nc; number of contraint forced by Xc and Uc % F; G; % constraints for state and input: Fx+Gu<=1, where 1 is a voctor poly2ineq = @(poly) poly.A./repmat(poly.b, 1, size(poly.A, 2)); F_tmp = poly2ineq(X); G_tmp = poly2ineq(U); if numel(F_tmp)==0 F_tmp = zeros(0, X.Dim); end if numel(G_tmp)==0 G_tmp = zeros(0, U.Dim); end F = [F_tmp; zeros(size(G_tmp, 1), X.Dim)]; G = [zeros(size(F_tmp, 1), U.Dim); G_tmp]; nc = size(F, 1); end ================================================ FILE: src/utils/number2string.m ================================================ function str = number2string(n) if n < 10 str = strcat('0', string(n)); else str = string(n); end end