From 271b2202778afb8014a1b9fee2cfcf6faea3155b Mon Sep 17 00:00:00 2001 From: mcarfagno Date: Thu, 29 Oct 2020 17:40:16 +0000 Subject: [PATCH] updated README to latest version --- .ipynb_checkpoints/README-checkpoint.md | 42 ++++++++++++++++++------ README.md | 11 +++++-- img/quicklatex_equation.png | Bin 0 -> 8513 bytes img/quicklatex_equation_white.png | Bin 0 -> 11529 bytes notebooks/MPC_racecar_tracking.ipynb | 2 +- 5 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 img/quicklatex_equation.png create mode 100644 img/quicklatex_equation_white.png diff --git a/.ipynb_checkpoints/README-checkpoint.md b/.ipynb_checkpoints/README-checkpoint.md index 217e39a..bc6c2b7 100644 --- a/.ipynb_checkpoints/README-checkpoint.md +++ b/.ipynb_checkpoints/README-checkpoint.md @@ -1,36 +1,53 @@ # mpc_python -Python implementation of mpc controller for path tracking. +Python implementation of a mpc controller for path tracking using **[CVXPY](https://www.cvxpy.org/)**. ## About -The MPC is a model predictive path following controller which does follow a predefined reference path Xref and Yref by solving an optimization problem. The resulting optimization problem is shown in the following equation: +The MPC is a model predictive path following controller which does follow a predefined reference by solving an optimization problem. The resulting optimization problem is shown in the following equation: -![](img/quicklatex1.png) +![](img/quicklatex_equation.png) -The vehicle dynamics are described by the differential drive model: +The terns of the cost function are the sum of the **reference tracking error**, **heading effort** and **actuaction rate of change**. + +Where R,P,Q are the cost matrices used to tune the response. + +The vehicle model is described by the bicycle kinematics model using the state space matrices A and B: ![](img/quicklatex2.png) -The state variables of the model are: +The state variables **(x)** of the model are: * **x** coordinate of the robot * **y** coordinate of the robot +* **v** velocuty of the robot * **theta** heading of the robot -The inputs of the model are: +The inputs **(u)** of the model are: -* **v** linear velocity of the robot -* **w** angular velocity of the robot +* **a** linear acceleration of the robot +* **delta** steering angle of the robot ## Demo +The MPC implementation is tested using **[bullet](https://pybullet.org/wordpress/)** physics simulator. Racing car model is from: *https://github.com/erwincoumans/pybullet_robots*. + +![](img/f10.png) + +Results: + ![](img/demo.gif) -To run the demo: +To run the pybullet demo: ```bash -python3 mpc_demo/main.py +python3 mpc_demo/mpc_demo_pybullet.py +``` + +To run the simulation-less demo: + +```bash +python3 mpc_demo/mpc_demo_pybullet.py ``` ## Requirements @@ -38,3 +55,8 @@ python3 mpc_demo/main.py ```bash pip3 install --user --requirement requirements.txt ``` + +## References +* [mpc paper](https://borrelli.me.berkeley.edu/pdfpub/IV_KinematicMPC_jason.pdf) +* [pythonrobotics](https://github.com/AtsushiSakai/PythonRobotics/) +* [pybullet](https://pybullet.org/wordpress/) \ No newline at end of file diff --git a/README.md b/README.md index 36418b6..bc6c2b7 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ Python implementation of a mpc controller for path tracking using **[CVXPY](http The MPC is a model predictive path following controller which does follow a predefined reference by solving an optimization problem. The resulting optimization problem is shown in the following equation: -![](img/quicklatex1.png) +![](img/quicklatex_equation.png) -The terns of the cost function are the sum of the **cross-track error**, **heading error**, **velocity error** and **actuaction effort**. +The terns of the cost function are the sum of the **reference tracking error**, **heading effort** and **actuaction rate of change**. -Where R,P,K,Q are the cost matrices used to tune the response. +Where R,P,Q are the cost matrices used to tune the response. The vehicle model is described by the bicycle kinematics model using the state space matrices A and B: @@ -55,3 +55,8 @@ python3 mpc_demo/mpc_demo_pybullet.py ```bash pip3 install --user --requirement requirements.txt ``` + +## References +* [mpc paper](https://borrelli.me.berkeley.edu/pdfpub/IV_KinematicMPC_jason.pdf) +* [pythonrobotics](https://github.com/AtsushiSakai/PythonRobotics/) +* [pybullet](https://pybullet.org/wordpress/) \ No newline at end of file diff --git a/img/quicklatex_equation.png b/img/quicklatex_equation.png new file mode 100644 index 0000000000000000000000000000000000000000..74eb6f989f78abedf2d3b19d817a2e858b6cd1c1 GIT binary patch literal 8513 zcma)icT`is^KV215u}4smELYNl zAfE{UJaGHeVyK9JL13$`{v2=NwL z0r&kopH@cj0*Ex9KYQsrw-4V=`=T{gop(O+uxkP+no9h#@s7wz$PU7qm*Qoy&`(u= zWh>GH6Tgsw59Z0l{1io;6eqZD_QaWID%y${>ZA-wNnceoUDzT$+0tiCf*u@qN2+%m z(`432SEWt25N>Wm`%Ss6KOqW))V9OrjM@7{+9*7#SQNu<^)9ab#tifrBwVK6)yQZGCWlGv?F17$@T&cP70g-nj_kys5t z08Q(a|EYrCYAvB?>(_1ppC|;K1OH!laxRFF)wzJv09|xk;myWb{6F#tmMpaRJpYB@ zhjkh=6@c`n*`?-{RUW%1E2%g=B*p{x&P8C(C{--+6wQr_EXH1a4&U5Z$-eVw(sQt1 zg-K5TH}94s`ky9mMw__}SkZclHE*lj>)4K8Fmm!uqf4CC%wc2co=>=MxL8}W8|;+y zf#~oeVrKWOTcOY8-GaV+oG`5~eo=p<#-~0H^B^zsjFj>xqvc9K&}}g5 zY?9Hb84Aao?kgM8sx@0rFz~|)+&X60Ig+U6EI;N7xrAxV|H zB^75T2nAbKhXyY#0p4e*q7LBW2)cF}i>B?l?1L_5b~0|I#L`Xv4CmjWmebiS%&&^J zQ=Qcg!>3HMiAjDUOUP_wS@{FsT!`~k&?J}O_r?QX>wcc`yl6YkueTOqJpOcu*^i{BWFK8)f_ou4x!=Ov?^{ANG|@SsYok|MqTe ztilxL(9qk*cdzS<9|(3cB;(MNthRJZ^CmoEYm}mndIo4u%#_};>0A4ZB@(C#0!l&x zqJ-}x9gQ8nZS{`TjkeL&P}b-pz{v~h>^pUQk5wnuGfniBEII2Oc%v;-TOl3W61;B8_Xy`_T(=H?>e4gE-XRNDBY!K< zkh&CePC2`9A?wt(#K^zje$_fxxyU*>wG6~shDDSHriipiE9Bmaz4=a+g)R4ny{ZoqZCG%*X-3S5GgW#Lbv#l?`XldKaZ{NPH#1{D( znBvR)gowI`Lvq^5dC2E9h=%@p9@ZTao2PWLI$#&F;ltj{J*PiQS*;mFqAvdGb!ELw zuo-UXz;^$;0?6Dv7VHedwZ7vgMIsV>WhKcm(I{!Zjbez6P4a4gk5{kDxS@^dL-_Vx-8v z@Ry~f0G8irPtqH%=bb%F#BHM^ko=QAcxjCy?HiSB?mll4L~}%XzJ_&P>_#pWo%C3r z7~iSzn2oskbRN9a%PdU9wez=^Nt=F1X&U_$a{JjWxF^)tT+R5~3uo zevbMQ7{Ik)-cW@}E)znuL7pWTk=PjR%2-!~e?=ATFIcXr8B4a^e)42aD_y2bdbFRO z{4rHk)bSsCy~OK#^uXBBHPcdgJ`R&TpNezlx$|DitF9L zIHRh!jvb3yH#BET64MfjO)luZqEl0TobP?k`669&xFlKX0ROW7+rLr$w7USOPT(q{ ze1BPDZ`#yDuKjtijbdH-6Po=hn=~ehvP8qOMzzuEs=b3L#NeBq<&1S;YL$jkVZ_>; z%mvhAGfQ=b-DOczOPw@NVesX=#iLuJZqFSmis(tYGO!;f-mVu)h6G< z-2SuWbg+!+4o)PADf{>A!%QBDpdd(8++thI zu7->D8($)6yzua$8S}Xe&-fSIpWZ9f%s}cs-7<8a`juYJA_|c$QKxDIL&;e6#L0h3 zJUw`WO=61bp=3O&M%hbF2VkHlh>n23!yVB#{kg`*t-4;H=6l@taZ$2_%DlIZ?g?^s zVTlJn6JvVHZ`r2SE5v_y<;zh1-EKnngm*|C2X zm$D0ANNl)%AE`R0Rj4E2I_>K!!A+M@tn5wM|&oAJ; zxAPAqgv7cx67Nf!oS^5O?z=swL|ZF0}#sIU&x;+je^X z)nC+TBfj~3)_(Tn(ETg# zuhbi3KhpC*jyo?k(&SX1^_%mp<@Ds7$ z&{}_5!-^2S+ywzu^GHzbdij;A?+Ol#}Zx`!GFlN{)4>$|FJc|D-u?~ z+toKx&EJwW&}&S)v7qB;5QfZvRdvJ+Na6eC^-DQ|f12`r`c_*tM;NFma##|svLYi> z6Q|wv#T2LVd11-D^*ZQz%UL7K)OL^YX=rtdnRKjSL)y>pUo}&T0^iUIez7z2*LBuo4)J>LXtEPZZlkYe?^M9u=BuSelK2F0Vgvv zPDClM>-H%3G!1P6gT`QdXRCTDB}GnEo2{K=jmzTRj5pJ{j^$x>9aNhj$j9hqMU7Te z0k4)yHvonSaXu)vU)8(&wtYKd6s%a6u+|JA z(Po(~cxZ3}v@87gUE#K)-+NMg5mG;SDhXxeIu^xTZI8#xL!-*`(s$x!TQ$%JzUb;g z1*I`wm*#&AsXdwbes&lwM6GqbgJg_d_AwD8qphwsh>_SODIl+6nN}-ifRP1nZa2wL z+g!hOx>%EDj^vIt=jC&WGnb6t<11l-u0n^L3_t5n7@gkXweJd*ptWK?wF$~$K*1K- zEM<;uhw#zMmca?I;(1g3*1NnesZ0Iuu)`9Irn$z;sIjnRkQ_K6TyVD|TFYuAbAW!qEe>BB+DpVX2)0C*5qx$zm+wFi$W~VB=t4 zLb~@KWt?qjnaX8 z|CA%4>DQ&t*t%@CiSeEKx56X+&x@xr9X4X50MQsd3;Z_UvTNaJ`^(|w#Blqs1ZW8F zx2oLlb%-bvEaY8$kK#tvFJ$l&72Bvd0^5c38c}>O4;SwLIBLcRqHv@CFUJmPb&qVP zC~UvmM{o71R6C!y5n~~%S_6cSu)u`!p$-E1gw(?-1@0^vzJ!9vI%T34U+MLww@kbv)zA=Ne_V zYg)bxn%_@}2=lqOXyWxERl!E%d~^fj=bbd4{3#(SJ_OUf(3i~ z%Lc*9Aay~du3sP7EIz1~sfvEPG=MB}dwZ)L$S)Q)2-3%%%UZfWF59?&xcE`p>9h>K zINa_*Jd${L1<90v_Czf3DMYt9Thnlo2dJMeCyI8VR6Px}^>aAGj9 z`KVV-l8sf`%CCQ5v($?B^48o|IE3e@iYDE0KtAQ*Bra<ZlI9QLKAWF^gA`^xz zaDmHpOf`AsynMw$QBE*kswF< zfs-(;UyFC(#143t3B?{*^d97fd-B;w9 zW(`E{+DMrM0$5Ccx_VrENvdb0m@1%y1t}?aNo5}4x@?`uLztNbx>6Cms@#}O;2!=| z2r^c4>7zU++(X|%J~e8TO_1AM304QW^nctTc2(5Qu_kq(p4$+fTx>~S+-bJ1oA9%B zkmbqMrPtnkOm4aWPANf0i+E<8B{wCOaHb6=%RQ}=$uUlg>do8s@tz$^ncy1%@}y8) zOSP*atYjBUqBaBs<%BoILsfGuiv8}E(RHwM!qB9 zLhF7$FFlBFsO`~3>Iyi=?a-STD|nb?v}*b|3nhr!&bWRw8NdEYlfVGO2nZ~N9$twA zl2N1f`eHL)N2@W4OYYk^kE!w6UdA!NbVE=)qq8&c*xuwsFJnKg4HLz#7`2dCjH`D@ z1ctZ@M{IWNXWP$nBxPwZ_H|n$td~MD{rTpD=uwWFwWJ6TH;02TYVEjz(T~+?wz~Y0 z3{BpaN2voxq_d~Yc4henfb>C*jM90XV}h(pjFlVkRBy%%<}t3b5kW$c0s1&E!EBkk z;BzX5qw&fX=2@j-5lObm9T`3wT1<4Dcs2F4)G1rhQ5j3E9cds*&(Z9J-!R9O*Uj#jZK3WFSaVy$`!jDJ8+Paa3#h; zIa3YTXQgvz0?X&ql6DN5jf#|{)yKcnMscw0e(bw+%dOy)4NSQBHf})^BB@1j(_`-# zN^G*KBY;xW*H@i>SG5iCk6gP!T0yJ2v37mcJ9@1vT9XYnQ_k@h{1~g^R@=wQg7n9$ zR#;_v9*6OL=;J!+PRWvqcF&X%17^@if1;6dY7rXuS*bh$*?Q^W)5h_@zQ#S*u86>Z z4lxc&exIJw*4(CZ!P@CliJ!4|hgrW}rrQU^aJ<^3w7t7_vMPbpe?)Z*tm%g=9YCGI z1k%vEJFKZ?P8>hB3#G=mg6j@8KLg6=A~;LXRcxDwittx$_|tAuoDx1|vtn{%fJ?*A zhFjoR>XChKnHZb9#Xu(eFyM9>;)mLfI@#{qUh6KT-j@}x(dEyrvkcib725FC_G05$ zpBlHZF1_{M*CS#V1Nwv9&S)T7jBtCBF!P;3ZnYvgv1Pl$H{qDjlD()YD_giTXh{&v zOMi>|K!3HGYmfe6jO^WSy7Xd185Q~ZuXXqrpBX#%O*2nBg*&g99K7bGgPUDc3OJpC4prLrR4@CD`Y>n@V>z2SnNt?m z_Q|~rpFI_8R~d88q!S4eY1cFzg8lzG%z$srji`?ugipn|q-VLMA3-d@VB7R^P+#8J zI!i4Viu_lyBnK?x(OG$%&WnUAx)$>_=3SFi;ZKflFg($5sAa4HP?3#3x;I&bxU3B( zFyd_;v>Hv*Dcrkv1$bAp(8n7np=~45>(a zxZ=^R^CJ6x3)CE-F`(Vj?l0paCYm&CnncOq(yVNObjvMLgb=%Sgf{Ewd+O^BynQ&+ z6*fzUqv@w}1i4|Xt|*Ix#40WvAc`8CL8#=m918MJGs-c#h>I)jfMd2pvVI2)(w(A# z#NGZ@xH$1?&kKSqZ4K+7u%>VW(201EI89zQ*ZBR0P|07KPA4)umE~c|Dk>{cgn>S< zV};mAS13rlGR_voG2?!UC+g7|ADijugK3coV}>e%wfm-+6IA4&K;?aN-L(32pd*v1 zv_QwqJhlG4)eLrumG*y2r$YWiUU7l1eplW;GZj{A4m=oRfFUzVQ3l;IPn5L` zi&n|u`LY4~KH)#N#X9|dEaeyIbh8{NC;d#OfKcXiDe7C?Q)_;DQ0d6)ve-N|Zl_dT z?^?pUwRP}`ZB(#GSmid=a*^!dBM<^Rq*If7a+BEta2%LJT1aar9=f{?_flp=9cY7x$?Jpls&?7S zqW!|`MyDqk1YqK+{+^MoVWO*+*maNm(SGCy?8P)>mi@V(Hptn)46nrI$N<^JLIMMU zHO<*7r2Ls8Wd@gPz#*HzBB-^rOw;fnL1C96d+`UyHhyPm^O8U6YMqUl_7t+M5X?FS zHoZ6Opm@Q&R9wmP&q7dwJDg|*w*QLeX>k{Di#jbUpTq@oQ}>?d0Y59R$0(UH&aP%k z3&65lB%>cu^o~1^e`sp<3^5 z8NSdFkV}BH;9ZiLYBo+z+gO>n-)E^Kg#*p_3fs41*nadJqx~v1zL+x*kHKEw)F;3PJIOSKTmzJxRRjwog{=LT{#q#yR_r59qzOLN$Cv~h zQpcIXMrwiYwza`;jge%Bi>!lX)v10K6c1yk4y=I|>06cKmX;pn6MwjZJiNw^t(EQjLBkaw*x&J4pRDU82U;O7J|fv;$h0ZXB2# zxX$|4FjZN_{pdF>-de94!rDs2QWR1@D8^;Cj$Hl&^(_N-OZ=k|?tNRXr_x%6mxGsO z7GJ6mCkwwVjHLh1slkXRlRCbHp{T?)qpuYQUdpD&=LPKt8V%nT0#WeRKe(Xwo!|N; z7I%vGeQm|`)f35{qxHXGIM^Sa-P%7ff~TqO`j@wG_$hJ>zGyG-UB6Ep=4w3|^r6^$ z-TAwnM__C>Qo8yZW0lieSFa5h{^nZ}zorWkhFErY`>+;xU|%HIdW{ylC{h{ZZol~N zKnVT7W^XM0InL*;nLkPC&~VUiI0~*SY(r&@9b#rigj+z0#b$`0wKb{>zWgK#R^Noj z_&av@$#TqfupQc{`J#5i!{}Y@1MjM0;VCD1J)R()%GtWsbs7C_CU?KbI*+!|dQ*Go zAX;vM55Rk?$F>gcKuXCxt3c;ieu{Y4*ESBFmm|L2Bb@ubbW!>M=-;Yo*fSZKso$Yo zdhCVcve~jo4p(XjSHp!cK7~-5M-F)l4YaWwO1kE?mM^eqDJe|l485M=tNOM6>#y|w zkl5&GIrb3825f?!+=LNc+~FSjlE<_qa7|1W-6E{igqWBbR{9MGC;jG2(Fc>!Tb{Ar zb07V_jMDZGpHX8Gvd8SwGyUFWK)8zazuHS~?O2H8vbt|I{`pC-%Qkb}kh;J3t!lJ5 zF5#UYQuvCZ$7W3su+X-5yKc-GbOU^C%b^z$ z9OP+H4>RF@QWAUfKG8zudzQ;9SzOhmv!Hs59_Mb{zSkE77B z&QWr&?esje3HGxPk62Qn5lu$xb`P}Nr&ZF>b6LgHG?8O{ZRV*i1?eMe@BW)*?4_W4 zN*HO-@t1AN+oveBZ=%N4soBQG>*`w1z_9cG85Gs0DT%Oy1a}?CHzVQ*#WNDSu87=8 z^4G2}hI9htMSQ5uRp`4OQJdyTSz2X`eb1}FEH*BLhKF}ikX)+&aB*xmvd%6(^<7V( z6k%BA7p&)vN7xIT2X^ zzPYy7EnJ1FrRufuvAs%mVieFV^mft6HfUq5kac@RM;~fW`Vc62D4_J^e%bM#B|Y%6 zKXIlmcokyajmUs*m2%(3-gnzTH3_$_XTM)gB6;~L9O|})Stnxdxh`d%L5qbCB}lhQ zU%Tww6L`63BDpniX;}^Pm-_GEco5PXiQ#JPO_|7w>mI6Vop^`ao=ab{Aj8&*-h67I z9WG5wq)XTo{u3l1wnOHhxTg@&_-?uMypQe9s{!Rs$>0iIDFNqCJEEz0=i*lMS~rbfJMO zZ%6nBIP{JMa1!s|4%pGDEdYz$Jo^7y|HuBv3cnL6vn@Ptoa>*AI0YM|0Z{J$_byV}2j`2F7**dF<|;u!#%YI@HrRjfb!4-#>jD*ylh literal 0 HcmV?d00001 diff --git a/img/quicklatex_equation_white.png b/img/quicklatex_equation_white.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f6b8e82b7b3f4164dc5c4d49897473b178fd29 GIT binary patch literal 11529 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 f{~_S$Y+-8!{@(?3wx?Ba0sz&QK&4W}_n-d{x