├── LICENSE ├── Misc ├── Ex3.gif └── stree.png ├── README.md ├── ThirdParty └── Robotics Toolbox for MATLAB │ └── plot_vehicle.m ├── data ├── car_figures │ ├── car_human.png │ ├── car_robot_b.png │ ├── car_robot_g.png │ ├── car_robot_o.png │ ├── car_robot_r.png │ ├── car_robot_y.png │ ├── human.png │ ├── human_2.png │ ├── human_red.png │ └── human_red_2.png └── params.mat ├── main.m └── util ├── getCEMPCcontrol.m ├── getCEMPChumancontrol.m ├── getSMPCcontrol.m ├── getiLQresults.m ├── iLQGames ├── costs │ ├── Cost.m │ ├── PlayerCost.m │ ├── ProductStateProximityCost.m │ ├── QuadraticCost.m │ ├── ReferenceDeviationCost.m │ └── SemiquadraticCost.m ├── dynamics │ ├── Bicycle4D.m │ ├── DubinsCar4D.m │ └── ProductMultiPlayerDynamicalSystem.m ├── geometry │ ├── LineSegment.m │ ├── Point.m │ └── Polyline.m ├── getiLQcontrol.m └── iLQSolver.m ├── node.m ├── plotSim.m ├── solveiLQgames.m ├── tree.m ├── updateEmpiricalCost.m ├── updateM.m └── updateTheta.m /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Safe Robotics Lab 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Misc/Ex3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/Misc/Ex3.gif -------------------------------------------------------------------------------- /Misc/stree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/Misc/stree.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Active Uncertainty Reduction for Human-Robot Interaction: An Implicit Dual Control Approach 2 | 3 | 4 | [![License][license-shield]][license-url] 5 | [![Homepage][homepage-shield]][homepage-url] 6 | 7 | 8 | 9 |
10 |

11 | 12 | Logo 13 | 14 | 15 |

Active Uncertainty Reduction for HRI

16 | 17 |

18 | Active Uncertainty Reduction for Human-Robot Interaction: An Implicit Dual Control Approach 19 | 30 |

31 |

32 | 33 | 34 | 35 | 36 |
37 |

Table of Contents

38 |
    39 |
  1. About The Project
  2. 40 |
  3. Dependencies
  4. 41 |
  5. Example
  6. 42 |
  7. License
  8. 43 |
  9. Contact
  10. 44 |
  11. Paper
  12. 45 |
46 |
47 | 48 | 49 | 50 | 51 | ## About The Project 52 | 53 | We provide an MATLAB implementation of implicit dual control-based active uncertainty learning for autonomous driving applications, which can be found [here](https://github.com/SafeRoboticsLab/Dual_Control_HRI/tree/main). 54 | 55 | Click to watch our spotlight video: 56 | [![Watch the video](https://haiminhu.files.wordpress.com/2022/02/dual_control_hri_video_cover.png)](https://haiminhu.files.wordpress.com/2022/02/dual_control_hri.mp4) 57 | 58 | 59 | ## Dependencies 60 | 61 | #### Trajectory Optimization 62 | * [`MPT3`](https://www.mpt3.org/) (Toolbox for MPC and parametric optimization.) 63 | * [`SNOPT`](https://ccom.ucsd.edu/~optimizers/solvers/snopt/) (Nonlinear programming solver. Academic licenses are available.) 64 | 65 | #### Visualization 66 | * [`Robotics Toolbox for MATLAB`](https://petercorke.com/toolboxes/robotics-toolbox/) (Tools for plotting the vehicles. You need to first install it in MATLAB and then replace `plot_vehicle.m` in the root directory with [ours](https://github.com/SafeRoboticsLab/Dual_Control_HRI/blob/main/ThirdParty/Robotics%20Toolbox%20for%20MATLAB/plot_vehicle.m).) 67 | 68 | ## Example 69 | In this repository, we provide an example of our method applied for human-robot interactive driving scenarios. 70 | 71 | 1. Clone the repo 72 | ```sh 73 | git clone https://github.com/SafeRoboticsLab/Dual_Control_HRI.git 74 | ``` 75 | 2. In MATLAB, run the [`main.m`](https://github.com/SafeRoboticsLab/Dual_Control_HRI/blob/main/main.m) script to reproduce our results. 76 | 77 | 84 | 85 | 86 | 91 | 92 | 93 | 104 | 105 | 106 | 107 | ## License 108 | 109 | Distributed under the BSD 3-Clause License. See `LICENSE` for more information. 110 | 111 | 112 | 113 | 114 | ## Contact 115 | 116 | Haimin Hu - [@HaiminHu](https://twitter.com/HaiminHu) - haiminh@princeton.edu 117 | 118 | Project Link: [https://github.com/SafeRoboticsLab/Dual_Control_HRI](https://github.com/SafeRoboticsLab/Dual_Control_HRI) 119 | 120 | Homepage Link: [https://haiminhu.org/dual_control_hri](https://haiminhu.org/dual_control_hri) 121 | 122 | 123 | 124 | ## Papers 125 | ### Journal paper: 126 | ```tex 127 | @article{hu2023aur, 128 | title={Active uncertainty reduction for safe and efficient interaction planning: A shielding-aware dual control approach}, 129 | author={Hu, Haimin and Isele, David and Bae, Sangjae and Fisac, Jaime F}, 130 | journal={The International Journal of Robotics Research}, 131 | pages={02783649231215371}, 132 | year={2023}, 133 | publisher={SAGE Publications Sage UK: London, England} 134 | } 135 | ``` 136 | 137 | 138 | 139 | ### Conference paper/book chapter: 140 | ```tex 141 | @inproceedings{hu2023active, 142 | title={Active Uncertainty Reduction for Human-Robot Interaction: An Implicit Dual Control Approach}, 143 | author={Hu, Haimin and Fisac, Jaime F}, 144 | booktitle={Algorithmic Foundations of Robotics XV}, 145 | pages={385--401}, 146 | year={2023}, 147 | publisher={Springer International Publishing} 148 | } 149 | ``` 150 | 151 | ### Our prior work: 152 | 153 | ```tex 154 | @article{hu2022sharp, 155 | author={Hu, Haimin and Nakamura, Kensuke and Fisac, Jaime F.}, 156 | journal={IEEE Robotics and Automation Letters}, 157 | title={SHARP: Shielding-Aware Robust Planning for Safe and Efficient Human-Robot Interaction}, 158 | year={2022}, 159 | volume={7}, 160 | number={2}, 161 | pages={5591-5598}, 162 | doi={10.1109/LRA.2022.3155229} 163 | } 164 | ``` 165 | 166 | 167 | 168 | [contributors-shield]: https://img.shields.io/github/contributors/SafeRoboticsLab/repo.svg?style=for-the-badge 169 | [contributors-url]: https://github.com/SafeRoboticsLab/Dual_Control_HRI/contributors 170 | [forks-shield]: https://img.shields.io/github/forks/SafeRoboticsLab/repo.svg?style=for-the-badge 171 | [forks-url]: https://github.com/SafeRoboticsLab/Dual_Control_HRI/network/members 172 | [stars-shield]: https://img.shields.io/github/stars/SafeRoboticsLab/repo.svg?style=for-the-badge 173 | [stars-url]: https://github.com/SafeRoboticsLab/Dual_Control_HRI/stargazers 174 | [issues-shield]: https://img.shields.io/github/issues/SafeRoboticsLab/repo.svg?style=for-the-badge 175 | [issues-url]: https://github.com/SafeRoboticsLab/Dual_Control_HRI/issues 176 | [license-shield]: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg 177 | [license-url]: https://opensource.org/licenses/BSD-3-Clause 178 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 179 | [linkedin-url]: https://linkedin.com/in/SafeRoboticsLab 180 | [homepage-shield]: https://img.shields.io/badge/-Homepage-brightgreen 181 | [homepage-url]: https://haiminhu.org/dual_control_hri 182 | -------------------------------------------------------------------------------- /ThirdParty/Robotics Toolbox for MATLAB/plot_vehicle.m: -------------------------------------------------------------------------------- 1 | %plot_vehicle Draw mobile robot pose 2 | % 3 | % PLOT_VEHICLE(X,OPTIONS) draws an oriented triangle to represent the pose 4 | % of a mobile robot moving in a planar world. The pose X (1x3) = [x,y,theta]. 5 | % If X is a matrix (Nx3) then an animation of the robot motion is shown and 6 | % animated at the specified frame rate. 7 | % 8 | % Image mode:: 9 | % 10 | % Create a structure with the following elements and pass it with the 11 | % 'model' option. 12 | % 13 | % image an RGB image (HxWx3) 14 | % alpha an alpha plane (HxW) with pixels 0 if transparent 15 | % rotation image rotation in degrees required for front to pointing to the right 16 | % centre image coordinate (U,V) of the centre of the back axle 17 | % length length of the car in real-world units 18 | % 19 | % Animation mode:: 20 | % 21 | % H = PLOT_VEHICLE(X,OPTIONS) as above draws the robot and returns a handle. 22 | % 23 | % PLOT_VEHICLE(X, 'handle', H) updates the pose X (1x3) of the previously drawn 24 | % robot. 25 | % 26 | % Options:: 27 | % 'scale',S draw vehicle with length S x maximum axis dimension (default 28 | % 1/60) 29 | % 'size',S draw vehicle with length S 30 | % 'fillcolor',F the color of the circle's interior, MATLAB color spec 31 | % 'alpha',A transparency of the filled circle: 0=transparent, 1=solid 32 | % 'box' draw a box shape (default is triangle) 33 | % 'fps',F animate at F frames per second (default 10) 34 | % 'image',I use an image to represent the robot pose 35 | % 'retain' when X (Nx3) then retain the robots, leaving a trail 36 | % 'model',M animate an image of the vehicle. M is a structure with 37 | % elements: image, alpha, rotation (deg), centre (pix), length (m). 38 | % 'axis',h handle of axis or UIAxis to draw into (default is current axis) 39 | % 'movie',M create a movie file in file M 40 | % 41 | % Example:: 42 | % [car.image,~,car.alpha] = imread('car2.png'); % image and alpha layer 43 | % car.rotation = 180; % image rotation to align front with world x-axis 44 | % car.centre = [648; 173]; % image coordinates of centre of the back wheels 45 | % car.length = 4.2; % real world length for scaling (guess) 46 | % h = plot_vehicle(x, 'model', car) % draw car at configuration x 47 | % plot_vehicle(x, 'handle', h) % animate car to configuration x 48 | % 49 | % Notes:: 50 | % - The vehicle is drawn relative to the size of the axes, so set them 51 | % first using axis(). 52 | % - For backward compatibility, 'fill', is a synonym for 'fillcolor' 53 | % - For the 'model' option, you provide a monochrome or color image of the 54 | % vehicle. Optionally you can provide an alpha mask (0=transparent). 55 | % Specify the reference point on the vehicle as the (x,y) pixel 56 | % coordinate within the image. Specify the rotation, in degrees, so that 57 | % the car's front points to the right. Finally specify a length of the 58 | % car, the image is scaled to be that length in the plot. 59 | % - Set 'fps' to Inf to have zero pause 60 | % 61 | % See also Vehicle.plot, Animate, plot_poly, demos/car_animation 62 | 63 | 64 | % Copyright (C) 1993-2017, by Peter I. Corke 65 | % 66 | % This file is part of The Robotics Toolbox for MATLAB (RTB). 67 | % 68 | % RTB is free software: you can redistribute it and/or modify 69 | % it under the terms of the GNU Lesser General Public License as published by 70 | % the Free Software Foundation, either version 3 of the License, or 71 | % (at your option) any later version. 72 | % 73 | % RTB is distributed in the hope that it will be useful, 74 | % but WITHOUT ANY WARRANTY; without even the implied warranty of 75 | % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 76 | % GNU Lesser General Public License for more details. 77 | % 78 | % You should have received a copy of the GNU Leser General Public License 79 | % along with RTB. If not, see . 80 | % 81 | % http://www.petercorke.com 82 | 83 | % TODO needs to work for 3D point 84 | 85 | function [h_, hDraw_] = plot_vehicle(x, varargin) 86 | 87 | opt.scale = 1/60; 88 | opt.size = []; 89 | opt.shape = {'triangle', 'box'}; 90 | opt.fps = 20; 91 | opt.handle = []; 92 | opt.image = []; 93 | opt.model = []; 94 | opt.retain = false; 95 | opt.axis = []; 96 | opt.trail = ''; 97 | opt.movie = []; 98 | 99 | if numel(x) == 3 100 | x = x(:)'; % enforce row vector 101 | end 102 | 103 | [opt,args] = tb_optparse(opt, varargin); 104 | 105 | hDraw = []; 106 | 107 | if isempty(opt.handle) 108 | % create a new robot 109 | % compute some default dimensions based on axis scaling 110 | if isempty(opt.axis) 111 | opt.axis = gca; 112 | end 113 | 114 | ax = opt.axis; 115 | a = [ax.XLim ax.YLim]; 116 | d = (a(2)+a(4) - a(1)-a(3)) * opt.scale; 117 | 118 | % create the robot 119 | [h.vehicle, hDraw] = draw_robot(d, opt, args); 120 | 121 | if ~isempty(opt.trail) 122 | hold on 123 | h.trail = plot(0,0, opt.trail); 124 | hold off 125 | end 126 | h.opt = opt; 127 | 128 | plot_vehicle(x, 'handle', h, varargin{:}); 129 | else 130 | % we are animating 131 | h = opt.handle; 132 | anim = Animate(opt.movie); 133 | 134 | for i=1:numrows(x) 135 | h.vehicle.Matrix = SE2(x(i,:)).SE3.T; % convert (x,y,th) to SE(3) 136 | 137 | % check if retain is on 138 | if h.opt.retain 139 | plot_vehicle(x(i,:)); 140 | end 141 | 142 | % extend the line if required 143 | if ~isempty(h.opt.trail) 144 | 145 | l = h.trail; 146 | l.XData = [l.XData x(i,1)]; 147 | l.YData = [l.YData x(i,2)]; 148 | end 149 | 150 | % pause a while 151 | anim.add(); 152 | if ~isinf(h.opt.fps) 153 | pause(1/h.opt.fps) 154 | end 155 | end 156 | anim.close(); 157 | end 158 | 159 | if nargout > 0 160 | h_ = h; 161 | hDraw_ = hDraw; 162 | end 163 | 164 | end 165 | 166 | function [h, hDraw] = draw_robot(d, opt, args) 167 | 168 | if ~isempty(opt.model) 169 | % display an image of a vehicle, pass in a struct 170 | if isstruct(opt.model) 171 | img = opt.model.image; 172 | rotation = opt.model.rotation; 173 | centre = opt.model.centre; 174 | scale = opt.model.length / max(numcols(img), numrows(img)) ; 175 | else 176 | img = opt.image; 177 | centre = [numcols(img)/2 numrows(img)/2]; 178 | end 179 | h = hgtransform('Tag', 'image'); 180 | h2 = hgtransform('Matrix', trotz(rotation, 'deg')*trscale(scale)*transl(-centre(1), -centre(2),0), 'Parent', h ); 181 | if isfield(opt.model, 'alpha') 182 | % use alpha data if provided 183 | alpha = opt.model.alpha; 184 | else 185 | % otherwise black pixels (0,0,0) are set to transparent 186 | alpha = any(img>0,3); 187 | end 188 | hDraw = image(img, 'AlphaData', alpha, 'Parent', h2); 189 | %axis equal 190 | else 191 | % display a simple polygon 192 | switch opt.shape 193 | case 'triangle' 194 | if ~isempty(opt.size) 195 | d = opt.size; 196 | end 197 | L = d; W = 0.6*d; 198 | corners = [ 199 | L 0 200 | -L -W 201 | -L W]'; 202 | case 'box' 203 | if ~isempty(opt.size) 204 | switch length(opt.size) 205 | case 1 206 | W = opt.size/2; L1 = opt.size/2; L2 = opt.size/2; 207 | case 2 208 | W = opt.size(1)/2; L1 = opt.size(2)/2; L2 = opt.size(2)/2; 209 | case 3 210 | W = opt.size(1)/2; L1 = opt.size(2); L2 = opt.size(3); 211 | end 212 | else 213 | L1 = d; L2 = d; W = 0.6*d; 214 | end 215 | corners = [ 216 | -L1 W 217 | 0.6*L2 W 218 | L2 0.5*W 219 | L2 -0.5*W 220 | 0.6*L2 -W 221 | -L1 -W ]'; 222 | end 223 | h = plot_poly(corners, 'animate', 'axis', opt.axis, args{:}); 224 | end 225 | end 226 | -------------------------------------------------------------------------------- /data/car_figures/car_human.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/car_human.png -------------------------------------------------------------------------------- /data/car_figures/car_robot_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/car_robot_b.png -------------------------------------------------------------------------------- /data/car_figures/car_robot_g.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/car_robot_g.png -------------------------------------------------------------------------------- /data/car_figures/car_robot_o.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/car_robot_o.png -------------------------------------------------------------------------------- /data/car_figures/car_robot_r.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/car_robot_r.png -------------------------------------------------------------------------------- /data/car_figures/car_robot_y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/car_robot_y.png -------------------------------------------------------------------------------- /data/car_figures/human.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/human.png -------------------------------------------------------------------------------- /data/car_figures/human_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/human_2.png -------------------------------------------------------------------------------- /data/car_figures/human_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/human_red.png -------------------------------------------------------------------------------- /data/car_figures/human_red_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/car_figures/human_red_2.png -------------------------------------------------------------------------------- /data/params.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/Dual_Control_HRI/b58243ee028ec428dc351c250638e61b9dfbea4c/data/params.mat -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | close all; clear all; clc 2 | addpath(genpath('./util')) 3 | addpath('./data') 4 | setenv('SNOPT_LICENSE','/Users/haiminhu/licenses/snopt7.lic'); %**REPLACE** 5 | % 6 | % Main simulation script of Implicit Dual Control for HRI 7 | % 8 | % Author: Haimin Hu (last modified 2022.2.13) 9 | 10 | 11 | %% Static paramters 12 | load ./data/params 13 | num_players = 2; % Number of players 14 | ts = 0.2; % Sampling time 15 | vDes_H = 12; % Human's desired cruising speed 16 | vDes_R = vDes_H + 3; % Robot's desired cruising speed 17 | lc = params.rd_bd_max/2; % Lane center position (lateral) 18 | 19 | % Horizons. 20 | N_ilq = 15; % iLQGame horizon 21 | N_sim = 80; % Maximum simulation horizon 22 | Nd = 2; % Number of dual control steps. 23 | Ne = 2; % Number of exploitation steps. 24 | 25 | % Inital states. 26 | xH_init = [15; lc; 0.0; 10]; 27 | xR_init = [0.0; -lc; 0.0; 10]; 28 | x_init = [xH_init; xR_init]; 29 | 30 | % Target states. 31 | % -> Human. 32 | xF_H_l = [0; 1.2; 0; vDes_H; 0; 0; 0; 0]; % Left lane 33 | xF_H_r = [0; -0.8; 0; vDes_H; 0; 0; 0; 0]; % Right lane 34 | uF_H = zeros(2,1); 35 | 36 | % -> Robot. 37 | xF_R = [0; 0; 0; 0; params.xr_tar_overtake; lc; 0; vDes_R]; 38 | uF_R = zeros(2,1); 39 | 40 | % Control limits. 41 | uH_ub = [2; 0.05]; % Human 42 | uH_lb = -uH_ub; 43 | uR_ub = [4; 0.05]; % Robot 44 | uR_lb = -uR_ub; 45 | 46 | uH_constraints.max = uH_ub; 47 | uH_constraints.min = uH_lb; 48 | uR_constraints.max = uR_ub; 49 | uR_constraints.min = uR_lb; 50 | 51 | u_constraints_cell = {uH_constraints; uR_constraints}; 52 | 53 | % Cost matrices. 54 | % -> Human. 55 | QH = blkdiag(0,5,1,1,0,0,0,0); 56 | RH = blkdiag(0.1,1); 57 | 58 | % -> Robot. 59 | QR_OT = 1*[ 1,0,0,0,-1,0,0,0; zeros(1,8); zeros(1,8); zeros(1,8); 60 | -1,0,0,0, 1,0,0,0; zeros(1,8); zeros(1,8); zeros(1,8)]; 61 | QR_x = blkdiag(0,0,0,0,0,2,1,1); 62 | QR = QR_OT + QR_x; 63 | RR = blkdiag(0.1,1); 64 | 65 | % Covariance of external disturbance d. 66 | Sigma_d = blkdiag(0.1,0.1,0.1,0.1, 0.1,0.1,0.1,0.1); 67 | 68 | % Verbose settings. 69 | verbose_iLQ = false; 70 | verbose_CEMPC = true; 71 | verbose_SMPC = true; 72 | 73 | 74 | %% iLQGame Setup - Focused Human 75 | % Set up iLQSolvers. 76 | alpha_scaling = 0.01; 77 | max_iteration = 100; 78 | tolerence_percentage = 1e-3; 79 | 80 | % Dynamical systems. 81 | Car_H = Bicycle4D(2.7, ts); % Human (Player 1) 82 | H_id = 1; 83 | Car_R = Bicycle4D(2.7, ts); % Robot (Player 2) 84 | R_id = 2; 85 | xH_dim = Car_H.x_dim; 86 | xR_dim = Car_R.x_dim; 87 | x_dim = xR_dim + xH_dim; 88 | xH_dims = 1:4; 89 | xR_dims = 5:8; 90 | x_dims = {xH_dims, xR_dims}; 91 | 92 | uH_dim = Car_H.u_dim; 93 | uR_dim = Car_R.u_dim; 94 | u_dim = uR_dim + uH_dim; 95 | uH_dims = 1:2; 96 | uR_dims = 3:4; 97 | u_dims = {uH_dims, uR_dims}; 98 | 99 | jointSys = ProductMultiPlayerDynamicalSystem(ts, x_dim, x_dims,... 100 | u_dim, u_dims, {Car_H, Car_R}); 101 | 102 | % Initialize Ps and alphas. 103 | Ps_cell = cell(num_players, N_ilq); 104 | alphas_cell = cell(num_players, N_ilq); 105 | for i = 1:num_players 106 | for k = 1:N_ilq 107 | Ps_cell{i,k} = zeros(jointSys.subsystems{i}.u_dim,x_dim); 108 | alphas_cell{i,k} = zeros(jointSys.subsystems{i}.u_dim,1); 109 | end 110 | end 111 | 112 | % -------------------- Set up Human's (Player 1) costs -------------------- 113 | % -> Tracking and control cost. 114 | RefDevCost_H_l = ReferenceDeviationCost(xF_H_l, uF_H, QH, RH,... 115 | "RefDevCost_H_l", x_dim, uH_dim); % left lane 116 | RefDevCost_H_r = ReferenceDeviationCost(xF_H_r, uF_H, QH, RH,... 117 | "RefDevCost_H_r", x_dim, uH_dim); % right lane 118 | 119 | % -> Proximity cost. 120 | px_indices = [1,5]; 121 | py_indices = [2,6]; 122 | max_distance_H = 5; 123 | ProxCost_H = ProductStateProximityCost(px_indices, py_indices,... 124 | max_distance_H, "ProxCost_H", x_dim, uH_dim); 125 | 126 | % -> Lane boundary cost. 127 | pyCost_upper_H = SemiquadraticCost(2, 0.7*params.rd_bd_max, true,... 128 | 'state', "pyCost_upper_H", x_dim, uH_dim); 129 | pyCost_lower_H = SemiquadraticCost(2, 0.7*params.rd_bd_min, false,... 130 | 'state', "pyCost_lower_H", x_dim, uH_dim); 131 | 132 | % -> Velocity constraint cost. 133 | vCost_upper_H = SemiquadraticCost(4, vDes_H*1.1, true, 'state',... 134 | "vCost_upper_H", x_dim, uH_dim); 135 | vCost_lower_H = SemiquadraticCost(4, vDes_R/1.5, false, 'state',... 136 | "vCost_lower_H", x_dim, uH_dim); 137 | 138 | % -------------------- Set up Robot's (Player 2) costs -------------------- 139 | % -> Tracking and control cost. 140 | RefDevCost_R = ReferenceDeviationCost(xF_R, uF_R, QR, RR,... 141 | "RefDevCostF_R", x_dim, uR_dim); 142 | 143 | % -> Proximity cost. 144 | max_distance_R = 7; 145 | ProxCost_R = ProductStateProximityCost(px_indices, py_indices,... 146 | max_distance_R, "ProxCost_R", x_dim, uR_dim); 147 | 148 | % -> Lane boundary cost. 149 | pyCost_upper_R = SemiquadraticCost(6, 0.7*params.rd_bd_max, true,... 150 | 'state', "pyCost_upper_R", x_dim, uR_dim); 151 | pyCost_lower_R = SemiquadraticCost(6, 0.7*params.rd_bd_min, false,... 152 | 'state', "pyCost_lower_R", x_dim, uR_dim); 153 | 154 | % -> Velocity constraint cost. 155 | vCost_upper_R = SemiquadraticCost(8, vDes_R*1.2, true, 'state',... 156 | "vCost_upper_R", x_dim, uR_dim); 157 | vCost_lower_R = SemiquadraticCost(8, vDes_R/1.5, false, 'state',... 158 | "vCost_lower_R", x_dim, uR_dim); 159 | 160 | % ----------- Set up (time-varying) total costs for each player ----------- 161 | % Build up total costs for H. 162 | Car_H_cost = PlayerCost(); 163 | Car_H_cost = Car_H_cost.add_cost(ProxCost_H, 'x', 1e4); 164 | Car_H_cost = Car_H_cost.add_cost(vCost_lower_H, 'x', 4e2); 165 | Car_H_cost = Car_H_cost.add_cost(pyCost_upper_H, 'x', 5e4); 166 | Car_H_cost = Car_H_cost.add_cost(pyCost_lower_H, 'x', 5e4); 167 | Car_H_cost_l = Car_H_cost.add_cost(RefDevCost_H_l, 'xu', 1); 168 | Car_H_cost_r = Car_H_cost.add_cost(RefDevCost_H_r, 'xu', 1); 169 | 170 | % Build up total costs for R. 171 | Car_R_cost = PlayerCost(); 172 | Car_R_cost = Car_R_cost.add_cost(ProxCost_R, 'x', 1e4); 173 | % Car_R_cost = Car_R_cost.add_cost(vCost_upper_R, 'x', 4e2); 174 | % Car_R_cost = Car_R_cost.add_cost(vCost_lower_R, 'x', 4e2); 175 | Car_R_cost = Car_R_cost.add_cost(pyCost_upper_R, 'x', 5e4); 176 | Car_R_cost = Car_R_cost.add_cost(pyCost_lower_R, 'x', 5e4); 177 | Car_R_cost = Car_R_cost.add_cost(RefDevCost_R, 'xu', 1); 178 | 179 | % Set up cost cells. 180 | player_costs_cell_l = cell(num_players, N_ilq); 181 | for k = 1:N_ilq 182 | player_costs_cell_l{H_id, k} = Car_H_cost_l; 183 | player_costs_cell_l{R_id, k} = Car_R_cost; 184 | end 185 | 186 | player_costs_cell_r = cell(num_players, N_ilq); 187 | for k = 1:N_ilq 188 | player_costs_cell_r{H_id, k} = Car_H_cost_r; 189 | player_costs_cell_r{R_id, k} = Car_R_cost; 190 | end 191 | 192 | 193 | %% iLQGame Setup - Distracted Human 194 | jointSys_DH = ProductMultiPlayerDynamicalSystem(ts, xH_dim, x_dims,... 195 | uH_dim, u_dims, {Car_H}); 196 | 197 | % Initialize Ps and alphas. 198 | Ps_cell_EH = cell(1, N_ilq); 199 | alphas_cell_EH = cell(1, N_ilq); 200 | for k = 1:N_ilq 201 | Ps_cell_EH{1,k} = zeros(jointSys.subsystems{1}.u_dim, xH_dim); 202 | alphas_cell_EH{1,k} = zeros(jointSys.subsystems{1}.u_dim, 1); 203 | end 204 | 205 | % Tracking and control cost. 206 | RefDevCost_EH_l = ReferenceDeviationCost(xF_H_l(xH_dims), uF_H,... 207 | QH(xH_dims,xH_dims), RH, "RefDevCost_EH_l", xH_dim, uH_dim); 208 | RefDevCost_EH_r = ReferenceDeviationCost(xF_H_r(xH_dims), uF_H,... 209 | QH(xH_dims,xH_dims), RH, "RefDevCost_EH_r", xH_dim, uH_dim); 210 | 211 | % Lane boundary cost. 212 | pyCost_upper_EH = SemiquadraticCost(2, 0.7*params.rd_bd_max, true,... 213 | 'state', "pyCost_upper_EH", xH_dim, uH_dim); 214 | pyCost_lower_EH = SemiquadraticCost(2, 0.7*params.rd_bd_min, false,... 215 | 'state', "pyCost_lower_EH", xH_dim, uH_dim); 216 | 217 | % Build up total costs. 218 | Car_H_cost_EH = PlayerCost(); 219 | Car_H_cost_EH = Car_H_cost_EH.add_cost(pyCost_upper_EH, 'x', 5e3); 220 | Car_H_cost_EH = Car_H_cost_EH.add_cost(pyCost_lower_EH, 'x', 5e3); 221 | Car_H_cost_EH_l = Car_H_cost_EH.add_cost(RefDevCost_EH_l, 'xu', 1); 222 | Car_H_cost_EH_r = Car_H_cost_EH.add_cost(RefDevCost_EH_r, 'xu', 1); 223 | 224 | % Set up cost cells. 225 | player_costs_cell_EH_l = cell(1, N_ilq); 226 | for k = 1:N_ilq 227 | player_costs_cell_EH_l{1,k} = Car_H_cost_EH_l; 228 | end 229 | 230 | player_costs_cell_EH_r = cell(1, N_ilq); 231 | for k = 1:N_ilq 232 | player_costs_cell_EH_r{1,k} = Car_H_cost_EH_r; 233 | end 234 | 235 | 236 | %% Main Planning loop 237 | 238 | % ===================== Choose your planner ===================== 239 | method = 'IDSMPC'; % 'IDSMPC', 'EDSMPC', 'NDSMPC', 'CEMPC', 'iLQ' 240 | % =============================================================== 241 | 242 | % Ground-truth human's hidden state. 243 | theta = 1.0; % [0,1] 244 | M = 'r'; % {'l', 'r'} 245 | 246 | % Prior distribution of theta. 247 | mu_theta_init = [0.5; 0.5]; 248 | Sigma_theta_init = 5*eye(2); 249 | theta_distr.l.mu = mu_theta_init; 250 | theta_distr.l.covar = Sigma_theta_init; 251 | theta_distr.r.mu = mu_theta_init; 252 | theta_distr.r.covar = Sigma_theta_init; 253 | 254 | % Prior distribution of M. 255 | M_distr = [0.5; 0.5]; 256 | 257 | % LQR terminal cost design. 258 | [AF_R, BF_R] = Car_R.linearizeDiscrete(xF_R(xR_dims), [0;0]); 259 | [~, PR] = dlqr(AF_R, BF_R, QR(xR_dims,xR_dims), RR); 260 | 261 | % Create the planner object. 262 | planner.ts = ts; 263 | planner.vDes_H = vDes_H; 264 | planner.vDes_R = vDes_R; 265 | planner.lc = lc; 266 | planner.params = params; 267 | planner.RH = RH; 268 | planner.alpha_scaling = alpha_scaling; 269 | planner.H_id = H_id; 270 | planner.R_id = R_id; 271 | planner.xH_dim = xH_dim; 272 | planner.xR_dim = xR_dim; 273 | planner.xH_dims = xH_dims; 274 | planner.xR_dims = xR_dims; 275 | planner.uH_dim = uH_dim; 276 | planner.uR_dim = uR_dim; 277 | planner.uH_dims = uH_dims; 278 | planner.uR_dims = uR_dims; 279 | planner.jointSys = jointSys; 280 | planner.M_distr_const = [0.5; 0.5]; 281 | planner.M_eps = 0.01; 282 | 283 | % MPC settings. 284 | extraArg_CEMPC.w_uH = 1e8; % human's control consistency weight 285 | extraArg_CEMPC.w_RB_l = 1e6; % road boundary (linear penalty) 286 | extraArg_CEMPC.w_RB_q = 5e6; % road boundary (quadratic penalty) 287 | extraArg_CEMPC.w_CA_l = 1e7; % collision avoidance (linear penalty) 288 | extraArg_CEMPC.w_CA_q = 5e7; % collision avoidance (quadratic penalty) 289 | 290 | extraArg_SMPC.M_branch = 1; % If M branch 291 | extraArg_SMPC.N_M = min(1, Nd); % M branching horizon 292 | extraArg_SMPC.M_dual = and(1, extraArg_SMPC.M_branch); % If M dual control 293 | extraArg_SMPC.theta_sample_sn = 0.3; % Sampling parameter of theta 294 | 295 | extraArg_SMPC.scale_ellips = 1.2; % Collision avoidance ellipsoid scaling 296 | extraArg_SMPC.w_uH = extraArg_CEMPC.w_uH; 297 | extraArg_SMPC.w_RB_l = extraArg_CEMPC.w_RB_l; 298 | extraArg_SMPC.w_RB_q = extraArg_CEMPC.w_RB_q; 299 | extraArg_SMPC.w_CA_l = extraArg_CEMPC.w_CA_l; 300 | extraArg_SMPC.w_CA_q = extraArg_CEMPC.w_CA_q; 301 | 302 | % Initialize simulation. 303 | x = x_init; 304 | X = x; 305 | U = []; 306 | J_em = 0; % Empirical cost of the robot 307 | THETA = theta_distr; % theta trajectory 308 | M_traj = M_distr; % M trajectory 309 | 310 | % Main loop. 311 | for t = 1:N_sim 312 | 313 | % Change human's hidden states 314 | if t >= 10 315 | theta = 0.0; % [0,1] 316 | M = 'l'; % 'l', 'r' 317 | end 318 | 319 | % Check goal reaching. 320 | if x(5) - x(1) >= params.xr_tar_overtake 321 | break 322 | end 323 | 324 | % Report simulation time. 325 | disp(['Simulation time: t = ', num2str(t)]) 326 | 327 | % ========================== Solve iLQGames =========================== 328 | ilq_solvers = solveiLQgames(x, xH_dims, jointSys, jointSys_DH,... 329 | Ps_cell, Ps_cell_EH, alphas_cell, alphas_cell_EH,... 330 | player_costs_cell_l, player_costs_cell_r,... 331 | player_costs_cell_EH_l, player_costs_cell_EH_r,... 332 | u_constraints_cell, alpha_scaling, max_iteration,... 333 | tolerence_percentage, verbose_iLQ); 334 | 335 | % ============================ Robot Block ============================ 336 | if strcmp(method, 'iLQ') 337 | % ISA-iLQ. 338 | if M_distr(1) >= M_distr(2) 339 | u_iLQ = ilq_solvers.AH_l.best_operating_point.us(:,1); 340 | else 341 | u_iLQ = ilq_solvers.AH_r.best_operating_point.us(:,1); 342 | end 343 | uR = u_iLQ(uR_dims); 344 | else 345 | % CEMPC. 346 | [uR_ce, xR_ce] = getCEMPCcontrol(x, xF_R, Nd + Ne, QR, RR, PR,... 347 | uR_constraints, uH_constraints, ilq_solvers, theta_distr,... 348 | M_distr, planner, extraArg_CEMPC, verbose_CEMPC); 349 | 350 | if strcmp(method, 'CEMPC') 351 | uR = uR_ce(:,1); 352 | 353 | elseif strcmp(method, 'NDSMPC') || strcmp(method, 'IDSMPC') ||... 354 | strcmp(method, 'EDSMPC') 355 | 356 | % Warm-start NDSMPC. 357 | extraArg_SMPC.uR_ws = uR_ce; 358 | extraArg_SMPC.xR_ws = xR_ce; 359 | extraArg_SMPC.opt_mode = 'ws'; 360 | [uR_ws, ~, ~, stree_ws, flag_ws] = getSMPCcontrol(x, xF_R,... 361 | theta_distr, M_distr, Nd, Ne, QR, RR, PR, Sigma_d,... 362 | uR_constraints, uH_constraints, ilq_solvers, planner,... 363 | verbose_SMPC, extraArg_SMPC); 364 | 365 | if strcmp(method, 'IDSMPC') 366 | % Implicit Dual SMPC. 367 | if flag_ws 368 | % Warm-start SMPC failed. 369 | uR = uR_ce(:,1); 370 | else 371 | % Implicit Dual SMPC. 372 | extraArg_SMPC.opt_mode = 'std'; 373 | extraArg_SMPC.fixed_values = 1; 374 | extraArg_SMPC.stree = stree_ws; 375 | [uR, ~, ~, stree, ~] = getSMPCcontrol(x, xF_R,... 376 | theta_distr, M_distr, Nd, Ne, QR, RR, PR,... 377 | Sigma_d, uR_constraints, uH_constraints,... 378 | ilq_solvers, planner, verbose_SMPC, extraArg_SMPC); 379 | end 380 | 381 | elseif strcmp(method, 'EDSMPC') 382 | % Explicit Dual SMPC. 383 | if flag_ws 384 | % Warm-start SMPC failed. 385 | uR = uR_ce(:,1); 386 | else 387 | % Explicit Dual SMPC. 388 | extraArg_SMPC.opt_mode = 'std'; 389 | extraArg_SMPC.fixed_values = 1; 390 | extraArg_SMPC.stree = stree_ws; 391 | extraArg_SMPC.weight_IG = 1e10; %(1e12) 392 | [uR, ~, ~, stree, ~] = getSMPCcontrol(x, xF_R,... 393 | theta_distr, M_distr, Nd, Ne, QR, RR, PR,... 394 | Sigma_d, uR_constraints, uH_constraints,... 395 | ilq_solvers, planner, verbose_SMPC,... 396 | extraArg_SMPC); 397 | end 398 | 399 | elseif strcmp(method, 'NDSMPC') 400 | % NDSMPC. 401 | if flag_ws 402 | % Warm-start SMPC failed. 403 | uR = uR_ce(:,1); 404 | else 405 | uR = uR_ws; 406 | end 407 | end 408 | else 409 | error('Invalid planner option!') 410 | end 411 | end 412 | 413 | % ============================ Human Block ============================ 414 | % Human control basis functions. 415 | if M == 'l' 416 | u_FH = ilq_solvers.AH_l.best_operating_point.us(:,1); 417 | u_DH = ilq_solvers.EH_l.best_operating_point.us(:,1); 418 | elseif M == 'r' 419 | u_FH = ilq_solvers.AH_r.best_operating_point.us(:,1); 420 | u_DH = ilq_solvers.EH_r.best_operating_point.us(:,1); 421 | end 422 | 423 | % Rational human action. 424 | uH_rat = theta*u_FH(uH_dims) + (1-theta)*u_DH(uH_dims); 425 | 426 | % Randomize human action. 427 | eps_noise = [0.2; 0.005]; 428 | uH = uH_rat + eps_noise.*(1-2*rand(2,1)); 429 | 430 | % Project uH back to its bound. 431 | uH = max(min(uH,uH_ub),uH_lb); 432 | 433 | % ========================= Update Simulation ========================= 434 | u = [uH; uR]; 435 | x_next = jointSys.integrate(x, u, ts); 436 | 437 | % Check collision. 438 | xR = x_next(xR_dims); 439 | xH = x_next(xH_dims); 440 | coll_flag = is_collision(xR, xH, params); 441 | if coll_flag 442 | disp('Collision!') 443 | end 444 | 445 | % ======================== Bayesian Inference ========================= 446 | [A_now, B_cell_now] = jointSys.linearizeDiscrete_B_cell(x, u); 447 | f_x_now = A_now * x; 448 | iLQ_result = getiLQresults(x, B_cell_now, ilq_solvers, planner, 1); 449 | 450 | % theta inference. 451 | [mu_theta_l, Sigma_theta_l] = updateTheta('l', theta_distr.l.mu,... 452 | theta_distr.l.covar, Sigma_d, x_next, u, f_x_now, B_cell_now,... 453 | iLQ_result, planner, []); 454 | mu_theta_l = mu_theta_l ./ sum(mu_theta_l); 455 | theta_distr.l.mu = mu_theta_l; 456 | theta_distr.l.covar = Sigma_theta_l; 457 | 458 | [mu_theta_r, Sigma_theta_r] = updateTheta('r', theta_distr.r.mu,... 459 | theta_distr.r.covar, Sigma_d, x_next, u, f_x_now, B_cell_now,... 460 | iLQ_result, planner, []); 461 | mu_theta_r = mu_theta_r ./ sum(mu_theta_r); 462 | theta_distr.r.mu = mu_theta_r; 463 | theta_distr.r.covar = Sigma_theta_r; 464 | 465 | % M inference. 466 | extraArg_updateM.scale = 5e2; 467 | M_distr = updateM(M_distr, theta_distr, x_next, uR, f_x_now,... 468 | B_cell_now, iLQ_result, planner, extraArg_updateM); 469 | 470 | % Store results. 471 | X = [X x_next]; 472 | U = [U u]; 473 | THETA = [THETA theta_distr]; 474 | M_traj = [M_traj M_distr]; 475 | 476 | % Shift current state. 477 | x = x_next; 478 | 479 | % Update the empirical cost. 480 | J_em = updateEmpiricalCost(J_em, x, xF_R, uR, QR, RR); 481 | end 482 | 483 | 484 | disp('*******************************************************') 485 | disp(['Simulation finished! Empirical cost = ', num2str(J_em)]) 486 | disp('*******************************************************') 487 | 488 | 489 | %% Visualization 490 | XH = X(xH_dims,:); 491 | UH = U(uH_dims,:); 492 | XR = X(xR_dims,:); 493 | UR = U(uR_dims,:); 494 | 495 | option.coordinate = 'rel'; % 'rel', 'abs' 496 | option.keep_traj = 1; 497 | option.is_fading = 1; 498 | option.t_skip = 1; 499 | option.t_start = []; 500 | option.t_end = []; 501 | option.pause = 0.0; 502 | option.N_interp = 2; 503 | option.UI = 0; 504 | plotSim(planner, XR, XH, THETA, M_traj, option) 505 | 506 | 507 | %% Auxiliary function 508 | function flag = is_collision(xR, xH, params) 509 | flag = false; 510 | % Check collision with the human. 511 | if (abs(xR(1)-xH(1)) <= 3) && (abs(xR(2)-xH(2)) <= 1) 512 | flag = true; 513 | end 514 | % Check driving out of lanes. 515 | if (xR(2) >= params.rd_bd_max) || (xR(2) <= params.rd_bd_min) 516 | flag = true; 517 | end 518 | end -------------------------------------------------------------------------------- /util/getCEMPCcontrol.m: -------------------------------------------------------------------------------- 1 | function [uR_Sol, xR_Sol, xH_Sol, uH_Sol, VSol, status] =... 2 | getCEMPCcontrol(x0, xF_R, N, QR, RR, PR, uR_constraints,... 3 | uH_constraints, ilq_solvers, theta_distr, M_distr, planner,... 4 | extraArg, verbose) 5 | % Nominal certainty-equivalent MPC planner that accounts for human 6 | % responses to robot's action. 7 | % 8 | % Author: Haimin Hu (last modified 2021.11.16) 9 | 10 | yalmip('clear') 11 | params = planner.params; 12 | 13 | % Get ILQ solutions. 14 | if M_distr(1) >= M_distr(2) 15 | mu_theta_MAP = theta_distr.l.mu; 16 | ilq_solver_AH = ilq_solvers.AH_l; 17 | ilq_solver_EH = ilq_solvers.EH_l; 18 | else 19 | mu_theta_MAP = theta_distr.r.mu; 20 | ilq_solver_AH = ilq_solvers.AH_r; 21 | ilq_solver_EH = ilq_solvers.EH_r; 22 | end 23 | 24 | Ps_AH = ilq_solver_AH.best_operating_point.Ps; 25 | Ps_EH = ilq_solver_EH.best_operating_point.Ps; 26 | 27 | alphas_AH = ilq_solver_AH.best_operating_point.alphas; 28 | alphas_EH = ilq_solver_EH.best_operating_point.alphas; 29 | 30 | % Systems. 31 | jointSys = ilq_solver_AH.dynamics; 32 | ts = jointSys.ts; 33 | 34 | H_id = planner.H_id; 35 | subsys_H = ilq_solver_AH.dynamics.subsystems{H_id}; 36 | xH_dim = subsys_H.x_dim; 37 | uH_dim = subsys_H.u_dim; 38 | xH_dims = jointSys.x_dims{H_id}; 39 | uH_dims = jointSys.u_dims{H_id}; 40 | 41 | R_id = planner.R_id; 42 | subsys_R = ilq_solver_AH.dynamics.subsystems{R_id}; 43 | xR_dim = subsys_R.x_dim; 44 | uR_dim = subsys_R.u_dim; 45 | xR_dims = jointSys.x_dims{R_id}; 46 | 47 | % Soft constraint weights. 48 | w_uH = extraArg.w_uH; % human's control 49 | w_RB_l = extraArg.w_RB_l; % road boundary (linear penalty) 50 | w_RB_q = extraArg.w_RB_q; % road boundary (quadratic penalty) 51 | w_CA_l = extraArg.w_CA_l; % collision avoidance (linear penalty) 52 | w_CA_q = extraArg.w_CA_q; % collision avoidance (quadratic penalty) 53 | 54 | % Define solver settings 55 | options = sdpsettings('verbose', 0, 'solver', 'snopt', 'usex0', 1,... 56 | 'cachesolvers', 1); 57 | 58 | %---------------------------------------------------------------------- 59 | % Define decision variables 60 | %---------------------------------------------------------------------- 61 | % States. 62 | xR = sdpvar(xR_dim, N+1); 63 | xH = sdpvar(xH_dim, N+1); 64 | 65 | % Nominal states. 66 | xR_ref_AH = ilq_solver_AH.best_operating_point.xs(xR_dims,:); 67 | xH_ref_AH = ilq_solver_AH.best_operating_point.xs(xH_dims,:); 68 | xH_ref_EH = ilq_solver_EH.best_operating_point.xs(xH_dims,:); 69 | 70 | % Inputs. 71 | uR = sdpvar(uR_dim, N); 72 | uH = sdpvar(uH_dim, N); 73 | uH_nom = sdpvar(uH_dim, N); 74 | 75 | % Nominal inputs. 76 | uH_ref_AH = ilq_solver_AH.best_operating_point.us(uH_dims,:); 77 | uH_ref_EH = ilq_solver_EH.best_operating_point.us(uH_dims,:); 78 | 79 | % Soft constraint slack variables. 80 | eps_RB_R = sdpvar(1, N+1); 81 | eps_RB_H = sdpvar(1, N+1); 82 | eps_CA_R = sdpvar(1, N+1); 83 | 84 | %---------------------------------------------------------------------- 85 | % Objective function 86 | %---------------------------------------------------------------------- 87 | % Terminal cost. 88 | objective = (xR(:,N+1)-(xF_R(xR_dims)+[xH(1,N+1);0;0;0]))'*... 89 | PR * (xR(:,N+1)-(xF_R(xR_dims)+[xH(1,N+1);0;0;0])); 90 | 91 | % Tracking cost for states and controls. 92 | for k = 1:N 93 | objective = objective + uR(:,k)'*RR*uR(:,k) +... 94 | ([xH(:,k); xR(:,k)] - xF_R)'*QR*([xH(:,k); xR(:,k)] - xF_R); 95 | end 96 | 97 | % Enforce human's controls. 98 | for k = 1:N 99 | objective = objective + w_uH*norm(uH(:,k) - uH_nom(:,k))^2; 100 | end 101 | 102 | % Soft state constraint penalties. 103 | for k = 1:N+1 104 | objective = objective + w_RB_l*eps_RB_R(k) + w_RB_q*eps_RB_R(k)^2; 105 | objective = objective + w_RB_l*eps_RB_H(k) + w_RB_q*eps_RB_H(k)^2; 106 | objective = objective + w_CA_l*eps_CA_R(k) + w_CA_q*eps_CA_R(k)^2; 107 | end 108 | 109 | %---------------------------------------------------------------------- 110 | % Input constraints 111 | %---------------------------------------------------------------------- 112 | constraint = []; 113 | for k = 1:N 114 | % Robot's control bounds. 115 | constraint = [constraint uR_constraints.min <= uR(:,k),... 116 | uR(:,k) <= uR_constraints.max]; 117 | 118 | % Human's rational control. 119 | uH_AH_k = getiLQcontrol(... 120 | [xH(:,k); xR(:,k)], [xH_ref_AH(:,k); xR_ref_AH(:,k)],... 121 | uH_ref_AH(:,k), Ps_AH{H_id,k}, alphas_AH{H_id,k},... 122 | ilq_solver_AH.alpha_scaling); 123 | uH_EH_k = getiLQcontrol(... 124 | xH(:,k), xH_ref_EH(:,k),... 125 | uH_ref_EH(:,k), Ps_EH{H_id,k}, alphas_EH{H_id,k},... 126 | ilq_solver_EH.alpha_scaling); 127 | 128 | constraint = [constraint... 129 | uH_nom(:,k) == mu_theta_MAP(1)*uH_AH_k + mu_theta_MAP(2)*uH_EH_k]; 130 | 131 | % Human's control bounds. 132 | constraint = [constraint... 133 | uH_constraints.min <= uH(:,k), uH(:,k) <= uH_constraints.max]; 134 | end 135 | 136 | %---------------------------------------------------------------------- 137 | % State constraints 138 | %---------------------------------------------------------------------- 139 | % Collision avoidance ellipsoids. 140 | scale_ellips = 1.2; 141 | ellips = ellipsoid(zeros(2,1),... 142 | blkdiag(scale_ellips*params.avoid.lgt_ub,... 143 | scale_ellips*params.avoid.lat_bd)^2); 144 | [~, Q_CA] = double(ellips); 145 | 146 | for k = 1:N+1 147 | % Road boundaries. 148 | constraint = [constraint... 149 | params.rd_bd_min*0.9 - eps_RB_R <= xR(2,k),... 150 | params.rd_bd_max*0.9 + eps_RB_R >= xR(2,k)]; 151 | 152 | constraint = [constraint... 153 | params.rd_bd_min*0.9 - eps_RB_H <= xH(2,k),... 154 | params.rd_bd_max*0.9 + eps_RB_H >= xH(2,k)]; 155 | 156 | % Collision avoidance. 157 | constraint = [constraint... 158 | -(xR(1:2,k)-xH(1:2,k))'*inv(Q_CA)*(xR(1:2,k)-xH(1:2,k))... 159 | + 1 <= eps_CA_R]; 160 | end 161 | 162 | %---------------------------------------------------------------------- 163 | % Initial state constraints 164 | %---------------------------------------------------------------------- 165 | constraint = [constraint xR(:,1) == x0(xR_dims)]; 166 | constraint = [constraint xH(:,1) == x0(xH_dims)]; 167 | 168 | %---------------------------------------------------------------------- 169 | % Dynamics constraints 170 | %---------------------------------------------------------------------- 171 | for k = 1:N 172 | % Dynamics. 173 | constraint = [constraint... 174 | xR(:,k+1) == subsys_R.integrate(xR(:,k), uR(:,k), ts)]; 175 | constraint = [constraint... 176 | xH(:,k+1) == subsys_H.integrate(xH(:,k), uH(:,k), ts)]; 177 | end 178 | 179 | %---------------------------------------------------------------------- 180 | % Warm-start 181 | %---------------------------------------------------------------------- 182 | % assign(xR, ilq_solver_AH.best_operating_point.xs(xR_dims, 1:N+1)) 183 | % assign(xH, ilq_solver_AH.best_operating_point.xs(xH_dims, 1:N+1)) 184 | % assign(uR, ilq_solver_AH.best_operating_point.us(uR_dims, 1:N)) 185 | % assign(uH, ilq_solver_AH.best_operating_point.us(uH_dims, 1:N)) 186 | 187 | %---------------------------------------------------------------------- 188 | % Solve the MPC problem 189 | %---------------------------------------------------------------------- 190 | tic 191 | status = optimize(constraint, objective, options); 192 | total_time = toc; 193 | stime = status.solvertime; 194 | isSolved = (status.problem==0); 195 | if ~isSolved 196 | message = ['[CE-MPC solver issue] ', status.info ]; 197 | warning(message); 198 | end 199 | 200 | if verbose 201 | disp(['CE-MPC modeling time: ', num2str(total_time-stime), ' s']) 202 | disp(['CE-SMPC solving time: ', num2str(stime), ' s']) 203 | end 204 | 205 | %---------------------------------------------------------------------- 206 | % Return values 207 | %---------------------------------------------------------------------- 208 | xR_Sol = value(xR); 209 | uR_Sol = value(uR); 210 | xH_Sol = value(xH); 211 | uH_Sol = value(uH); 212 | % uH_nom_Sol = value(uH_nom); 213 | VSol = value(objective); 214 | end -------------------------------------------------------------------------------- /util/getCEMPChumancontrol.m: -------------------------------------------------------------------------------- 1 | function [xH_Sol, uH_Sol, VSol, status] = getCEMPChumancontrol(x0, xR,... 2 | xF_H, N, QH, RH, PH, uH_constraints, scale_ellips, jointSys,... 3 | planner, extraArg, verbose) 4 | % Nominal Certainty-Equivalent MPC planner that produces the human's 5 | % controls as a response to the robot's plans. 6 | % 7 | % Author: Haimin Hu (last modified 2022.12.24) 8 | 9 | yalmip('clear') 10 | params = planner.params; 11 | 12 | % Systems 13 | ts = jointSys.ts; 14 | H_id = planner.H_id; 15 | subsys_H = jointSys.subsystems{H_id}; 16 | xH_dim = subsys_H.x_dim; 17 | uH_dim = subsys_H.u_dim; 18 | xH_dims = jointSys.x_dims{H_id}; 19 | 20 | % Soft constraint weights 21 | w_RB_l = extraArg.w_RB_l; % road boundary (linear penalty) 22 | w_RB_q = extraArg.w_RB_q; % road boundary (quadratic penalty) 23 | w_CA_l = extraArg.w_CA_l; % collision avoidance (linear penalty) 24 | w_CA_q = extraArg.w_CA_q; % collision avoidance (quadratic penalty) 25 | 26 | % Define solver settings 27 | options = sdpsettings('verbose', 0, 'solver', 'snopt', 'usex0', 1,... 28 | 'cachesolvers', 1); 29 | options.snopt.Major_feasibility_tolerance = 1e-5; 30 | options.snopt.Major_optimality_tolerance = 1e-5; 31 | options.snopt.Minor_feasibility_tolerance = 1e-5; 32 | options.snopt.Iterations_limit = 4000; 33 | options.snopt.Major_iterations_limit = 250; 34 | options.snopt.Minor_iterations_limit = 125; 35 | options.snopt.Time_limit = 2; 36 | 37 | %---------------------------------------------------------------------- 38 | % Define decision variables 39 | %---------------------------------------------------------------------- 40 | % States 41 | xH = sdpvar(xH_dim, N+1); 42 | 43 | % Inputs 44 | uH = sdpvar(uH_dim, N); 45 | 46 | % Soft constraint slack variables 47 | eps_RB_H_min = sdpvar(1, N+1); 48 | eps_RB_H_max = sdpvar(1, N+1); 49 | eps_CA_H = sdpvar(1, N+1); 50 | 51 | %---------------------------------------------------------------------- 52 | % Objective function 53 | %---------------------------------------------------------------------- 54 | % Terminal cost 55 | objective = (xH(:,N+1) - xF_H)'*PH*(xH(:,N+1) - xF_H); 56 | 57 | % Tracking cost for states and controls 58 | for k = 1:N 59 | objective = objective + uH(:,k)'*RH*uH(:,k) +... 60 | (xH(:,k) - xF_H)'*QH*(xH(:,k) - xF_H); 61 | end 62 | 63 | % Soft state constraint penalties 64 | for k = 1:N+1 65 | objective = objective + w_RB_l*eps_RB_H_min(k) +... 66 | w_RB_q*eps_RB_H_min(k)^2; 67 | objective = objective + w_RB_l*eps_RB_H_max(k) +... 68 | w_RB_q*eps_RB_H_max(k)^2; 69 | objective = objective + w_CA_l*eps_CA_H(k) + w_CA_q*eps_CA_H(k)^2; 70 | end 71 | 72 | %---------------------------------------------------------------------- 73 | % Input constraints 74 | %---------------------------------------------------------------------- 75 | constraint = []; 76 | for k = 1:N 77 | constraint = [constraint... 78 | uH_constraints.min <= uH(:,k), uH(:,k) <= uH_constraints.max]; 79 | end 80 | 81 | %---------------------------------------------------------------------- 82 | % State constraints 83 | %---------------------------------------------------------------------- 84 | % Collision avoidance ellipsoids 85 | ellips = ellipsoid(zeros(2,1),... 86 | blkdiag(scale_ellips*params.avoid.lgt_ub,... 87 | scale_ellips*params.avoid.lat_bd)^2); 88 | [~, Q_CA] = double(ellips); 89 | 90 | for k = 1:N+1 91 | % Road boundaries 92 | constraint = [constraint... 93 | params.rd_bd_min - eps_RB_H_min(k) <= xH(2,k),... 94 | params.rd_bd_max + eps_RB_H_max(k) >= xH(2,k)]; 95 | 96 | % Collision avoidance 97 | constraint = [constraint... 98 | 1 - (xR(1:2,k)-xH(1:2,k))'*inv(Q_CA)*... 99 | (xR(1:2,k)-xH(1:2,k)) <= eps_CA_H(k)]; %#ok 100 | end 101 | 102 | % Slack variables 103 | constraint = [constraint, eps_RB_H_min >= 0, eps_RB_H_max >= 0,... 104 | eps_CA_H >= 0]; 105 | 106 | %---------------------------------------------------------------------- 107 | % Initial state constraints 108 | %---------------------------------------------------------------------- 109 | constraint = [constraint xH(:,1) == x0(xH_dims)]; 110 | 111 | %---------------------------------------------------------------------- 112 | % Dynamics constraints 113 | %---------------------------------------------------------------------- 114 | for k = 1:N 115 | constraint = [constraint... 116 | xH(:,k+1) == subsys_H.integrate(xH(:,k), uH(:,k), ts)]; 117 | end 118 | 119 | %---------------------------------------------------------------------- 120 | % Warm-start 121 | %---------------------------------------------------------------------- 122 | % assign(xH, extraArg.xH_ws) 123 | % assign(uH, extraArg.uH_ws) 124 | 125 | %---------------------------------------------------------------------- 126 | % Solve the MPC problem 127 | %---------------------------------------------------------------------- 128 | tic 129 | status = optimize(constraint, objective, options); 130 | total_time = toc; 131 | stime = status.solvertime; 132 | isSolved = (status.problem==0); 133 | if ~isSolved 134 | message = ['[Human CEMPC solver issue] ', status.info ]; 135 | warning(message); 136 | end 137 | 138 | if verbose 139 | disp(['Human CEMPC modeling time: ', num2str(total_time-stime),... 140 | ' s']) 141 | disp(['Human CEMPC solving time: ', num2str(stime), ' s']) 142 | end 143 | 144 | %---------------------------------------------------------------------- 145 | % Return values 146 | %---------------------------------------------------------------------- 147 | xH_Sol = value(xH); 148 | uH_Sol = value(uH); 149 | VSol = value(objective); 150 | end 151 | -------------------------------------------------------------------------------- /util/getSMPCcontrol.m: -------------------------------------------------------------------------------- 1 | function [uR_Sol, VSol, stime, stree, flag] = getSMPCcontrol(x0, xF_R,... 2 | theta_distr_0, M_distr_0, Nd, Ne, QR, RR, PR, Sigma_d,... 3 | uR_constraints, uH_constraints, ilq_solvers, planner, verbose,... 4 | extraArg) 5 | % Computes the dual SMPC policy. 6 | % 7 | % NOTE: 8 | % 1. Human's controls and M samples at time t are stored in nodes whose 9 | % time is t+1. 10 | % 11 | % Author: Haimin Hu (last modified 2021.12.13) 12 | 13 | yalmip('clear') 14 | 15 | params = planner.params; 16 | 17 | if strcmp(extraArg.opt_mode, 'std') 18 | stree_ws = extraArg.stree; 19 | end 20 | 21 | flag = 0; 22 | 23 | %---------------------------------------------------------------------- 24 | % Solver setup 25 | %---------------------------------------------------------------------- 26 | % Soft constraint weights. 27 | w_uH = extraArg.w_uH; % human's control 28 | w_RB_l = extraArg.w_RB_l; % road boundary (linear penalty) 29 | w_RB_q = extraArg.w_RB_q; % road boundary (quadratic penalty) 30 | w_CA_l = extraArg.w_CA_l; % collision avoidance (linear penalty) 31 | w_CA_q = extraArg.w_CA_q; % collision avoidance (quadratic penalty) 32 | 33 | % Define solver settings. 34 | options = sdpsettings('verbose', 0, 'solver', 'snopt', 'usex0', 1,... 35 | 'cachesolvers', 1, 'showprogress', 0, 'debug', 0); 36 | 37 | %---------------------------------------------------------------------- 38 | % iLQ solutions 39 | %---------------------------------------------------------------------- 40 | % Systems. 41 | if M_distr_0(1) >= M_distr_0(2) 42 | ilq_solver_AH_tmp = ilq_solvers.AH_l; % Altruistic Human, M = 'l' 43 | else 44 | ilq_solver_AH_tmp = ilq_solvers.AH_r; % Altruistic Human, M = 'r' 45 | end 46 | 47 | jointSys = ilq_solver_AH_tmp.dynamics; 48 | ts = jointSys.ts; 49 | 50 | H_id = planner.H_id; 51 | subsys_H = ilq_solver_AH_tmp.dynamics.subsystems{H_id}; 52 | xH_dims = jointSys.x_dims{H_id}; 53 | uH_dims = jointSys.u_dims{H_id}; 54 | 55 | R_id = planner.R_id; 56 | subsys_R = ilq_solver_AH_tmp.dynamics.subsystems{R_id}; 57 | xR_dims = jointSys.x_dims{R_id}; 58 | uR_dims = jointSys.u_dims{R_id}; 59 | 60 | % Nominal states (for warm-start). 61 | xH_ref_AH = ilq_solver_AH_tmp.best_operating_point.xs(xH_dims,:); 62 | % xR_ref_AH = ilq_solver_AH_tmp.best_operating_point.xs(xR_dims,:); 63 | 64 | % Nominal inputs (for warm-start). 65 | uH_ref_AH = ilq_solver_AH_tmp.best_operating_point.us(uH_dims,:); 66 | % uR_ref_AH = ilq_solver_AH_tmp.best_operating_point.us(uR_dims,:); 67 | 68 | %---------------------------------------------------------------------- 69 | % Construct a scenario tree 70 | %---------------------------------------------------------------------- 71 | % Define the root node. 72 | path_prob_root = 1; 73 | theta_dim = 2; 74 | M_dim = 2; 75 | root_node = node(1, 0, 1, path_prob_root, [], [],... 76 | planner.xR_dim, planner.xH_dim, planner.uR_dim, planner.uH_dim,... 77 | theta_dim, M_dim, planner.xR_dims, planner.xH_dims,... 78 | planner.uR_dims, planner.uH_dims); 79 | 80 | if M_distr_0(1) >= M_distr_0(2) 81 | root_node.theta_distr = theta_distr_0.l; 82 | else 83 | root_node.theta_distr = theta_distr_0.r; 84 | end 85 | root_node.M_distr = M_distr_0; 86 | if M_distr_0(1) >= M_distr_0(2) 87 | root_node.M_sample = 'l'; 88 | else 89 | root_node.M_sample = 'r'; 90 | end 91 | 92 | % Initialize a scenario tree. 93 | stree = tree(root_node, planner); 94 | 95 | % Tree branching options (M branching/non-branching). 96 | if extraArg.M_branch 97 | % Branching M. 98 | M_set = ['l', 'r']; 99 | else 100 | % Non-branching M: Use the MAP value of M. 101 | if M_distr_0(1) >= M_distr_0(2) 102 | M_set = 'l'; 103 | else 104 | M_set = 'r'; 105 | end 106 | end 107 | 108 | % Construct the scenario tree. 109 | stree = stree.constructTree(Nd, Ne, M_set, extraArg, verbose); 110 | 111 | %---------------------------------------------------------------------- 112 | % Assign probabilities of M 113 | %---------------------------------------------------------------------- 114 | constraint = []; 115 | if extraArg.M_branch 116 | % Branching M. 117 | for i = 1:numel(stree.N) 118 | if stree.N(i).t == 1 119 | continue 120 | end 121 | 122 | if strcmp(extraArg.opt_mode,'ws') 123 | % Warmstart mode: Assign probabilities of M with the prior. 124 | if stree.N(i).M_sample == 'l' 125 | stree.N(i).M_sample_prob = M_distr_0(1); 126 | elseif stree.N(i).M_sample == 'r' 127 | stree.N(i).M_sample_prob = M_distr_0(2); 128 | end 129 | elseif ~extraArg.M_dual || strcmp(extraArg.opt_mode,'std') 130 | % Non-dual control of M: Assign probabilities of M using 131 | % the warmstart results. 132 | ip = stree.N(i).pre_node_idx; 133 | if stree.N(i).M_sample == 'l' 134 | stree.N(i).M_sample_prob = stree_ws.N(ip).M_distr(1); 135 | elseif stree.N(i).M_sample == 'r' 136 | stree.N(i).M_sample_prob = stree_ws.N(ip).M_distr(2); 137 | end 138 | elseif extraArg.M_dual || strcmp(extraArg.opt_mode,'std') 139 | % Dual control of M: 'Optimize' M probabilities as decision 140 | % variables. 141 | ip = stree.N(i).pre_node_idx; 142 | if stree.N(i).M_sample == 'l' 143 | stree.N(i).M_sample_prob = stree.N(ip).M_distr(1); 144 | elseif stree.N(i).M_sample == 'r' 145 | stree.N(i).M_sample_prob = stree.N(ip).M_distr(2); 146 | end 147 | else 148 | % Sanity check. 149 | error('Invalid M mode and/or optimization mode!') 150 | end 151 | end 152 | else 153 | % Non-branching M. 154 | for i = 1:numel(stree.N) 155 | stree.N(i).M_sample_prob = 1; 156 | end 157 | end 158 | 159 | %---------------------------------------------------------------------- 160 | % Objective function 161 | %---------------------------------------------------------------------- 162 | objective = 0; 163 | for i = 1:numel(stree.N) 164 | % Terminal costs. 165 | if stree.N(i).t == Nd+Ne+1 166 | objective = objective +... 167 | stree.N(i).M_sample_prob * stree.N(i).path_prob *... 168 | (stree.N(i).xR-(xF_R(xR_dims)+[stree.N(i).xH(1);0;0;0]))'*... 169 | PR*(stree.N(i).xR-(xF_R(xR_dims)+[stree.N(i).xH(1);0;0;0])); 170 | end 171 | 172 | % Enforce human's controls. 173 | if stree.N(i).t > 1 174 | objective = objective +... 175 | stree.N(i).M_sample_prob * stree.N(i).path_prob *... 176 | w_uH * norm(stree.N(i).uH - stree.N(i).uH_nom)^2; 177 | end 178 | 179 | % Stage costs. 180 | if stree.N(i).t < Nd+Ne+1 181 | objective = objective +... 182 | stree.N(i).M_sample_prob * stree.N(i).path_prob *... 183 | (stree.N(i).uR' * RR * stree.N(i).uR +... 184 | ([stree.N(i).xH; stree.N(i).xR] - xF_R)' * QR *... 185 | ([stree.N(i).xH; stree.N(i).xR] - xF_R)); 186 | end 187 | end 188 | 189 | %---------------------------------------------------------------------- 190 | % Information gathering / entropy minimization (Explicit Dual Control) 191 | %---------------------------------------------------------------------- 192 | if isfield(extraArg, 'weight_IG') 193 | for i = 1:numel(stree.N) 194 | if stree.N(i).t == Nd+1 195 | objective = objective +... 196 | stree.N(i).M_sample_prob * extraArg.weight_IG *... 197 | (stree.N(i).theta_distr.covar(1) +... 198 | stree.N(i).theta_distr.covar(2) -... 199 | (stree.N(i).M_distr(1)*log(stree.N(i).M_distr(1))+... 200 | stree.N(i).M_distr(2)*log(stree.N(i).M_distr(2)))/... 201 | sum(stree.N(i).M_distr)); 202 | end 203 | end 204 | end 205 | 206 | %---------------------------------------------------------------------- 207 | % Inputs constraints 208 | %---------------------------------------------------------------------- 209 | for i = 1:numel(stree.N) 210 | if stree.N(i).t < Nd+Ne+1 211 | % Robot's control bounds. 212 | constraint = [constraint uR_constraints.min<=stree.N(i).uR,... 213 | stree.N(i).uR<=uR_constraints.max]; 214 | end 215 | 216 | if stree.N(i).t > 1 217 | % Human's rational control (linear combination of basis). 218 | ip = stree.N(i).pre_node_idx; 219 | 220 | iLQ_result = getiLQresults([stree.N(ip).xH; stree.N(ip).xR],... 221 | [], ilq_solvers,planner, stree.N(ip).t); 222 | 223 | if stree.N(i).M_sample == 'l' 224 | uH_AH_k = iLQ_result.AH_l.uH; 225 | uH_EH_k = iLQ_result.EH_l.uH; 226 | elseif stree.N(i).M_sample == 'r' 227 | uH_AH_k = iLQ_result.AH_r.uH; 228 | uH_EH_k = iLQ_result.EH_r.uH; 229 | end 230 | 231 | if strcmp(extraArg.opt_mode, 'ws') 232 | theta_1_k = root_node.theta_distr.mu(1); 233 | theta_2_k = root_node.theta_distr.mu(2); 234 | elseif strcmp(extraArg.opt_mode, 'std') 235 | theta_1_k = stree.N(i).theta_sample(1)/... 236 | (stree.N(i).theta_sample(1)+stree.N(i).theta_sample(2)); 237 | theta_2_k = stree.N(i).theta_sample(2)/... 238 | (stree.N(i).theta_sample(1)+stree.N(i).theta_sample(2)); 239 | end 240 | 241 | constraint = [constraint... 242 | stree.N(i).uH_nom == theta_1_k*uH_AH_k+theta_2_k*uH_EH_k]; 243 | 244 | % Human's control bounds. 245 | constraint = [constraint uH_constraints.min<=stree.N(i).uH,... 246 | stree.N(i).uH<=uH_constraints.max]; 247 | end 248 | end 249 | 250 | %---------------------------------------------------------------------- 251 | % State constraints 252 | %---------------------------------------------------------------------- 253 | % Collision avoidance ellipsoids. 254 | scale_ellips = extraArg.scale_ellips; 255 | ellips = ellipsoid(zeros(2,1),... 256 | blkdiag(scale_ellips * params.avoid.lgt_ub,... 257 | scale_ellips * params.avoid.lat_bd)^2); 258 | [~, Q_CA] = double(ellips); 259 | 260 | for i = 1:numel(stree.N) 261 | if stree.N(i).t == 1 262 | continue 263 | end 264 | 265 | % Road boundaries. 266 | constraint = [constraint... 267 | params.rd_bd_min*0.9-stree.N(i).eps.RB_R<=stree.N(i).xR(2),... 268 | params.rd_bd_max*0.9+stree.N(i).eps.RB_R>=stree.N(i).xR(2)]; 269 | 270 | constraint = [constraint... 271 | params.rd_bd_min*0.9-stree.N(i).eps.RB_H<=stree.N(i).xH(2),... 272 | params.rd_bd_max*0.9+stree.N(i).eps.RB_H>=stree.N(i).xH(2)]; 273 | 274 | % Collision avoidance. 275 | constraint = [constraint... 276 | -(stree.N(i).xR(1:2)-stree.N(i).xH(1:2))' * inv(Q_CA) *... 277 | (stree.N(i).xR(1:2)-stree.N(i).xH(1:2))+1<=stree.N(i).eps.CA]; 278 | 279 | % Soft state constraint penalties. 280 | objective = objective +... 281 | stree.N(i).M_sample_prob * stree.N(i).path_prob *... 282 | (w_RB_l*stree.N(i).eps.RB_R + w_RB_q*stree.N(i).eps.RB_R^2); 283 | objective = objective +... 284 | stree.N(i).M_sample_prob * stree.N(i).path_prob *... 285 | (w_RB_l*stree.N(i).eps.RB_H + w_RB_q*stree.N(i).eps.RB_H^2); 286 | objective = objective +... 287 | stree.N(i).M_sample_prob * stree.N(i).path_prob *... 288 | (w_CA_l*stree.N(i).eps.CA + w_CA_q*stree.N(i).eps.CA^2); 289 | end 290 | 291 | %---------------------------------------------------------------------- 292 | % Initial state constraints 293 | %---------------------------------------------------------------------- 294 | for i = 1:numel(stree.N) 295 | if stree.N(i).t == 1 296 | constraint = [constraint stree.N(i).xR == x0(xR_dims)]; 297 | constraint = [constraint stree.N(i).xH == x0(xH_dims)]; 298 | end 299 | end 300 | 301 | %---------------------------------------------------------------------- 302 | % Dynamics constraints 303 | %---------------------------------------------------------------------- 304 | for i = 1:numel(stree.N) 305 | if stree.N(i).t > 1 306 | ip = stree.N(i).pre_node_idx; 307 | constraint = [constraint... 308 | stree.N(i).xR == subsys_R.integrate(stree.N(ip).xR,... 309 | stree.N(ip).uR, ts)]; 310 | constraint = [constraint... 311 | stree.N(i).xH == subsys_H.integrate(stree.N(ip).xH,... 312 | stree.N(i).uH, ts)]; % Be careful with human's control. 313 | end 314 | end 315 | 316 | %---------------------------------------------------------------------- 317 | % Belief state dynamics constraints 318 | %---------------------------------------------------------------------- 319 | if strcmp(extraArg.opt_mode, 'std') 320 | for i = 1:numel(stree.N) 321 | % Dual control steps: propagate the belief state via the 322 | % recursive Bayesian estimation. 323 | if 1 < stree.N(i).t && stree.N(i).t <= Nd+1 324 | % Linearize dynamics. 325 | ip = stree.N(i).pre_node_idx; 326 | [A_ip, B_cell_ip] = jointSys.linearizeDiscrete_B_cell(... 327 | [stree_ws.N(ip).xH; stree_ws.N(ip).xR],... 328 | [stree_ws.N(i).uH; stree_ws.N(ip).uR]); 329 | 330 | % Compute fixed basis of human's control. 331 | k = stree.N(ip).t; 332 | 333 | iLQ_result_ws = getiLQresults(... 334 | [stree_ws.N(ip).xH; stree_ws.N(ip).xR], B_cell_ip,... 335 | ilq_solvers, planner, k); 336 | 337 | uH_AH_fixed = iLQ_result_ws.AH_l.uH; 338 | uH_EH_fixed = iLQ_result_ws.EH_l.uH; 339 | 340 | extraArg.U_fixed = [uH_AH_fixed uH_EH_fixed]; 341 | extraArg.mu_theta_fixed = stree_ws.N(ip).theta_distr.mu; 342 | 343 | % Propagate theta. 344 | iLQ_result = getiLQresults(... 345 | [stree.N(ip).xH; stree.N(ip).xR], B_cell_ip,... 346 | ilq_solvers, planner, k); 347 | 348 | [mu_next, covar_next] = updateTheta(... 349 | stree.N(i).M_sample, stree.N(ip).theta_distr.mu,... 350 | stree.N(ip).theta_distr.covar, Sigma_d,... 351 | [stree.N(i).xH; stree.N(i).xR],... 352 | [stree.N(i).uH; stree.N(ip).uR],... 353 | A_ip * [stree.N(ip).xH; stree.N(ip).xR], B_cell_ip,... 354 | iLQ_result, planner, extraArg); %!!! uH <- uH_nom 355 | 356 | constraint = [constraint... 357 | stree.N(i).theta_distr.mu == mu_next 358 | stree.N(i).theta_distr.covar == covar_next]; 359 | 360 | % Propagate M. 361 | if extraArg.M_dual 362 | extraArg.theta_distr_M_fixed =... 363 | stree_ws.N(ip).theta_distr_M_ws; 364 | M_distr_next = updateM(stree.N(ip).M_distr, [],... 365 | [stree.N(i).xH; stree.N(i).xR], stree.N(ip).uR,... 366 | A_ip * [stree.N(ip).xH; stree.N(ip).xR],... 367 | B_cell_ip, iLQ_result, planner, extraArg); 368 | constraint = [constraint... 369 | stree.N(i).M_distr == M_distr_next]; 370 | end 371 | end 372 | 373 | % Exploitation steps. 374 | if stree.N(i).t > Nd+1 375 | ip = stree.N(i).pre_node_idx; 376 | constraint = [constraint... 377 | stree.N(i).theta_distr.mu==stree.N(ip).theta_distr.mu,... 378 | stree.N(i).theta_distr.covar==stree.N(ip).theta_distr.covar]; 379 | end 380 | end 381 | end 382 | 383 | %---------------------------------------------------------------------- 384 | % Reconstruct theta samples 385 | %---------------------------------------------------------------------- 386 | if strcmp(extraArg.opt_mode, 'std') 387 | for i = 1:numel(stree.N) 388 | % Dual control steps. 389 | if 1 < stree.N(i).t && stree.N(i).t <= Nd+1 390 | constraint = [constraint... 391 | stree.N(i).theta_sample == transform_theta(stree.N(i))]; 392 | end 393 | 394 | % Exploitation steps. 395 | if stree.N(i).t > Nd+1 396 | constraint = [constraint... 397 | stree.N(i).theta_sample == stree.N(i).theta_distr.mu]; 398 | end 399 | end 400 | end 401 | 402 | %---------------------------------------------------------------------- 403 | % Warm-start 404 | %---------------------------------------------------------------------- 405 | if strcmp(extraArg.opt_mode, 'ws') 406 | for i = 1:numel(stree.N) 407 | k = stree.N(i).t; 408 | assign(stree.N(i).xH, xH_ref_AH(:,k)) 409 | % assign(stree.N(i).xR, xR_ref_AH(:,k)) 410 | assign(stree.N(i).xR, extraArg.xR_ws(:,k)) 411 | if k > 1 412 | assign(stree.N(i).uH, uH_ref_AH(:,k-1)) 413 | assign(stree.N(i).uH_nom, uH_ref_AH(:,k-1)) 414 | % assign(stree.N(i).uR, uR_ref_AH(:,k-1)) 415 | assign(stree.N(i).uR, extraArg.uR_ws(:,k-1)) 416 | end 417 | end 418 | elseif strcmp(extraArg.opt_mode, 'std') 419 | for i = 1:numel(stree.N) 420 | assign(stree.N(i).xH, stree_ws.N(i).xH) 421 | assign(stree.N(i).xR, stree_ws.N(i).xR) 422 | if stree.N(i).t > 1 423 | assign(stree.N(i).uH, stree_ws.N(i).uH) 424 | assign(stree.N(i).uH_nom, stree_ws.N(i).uH_nom) 425 | assign(stree.N(i).uR, stree_ws.N(i).uR) 426 | % assign(stree.N(i).theta_distr.mu, stree_ws.N(i).theta_distr.mu) 427 | assign(stree.N(i).theta_distr.covar, stree_ws.N(i).theta_distr.covar) 428 | % assign(stree.N(i).theta_sample, stree_ws.N(i).theta_sample) 429 | % if extraArg.M_dual 430 | % assign(stree.N(i).M_distr, stree_ws.N(i).M_distr) 431 | % end 432 | end 433 | end 434 | end 435 | 436 | %---------------------------------------------------------------------- 437 | % Solve the ST-SMPC problem 438 | %---------------------------------------------------------------------- 439 | tic 440 | status = optimize(constraint, objective, options); 441 | total_time = toc; 442 | stime = status.solvertime; 443 | isSolved = (status.problem==0); 444 | if ~isSolved 445 | if strcmp(extraArg.opt_mode, 'ws') 446 | message = ['[WS-SMPC solver issue] ', status.info]; 447 | elseif strcmp(extraArg.opt_mode, 'std') 448 | message = ['[Dual-SMPC solver issue] ', status.info]; 449 | else 450 | message = ['[Unknown-SMPC solver issue] ', status.info]; 451 | end 452 | warning(message); 453 | if strcmp(status.info,... 454 | 'Maximum iterations or time limit exceeded (SNOPT)') 455 | flag = 1; 456 | end 457 | end 458 | 459 | if verbose 460 | if strcmp(extraArg.opt_mode, 'ws') 461 | disp(['WS-SMPC modeling time: ', num2str(total_time-stime),... 462 | ' s']) 463 | disp(['WS-SMPC solving time: ', num2str(stime), ' s']) 464 | elseif strcmp(extraArg.opt_mode, 'std') 465 | disp(['Dual-SMPC modeling time: ',... 466 | num2str(total_time-stime), ' s']) 467 | disp(['Dual-SMPC solving time: ', num2str(stime), ' s']) 468 | end 469 | end 470 | 471 | %---------------------------------------------------------------------- 472 | % Retrieve value and propagate the belief states (for warm-start only) 473 | %---------------------------------------------------------------------- 474 | if strcmp(extraArg.opt_mode, 'ws') 475 | for i = 1:numel(stree.N) 476 | stree.N(i).xH = value(stree.N(i).xH); 477 | stree.N(i).xR = value(stree.N(i).xR); 478 | stree.N(i).uH = value(stree.N(i).uH); 479 | stree.N(i).uH_nom = value(stree.N(i).uH_nom); 480 | stree.N(i).uR = value(stree.N(i).uR); 481 | end 482 | 483 | stree.N(1).theta_distr_M_ws = theta_distr_0; 484 | stree.N(1).theta_sample = root_node.theta_distr.mu; 485 | for i = 1:numel(stree.N) 486 | if stree.N(i).t > 1 487 | ip = stree.N(i).pre_node_idx; 488 | 489 | % Get iLQ results. 490 | [A_ip, B_cell_ip] = jointSys.linearizeDiscrete_B_cell(... 491 | [stree.N(ip).xH; stree.N(ip).xR],... 492 | [stree.N(i).uH; stree.N(ip).uR]); 493 | 494 | iLQ_result = getiLQresults(... 495 | [stree.N(ip).xH; stree.N(ip).xR], B_cell_ip,... 496 | ilq_solvers, planner, stree.N(i).t); 497 | 498 | % Propagate theta. 499 | if extraArg.M_branch 500 | for M = M_set 501 | if M == 'l' 502 | theta_distr_now=stree.N(ip).theta_distr_M_ws.l; 503 | elseif M == 'r' 504 | theta_distr_now=stree.N(ip).theta_distr_M_ws.r; 505 | end 506 | 507 | [mu_next, covar_next] = updateTheta(... 508 | M, theta_distr_now.mu,... 509 | theta_distr_now.covar, Sigma_d,... 510 | [stree.N(i).xH; stree.N(i).xR],... 511 | [stree.N(i).uH; stree.N(ip).uR],... 512 | A_ip*[stree.N(ip).xH; stree.N(ip).xR],... 513 | B_cell_ip, iLQ_result, planner, []); 514 | 515 | if M == 'l' 516 | stree.N(i).theta_distr_M_ws.l.mu = mu_next; 517 | stree.N(i).theta_distr_M_ws.l.covar =... 518 | covar_next; 519 | elseif M == 'r' 520 | stree.N(i).theta_distr_M_ws.r.mu = mu_next; 521 | stree.N(i).theta_distr_M_ws.r.covar =... 522 | covar_next; 523 | end 524 | 525 | if stree.N(i).M_sample == M 526 | theta_distr_i.mu = mu_next; 527 | theta_distr_i.covar = covar_next; 528 | stree.N(i).theta_distr = theta_distr_i; 529 | stree.N(i).theta_sample =... 530 | transform_theta(stree.N(i)); 531 | end 532 | end 533 | else 534 | [mu_next, covar_next] = updateTheta(... 535 | stree.N(i).M_sample, stree.N(ip).theta_distr.mu,... 536 | stree.N(ip).theta_distr.covar, Sigma_d,... 537 | [stree.N(i).xH; stree.N(i).xR],... 538 | [stree.N(i).uH; stree.N(ip).uR],... 539 | A_ip*[stree.N(ip).xH; stree.N(ip).xR],... 540 | B_cell_ip, iLQ_result, planner, []); 541 | 542 | theta_distr_i.mu = mu_next; 543 | theta_distr_i.covar = covar_next; 544 | stree.N(i).theta_distr = theta_distr_i; 545 | stree.N(i).theta_sample = transform_theta(stree.N(i)); 546 | end 547 | 548 | % Propagate M. 549 | if extraArg.M_branch 550 | M_distr_next = updateM(stree.N(ip).M_distr,... 551 | stree.N(ip).theta_distr_M_ws,... 552 | [stree.N(i).xH; stree.N(i).xR], stree.N(ip).uR,... 553 | A_ip*[stree.N(ip).xH; stree.N(ip).xR],... 554 | B_cell_ip, iLQ_result, planner, []); 555 | 556 | stree.N(i).M_distr = M_distr_next; 557 | end 558 | end 559 | end 560 | end 561 | 562 | %---------------------------------------------------------------------- 563 | % Return values 564 | %---------------------------------------------------------------------- 565 | uR_Sol = value(stree.N(1).uR); 566 | VSol = value(objective); 567 | 568 | function theta_out = transform_theta(node) 569 | theta_out = node.theta_distr.mu + sqrtm(node.theta_distr.covar)*... 570 | node.theta_sample_sn; 571 | end 572 | end 573 | 574 | -------------------------------------------------------------------------------- /util/getiLQresults.m: -------------------------------------------------------------------------------- 1 | function iLQ_result = getiLQresults(x, B_cell, ilq_solvers, planner, t) 2 | % Get iLQ computation results. 3 | 4 | ilq_solver_AH_l = ilq_solvers.AH_l; 5 | ilq_solver_EH_l = ilq_solvers.EH_l; 6 | ilq_solver_AH_r = ilq_solvers.AH_r; 7 | ilq_solver_EH_r = ilq_solvers.EH_r; 8 | 9 | xH_dims = planner.xH_dims; 10 | uH_dims = planner.uH_dims; 11 | 12 | H_id = planner.H_id; 13 | R_id = planner.R_id; 14 | 15 | if H_id ~= 1 || R_id ~= 2 16 | error('Incorrect subsystem index!') % sanity check 17 | end 18 | 19 | if ~isempty(B_cell) 20 | BH = B_cell{H_id}; 21 | end 22 | 23 | % --------- M = 'l' --------- 24 | % Altruistic human, M = 'l'. 25 | x_nom_AH_l = ilq_solver_AH_l.best_operating_point.xs(:, t); 26 | u_nom_AH_l = ilq_solver_AH_l.best_operating_point.us(:, t); 27 | P_H_AH_l = [ilq_solver_AH_l.best_operating_point.Ps{1, t}; 28 | ilq_solver_AH_l.best_operating_point.Ps{2, t}]; 29 | alpha_H_AH_l = [ilq_solver_AH_l.best_operating_point.alphas{1, t};... 30 | ilq_solver_AH_l.best_operating_point.alphas{2, t}]; 31 | Z_H_AH_l = ilq_solver_AH_l.best_operating_point.Zs{H_id, t}; 32 | 33 | uH_AH_l = getiLQcontrol(x, x_nom_AH_l, u_nom_AH_l, P_H_AH_l,... 34 | alpha_H_AH_l, planner.alpha_scaling); 35 | uH_AH_l = uH_AH_l(uH_dims); 36 | 37 | % Egoistic Human, M = 'l'. 38 | x_nom_EH_l = ilq_solver_EH_l.best_operating_point.xs(:, t); 39 | u_nom_EH_l = ilq_solver_EH_l.best_operating_point.us(:, t); 40 | P_H_EH_l = ilq_solver_EH_l.best_operating_point.Ps{1, t}; 41 | alpha_H_EH_l = ilq_solver_EH_l.best_operating_point.alphas{1, t}; 42 | Z_H_EH_l = ilq_solver_EH_l.best_operating_point.Zs{1, t}; 43 | 44 | uH_EH_l = getiLQcontrol(x(xH_dims), x_nom_EH_l, u_nom_EH_l,... 45 | P_H_EH_l, alpha_H_EH_l, planner.alpha_scaling); 46 | 47 | % --------- M = 'r' --------- 48 | % Altruistic human, M = 'r'. 49 | x_nom_AH_r = ilq_solver_AH_r.best_operating_point.xs(:, t); 50 | u_nom_AH_r = ilq_solver_AH_r.best_operating_point.us(:, t); 51 | P_H_AH_r = [ilq_solver_AH_r.best_operating_point.Ps{1, t}; 52 | ilq_solver_AH_r.best_operating_point.Ps{2, t}]; 53 | alpha_H_AH_r = [ilq_solver_AH_r.best_operating_point.alphas{1, t};... 54 | ilq_solver_AH_r.best_operating_point.alphas{2, t}]; 55 | Z_H_AH_r = ilq_solver_AH_r.best_operating_point.Zs{H_id, t}; 56 | 57 | uH_AH_r = getiLQcontrol(x, x_nom_AH_r, u_nom_AH_r, P_H_AH_r,... 58 | alpha_H_AH_r, planner.alpha_scaling); 59 | uH_AH_r = uH_AH_r(uH_dims); 60 | 61 | % Egoistic Human, M = 'l'. 62 | x_nom_EH_r = ilq_solver_EH_r.best_operating_point.xs(:, t); 63 | u_nom_EH_r = ilq_solver_EH_r.best_operating_point.us(:, t); 64 | P_H_EH_r = ilq_solver_EH_r.best_operating_point.Ps{1, t}; 65 | alpha_H_EH_r = ilq_solver_EH_r.best_operating_point.alphas{1, t}; 66 | Z_H_EH_r = ilq_solver_EH_r.best_operating_point.Zs{1, t}; 67 | 68 | uH_EH_r = getiLQcontrol(x(xH_dims), x_nom_EH_r, u_nom_EH_r,... 69 | P_H_EH_r, alpha_H_EH_r, planner.alpha_scaling); 70 | 71 | % Return results. 72 | iLQ_result.AH_l.uH = uH_AH_l; 73 | iLQ_result.EH_l.uH = uH_EH_l; 74 | iLQ_result.AH_r.uH = uH_AH_r; 75 | iLQ_result.EH_r.uH = uH_EH_r; 76 | 77 | if ~isempty(B_cell) 78 | Sigma_AH_l = planner.RH + BH'*Z_H_AH_l*BH; 79 | iLQ_result.AH_l.Sigma = Sigma_AH_l; 80 | 81 | Sigma_EH_l = planner.RH + BH(1:4,:)'*Z_H_EH_l*BH(1:4,:); 82 | iLQ_result.EH_l.Sigma = Sigma_EH_l; 83 | 84 | Sigma_AH_r = planner.RH + BH'*Z_H_AH_r*BH; 85 | iLQ_result.AH_r.Sigma = Sigma_AH_r; 86 | 87 | Sigma_EH_r = planner.RH + BH(1:4,:)'*Z_H_EH_r*BH(1:4,:); 88 | iLQ_result.EH_r.Sigma = Sigma_EH_r; 89 | end 90 | end 91 | 92 | -------------------------------------------------------------------------------- /util/iLQGames/costs/Cost.m: -------------------------------------------------------------------------------- 1 | classdef Cost 2 | % Base class for all cost functions. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-09, Last modified: 2021-11-09 6 | 7 | properties 8 | name % Name of the cost 9 | x_dim % State dimension 10 | u_dim % Control dimension 11 | dldx % Gradient of the cost l w.r.t. state x 12 | Hx % Hessian of the cost l w.r.t. state x 13 | Hu % Hessian of the cost l w.r.t. control u 14 | end 15 | 16 | methods 17 | function obj = Cost(name, x_dim, u_dim) 18 | % Constructor. 19 | obj.name = name; 20 | obj.x_dim = x_dim; 21 | obj.u_dim = u_dim; 22 | end 23 | 24 | function out = get_dldx(obj, x0) 25 | % Evaluate gradient dldx at x0. 26 | out = obj.dldx(x0); 27 | end 28 | 29 | function out = get_Hx(obj, x0) 30 | % Evaluate hessian Hx at x0. 31 | out = obj.Hx(x0); 32 | end 33 | 34 | function out = get_Hu(obj, u0) 35 | % Evaluate hessian Hu at u0. 36 | out = obj.Hu(u0); 37 | end 38 | end % end methods 39 | end % end class 40 | 41 | -------------------------------------------------------------------------------- /util/iLQGames/costs/PlayerCost.m: -------------------------------------------------------------------------------- 1 | classdef PlayerCost 2 | % Container to hold a bunch of different Costs and keep track of the 3 | % arguments to each one. 4 | % Author: Haimin Hu 5 | % Reference: ilqgames by David Fridovich-Keil 6 | % Created: 2021-11-09, Last modified: 2021-11-09 7 | 8 | properties 9 | costs % cell of costs 10 | args % cell of args `x` or `u` or `xu` 11 | weights % vector of weights 12 | end 13 | 14 | methods 15 | function obj = PlayerCost() 16 | % Constructor. 17 | obj.costs = {}; 18 | obj.args = {}; 19 | obj.weights = []; 20 | end 21 | 22 | function total_cost = cost(obj, x, u) 23 | % Evaluate the game cost function at the current state 24 | % and/or controls. 25 | total_cost = 0; 26 | for i = 1:length(obj.costs) 27 | current_cost = obj.costs{i}; 28 | if strcmp(obj.args{i},'x') 29 | current_term = obj.weights(i) * current_cost.cost(x); 30 | elseif strcmp(obj.args{i},'u') 31 | current_term = obj.weights(i) * current_cost.cost(u); 32 | elseif strcmp(obj.args{i},'xu') 33 | current_term = obj.weights(i) * current_cost.cost(x,u); 34 | else 35 | error('Invalid args') 36 | end 37 | 38 | if current_term > 1e12 39 | warning(["cost "+current_cost.name+" is "+... 40 | num2str(current_term)]) 41 | warning(['Input is ', obj.args{i}]) 42 | end 43 | 44 | total_cost = total_cost + current_term; 45 | end 46 | end 47 | 48 | function obj = add_cost(obj, cost, arg, weight) 49 | % Add a new cost to the game. 50 | obj.costs{end+1} = cost; 51 | obj.args{end+1} = arg; 52 | obj.weights = [obj.weights weight]; 53 | end 54 | 55 | function [total_cost, dldx, Hx, Hu] = quadraticize(obj, x, u) 56 | % Compute a quadratic approximation to the overall cost for a 57 | % particular choice of operating point `x`, `u` or `xu` 58 | % Returns the gradient and Hessian of the overall cost s.t.: 59 | % 60 | % cost(x + dx, u + du) \approx 61 | % cost(x, u) + 62 | % dldx^T dx + 63 | % 0.5 * (dx^T Hx dx + du^T Hu du) 64 | 65 | % Evaluate cost at the operating point. 66 | total_cost = obj.cost(x,u); 67 | 68 | % Compute gradient and Hessians. 69 | dldx = zeros(length(x), 1); 70 | Hx = zeros(length(x), length(x)); 71 | Hu = zeros(length(u), length(u)); 72 | 73 | for i = 1:length(obj.costs) 74 | current_cost = obj.costs{i}; 75 | dldx = dldx + current_cost.get_dldx(x); 76 | Hx = Hx + current_cost.get_Hx(x); 77 | Hu = Hu + current_cost.get_Hu(u); 78 | end 79 | end 80 | end % end methods 81 | end % end class 82 | 83 | -------------------------------------------------------------------------------- /util/iLQGames/costs/ProductStateProximityCost.m: -------------------------------------------------------------------------------- 1 | classdef ProductStateProximityCost < Cost 2 | % Proximity cost for state spaces that are Cartesian products of 3 | % individual systems' state spaces. Penalizes 4 | % ``` sum_{i \ne j} min(distance(i, j) - max_distance, 0)^2 ``` 5 | % for all players i, j. 6 | % Author: Haimin Hu 7 | % Reference: ilqgames by David Fridovich-Keil 8 | % Created: 2021-11-09, Last modified: 2021-11-09 9 | 10 | properties 11 | px_indices % px indices of all players 12 | py_indices % py indices of all players 13 | max_distance % Maximum value of distance to penalize 14 | num_players % Number of players 15 | end 16 | 17 | methods 18 | function obj = ProductStateProximityCost(px_indices,... 19 | py_indices, max_distance, name, x_dim, u_dim) 20 | % Constructor. 21 | % Initialize with dimension to add cost to and threshold 22 | % BELOW which to impose quadratic cost. 23 | super_args{1} = name; 24 | super_args{2} = x_dim; 25 | super_args{3} = u_dim; 26 | obj@Cost(super_args{:}); 27 | 28 | obj.px_indices = px_indices; 29 | obj.py_indices = py_indices; 30 | obj.max_distance = max_distance; 31 | obj.num_players = length(px_indices); 32 | 33 | % Define symbolic gradients and Hessians. 34 | x = sym('x', [obj.x_dim 1]); 35 | u = sym('u', [obj.u_dim 1]); 36 | l = 0; 37 | for i = 1:obj.num_players 38 | xi_idx = [obj.px_indices(i), obj.py_indices(i)]; 39 | for j = 1:obj.num_players 40 | if i ~= j 41 | xj_idx = [obj.px_indices(j), obj.py_indices(j)]; 42 | 43 | % Compute relative distance. 44 | dx = x(xi_idx) - x(xj_idx); 45 | relative_distance = norm(dx); 46 | 47 | % Note: min does not work with sym. 48 | l_tmp = symfun(piecewise(relative_distance-... 49 | obj.max_distance<=0,(relative_distance-... 50 | obj.max_distance)^2,0),x); 51 | 52 | l = l + l_tmp; 53 | end 54 | end 55 | end 56 | 57 | file_name = "./util/iLQGames/Costs/ProductStateProximityCost_" + name; 58 | 59 | obj.dldx = matlabFunction(gradient(l,x),'Vars',{x},... 60 | 'file', file_name + "_dldx.m"); 61 | obj.Hx = matlabFunction(hessian(l,x),'Vars',{x},... 62 | 'file', file_name + "_Hx.m"); 63 | obj.Hu = matlabFunction(hessian(l,u),'Vars',{u,x},... 64 | 'file', file_name + "_Hu.m"); 65 | end 66 | 67 | function total_cost = cost(obj, x) 68 | % Evaluate this cost function on the given state. 69 | % x: concatenated state vector of all subsystems (column vector) 70 | total_cost = 0; 71 | for ii = 1:obj.num_players 72 | xi_idx = [obj.px_indices(ii), obj.py_indices(ii)]; 73 | for jj = 1:obj.num_players 74 | if ii ~= jj 75 | xj_idx = [obj.px_indices(jj), obj.py_indices(jj)]; 76 | 77 | % Compute relative distance. 78 | dx = x(xi_idx) - x(xj_idx); 79 | relative_distance = norm(dx); 80 | 81 | total_cost = total_cost + min(relative_distance-... 82 | obj.max_distance, 0.0)^2; 83 | end 84 | end 85 | end 86 | end 87 | 88 | function out = get_Hu(obj, u0) 89 | % Evaluate hessian Hu at u0. 90 | out = obj.Hu(u0, zeros(obj.x_dim,1)); 91 | end 92 | 93 | end % end methods 94 | end % end class 95 | 96 | -------------------------------------------------------------------------------- /util/iLQGames/costs/QuadraticCost.m: -------------------------------------------------------------------------------- 1 | classdef QuadraticCost < Cost 2 | % Quadratic cost, derived from Cost base class. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-09, Last modified: 2021-11-09 6 | 7 | properties 8 | dimensions % dimensions to add cost 9 | origin % Value along the specified dim where the cost is 0 10 | type % 'state' or 'control' 11 | P % Cost matrix 12 | end 13 | 14 | methods 15 | function obj = QuadraticCost(dimensions, origin, type, P,... 16 | name, x_dim, u_dim) 17 | % Constructor. 18 | % Initialize with dimensions to add cost to and origin to 19 | % center the quadratic cost about. 20 | super_args{1} = name; 21 | super_args{2} = x_dim; 22 | super_args{3} = u_dim; 23 | obj@Cost(super_args{:}); 24 | 25 | obj.dimensions = dimensions; 26 | obj.origin = origin; 27 | obj.type = type; 28 | obj.P = P; 29 | 30 | % Define symbolic gradients and Hessians. 31 | x = sym('x', [obj.x_dim 1]); 32 | u = sym('u', [obj.u_dim 1]); 33 | if strcmp(obj.type,'state') 34 | l = (x(obj.dimensions) - obj.origin)'*obj.P*... 35 | (x(obj.dimensions) - obj.origin); 36 | elseif strcmp(obj.type,'control') 37 | l = (u(obj.dimensions) - obj.origin)'*obj.P*... 38 | (u(obj.dimensions) - obj.origin); 39 | else 40 | error('Invalid type') 41 | end 42 | obj.dldx = matlabFunction(gradient(l,x),'Vars',{x}); 43 | obj.Hx = matlabFunction(hessian(l,x),'Vars',{x}); 44 | obj.Hu = matlabFunction(hessian(l,u),'Vars',{u}); 45 | end 46 | 47 | function out = cost(obj, xu) 48 | % Evaluate this cost function on the given input and time, 49 | % which might either be a state `x` or a control `u`. Hence 50 | % the input is named `xu`. 51 | out = (xu(obj.dimensions) - obj.origin)'*obj.P*... 52 | (xu(obj.dimensions) - obj.origin); 53 | end 54 | end % end methods 55 | end % end class 56 | 57 | -------------------------------------------------------------------------------- /util/iLQGames/costs/ReferenceDeviationCost.m: -------------------------------------------------------------------------------- 1 | classdef ReferenceDeviationCost < Cost 2 | % Reference following cost. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-09, Last modified: 2021-11-09 6 | 7 | properties 8 | x_ref % Reference state (nx-by-1) 9 | u_ref % Reference control (nu-by-1) 10 | Q % Cost matrix of states 11 | R % Cost matrix of controls 12 | end 13 | 14 | methods 15 | function obj = ReferenceDeviationCost(x_ref, u_ref, Q, R,... 16 | name, x_dim, u_dim) 17 | % Constructor 18 | % NOTE: This list will be updated externally as the reference 19 | % trajectory evolves with each iteration. 20 | super_args{1} = name; 21 | super_args{2} = x_dim; 22 | super_args{3} = u_dim; 23 | obj@Cost(super_args{:}); 24 | 25 | obj.x_ref = x_ref; 26 | obj.u_ref = u_ref; 27 | obj.Q = Q; 28 | obj.R = R; 29 | 30 | % Define symbolic gradients and Hessians. 31 | x = sym('x', [obj.x_dim 1]); 32 | u = sym('u', [obj.u_dim 1]); 33 | l = obj.cost(x, u); 34 | obj.dldx = matlabFunction(gradient(l,x),'Vars',{x}); 35 | obj.Hx = matlabFunction(hessian(l,x),'Vars',{x}); 36 | obj.Hu = matlabFunction(hessian(l,u),'Vars',{u}); 37 | end 38 | 39 | function out = cost(obj, x, u) 40 | % Evaluate this cost function on the given vector and time. 41 | out = (x-obj.x_ref)'*obj.Q*(x-obj.x_ref) +... 42 | (u-obj.u_ref)'*obj.R*(u-obj.u_ref); 43 | end 44 | end % end methods 45 | end % end class 46 | 47 | -------------------------------------------------------------------------------- /util/iLQGames/costs/SemiquadraticCost.m: -------------------------------------------------------------------------------- 1 | classdef SemiquadraticCost < Cost 2 | % Semiquadratic cost, derived from Cost base class. Implements a 3 | % cost function that is flat below a threshold and quadratic above, in 4 | % the given dimension. 5 | % Author: Haimin Hu 6 | % Reference: ilqgames by David Fridovich-Keil 7 | % Created: 2021-11-09, Last modified: 2021-11-09 8 | 9 | properties 10 | dimension % Dimension to add cost 11 | threshold % Value above which to impose quadratic cost 12 | oriented_right % Boolean flag determining which side of threshold 13 | % to penalize 14 | type % 'state' or 'control' 15 | end 16 | 17 | methods 18 | function obj = SemiquadraticCost(dimension, threshold,... 19 | oriented_right, type, name, x_dim, u_dim) 20 | % Constructor. 21 | % Initialize with dimension to add cost to and threshold 22 | % above which to impose quadratic cost. 23 | super_args{1} = name; 24 | super_args{2} = x_dim; 25 | super_args{3} = u_dim; 26 | obj@Cost(super_args{:}); 27 | 28 | obj.dimension = dimension; 29 | obj.threshold = threshold; 30 | obj.oriented_right = oriented_right; 31 | obj.type = type; 32 | 33 | % Define symbolic gradients and Hessians. 34 | x = sym('x', [obj.x_dim 1]); 35 | u = sym('u', [obj.u_dim 1]); 36 | if strcmp(obj.type,'state') 37 | if obj.oriented_right 38 | l = symfun(piecewise(x(obj.dimension)>obj.threshold,... 39 | (x(obj.dimension)-obj.threshold)^2, 0), x); 40 | else 41 | l = symfun(piecewise(x(obj.dimension)obj.threshold,... 47 | (u(obj.dimension)-obj.threshold)^2, 0), u); 48 | else 49 | l = symfun(piecewise(u(obj.dimension) obj.threshold 72 | out = (xu(obj.dimension) - obj.threshold)^2; 73 | else 74 | out = 0; 75 | end 76 | else 77 | if xu(obj.dimension) < obj.threshold 78 | out = (xu(obj.dimension) - obj.threshold)^2; 79 | else 80 | out = 0; 81 | end 82 | end 83 | end 84 | 85 | function out = get_Hu(obj, u0) 86 | % Evaluate hessian Hu at u0. 87 | out = obj.Hu(u0, zeros(obj.x_dim,1)); 88 | end 89 | end % end methods 90 | end % end class 91 | 92 | -------------------------------------------------------------------------------- /util/iLQGames/dynamics/Bicycle4D.m: -------------------------------------------------------------------------------- 1 | classdef Bicycle4D 2 | % 4D kinematic bicycle model. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-08, Last modified: 2021-11-08 6 | 7 | properties 8 | L % Wheel base 9 | ts % Sampling time (s) 10 | x_dim % State dimension 11 | u_dim % Control dimension 12 | dfdx % Symbolic Jacobian matrix dfdx 13 | dfdu % Symbolic Jacobian matrix dfdu 14 | end 15 | 16 | methods 17 | function obj = Bicycle4D(L, ts) 18 | % Constructor. 19 | 20 | % Static parameters. 21 | obj.L = L; 22 | obj.ts = ts; 23 | obj.x_dim = 4; 24 | obj.u_dim = 2; 25 | 26 | % Define linearized dynamics symbolically. 27 | x_sym = sym('x', [obj.x_dim 1]); 28 | u_sym = sym('u', [obj.u_dim 1]); 29 | 30 | f = [x_sym(4) * cos(x_sym(3)); 31 | x_sym(4) * sin(x_sym(3)); 32 | (x_sym(4) / obj.L) * tan(u_sym(2)); 33 | u_sym(1)]; 34 | 35 | obj.dfdx = matlabFunction(jacobian(f,x_sym),'Vars',{x_sym,u_sym}); 36 | obj.dfdu = matlabFunction(jacobian(f,u_sym),'Vars',{x_sym,u_sym}); 37 | end 38 | 39 | function x_dot = dynamicsODE(obj, x, u) 40 | % Compute the time derivative of state for a particular x/u. 41 | % 4D (kinematic) bicycle model. Dynamics are as follows: 42 | % \dot x = v cos(phi) 43 | % \dot y = v sin(phi) 44 | % \dot phi = (v / L) tan(u2) 45 | % \dot v = u1 46 | % 47 | % Dynamics were taken from: 48 | % https://arxiv.org/abs/1711.03449 49 | 50 | x_dot(1,1) = x(4) * cos(x(3)); 51 | x_dot(2,1) = x(4) * sin(x(3)); 52 | x_dot(3,1) = (x(4) / obj.L) * tan(u(2)); 53 | x_dot(4,1) = u(1); 54 | end 55 | 56 | function x = integrate(obj, x0, u, dt) 57 | % Integrate initial state x0 (applying constant control u) over 58 | % a time interval of length dt. 59 | 60 | t = 0.0; 61 | x = x0; 62 | while t < obj.ts - 1e-8 63 | % Make sure we don't step past T. 64 | step = min(dt, obj.ts - t); 65 | 66 | % Use Runge-Kutta order 4 integration. For details please 67 | % refer to https://en.wikipedia.org/wiki/Runge-Kutta_methods. 68 | k1 = step * obj.dynamicsODE(x, u); 69 | k2 = step * obj.dynamicsODE(x + 0.5 * k1, u); 70 | k3 = step * obj.dynamicsODE(x + 0.5 * k2, u); 71 | k4 = step * obj.dynamicsODE(x + k3, u); 72 | 73 | x = x + (k1 + 2.0 * k2 + 2.0 * k3 + k4) / 6.0; 74 | t = t + step; 75 | end 76 | end 77 | 78 | function [Ac, Bc] = linearize(obj, x0, u0) 79 | % Compute the Jacobian linearization of the dynamics for a 80 | % particular state x0 and control u0. Outputs A and B matrices 81 | % of a linear system: 82 | % \dot x - f(x0, u0) = A (x - x0) + B (u - u0) 83 | Ac = obj.dfdx(x0, u0); 84 | Bc = obj.dfdu(x0, u0); 85 | end 86 | 87 | function [Ad, Bd] = linearizeDiscrete(obj, x0, u0) 88 | % Compute the Jacobian linearization of the dynamics for a 89 | % particular state x0 and control u0. Outputs A and B matrices 90 | % of a discrete-time linear system: 91 | % x(k + 1) - x0 = A (x(k) - x0) + B (u(k) - u0) 92 | [Ac, Bc] = obj.linearize(x0, u0); 93 | % sysc = ss(Ac,Bc,eye(obj.x_dim),[]); 94 | % sysd = c2d(sysc,obj.ts); 95 | % Ad = sysd.A; 96 | % Bd = sysd.B; 97 | 98 | % See https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models 99 | % for derivation of discrete-time from continuous time linear 100 | % system. 101 | Ad = expm(Ac * obj.ts); 102 | Bd = pinv(Ac)*(Ad - eye(obj.x_dim))*Bc; 103 | end 104 | 105 | function P = LQRTerminalCost(obj, xF, Q, R) 106 | % Compute LQR terminal cost. 107 | [Ad, Bd] = obj.linearizeDiscrete(xF, [0;0]); 108 | [~,P,~] = dlqr(Ad,Bd,Q,R); 109 | end 110 | 111 | function P = LQRTerminalCost_no_px(obj, xF, Q, R) 112 | % Compute LQR terminal cost (no px). 113 | [Ad, Bd] = obj.linearizeDiscrete(xF, [0;0]); 114 | [~,P,~] = dlqr(Ad(2:4,2:4),Bd(2:4,:),Q(2:4,2:4),R); 115 | end 116 | 117 | function [uSol, xSol, VSol] = NMPC(obj, x0, xF, N, Q, R, ulb,... 118 | uub, plb, pub, vlb, vub, planner) 119 | % Compute an NMPC control. 120 | 121 | % yalmip('clear') 122 | params = planner.params; 123 | 124 | % Define solver settings. 125 | options = sdpsettings('verbose', 0, 'solver', 'snopt',... 126 | 'usex0', 0, 'cachesolvers', 1); 127 | 128 | %-------------------------------------------------------------- 129 | % Define decision variables 130 | %-------------------------------------------------------------- 131 | % States. 132 | x = sdpvar(obj.x_dim, N+1); 133 | 134 | % Inputs. 135 | u = sdpvar(obj.u_dim, N); 136 | 137 | %-------------------------------------------------------------- 138 | % Objective function 139 | %-------------------------------------------------------------- 140 | % DLQR-based terminal cost. 141 | [Ad, Bd] = obj.linearizeDiscrete(xF, [0;0]); 142 | [~,P,~] = dlqr(Ad(2:4,2:4),Bd(2:4,:),Q(2:4,2:4),R); 143 | 144 | objective = (x(2:4,N+1)-xF(2:4))'*P*(x(2:4,N+1)-xF(2:4)); 145 | for i = 1:N 146 | objective = objective + u(:,i)'*R*u(:,i)+... 147 | (x(2:4,i)-xF(2:4))'*Q(2:4,2:4)*(x(2:4,i)-xF(2:4)); 148 | end 149 | 150 | %-------------------------------------------------------------- 151 | % Bounds on the states and inputs 152 | %-------------------------------------------------------------- 153 | constraint = []; 154 | % Input constraints. 155 | for i = 1:N 156 | constraint = [constraint ulb<=u(:,i)<=uub]; 157 | end 158 | 159 | % State constraints. 160 | for i = 1:N+1 161 | constraint = [constraint plb<=x(3,i)<=pub,... % phi bounds 162 | constraint vlb<=x(4,i)<=vub,... % velocity bounds 163 | params.rd_bd_min<=x(2,i)<=params.rd_bd_max]; % road boundaries 164 | end 165 | 166 | %-------------------------------------------------------------- 167 | % Initial state constraints 168 | %-------------------------------------------------------------- 169 | constraint = [constraint x(:,1)==x0]; 170 | 171 | %-------------------------------------------------------------- 172 | % Dynamics constraints 173 | %-------------------------------------------------------------- 174 | for i = 1:N 175 | constraint = [constraint... 176 | x(:,i+1)==x(:,i)+obj.ts*obj.dynamicsODE(x(:,i),u(:,i))]; 177 | end 178 | 179 | %-------------------------------------------------------------- 180 | % Solve the MPC problem 181 | %-------------------------------------------------------------- 182 | status = optimize(constraint, objective, options); 183 | isSolved = (status.problem==0); 184 | if ~isSolved 185 | message = ['solver info: ', status.info ]; 186 | warning(message); 187 | end 188 | 189 | %-------------------------------------------------------------- 190 | % Return values 191 | %-------------------------------------------------------------- 192 | xSol = value(x); 193 | uSol = value(u); 194 | VSol = value(objective); 195 | end 196 | end % end methods 197 | end % end class 198 | 199 | -------------------------------------------------------------------------------- /util/iLQGames/dynamics/DubinsCar4D.m: -------------------------------------------------------------------------------- 1 | classdef DubinsCar4D 2 | % 4D Dubins car model. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2022-02-01, Last modified: 2022-02-01 6 | 7 | properties 8 | ts % Sampling time (s) 9 | x_dim % State dimension 10 | u_dim % Control dimension 11 | dfdx % Symbolic Jacobian matrix dfdx 12 | dfdu % Symbolic Jacobian matrix dfdu 13 | end 14 | 15 | methods 16 | function obj = DubinsCar4D(ts) 17 | % Constructor 18 | 19 | % Static parameters 20 | obj.ts = ts; 21 | obj.x_dim = 4; 22 | obj.u_dim = 2; 23 | 24 | % Define linearized dynamics symbolically 25 | x_sym = sym('x', [obj.x_dim 1]); 26 | u_sym = sym('u', [obj.u_dim 1]); 27 | 28 | f = [x_sym(4) * cos(x_sym(3)); 29 | x_sym(4) * sin(x_sym(3)); 30 | u_sym(2); 31 | u_sym(1)]; 32 | 33 | obj.dfdx = matlabFunction(jacobian(f,x_sym),'Vars',{x_sym,u_sym}); 34 | obj.dfdu = matlabFunction(jacobian(f,u_sym),'Vars',{x_sym,u_sym}); 35 | end 36 | 37 | function x_dot = dynamicsODE(obj, x, u) 38 | % Compute the time derivative of state for a particular x/u. 39 | % 4D Dubins car model. Dynamics are as follows: 40 | % \dot x = v cos(theta) 41 | % \dot y = v sin(theta) 42 | % \dot theta = u2 43 | % \dot v = u1 44 | % 45 | % `u1` is the acceleration. 46 | % `u2` is the steering angle. 47 | 48 | x_dot(1,1) = x(4) * cos(x(3)); 49 | x_dot(2,1) = x(4) * sin(x(3)); 50 | x_dot(3,1) = u(2); 51 | x_dot(4,1) = u(1); 52 | end 53 | 54 | function x = integrate(obj, x0, u, dt) 55 | % Integrate initial state x0 (applying constant control u) over 56 | % a time interval of length dt, using a time discretization of 57 | % dt 58 | 59 | t = 0.0; 60 | x = x0; 61 | while t < obj.ts - 1e-8 62 | % Make sure we don't step past T. 63 | step = min(dt, obj.ts - t); 64 | 65 | % Use Runge-Kutta order 4 integration. For details please 66 | % refer to: 67 | % https://en.wikipedia.org/wiki/Runge-Kutta_methods. 68 | k1 = step * obj.dynamicsODE(x, u); 69 | k2 = step * obj.dynamicsODE(x + 0.5 * k1, u); 70 | k3 = step * obj.dynamicsODE(x + 0.5 * k2, u); 71 | k4 = step * obj.dynamicsODE(x + k3, u); 72 | 73 | x = x + (k1 + 2.0 * k2 + 2.0 * k3 + k4) / 6.0; 74 | t = t + step; 75 | end 76 | end 77 | 78 | function [Ac, Bc] = linearize(obj, x0, u0) 79 | % Compute the Jacobian linearization of the dynamics for a 80 | % particular state x0 and control u0. Outputs A and B matrices 81 | % of a linear system: 82 | % \dot x - f(x0, u0) = A (x - x0) + B (u - u0) 83 | Ac = obj.dfdx(x0, u0); 84 | Bc = obj.dfdu(x0, u0); 85 | end 86 | 87 | function [Ad, Bd] = linearizeDiscrete(obj, x0, u0) 88 | % Compute the Jacobian linearization of the dynamics for a 89 | % particular state x0 and control u0. Outputs A and B matrices 90 | % of a discrete-time linear system: 91 | % x(k + 1) - x0 = A (x(k) - x0) + B (u(k) - u0) 92 | [Ac, Bc] = obj.linearize(x0, u0); 93 | % sysc = ss(Ac,Bc,eye(obj.x_dim),[]); 94 | % sysd = c2d(sysc,obj.ts); 95 | % Ad = sysd.A; 96 | % Bd = sysd.B; 97 | 98 | % See https://en.wikipedia.org/wiki/Discretization#Discretization_of_linear_state_space_models 99 | % for derivation of discrete-time from continuous time linear 100 | % system. 101 | Ad = expm(Ac * obj.ts); 102 | Bd = pinv(Ac)*(Ad - eye(obj.x_dim))*Bc; 103 | end 104 | 105 | function P = LQRTerminalCost(obj, xF, Q, R) 106 | % Compute LQR terminal cost. 107 | [Ad, Bd] = obj.linearizeDiscrete(xF, [0;0]); 108 | [~,P,~] = dlqr(Ad,Bd,Q,R); 109 | end 110 | 111 | function P = LQRTerminalCost_no_px(obj, xF, Q, R) 112 | % Compute LQR terminal cost (no px). 113 | [Ad, Bd] = obj.linearizeDiscrete(xF, [0;0]); 114 | [~,P,~] = dlqr(Ad(2:4,2:4),Bd(2:4,:),Q(2:4,2:4),R); 115 | end 116 | end % end methods 117 | end % end class 118 | 119 | -------------------------------------------------------------------------------- /util/iLQGames/dynamics/ProductMultiPlayerDynamicalSystem.m: -------------------------------------------------------------------------------- 1 | classdef ProductMultiPlayerDynamicalSystem 2 | % Implements a multiplayer dynamical system who's dynamics decompose 3 | % into a Cartesian product of single-player dynamical systems. 4 | % Author: Haimin Hu 5 | % Reference: ilqgames by David Fridovich-Keil 6 | % Created: 2021-11-10, Last modified: 2021-11-10 7 | 8 | properties 9 | ts % Sampling time (s) 10 | x_dim % State dimension 11 | x_dims % Cell of index lists of states of each player 12 | u_dim % Control dimension 13 | u_dims % Cell of index lists of controls of each player 14 | num_players % Number of players 15 | subsystems % Cell of DynamicalSystem objects 16 | is_linear % Flag indicating if all subsystems are linear 17 | Ac_lin % Continuous-time A matrix (only for linear case) 18 | Bc_lin % Continuous-time B matrix (only for linear case) 19 | Ad_lin % Discrete-time A matrix (only for linear case) 20 | Bd_lin % Discrete-time B matrix (only for linear case) 21 | end 22 | 23 | methods 24 | function obj = ProductMultiPlayerDynamicalSystem(ts, x_dim,... 25 | x_dims, u_dim, u_dims, subsystems, is_linear) 26 | % Constructor. 27 | obj.ts = ts; 28 | obj.x_dim = x_dim; 29 | obj.x_dims = x_dims; 30 | obj.u_dim = u_dim; 31 | obj.u_dims = u_dims; 32 | obj.num_players = length(subsystems); 33 | obj.subsystems = subsystems; 34 | 35 | if nargin < 7 36 | obj.is_linear = 0; 37 | else 38 | obj.is_linear = is_linear; 39 | end 40 | 41 | if obj.is_linear 42 | Ac = []; 43 | Bc = []; 44 | for i = 1:obj.num_players 45 | [Ac_i, Bc_i] = obj.subsystems{i}.linearize([], []); 46 | Ac = blkdiag(Ac, Ac_i); 47 | Bc = blkdiag(Bc, Bc_i); 48 | end 49 | obj.Ac_lin = Ac; 50 | obj.Bc_lin = Bc; 51 | sysc = ss(obj.Ac_lin, obj.Bc_lin, eye(obj.x_dim), []); 52 | sysd = c2d(sysc, obj.ts); 53 | obj.Ad_lin = sysd.A; 54 | obj.Bd_lin = sysd.B; 55 | end 56 | end 57 | 58 | function x = integrate(obj, x0, u, dt) 59 | % Integrate initial state x0 (applying constant control u) over 60 | % a time interval of length dt, using a time discretization of 61 | % dt. 62 | x = []; 63 | for i = 1:obj.num_players 64 | xi = x0(obj.x_dims{i}); 65 | ui = u(obj.u_dims{i}); 66 | xi = obj.subsystems{i}.integrate(xi, ui, dt); 67 | x = [x; xi]; 68 | end 69 | end 70 | 71 | function [Ac, Bc] = linearize(obj, x0, u0) 72 | % Compute the Jacobian linearization of the dynamics. 73 | Ac = []; 74 | Bc = []; 75 | for i = 1:obj.num_players 76 | x0_i = x0(obj.x_dims{i}); 77 | u0_i = u0(obj.u_dims{i}); 78 | [Ac_i, Bc_i] = obj.subsystems{i}.linearize(x0_i, u0_i); 79 | Ac = blkdiag(Ac, Ac_i); 80 | Bc = blkdiag(Bc, Bc_i); 81 | end 82 | end 83 | 84 | function [Ad, Bd] = linearizeDiscrete(obj, x0, u0) 85 | % Compute linearization & discretization of the system. 86 | [Ac, Bc] = obj.linearize(x0, u0); 87 | % sysc = ss(Ac,Bc,eye(obj.x_dim),[]); 88 | % sysd = c2d(sysc,obj.ts); 89 | % Ad = sysd.A; 90 | % Bd = sysd.B; 91 | 92 | Ad = expm(Ac * obj.ts); 93 | Bd = pinv(Ac)*(Ad - eye(obj.x_dim))*Bc; 94 | 95 | % Ad = eye(obj.x_dim) + Ac*obj.ts; 96 | % Bd = Bc*obj.ts; 97 | end 98 | 99 | function [Ad, Bd_cell] = linearizeDiscrete_B_cell(obj, x0, u0) 100 | % Compute linearization & discretization of the system, output 101 | % Bd as a cell. 102 | if obj.is_linear 103 | Ad = obj.Ad_lin; 104 | Bd_cell = {}; 105 | idx = 1; 106 | for i = 1:obj.num_players 107 | Bd_cell{end+1} =... 108 | obj.Bd_lin(:,idx:idx+obj.subsystems{i}.u_dim-1); 109 | idx = idx+obj.subsystems{i}.u_dim; 110 | end 111 | else 112 | [Ad, Bd] = obj.linearizeDiscrete(x0, u0); 113 | Bd_cell = {}; 114 | idx = 1; 115 | for i = 1:obj.num_players 116 | Bd_cell{end+1} =... 117 | Bd(:,idx:idx+obj.subsystems{i}.u_dim-1); 118 | idx = idx+obj.subsystems{i}.u_dim; 119 | end 120 | end 121 | end 122 | end % end methods 123 | end % end class 124 | 125 | -------------------------------------------------------------------------------- /util/iLQGames/geometry/LineSegment.m: -------------------------------------------------------------------------------- 1 | classdef LineSegment 2 | % Class for 2D line segments. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-08, Last modified: 2021-11-08 6 | 7 | properties 8 | p1 9 | p2 10 | end 11 | 12 | methods 13 | function obj = LineSegment(p1, p2) 14 | % Constructor 15 | obj.p1 = p1; 16 | obj.p2 = p2; 17 | end 18 | 19 | function out = len(obj) 20 | out = norm(obj.p1 - obj.p2); 21 | end 22 | 23 | function out = signed_distance_to(obj, point) 24 | % Compute signed distance to other point. 25 | % Sign convention is positive to the right and negative to the left, e.g.: 26 | % * 27 | % | 28 | % negative | positive 29 | % | 30 | % | 31 | % * 32 | 33 | % Vector from p1 to query. 34 | relative = point - obj.p1; 35 | 36 | % Compute the unit direction of this line segment. 37 | direction = obj.p2 - obj.p1; 38 | direction = direction / norm(direction); 39 | 40 | % Find signed length of projection and of cross product. 41 | projection = relative.x * direction.x + relative.y * direction.y; 42 | cross = relative.x * direction.y - direction.x * relative.y; 43 | if cross >= 0.0 44 | cross_sign = 1.0; 45 | else 46 | cross_sign = -1.0; 47 | end 48 | 49 | if projection < 0.0 50 | % Query lies behind this line segment, so closest distance 51 | % will be from p1. 52 | out = cross_sign * norm(relative); 53 | elseif projection > obj.len() 54 | % Closest distance will be to p2. 55 | out = cross_sign * norm(obj.p2 - point); 56 | else 57 | out = cross; 58 | end 59 | end 60 | end % end methods 61 | end % end class 62 | 63 | -------------------------------------------------------------------------------- /util/iLQGames/geometry/Point.m: -------------------------------------------------------------------------------- 1 | classdef Point 2 | % Point class for 2D points. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-08, Last modified: 2021-11-08 6 | 7 | properties 8 | x 9 | y 10 | end 11 | 12 | methods 13 | function obj = Point(x, y) 14 | % Constructor 15 | obj.x = x; 16 | obj.y = y; 17 | end 18 | 19 | function new_Point = plus(obj, rhs) 20 | new_Point = Point(obj.x + rhs.x, obj.y + rhs.y); 21 | end 22 | 23 | function new_Point = minus(obj, rhs) 24 | new_Point = Point(obj.x - rhs.x, obj.y - rhs.y); 25 | end 26 | 27 | function new_Point = mtimes(obj, rhs) 28 | new_Point = Point(obj.x * rhs, obj.y * rhs); 29 | end 30 | 31 | function new_Point = mrdivide(obj, rhs) 32 | new_Point = Point(obj.x / rhs, obj.y / rhs); 33 | end 34 | 35 | function out = norm_squared(obj) 36 | out = obj.x^2 + obj.y^2; 37 | end 38 | 39 | function out = norm(obj) 40 | out = sqrt(obj.norm_squared()); 41 | end 42 | end % end methods 43 | end % end class 44 | 45 | -------------------------------------------------------------------------------- /util/iLQGames/geometry/Polyline.m: -------------------------------------------------------------------------------- 1 | classdef Polyline 2 | % Polyline class to represent piecewise linear path in 2D. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-08, Last modified: 2021-11-08 6 | 7 | properties 8 | points 9 | end 10 | 11 | methods 12 | function obj = Polyline(points) 13 | % Initialize from a list of points. Keeps only a reference to 14 | % input list. 15 | obj.points = points; 16 | end 17 | 18 | function out = signed_distance_to(obj, point) 19 | % Compute signed distance from this polyline to the given point. 20 | % Sign convention is positive to the right and negative to the left, e.g.: 21 | % * 22 | % | 23 | % negative | positive 24 | % | 25 | % | 26 | % * 27 | % NOTE: for now, we'll just implement this with a naive linear search. 28 | % In future, if we want to optimize at all we can pass in a guess of 29 | % which index we expect the closest point to be close to. 30 | best_signed_distance = 1e8; 31 | for ii = 1:length(obj.points)-1 32 | segment = LineSegment(obj.points(ii), obj.points(ii + 1)); 33 | signed_distance = segment.signed_distance_to(point); 34 | 35 | if abs(signed_distance) < abs(best_signed_distance) 36 | best_signed_distance = signed_distance; 37 | end 38 | end 39 | out = best_signed_distance; 40 | end 41 | 42 | end % end methods 43 | end % end class 44 | 45 | -------------------------------------------------------------------------------- /util/iLQGames/getiLQcontrol.m: -------------------------------------------------------------------------------- 1 | function u_ik = getiLQcontrol(x_k, x_ref_k, u_ref_k, P_ik, alpha_ik,... 2 | alpha_scaling) 3 | % Computes iLQ control law. 4 | % 5 | % Author: Haimin Hu (last modified 2021.11.11) 6 | u_ik = u_ref_k - P_ik * (x_k - x_ref_k) - alpha_scaling * alpha_ik; 7 | end 8 | 9 | -------------------------------------------------------------------------------- /util/iLQGames/iLQSolver.m: -------------------------------------------------------------------------------- 1 | classdef iLQSolver 2 | % Iterative LQ solver. 3 | % Author: Haimin Hu 4 | % Reference: ilqgames by David Fridovich-Keil 5 | % Created: 2021-11-10, Last modified: 2021-11-10 6 | 7 | properties 8 | dynamics % Joint dynamics (an ProductMultiPlayerDynamicalSystem object) 9 | x0 % Initial states 10 | Ps_cell % Cell of P matrices (feedback gains, row = player, col = horizon) 11 | alphas_cell % Cell of alphas (feedforward terms, row = player, col = horizon) 12 | Zs_cell % Cell of Zs (value function matrices, row = player, col = horizon) 13 | player_costs_cell % Cell of PlayerCost (row = player, col = horizon) 14 | u_constraints_cell % Cell of constraints on controls (row = player) 15 | horizon % Game horizon 16 | num_players % Number of players 17 | alpha_scaling % Step size on the alpha 18 | max_iteration % Maximum number of iterations 19 | last_operating_point % Previous operating points 20 | current_operating_point % Current operating points 21 | best_operating_point % Best operating points 22 | tolerence_percentage % Convergence tolerence percentage ([0,1]) 23 | name % Name of the object 24 | end 25 | 26 | methods 27 | function obj = iLQSolver(dynamics, x0, Ps_cell, alphas_cell,... 28 | player_costs_cell, u_constraints_cell, alpha_scaling,... 29 | max_iteration, tolerence_percentage, name) 30 | % Construct an instance of this class. 31 | obj.dynamics = dynamics; 32 | obj.x0 = x0; 33 | obj.Ps_cell = Ps_cell; 34 | obj.alphas_cell = alphas_cell; 35 | obj.player_costs_cell = player_costs_cell; 36 | obj.u_constraints_cell = u_constraints_cell; 37 | obj.num_players = size(player_costs_cell, 1); 38 | obj.horizon = size(player_costs_cell, 2); 39 | obj.alpha_scaling = alpha_scaling; 40 | obj.max_iteration = max_iteration; 41 | obj.tolerence_percentage = tolerence_percentage; 42 | obj.name = name; 43 | 44 | % Operating points consist of x, u, costs, Z. 45 | obj.last_operating_point = []; 46 | obj.current_operating_point = []; 47 | obj.best_operating_point = []; 48 | end 49 | 50 | function obj = run(obj, verbose) 51 | % Run the algorithm for the specified parameters. 52 | iteration = 0; 53 | Ps = obj.Ps_cell; 54 | alphas = obj.alphas_cell; 55 | Zs = cell(obj.num_players, obj.horizon + 1); 56 | while ~(obj.is_converged_cost(verbose)) &&... 57 | (iteration<=obj.max_iteration) 58 | 59 | % (1) Compute current operating point and update last one. 60 | [xs, us, costs] = obj.compute_operating_point(); 61 | obj.last_operating_point = obj.current_operating_point; 62 | obj.current_operating_point.xs = xs; 63 | obj.current_operating_point.us = us; 64 | obj.current_operating_point.costs = costs; 65 | obj.current_operating_point.Ps = Ps; 66 | obj.current_operating_point.alphas = alphas; 67 | obj.current_operating_point.Zs = Zs; 68 | 69 | % (2) Linearize about this operating point. Make sure to 70 | % stack appropriately since we will concatenate state 71 | % vectors but not control vectors, so that 72 | % ``` x_{k+1} - xs_k = A_k (x_k - xs_k) + 73 | % sum_i Bi_k (ui_k - uis_k) ``` 74 | As = cell(1, obj.horizon); 75 | Bs = cell(obj.num_players, obj.horizon); 76 | for k = 1:obj.horizon 77 | [A, B] = obj.dynamics.linearizeDiscrete_B_cell(... 78 | xs(:,k), us(:,k)); 79 | for i = 1:obj.num_players 80 | As{k} = A; 81 | Bs{i,k} = B{i}; 82 | end 83 | end 84 | 85 | % (3) Quadraticize costs. 86 | ls = cell(obj.num_players, obj.horizon); 87 | Qs = cell(obj.num_players, obj.horizon); 88 | Rs = cell(obj.num_players, obj.horizon); 89 | for i = 1:obj.num_players 90 | for k = 1:obj.horizon 91 | us_ik = us(obj.dynamics.u_dims{i}, k); 92 | [~, l, Q, R] =... 93 | obj.player_costs_cell{i,k}.quadraticize(xs(:,k), us_ik); 94 | ls{i,k} = l; 95 | Qs{i,k} = Q; 96 | Rs{i,k} = R; 97 | end 98 | end 99 | 100 | % (4) Compute feedback Nash equilibrium of the resulting 101 | % LQ game. 102 | [Ps, alphas, Zs] = obj.solve_lq_game(As, Bs, ls, Qs, Rs); 103 | 104 | % Report time-accumulated cost for all players. 105 | if verbose 106 | disp(['Total cost for all players: ',... 107 | num2str(sum(costs,2)')]) 108 | end 109 | 110 | % Update the member variables. 111 | obj.Ps_cell = Ps; 112 | obj.alphas_cell = alphas; 113 | 114 | % (5) Check if the current operating point is the best. 115 | if isempty(obj.best_operating_point) 116 | obj.best_operating_point = obj.current_operating_point; 117 | best_cost = sum(costs(:)); 118 | elseif isempty(obj.best_operating_point.Zs{1,1}) 119 | obj.best_operating_point = obj.current_operating_point; 120 | best_cost = sum(costs(:)); 121 | elseif sum(costs(:)) < best_cost &&... 122 | ~isempty(obj.current_operating_point.Zs{1,1}) 123 | obj.best_operating_point = obj.current_operating_point; 124 | best_cost = sum(costs(:)); 125 | end 126 | 127 | iteration = iteration + 1; 128 | end 129 | end 130 | 131 | function [xs, us, costs] = compute_operating_point(obj) 132 | % Compute current operating point by propagating through 133 | % the dynamics. 134 | % OUTPUT: 135 | % xs: x_dim-by-horizon 136 | % us: u_dim-by-horizon 137 | % costs: num_player-by-horizon 138 | xs = obj.x0; 139 | us = []; 140 | costs = []; 141 | 142 | for k = 1:obj.horizon 143 | % Get current state and control. 144 | if isempty(obj.current_operating_point) 145 | current_x = zeros(obj.dynamics.x_dim,1); 146 | current_u = zeros(obj.dynamics.u_dim,1); 147 | else 148 | current_x = obj.current_operating_point.xs(:,k); 149 | current_u = obj.current_operating_point.us(:,k); 150 | end 151 | 152 | % Compute control for each player. 153 | uk = []; 154 | for i = 1:obj.num_players 155 | % Feedback control law for Player i. 156 | x_ref = current_x; 157 | u_ref = current_u(obj.dynamics.u_dims{i}); 158 | P_ik = obj.Ps_cell{i,k}; 159 | alpha_ik = obj.alphas_cell{i,k}; 160 | ui = getiLQcontrol(xs(:,k), x_ref, u_ref, P_ik,... 161 | alpha_ik, obj.alpha_scaling); 162 | 163 | % Clip the control. 164 | if ~isempty(obj.u_constraints_cell{i}) 165 | ui = max(min(ui, obj.u_constraints_cell{i}.max),... 166 | obj.u_constraints_cell{i}.min); 167 | end 168 | 169 | uk = [uk; ui]; 170 | end 171 | us = [us uk]; 172 | 173 | % Compute cost for each player. 174 | costk = []; 175 | for i = 1:obj.num_players 176 | costi = obj.player_costs_cell{i,k}.cost(xs(:,k),... 177 | uk(obj.dynamics.u_dims{i})); 178 | costk = [costk; costi]; 179 | end 180 | costs = [costs costk]; 181 | 182 | % Evolve the dynamics. 183 | xs = [xs obj.dynamics.integrate(xs(:,k), uk,... 184 | obj.dynamics.ts)]; 185 | end 186 | end 187 | 188 | function flag = is_converged_cost(obj, verbose) 189 | % Check if the last two operating points are close enough 190 | % using costs. 191 | if isempty(obj.last_operating_point) 192 | flag = false; 193 | else 194 | last_costs = obj.last_operating_point.costs; 195 | current_costs = obj.current_operating_point.costs; 196 | for i = 1:obj.num_players 197 | last_cost = sum(last_costs(i,:)); 198 | current_cost = sum(current_costs(i,:)); 199 | if norm((current_cost - last_cost)/last_cost) >... 200 | obj.tolerence_percentage 201 | if verbose 202 | disp(['Player ', num2str(i),... 203 | ': (current_cost-last_cost)/last_cost = ',... 204 | num2str(((current_cost-last_cost)/last_cost)),... 205 | '. Convergence criterion not met!']) 206 | end 207 | flag = false; 208 | return 209 | end 210 | end 211 | flag = true; 212 | end 213 | end 214 | 215 | function [Ps, alphas, Zs] = solve_lq_game(obj, As, Bs, ls, Qs, Rs) 216 | % Function to solve time-varying finite horizon LQ game. 217 | % Please refer to Corollary 6.1 on pp. 279 of Dynamic 218 | % Noncooperative Game Theory (second edition) by Tamer Basar 219 | % and Geert Jan Olsder. 220 | 221 | % Initialization. 222 | Ps = cell(obj.num_players, obj.horizon); 223 | alphas = cell(obj.num_players, obj.horizon); 224 | Zs = cell(obj.num_players, obj.horizon + 1); 225 | zetas = cell(obj.num_players, obj.horizon + 1); 226 | for i = 1:obj.num_players 227 | Zs{i,end} = Qs{i,end}; 228 | zetas{i,end} = ls{i,end}; 229 | end 230 | 231 | % Cache dimensions. 232 | u_dims = obj.dynamics.u_dims; 233 | 234 | % Note: notation and variable naming closely follows that 235 | % introduced in the "Preliminary Notation for Corollary 6.1" 236 | % section, which may be found on pp. 279 of Basar and Olsder. 237 | % NOTE: we will assume that `c` from Basar and Olsder is 238 | % always `0`. 239 | 240 | % Backward coupled-Riccati 241 | for k = obj.horizon:-1:1 242 | 243 | % Compute Ps given previously computed Zs. 244 | % Refer to equation 6.17a in Basar and Olsder. 245 | % This will involve solving a system of matrix linear 246 | % equations of the form: 247 | % [S1s; S2s; ...] * [P1; P2; ...] = [Y1; Y2; ...]. 248 | S = []; 249 | for i = 1:obj.num_players 250 | Sis = []; 251 | for j = 1:obj.num_players 252 | if j == 1 253 | Sis = [Sis Rs{i,k}+Bs{i,k}'*Zs{i,k+1}*Bs{i,k}]; 254 | else 255 | Sis = [Sis Bs{i,k}'*Zs{i,k+1}*Bs{j,k}]; 256 | end 257 | end 258 | S = [S; Sis]; 259 | end 260 | 261 | Y = []; 262 | for i = 1:obj.num_players 263 | Y = [Y; Bs{i,k}'*Zs{i,k+1}*As{k}]; 264 | end 265 | 266 | % P = S \ Y; 267 | P = pinv(S)*Y; 268 | 269 | for i = 1:obj.num_players 270 | Ps{i,k} = P(u_dims{i},:); 271 | end 272 | 273 | % Compute F_k = A_k - B1_k P1_k - B2_k P2_k. 274 | % This is eq. 6.17c from Basar and Olsder. 275 | F = As{k}; 276 | for i = 1:obj.num_players 277 | F = F - Bs{i,k}*Ps{i,k}; 278 | end 279 | 280 | % Compute Zs to be the next step earlier in time. 281 | for i = 1:obj.num_players 282 | Z = F'*Zs{i,k+1}*F+Qs{i,k}+Ps{i,k}'*Rs{i,k}*Ps{i,k}; 283 | Zs{i,k} = Z; 284 | end 285 | 286 | % Compute alphas using previously computed zetas. 287 | % Refer to equation 6.17d in Basar and Olsder. 288 | % This will involve solving a system of linear matrix 289 | % equations of the form: 290 | % [S1s; S2s; ...] * [alpha1; alpha2; ..] = [Y1; Y2; ...]. 291 | % In fact, this is the same S matrix as before (just a 292 | % different Y). 293 | Y = []; 294 | for i = 1:obj.num_players 295 | Y = [Y; Bs{i,k}'*zetas{i,k+1}]; 296 | end 297 | 298 | % alpha = S \ Y; 299 | alpha = pinv(S)*Y; 300 | 301 | for i = 1:obj.num_players 302 | alphas{i,k} = alpha(u_dims{i},:); 303 | end 304 | 305 | % Compute beta_k = -B1_k alpha1 - B2_k alpha2_k. 306 | % This is eq. 6.17f in Basar and Olsder (with `c = 0`). 307 | beta = 0; 308 | for i = 1:obj.num_players 309 | beta = beta - Bs{i,k}*alphas{i,k}; 310 | end 311 | 312 | % Compute zetas to be the next step earlier in time. 313 | % This is Remark 6.3 in Basar and Olsder. 314 | for i = 1:obj.num_players 315 | zeta = F'*(zetas{i,k+1}+Zs{i,k+1}*beta)+ls{i,k} +... 316 | Ps{i,k}'*Rs{i,k}*alphas{i,k}; 317 | zetas{i,k} = zeta; 318 | end 319 | end 320 | end 321 | end % end methods 322 | end % end class 323 | 324 | -------------------------------------------------------------------------------- /util/node.m: -------------------------------------------------------------------------------- 1 | classdef node 2 | % Class for a node in the scenario tree. 3 | % Author: Haimin Hu 4 | % Created: 2021-12-11, Last modified: 2021-12-11 5 | 6 | properties 7 | idx % Node index. 8 | pre_node_idx % Index of the pre node. 9 | t % Time step. 10 | xR % Robot's state. 11 | xH % Human's state. 12 | uR % Robot's control. 13 | uH % Human's control. 14 | uH_nom % Human's nominal control (rational). 15 | eps % Soft constraint slack variables. 16 | theta_sample % Sample of theta (transformed). 17 | theta_sample_sn % Sample of theta (sampled from a std. normal). 18 | theta_distr % Distribution of theta. 19 | theta_distr_M_ws % Distribution of theta given M (for warmstart only). 20 | M_sample % Sample of M. 21 | M_sample_prob % Probability of sample of M. 22 | M_distr % Distribution of M. 23 | path_prob % Path transition probability of the node. 24 | xR_dim % Dimension of robot's state. 25 | xH_dim % Dimension of human's state. 26 | uR_dim % Dimension of robot's control. 27 | uH_dim % Dimension of human's control. 28 | theta_dim % Dimension of theta. 29 | M_dim % Dimension of M. 30 | xR_dims % Indices of robot's state. 31 | xH_dims % Indices of human's state. 32 | uR_dims % Indices of robot's control. 33 | uH_dims % Indices of human's control. 34 | end 35 | 36 | methods 37 | function obj = node(idx, pre_node_idx, t, path_prob,... 38 | theta_sample_sn, M_sample, xR_dim, xH_dim, uR_dim, uH_dim,... 39 | theta_dim, M_dim, xR_dims, xH_dims, uR_dims, uH_dims) 40 | % Constructor for the node class. 41 | obj.idx = idx; 42 | obj.pre_node_idx = pre_node_idx; 43 | obj.t = t; 44 | obj.xR = sdpvar(xR_dim,1); 45 | obj.xH = sdpvar(xH_dim,1); 46 | obj.uR = sdpvar(uR_dim,1); 47 | obj.uH = sdpvar(uH_dim,1); 48 | obj.uH_nom = sdpvar(uH_dim,1); 49 | obj.eps.RB_R = sdpvar(1,1); 50 | obj.eps.RB_H = sdpvar(1,1); 51 | obj.eps.CA = sdpvar(1,1); 52 | obj.theta_distr.mu = sdpvar(theta_dim,1); 53 | obj.theta_distr.covar = sdpvar(theta_dim,theta_dim,'diagonal'); 54 | obj.path_prob = path_prob; 55 | obj.theta_sample = sdpvar(theta_dim,1); 56 | obj.theta_sample_sn = theta_sample_sn; 57 | obj.M_sample = M_sample; 58 | obj.M_distr = sdpvar(M_dim,1); 59 | obj.xR_dim = xR_dim; 60 | obj.xH_dim = xH_dim; 61 | obj.uR_dim = uR_dim; 62 | obj.uH_dim = uH_dim; 63 | obj.theta_dim = theta_dim; 64 | obj.M_dim = M_dim; 65 | obj.xR_dims = xR_dims; 66 | obj.xH_dims = xH_dims; 67 | obj.uR_dims = uR_dims; 68 | obj.uH_dims = uH_dims; 69 | end 70 | 71 | 72 | function node_vec = branch(obj, M_set, extraArg) 73 | % Branches from this node to obtain descendants (for dual 74 | % control steps). 75 | node_vec = []; 76 | 77 | % Generate samples of theta from a std. normal (binary). 78 | sample_sn = extraArg.theta_sample_sn; 79 | theta_sample_sn_1 = [sample_sn;-sample_sn]; theta_prob_1 = 1/2; 80 | theta_sample_sn_2 = [-sample_sn;sample_sn]; theta_prob_2 = 1/2; 81 | theta_sample_sn_vec = [theta_sample_sn_1 theta_sample_sn_2]; 82 | theta_prob_vec = [theta_prob_1 theta_prob_2]; 83 | if obj.t >= extraArg.N_M + 1 84 | M_set = obj.M_sample; 85 | end 86 | 87 | for i = 1:numel(theta_prob_vec) 88 | % Create new nodes. 89 | for M = M_set 90 | new_node_path_prob = obj.path_prob * theta_prob_vec(i); 91 | 92 | new_node = node([], obj.idx, obj.t + 1,... 93 | new_node_path_prob, theta_sample_sn_vec(:,i), M,... 94 | obj.xR_dim, obj.xH_dim, obj.uR_dim, obj.uH_dim,... 95 | obj.theta_dim, obj.M_dim, obj.xR_dims,... 96 | obj.xH_dims, obj.uR_dims, obj.uH_dims); 97 | 98 | % Update the descendant node vector. 99 | node_vec = [node_vec new_node]; 100 | end 101 | end 102 | end 103 | 104 | 105 | function new_node = extend(obj) 106 | % Extends this node to obtain one descendant (for exploitation 107 | % steps). 108 | theta_sample_sn_1 = [0; 0]; % mean of the std. normal. 109 | 110 | new_node = node([], obj.idx, obj.t + 1, obj.path_prob,... 111 | theta_sample_sn_1, obj.M_sample, obj.xR_dim, obj.xH_dim,... 112 | obj.uR_dim, obj.uH_dim, obj.theta_dim, obj.M_dim,... 113 | obj.xR_dims, obj.xH_dims, obj.uR_dims, obj.uH_dims); 114 | end 115 | end % end methods 116 | end % end class 117 | 118 | -------------------------------------------------------------------------------- /util/plotSim.m: -------------------------------------------------------------------------------- 1 | function plotSim(planner, XR_in, XH_in, THETA, M_traj, option) 2 | % Plot simulation trajectories. 3 | % 4 | % Author: Haimin Hu (last modified 2021.11.11) 5 | 6 | params = planner.params; 7 | 8 | if option.N_interp == 1 9 | XR = XR_in; 10 | XH = XH_in; 11 | else 12 | % Interpolate data. 13 | XR = []; 14 | for i = 1:size(XR_in,1) 15 | XR = [XR; interp(XR_in(i,:), option.N_interp)]; 16 | end 17 | 18 | XH = []; 19 | for i = 1:size(XH_in,1) 20 | XH = [XH; interp(XH_in(i,:), option.N_interp)]; 21 | end 22 | end 23 | 24 | % Extend the road (default factor: 1.6). 25 | params.rd_len_ub = params.rd_len_ub*1.6; 26 | option.scale = 1; 27 | 28 | % Image rotation to align the front with the world-frame x-axis. 29 | option.rotation = 0; 30 | 31 | % Image coordinates of the centre of the back wheels. 32 | option.centre = [348; 203]; 33 | 34 | % Real world length for scaling. 35 | option.length = 4.5; 36 | option.fps = Inf; 37 | 38 | % Start and end time. 39 | if ~isempty(option.t_start) 40 | t_start = option.t_start; 41 | if t_start ~= 1 42 | t_start = t_start * option.N_interp; 43 | end 44 | else 45 | t_start = 1; 46 | end 47 | 48 | if ~isempty(option.t_end) 49 | t_end = option.t_end * option.N_interp; 50 | else 51 | t_end = length(XR); 52 | end 53 | 54 | % Transparency settings. 55 | if option.keep_traj && option.is_fading 56 | % alpha_vec = linspace(0.1,1,t_end-t_start+option.t_skip); 57 | alpha_vec = logspace(-1,0,t_end-t_start+option.t_skip); 58 | else 59 | alpha_vec = linspace(1,1,length(XR)); 60 | end 61 | 62 | % Figure setup. 63 | fs = 25; 64 | f = figure('Color','white'); 65 | 66 | % Plot in the relative coordinate. 67 | if strcmp(option.coordinate,'rel') 68 | f.Position = [1050 660 1500 665]; 69 | % Plot in the absolute coordinate. 70 | elseif strcmp(option.coordinate,'abs') 71 | f.Position = [0 660 3000 665]; 72 | else 73 | error('Invalid coordinate option') 74 | end 75 | set(gca,'FontSize',fs) 76 | if ~option.UI 77 | set(gca,'Visible','off') 78 | end 79 | hold on 80 | daspect([1,1,1]) 81 | 82 | % Initial (px,py) limits. 83 | road_h_lb = -XH_in(1,1)-4; % horizontal lower bound of the road 84 | if strcmp(option.coordinate,'rel') 85 | xlimSpan = [road_h_lb, params.rd_len_ub]; 86 | elseif strcmp(option.coordinate,'abs') 87 | xlimSpan = [params.rd_len_lb + XH(1,t_start),... 88 | params.rd_len_ub + XR(1,t_start)]; 89 | end 90 | ylimSpan = [params.rd_bd_min-1.5, params.rd_bd_max+1.5]; 91 | xlim(xlimSpan) 92 | ylim(ylimSpan) 93 | 94 | % Plot the roads. 95 | if strcmp(option.coordinate,'rel') 96 | % Road color. 97 | fill([road_h_lb,road_h_lb,params.rd_len_ub,params.rd_len_ub],... 98 | [params.rd_bd_min,params.rd_bd_max,params.rd_bd_max,... 99 | params.rd_bd_min],[191,191,191]/255,'LineStyle','none'); 100 | % Road boundaries. 101 | plot(linspace(road_h_lb,params.rd_len_ub,2),... 102 | linspace(params.rd_bd_min,params.rd_bd_min,2),... 103 | 'k-','LineWidth',5) 104 | plot(linspace(road_h_lb,params.rd_len_ub,2),... 105 | linspace(params.rd_bd_max,params.rd_bd_max,2),... 106 | 'k-','LineWidth',5) 107 | % Center line of the road. 108 | plot(linspace(road_h_lb,params.rd_len_ub,2),... 109 | linspace(0,0,2),'--','LineWidth',8,'Color',[255,255,255]/255) 110 | elseif strcmp(option.coordinate,'abs') 111 | road_end = XR(1,end)+params.rd_len_ub; 112 | % Road color. 113 | fill([xlimSpan(1),xlimSpan(1),road_end,road_end],... 114 | [params.rd_bd_min,params.rd_bd_max,params.rd_bd_max,... 115 | params.rd_bd_min],[191,191,191]/255); 116 | % Road boundaries. 117 | plot(linspace(params.rd_len_lb,road_end,2),... 118 | linspace(params.rd_bd_min,params.rd_bd_min,2),... 119 | 'k-','LineWidth',5) 120 | plot(linspace(params.rd_len_lb,road_end,2),... 121 | linspace(params.rd_bd_max,params.rd_bd_max,2),... 122 | 'k-','LineWidth',5) 123 | % Center line of the road. 124 | plot(linspace(params.rd_len_lb,road_end,2),... 125 | linspace(0,0,2),'--','LineWidth',8,'Color',[255,255,255]/255) 126 | end 127 | 128 | % Plot vehicle movements. 129 | cnt = 1; 130 | for t = t_start:t_end 131 | 132 | if mod(t-t_start,option.t_skip)~=0 && t~=t_end 133 | continue 134 | end 135 | 136 | % Human uncertainty model parameters. 137 | theta_distr_t = THETA(:, ceil(t / option.N_interp)); 138 | M_distr_t = M_traj(:, ceil(t / option.N_interp)); 139 | 140 | % Top-down view of the cars in the relative coordinate. 141 | if strcmp(option.coordinate,'rel') 142 | xR_plt = [XR(1,t)-XH(1,t); XR(2:3,t)]; 143 | [option.image,~,option.alpha] = imread('car_robot_y.png'); 144 | % Transparent snapshots. 145 | try 146 | option.alpha = option.alpha*alpha_vec(cnt); 147 | catch 148 | option.alpha = option.alpha*alpha_vec(end); 149 | end 150 | [~, hR] = plot_vehicle(xR_plt', 'model', option); 151 | xH_plt = [0; XH(2:3,t)]; 152 | [option.image, ~, option.alpha] = imread('car_human.png'); 153 | if ~option.keep_traj && t~=t_end && t>t_start 154 | delete(hH) 155 | end 156 | try 157 | option.alpha = option.alpha*alpha_vec(cnt); 158 | catch 159 | option.alpha = option.alpha*alpha_vec(end); 160 | end 161 | [~, hH] = plot_vehicle(xH_plt', 'model', option); 162 | % Top-down view of the cars in the absolute coordinate. 163 | elseif strcmp(option.coordinate,'abs') 164 | if t>t_start 165 | xlimSpan(1) = xlimSpan(1) + 0.8*(XH(1,t) -... 166 | XH(1,t-option.t_skip)); 167 | xlimSpan(2) = xlimSpan(2) + XR(1,t) -... 168 | XR(1,t-option.t_skip); 169 | xlim(xlimSpan) 170 | end 171 | xR_plt = XR(1:3,t); 172 | [option.image,~,option.alpha] = imread('car_robot_y.png'); 173 | % Transparent snapshots. 174 | option.alpha = option.alpha*alpha_vec(cnt); 175 | [~, hR] = plot_vehicle(xR_plt', 'model', option); 176 | xH_plt = XH(1:3,t); 177 | [option.image, ~, option.alpha] = imread('car_human.png'); 178 | if ~option.keep_traj && t~=t_end && t>t_start 179 | delete(hH) 180 | end 181 | option.alpha = option.alpha*alpha_vec(cnt); 182 | [~, hH] = plot_vehicle(xH_plt', 'model', option); 183 | end 184 | 185 | % Axis and title. 186 | if option.UI 187 | if strcmp(option.coordinate,'rel') 188 | xlabel('$p_x^r$','Interpreter','latex','FontSize',1.2*fs) 189 | elseif strcmp(option.coordinate,'abs') 190 | xlabel('$p_x$','Interpreter','latex','FontSize',1.2*fs) 191 | end 192 | ylabel('$p_y$','Interpreter','latex','FontSize',1.2*fs) 193 | title(['$t = $',... 194 | num2str((t-1)*planner.ts/option.N_interp,'%.1f'),... 195 | ', $\mu^{\theta^l} =$ [',... 196 | num2str(theta_distr_t.l.mu(1),'%.2f'),... 197 | ',', num2str(theta_distr_t.l.mu(2),'%.2f'),... 198 | '], $\mu^{\theta^r} =$ [',... 199 | num2str(theta_distr_t.r.mu(1),'%.2f'),... 200 | ',', num2str(theta_distr_t.r.mu(2),'%.2f'),... 201 | '], $P(M) =$ [', num2str(M_distr_t(1),'%.2f'),... 202 | ',', num2str(M_distr_t(2),'%.2f'), ']'],... 203 | 'Interpreter', 'latex', 'FontSize',1.2*fs) 204 | end 205 | 206 | % Pausing settings. 207 | if t == t_start 208 | pause(1.0) 209 | else 210 | pause(option.pause) 211 | end 212 | 213 | % Delete the last frame. 214 | if t~=t_end 215 | if ~option.keep_traj 216 | delete(hR) 217 | end 218 | end 219 | cnt = cnt + option.t_skip; 220 | end 221 | if ~option.keep_traj 222 | delete(hH) 223 | end 224 | end 225 | 226 | -------------------------------------------------------------------------------- /util/solveiLQgames.m: -------------------------------------------------------------------------------- 1 | function ilq_solvers = solveiLQgames(x, xH_dims, jointSys, jointSys_EH,... 2 | Ps_cell, Ps_cell_EH, alphas_cell, alphas_cell_EH,... 3 | player_costs_cell_l, player_costs_cell_r, player_costs_cell_EH_l,... 4 | player_costs_cell_EH_r, u_constraints_cell, alpha_scaling,... 5 | max_iteration, tolerence_percentage, verbose) 6 | % Solve iLQGames for different theta's and M's. 7 | % 8 | % Author: Haimin Hu (last modified 2021.11.11) 9 | 10 | % -> Human targets the left lane (M = 'l'). 11 | % - Altruistic Human. 12 | ilq_solver_AH_l = iLQSolver(jointSys, x, Ps_cell, alphas_cell,... 13 | player_costs_cell_l, u_constraints_cell, alpha_scaling,... 14 | max_iteration, tolerence_percentage, 'ilq_solver_AH_l'); 15 | ilq_solver_AH_l = ilq_solver_AH_l.run(verbose); 16 | 17 | % - Egoistic Human. 18 | ilq_solver_EH_l = iLQSolver(jointSys_EH, x(xH_dims), Ps_cell_EH,... 19 | alphas_cell_EH, player_costs_cell_EH_l, u_constraints_cell,... 20 | alpha_scaling, max_iteration, tolerence_percentage,... 21 | 'ilq_solver_EH_l'); 22 | ilq_solver_EH_l = ilq_solver_EH_l.run(verbose); 23 | 24 | % -> Human targets the right lane (M = 'r'). 25 | % - Altruistic Human. 26 | ilq_solver_AH_r = iLQSolver(jointSys, x, Ps_cell, alphas_cell,... 27 | player_costs_cell_r, u_constraints_cell, alpha_scaling,... 28 | max_iteration, tolerence_percentage, 'ilq_solver_AH_r'); 29 | ilq_solver_AH_r = ilq_solver_AH_r.run(verbose); 30 | 31 | % - Egoistic Human. 32 | ilq_solver_EH_r = iLQSolver(jointSys_EH, x(xH_dims), Ps_cell_EH,... 33 | alphas_cell_EH, player_costs_cell_EH_r, u_constraints_cell,... 34 | alpha_scaling, max_iteration, tolerence_percentage,... 35 | 'ilq_solver_EH_r'); 36 | ilq_solver_EH_r = ilq_solver_EH_r.run(verbose); 37 | 38 | ilq_solvers.AH_l = ilq_solver_AH_l; 39 | ilq_solvers.AH_r = ilq_solver_AH_r; 40 | ilq_solvers.EH_l = ilq_solver_EH_l; 41 | ilq_solvers.EH_r = ilq_solver_EH_r; 42 | end 43 | 44 | -------------------------------------------------------------------------------- /util/tree.m: -------------------------------------------------------------------------------- 1 | classdef tree 2 | % Class for a scenario tree. 3 | % Author: Haimin Hu 4 | % Created: 2021-12-11, Last modified: 2021-12-11 5 | 6 | properties 7 | Layer % Index set of nodes in each layer. 8 | N % Node set. 9 | num_node % Number of nodes. 10 | planner % The planner object. 11 | end 12 | 13 | methods 14 | function obj = tree(root_node, planner) 15 | % Constructor for the scenario tree. 16 | obj.Layer = cell(1,100); 17 | obj.Layer{1} = 1; 18 | if isnan(root_node.idx) 19 | root_node.idx = 1; 20 | end 21 | if root_node.t ~= 1 22 | root_node.t = 1; 23 | end 24 | obj.N = root_node; 25 | obj.num_node = 1; 26 | obj.planner = planner; 27 | end 28 | 29 | 30 | function obj = addNewNode(obj, node) 31 | % Adds a new node to the tree. 32 | obj.num_node = obj.num_node + 1; 33 | node.idx = obj.num_node; 34 | obj.N = [obj.N node]; 35 | 36 | % Update Layer. 37 | obj.Layer{node.t} = [obj.Layer{node.t} node.idx]; 38 | 39 | % Sanity check. 40 | if length(obj.N) ~= node.idx 41 | error('Node index incorrect! Check for bugs!') 42 | end 43 | end 44 | 45 | 46 | function obj = constructTree(obj, Nd, Ne, M_set, extraArg, verbose) 47 | % Constructs a scenario tree with Nd dual steps and Ne 48 | % exploitation steps. 49 | 50 | % ------- Begin tree construction ------- 51 | % if verbose 52 | % disp('Constructing the scenario tree...') 53 | % end 54 | 55 | % ------- Dual control steps ------- 56 | num_node_dual = 0; 57 | for k = 1:Nd 58 | for idx = obj.Layer{k} 59 | new_node_vec = obj.N(idx).branch(M_set, extraArg); 60 | for new_node = new_node_vec 61 | obj = obj.addNewNode(new_node); 62 | num_node_dual = num_node_dual + 1; 63 | end 64 | end 65 | end 66 | 67 | % ------- Exploitation steps ------- 68 | num_node_expl = 0; 69 | for k = Nd+1:Nd+Ne 70 | for idx = obj.Layer{k} 71 | new_node = obj.N(idx).extend(); 72 | obj = obj.addNewNode(new_node); 73 | num_node_expl = num_node_expl + 1; 74 | end 75 | end 76 | 77 | % ------- End tree construction ------- 78 | 79 | % Report construction results. 80 | % if verbose 81 | % disp('Tree construction completed!') 82 | % for k = 1:Nd+Ne+1 83 | % disp(['Layer #', num2str(k), ': ',... 84 | % num2str(length(obj.Layer{k})), ' node(s)']) 85 | % end 86 | % disp(['Number of dual control node(s): ',... 87 | % num2str(num_node_dual)]) 88 | % disp(['Number of exploitation node(s): ',... 89 | % num2str(num_node_expl)]) 90 | % end 91 | end % end constructTree 92 | end % end methods 93 | end % end class 94 | 95 | -------------------------------------------------------------------------------- /util/updateEmpiricalCost.m: -------------------------------------------------------------------------------- 1 | function J_em = updateEmpiricalCost(J_em, x, xF_R, uR, QR, RR) 2 | % Update the robot's empirical (closed-loop) cost. 3 | % 4 | % Author: Haimin Hu (last modified 2021.11.11) 5 | 6 | J_em = J_em + (x - xF_R)' * QR * (x - xF_R) + uR' * RR * uR; 7 | end 8 | 9 | -------------------------------------------------------------------------------- /util/updateM.m: -------------------------------------------------------------------------------- 1 | function M_distr = updateM(M_distr_now, theta_distr_now, x_next, uR,... 2 | f_x, B_cell, iLQ_result, planner, extraArg) 3 | % Update the distribution of M. 4 | % 5 | % Author: Haimin Hu (last modified 2021.11.11) 6 | 7 | fixed_values = 0; 8 | if isfield(extraArg, 'fixed_values') 9 | if extraArg.fixed_values 10 | fixed_values = 1; 11 | theta_distr_M_fixed = extraArg.theta_distr_M_fixed; 12 | end 13 | end 14 | 15 | xH_dims = planner.xH_dims; 16 | 17 | H_id = planner.H_id; 18 | R_id = planner.R_id; 19 | 20 | BH = B_cell{H_id}; 21 | BR = B_cell{R_id}; 22 | 23 | if fixed_values 24 | mu_theta_l = theta_distr_M_fixed.l.mu; 25 | mu_theta_r = theta_distr_M_fixed.r.mu; 26 | else 27 | mu_theta_l = theta_distr_now.l.mu; 28 | mu_theta_r = theta_distr_now.r.mu; 29 | end 30 | 31 | % Get iLQ results. 32 | uH_AH_l = iLQ_result.AH_l.uH; 33 | uH_EH_l = iLQ_result.EH_l.uH; 34 | uH_l = mu_theta_l(1) * uH_AH_l + mu_theta_l(2) * uH_EH_l; 35 | 36 | uH_AH_r = iLQ_result.AH_r.uH; 37 | uH_EH_r = iLQ_result.EH_r.uH; 38 | uH_r = mu_theta_r(1) * uH_AH_r + mu_theta_r(2) * uH_EH_r; 39 | 40 | % Propagate states with different M's. 41 | x_next_l = f_x + BH * uH_l + BR * uR; 42 | x_next_r = f_x + BH * uH_r + BR * uR; 43 | 44 | % Distribution update. 45 | if isfield(extraArg, 'scale') 46 | scale = extraArg.scale; 47 | else 48 | scale = 1e1; 49 | end 50 | 51 | % Compute surrogate weights. 52 | % weight_l = exp(-scale*norm(x_next_l(xH_dims) - x_next(xH_dims))^2); 53 | % weight_r = exp(-scale*norm(x_next_r(xH_dims) - x_next(xH_dims))^2); 54 | weight_l = exp(-scale*norm(x_next_l(2:3) - x_next(2:3))^2); 55 | weight_r = exp(-scale*norm(x_next_r(2:3) - x_next(2:3))^2); 56 | denom = weight_l * M_distr_now(1) + weight_r * M_distr_now(2); 57 | 58 | M_distr = [weight_l * M_distr_now(1) / denom; 59 | weight_r * M_distr_now(2) / denom]; 60 | 61 | M_distr = (1-planner.M_eps)*M_distr + planner.M_eps*... 62 | planner.M_distr_const; 63 | end 64 | 65 | -------------------------------------------------------------------------------- /util/updateTheta.m: -------------------------------------------------------------------------------- 1 | function [mu, Sigma] = updateTheta(M, mu_now, Sigma_now, Sigma_d,... 2 | x_next, u, f_x, B_cell, iLQ_result, planner, extraArg) 3 | % Update the conditional distribution of theta given M. 4 | % Assumes that Sigma_now and Sigma_d are diagonal matrices. 5 | % 6 | % Author: Haimin Hu (last modified 2021.11.11) 7 | 8 | fixed_values = 0; 9 | if isfield(extraArg, 'fixed_values') 10 | if extraArg.fixed_values 11 | fixed_values = 1; 12 | U_fixed = extraArg.U_fixed; 13 | mu_fixed = extraArg.mu_theta_fixed; 14 | end 15 | end 16 | 17 | uR_dims = planner.uR_dims; 18 | 19 | H_id = planner.H_id; 20 | R_id = planner.R_id; 21 | 22 | BH = B_cell{H_id}; 23 | BR = B_cell{R_id}; 24 | 25 | % Get iLQ results. 26 | if M == 'l' 27 | uH_AH = iLQ_result.AH_l.uH; 28 | Sigma_AH = iLQ_result.AH_l.Sigma; 29 | 30 | uH_EH = iLQ_result.EH_l.uH; 31 | Sigma_EH = iLQ_result.EH_l.Sigma; 32 | elseif M == 'r' 33 | uH_AH = iLQ_result.AH_r.uH; 34 | Sigma_AH = iLQ_result.AH_r.Sigma; 35 | 36 | uH_EH = iLQ_result.EH_r.uH; 37 | Sigma_EH = iLQ_result.EH_r.Sigma; 38 | end 39 | 40 | % Augment covariance. 41 | U = [uH_AH uH_EH]; 42 | 43 | if fixed_values 44 | Sigma_d_aug = Sigma_d +... 45 | mu_fixed(1)^2*BH*U_fixed*Sigma_AH*(BH*U_fixed)'+... 46 | mu_fixed(2)^2*BH*U_fixed*Sigma_EH*(BH*U_fixed)'; 47 | else 48 | Sigma_d_aug = Sigma_d+mu_now(1)^2*BH*U*Sigma_AH*(BH*U)'+... 49 | mu_now(2)^2*BH*U*Sigma_EH*(BH*U)'; 50 | end 51 | 52 | Sigma_now_inv = diag(1 ./ diag(Sigma_now)'); 53 | 54 | % Covariance update. 55 | Sigma_inv = Sigma_now_inv + (BH*U)'*inv(Sigma_d_aug)*(BH*U); 56 | Sigma = diag(1 ./ diag(Sigma_inv)'); 57 | 58 | % Mean update. 59 | uR = u(uR_dims); 60 | mu = Sigma * ((BH*U)'*inv(Sigma_d_aug)*(x_next - f_x - BR*uR) +... 61 | Sigma_now_inv*mu_now); 62 | end 63 | 64 | --------------------------------------------------------------------------------