PSOPT Manual R4
User Manual:
Open the PDF directly: View PDF .
Page Count: 437
Download | |
Open PDF In Browser | View PDF |
PSOPT Optimal Control Solver User Manual Release 4.0.0 build 2019.02.24 Victor M. Becerra Email: v.m.becerra@ieee.org http://www.psopt.org Copyright c 2019 Victor M. Becerra Disclaimer This software is provided “as is” and is distributed free of charge. It comes with no warrantees of any kind. See the license terms for more details. The author does hope, however, that users will find this software useful for research and other purposes. 1 Licensing Agreement The software package PSOPT is distributed under the GNU Lesser General Public License version 2.1. Users of the software must abide by the terms of the license. GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave 2 you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author’s reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user’s freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. 3 Although the Lesser General Public License is Less protective of the users’ freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library’s complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and 4 distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of 5 the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer’s own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: 6 a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user’s computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining 7 where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients’ exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add 8 an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS 9 Contents 1 Introduction to PSOPT 14 1.1 What is PSOPT . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.1.1 Why use PSOPT and what alternatives exist . . . . . 15 1.2 PSOPT user’s group . . . . . . . . . . . . . . . . . . . . . . . 17 1.2.1 About the author . . . . . . . . . . . . . . . . . . . . . 17 1.2.2 Contacting the author . . . . . . . . . . . . . . . . . . 17 1.2.3 How you can help . . . . . . . . . . . . . . . . . . . . 17 1.3 What is new in Release 4 . . . . . . . . . . . . . . . . . . . . 18 1.4 General problem formulation . . . . . . . . . . . . . . . . . . 19 1.5 Overview of the Legendre and Chebyshev pseudospectral methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 1.5.1 Introduction to pseudospectral optimal control . . . . 20 1.6 Pseudospectral approximations . . . . . . . . . . . . . . . . . 22 1.6.1 Interpolation and the Lagrange polynomial . . . . . . 22 1.6.2 Polynomial expansions . . . . . . . . . . . . . . . . . . 22 1.6.3 Legendre polynomials and numerical quadrature . . . 23 1.6.4 Interpolation and Legendre polynomials . . . . . . . . 25 1.6.5 Approximate differentiation . . . . . . . . . . . . . . . 26 1.6.6 Approximating a continuous function using Chebyshev polynomials . . . . . . . . . . . . . . . . . . . . . 28 1.6.7 Differentiation with Chebyshev polynomials . . . . . . 31 1.6.8 Numerical quadrature with the Chebyshev-Gauss-Lobatto method . . . . . . . . . . . . . . . . . . . . . . . . . . 31 1.6.9 Differentiation with reduced round-off errors . . . . . 32 1.7 The pseudospectral discretizations used in PSOPT . . . . . 32 1.7.1 Costate estimates . . . . . . . . . . . . . . . . . . . . . 37 1.7.2 Discretizing a multiphase problem . . . . . . . . . . . 37 1.8 Parameter estimation problems . . . . . . . . . . . . . . . . . 38 1.8.1 Single phase case . . . . . . . . . . . . . . . . . . . . . 39 1.8.2 Multi-phase case . . . . . . . . . . . . . . . . . . . . . 40 1.8.3 Statistical measures on parameter estimates . . . . . . 41 1.8.4 Remarks on parameter estimation . . . . . . . . . . . 41 1.9 Alternative local discretizations . . . . . . . . . . . . . . . . 42 10 1.10 1.11 1.12 1.13 1.14 1.15 1.9.1 Trapezoidal method . . . . . . . . . . . . . 1.9.2 Hermite-Simpson method . . . . . . . . . . 1.9.3 Central difference method . . . . . . . . . . 1.9.4 Costate estimates with local discretizations External software libraries used by PSOPT . . . 1.10.1 BLAS and CLAPACK (or LAPACK) . . . 1.10.2 DMatrix library . . . . . . . . . . . . . . . 1.10.3 SuiteSparse . . . . . . . . . . . . . . . . . . 1.10.4 LUSOL . . . . . . . . . . . . . . . . . . . . 1.10.5 IPOPT . . . . . . . . . . . . . . . . . . . . 1.10.6 ADOL-C . . . . . . . . . . . . . . . . . . . 1.10.7 GNUplot (optional) . . . . . . . . . . . . . Supported platform . . . . . . . . . . . . . . . . . . Repository and home page . . . . . . . . . . . . . . Release numbering . . . . . . . . . . . . . . . . . . Installing and compiling PSOPT . . . . . . . . . 1.14.1 Ubuntu Linux 18.4 . . . . . . . . . . . . . . Limitations and known issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Defining optimal control and estimation problems for PSOPT 2.1 Interface data structures . . . . . . . . . . . . . . . . . . . . . 2.2 Required functions . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 endpoint cost function . . . . . . . . . . . . . . . . . 2.2.2 integrand cost function . . . . . . . . . . . . . . . . 2.2.3 dae function . . . . . . . . . . . . . . . . . . . . . . . 2.2.4 events function . . . . . . . . . . . . . . . . . . . . . 2.2.5 linkages function . . . . . . . . . . . . . . . . . . . . 2.2.6 Main function . . . . . . . . . . . . . . . . . . . . . . . 2.3 Specifying a parameter estimation problem . . . . . . . . . . 2.4 Automatic scaling . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Differentiation . . . . . . . . . . . . . . . . . . . . . . . . . . 2.6 Generation of initial guesses . . . . . . . . . . . . . . . . . . . 2.7 Evaluating the discretization error . . . . . . . . . . . . . . . 2.8 Mesh refinement . . . . . . . . . . . . . . . . . . . . . . . . . 2.8.1 Manual mesh refinement . . . . . . . . . . . . . . . . . 2.8.2 Automatic mesh refinement with pseudospectral grids 2.8.3 Automatic mesh refinement with local collocation . . 2.8.4 LATEX code generation . . . . . . . . . . . . . . . . . . 2.9 Implementing multi-segment problems . . . . . . . . . . . . . 2.10 Other auxiliary functions available to the user . . . . . . . . . 2.10.1 cross function . . . . . . . . . . . . . . . . . . . . . . 2.10.2 dot function . . . . . . . . . . . . . . . . . . . . . . . 2.10.3 get delayed state function . . . . . . . . . . . . . . 2.10.4 get delayed control function . . . . . . . . . . . . . 11 43 44 45 45 45 45 46 46 46 46 46 47 47 47 47 48 48 48 51 51 51 52 53 54 55 56 57 71 72 73 73 74 75 75 75 77 79 80 82 82 82 82 83 2.10.5 get interpolated state function . . . . . 2.10.6 get interpolated control function . . . 2.10.7 get control derivative function . . . . . 2.10.8 get state derivative function . . . . . . 2.10.9 get initial states function . . . . . . . 2.10.10 get final states function . . . . . . . . . 2.10.11 get initial controls function . . . . . . 2.10.12 get final controls function . . . . . . . 2.10.13 get initial time function . . . . . . . . . 2.10.14 get final time function . . . . . . . . . . 2.10.15 auto link function . . . . . . . . . . . . . 2.10.16 auto link 2 function . . . . . . . . . . . . 2.10.17 auto phase guess function . . . . . . . . . 2.10.18 linear interpolation function . . . . . . 2.10.19 smoothed linear interpolation function 2.10.20 spline interpolation function . . . . . . 2.10.21 bilinear interpolation function . . . . 2.10.22 smooth bilinear interpolation function 2.10.23 spline 2d interpolation function . . . 2.10.24 smooth heaviside function . . . . . . . . 2.10.25 smooth sign function . . . . . . . . . . . . 2.10.26 smooth fabs function . . . . . . . . . . . . 2.10.27 integrate function . . . . . . . . . . . . . 2.10.28 product ad functions . . . . . . . . . . . . 2.10.29 sum ad function . . . . . . . . . . . . . . . 2.10.30 subtract ad function . . . . . . . . . . . . 2.10.31 inverse ad function . . . . . . . . . . . . 2.10.32 rk4 propagate function . . . . . . . . . . 2.10.33 rkf propagate function . . . . . . . . . . 2.10.34 resample trajectory function . . . . . . 2.11 Pre-defined constants . . . . . . . . . . . . . . . . . 2.12 Standard output . . . . . . . . . . . . . . . . . . . 2.13 Implementing your own problem . . . . . . . . . . 2.13.1 Building the user code from Linux . . . . . 3 Examples of using PSOPT 3.1 Alp rider problem . . . . . . . . . 3.2 Brachistochrone problem . . . . . . 3.3 Breakwell problem . . . . . . . . . 3.4 Bryson-Denham problem . . . . . . 3.5 Bryson’s maximum range problem 3.6 Catalytic cracking of gas oil . . . . 3.7 Catalyst mixing problem . . . . . . 3.8 Coulomb friction . . . . . . . . . . 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 . 84 . 85 . 85 . 86 . 86 . 86 . 87 . 87 . 87 . 88 . 88 . 89 . 89 . 90 . 90 . 91 . 92 . 92 . 93 . 93 . 93 . 94 . 95 . 95 . 96 . 96 . 96 . 97 . 99 . 99 . 99 . 100 . 100 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 101 106 114 121 127 133 138 144 3.9 3.10 3.11 3.12 3.13 3.14 3.15 3.16 3.17 3.18 3.19 3.20 3.21 3.22 3.23 3.24 3.25 3.26 3.27 3.28 3.29 3.30 3.31 3.32 3.33 3.34 3.35 3.36 3.37 3.38 3.39 3.40 3.41 3.42 3.43 3.44 DAE index 3 parameter estimation problem . . . . . . . . . . Delayed states problem 1 . . . . . . . . . . . . . . . . . . . . Dynamic MPEC problem . . . . . . . . . . . . . . . . . . . . Geodesic problem . . . . . . . . . . . . . . . . . . . . . . . . . Goddard rocket maximum ascent problem . . . . . . . . . . . Hang glider . . . . . . . . . . . . . . . . . . . . . . . . . . . . Hanging chain problem . . . . . . . . . . . . . . . . . . . . . . Heat difussion problem . . . . . . . . . . . . . . . . . . . . . . Hypersensitive problem . . . . . . . . . . . . . . . . . . . . . Interior point constraint problem . . . . . . . . . . . . . . . . Isoperimetric constraint problem . . . . . . . . . . . . . . . . Lambert’s problem . . . . . . . . . . . . . . . . . . . . . . . . Lee-Ramirez bioreactor . . . . . . . . . . . . . . . . . . . . . Li’s parameter estimation problem . . . . . . . . . . . . . . . Linear tangent steering problem . . . . . . . . . . . . . . . . . Low thrust orbit transfer . . . . . . . . . . . . . . . . . . . . Manutec R3 robot . . . . . . . . . . . . . . . . . . . . . . . . Minimum swing control for a container crane . . . . . . . . . Minimum time to climb for a supersonic aircraft . . . . . . . Missile terminal burn maneouvre . . . . . . . . . . . . . . . . Moon lander problem . . . . . . . . . . . . . . . . . . . . . . Multi-segment problem . . . . . . . . . . . . . . . . . . . . . . Notorious parameter estimation problem . . . . . . . . . . . . Predator-prey parameter estimation problem . . . . . . . . . Rayleigh problem with mixed state-control path constraints . Obstacle avoidance problem . . . . . . . . . . . . . . . . . . . Reorientation of an asymmetric rigid body . . . . . . . . . . . Shuttle re-entry problem . . . . . . . . . . . . . . . . . . . . . Singular control problem . . . . . . . . . . . . . . . . . . . . . Time varying state constraint problem . . . . . . . . . . . . . Two burn orbit transfer . . . . . . . . . . . . . . . . . . . . . Two link robotic arm . . . . . . . . . . . . . . . . . . . . . . . Two-phase path tracking robot . . . . . . . . . . . . . . . . . Two-phase Schwartz problem . . . . . . . . . . . . . . . . . . Vehicle launch problem . . . . . . . . . . . . . . . . . . . . . . Zero propellant maneouvre of the International Space Station 13 149 155 161 167 175 181 189 193 201 206 211 216 223 229 236 240 252 271 276 288 294 300 306 311 316 321 329 335 342 351 356 376 380 388 394 409 Chapter 1 Introduction to PSOPT 1.1 What is PSOPT PSOPT is an open source optimal control package written in C++ that uses direct collocation methods. These methods solve optimal control problems by approximating the time-dependent variables using global or local polynomials. This allows to discretize the differential equations and continuous constraints over a grid of nodes, and to compute any integrals associated with the problem using well known quadrature formulas. Nonlinear programming then is used to find local optimal solutions. PSOPT is able to deal with problems with the following characteristics: • Single or multiphase problems • Continuous time nonlinear dynamics • General endpoint constraints • Nonlinear path constraints (equalities or inequalities) on states and/or control variables • Integral constraints • Interior point constraints • Bounds on controls and state variables • General cost function with Lagrange and Mayer terms. • Free or fixed initial and final conditions • Linear or nonlinear linkages between phases • Fixed or free initial time • Fixed or free final time 14 • Optimisation of static parameters • Parameter estimation problems with sampled measurements • Differential equations with delayed variables. The implementation has the following features: • Automatic scaling • Automatic first and second derivatives using the ADOL-C library • Numerical differentiation by using sparse finite differences • Automatic mesh refinement • Automatic identification of the Jacobian and Hessian sparsity. • DAE formulation, so that differential and algebraic constraints can be implemented in the same C++ function. PSOPT has interfaces to the following NLP solver: • IPOPT: an open source C++ implementation of an interior point method for large scale problems. See https://projects.coin-or.org/Ipopt for further details. 1.1.1 Why use PSOPT and what alternatives exist These are some reasons why users may wish to use PSOPT : • Users who for any reason do not have access to commercial optimal control solvers and wish to employ a free open source package for optimal control which does not need a proprietary software environment to run. • Users who need to link an optimal control solver from stand alone applications written in C++ or other programming languages. • Users who want to do research with the software, for instance by implementing their own problems, or by customising the code. PSOPT does not require a commercial software environment to run on, or to be compiled. PSOPT is fully compatible with the gcc compiler, and has been developed under Linux, a free operating system. Note also that the default NLP solver (IPOPT) requires a sparse linear solver from a range of options, some of which are available at no cost. The author has personally used free linear solver MUMPS. There are commercial tools for solving large scale optimal control problems. Some modern commercial tools include: 15 • SOCS developed by J.T. Betts from Boeing, which is a well known tool that is able to solve very large optimal control problems and uses a direct transcription method. See: http://www.boeing.com/phantom/socs/ • GESOP developed by Astos Solutions GmbH, Germany, which uses various methods for solving complex optimal control problems and includes a graphical user interface. See: http://www.astos.de/products/gesop • PROPT, developed by P. E. Rutquist and M. M. Edvall from Tomlab Optimization, which runs under Matlab and uses pseudospectral methods. See: http://www.tomdyn.com • DIDO, developed by I.M. Ross from the Postgraduate Naval School in Monterey, California, is a package that runs under Matlab and uses pseudospectral methods. See: http://www.elissar.biz/DIDO.html • GPOPS-II [33], which has been developed by Anil Rao (University of Florida) and co-workers. GPOPS-II uses pseudospectral methods and requires Matlab. GPOPS-II. See: http://www.gpops2.com/ Other software tools implementing direct transcription methods for optimal control include: • DIRCOL , authored by O. von Stryk, which is a Fortran 77 based tool that uses a direct collocation method. See: http://www.sim.informatik.tu-darmstadt.de/sw/dircol/dircol.html • DYNOPT, authored by M. Fikar and M. Cizniar, which is a Matlab based tool that uses orthogonal collocation on finite elements. See: http://www.kirp.chtf.stuba.sk/ fikar/research/dynopt/dynopt.htm 16 1.2 PSOPT user’s group A user’s group has been created with the purpose of enabling users to share their experiences with using PSOPT , and to keep a public record of exchanges with the author. It is also a way of being informed about the latest developments with PSOPT and to ask for help. Membership is free and open. The PSOPT user’s group is located at: http://groups.google.com/group/psopt-users-group 1.2.1 About the author Victor M. Becerra obtained his first degree in Electrical Engineering in 1990 from Simon Bolivar University, Caracas Venezuela. Between 1989 and 1991 he worked in power systems analysis and control in CVG Edelca, Caracas. He obtained his PhD for his work on the development of nonlinear optimal control methods from City University, London, in 1994. Between 1994 and 1999 he was a Research Fellow at the Control Engineering Research Centre at City University, London. Between 2000 and 2015 he was an academic at the School of Systems Engineering, University of Reading, UK, where he became a Professor of Automatic Control in 2012. Between 2011 and 2012, he was seconded at the Ford Motor Company in Dunton, Essex, with funding by the Royal Academy of Engineering, where he developed methods for the calibration of gasoline engine oil temperature dynamic models. In 2015, he took the position of Professor of Power Systems Engineering at the University of Portsmouth, UK. He is a Senior Member of the IEEE, a Senior Member of the AIAA, and a Fellow of the Institute of Engineering and Technology. During his career, he has received research funding from the EPSRC, the Royal Academy of Engineering, the European Union, the Knowledge Transfer Partnership programme, Innovate UK and UK industry. He has published over 140 research papers and two books. His web site is: https://sites.google.com/a/port.ac.uk/victor-becerra/home. 1.2.2 Contacting the author The author is open to discussing with users potential research collaboration leading to publications, academic exchanges, or joint projects. He can be contacted directly for help on the installation and use of the software. His email address is: v.m.becerra@ieee.org. 1.2.3 How you can help You may help improve PSOPT in a number of ways. • Sending bug reports. • Sending corrections to the documentation. 17 • Discussing with the author ways to improve the computational aspects or capabilities of the software. • Sending to the author proposed modifications to the source code, for consideration to be included in a future release of PSOPT . • Sending source code with new examples which may be included (with due acknowledgement) in future releases of PSOPT . • Porting the software to new architectures. • If you have had a good experience with PSOPT , tell your students or colleagues about it. • Quoting the use of PSOPT in your scientific publications. This document may be referenced as follows: – Becerra, V.M. (2010). ”Solving complex optimal control problems at no cost with PSOPT”. Proc. IEEE Multi-conference on Systems and Control, Yokohama, Japan, September 7-10, 2010, pp. 1391-1396 – Becerra, V.M. (2010). PSOPT Optimal Control Solver User Manual. Release 3. Available: http://code.google.com/p/psopt/ downloads/list • Developing interfaces to other NLP solvers. 1.3 What is new in Release 4 1. PSOPT now builds on the latest long term Ubuntu release, which is Ubuntu 18.04. 2. Support of newer versions of IPOPT and ADOL-C. 3. Fixing of various memory leaks (with thanks to Flavio Santes) 4. Improvements and corrections to local mesh refinement procedure (with thanks to Emmanuel Schneider) 5. General improvements to the code. 6. Fixing of various bugs (with thanks to Flavio Santes and Emmanuel Schneider) 7. Support for a newer version of GNUPLOT. 8. Additional examples 18 9. Removed official support for SNOPT solver (this can be reinstated by users, if desired. The source code for the old interface is still there but it does not work at present) 10. Removed support for Microsoft Windows (this can be reinstated by users if desired. The old Makefiles are still part of the distribution, but they have not been updated) 1.4 General problem formulation PSOPT solves the following general optimal control problem with Np phases: Problem P1 (i) (i) Find the control trajectories, u(i) (t), t ∈ [t0 , tf ], state trajectories x(i) (t), t ∈ (i) (i) (i) (i) [t0 , tf ], static parameters p(i) , and times t0 , tf , i = 1, . . . , Np , to minimise the following performance index: J= Np X " (i) (i) ϕ(i) [x(i) (tf ), p(i) , tf ] (i) tf Z + (i) # L(i) [x(i) (t), u(i) (t), p(i) , t]dt t0 i=1 subject to the differential constraints: (i) (i) ẋ(i) (t) = f (i) [x(i) (t), u(i) (t), p(i) , t], t ∈ [t0 , tf ], the path constraints (i) (i) (i) (i) hL ≤ h(i) [x(i) (t), u(i) (t), p(i) , t] ≤ hU , t ∈ [t0 , tf ], the event constraints: (i) (i) (i) (i) (i) (i) (i) (i) eL ≤ e(i) [x(i) (t0 ), u(i) (t0 ), x(i) (tf ), u(i) (tf ), p(i) , t0 , tf ] ≤ eU , the phase linkage constraints: (1) (1) (1) (1) (2) (2) (2) (2) Ψl ≤ Ψ[x(1) (t0 ), u(1) (t0 ), (1) (1) (2) (2) x(1) (tf ), u(1) (tf ), p(1) , t0 , tf , x(2) (t0 ), u(2) (t0 ) ,x(2) (tf ), u(2) (tf ), p(2) , t0 , tf , .. . (Np ) x(Np ) (t0 (Np ) ), u(Np ) (t0 ), (N ) (N ) (N ) (N ) x(Np ) (tf p ), u(Np ) (tf p )), p(Np ) , t0 p , tf p ] 19 ≤ Ψu the bound constraints: (i) (i) (i) (i) (i) (i) (i) (i) uL ≤ ui (t) ≤ uU , t ∈ [t0 , tf ], xL ≤ xi (t) ≤ xU , t ∈ [t0 , tf ], (i) (i) pL ≤ p(i) ≤ pU , (i) (i) (i) (i) (i) (i) t0 ≤ t0 ≤ t̄0 , tf ≤ tf ≤ t̄f , and the following constraints: (i) (i) tf − t0 ≥ 0, where i = 1, . . . , Np , and (i) (i) (i) (i) (i) (i) u(i) : [t0 , tf ] → Rnu x(i) : [t0 , tf ] → Rnx (i) p(i) ∈ Rnp (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) (i) ϕ(i) : Rnx × Rnx × Rnp × R × R → R (1.1) L(i) : Rnx × Rnu × Rnp × [t0 , tf ] → R f (i) : Rnx × Rnu × Rnp × [t0 , tf ] → Rnx h(i) : Rnx × Rnu × Rnp × [t0 , tf ] → Rnh (i) (i) (i) e(i) : Rnx × Rnu × Rnx × Rnu ×c1 = 1.0; md->c2 = 100.0; md->c3 = 1000.0; In the main() function, after the problem data structure has been declared, then the pointer to the instance of the Mydata data structure can be stored in the user_data member of the problem data structure, as follows: problem.user_data = (void*) md; Finally, the user can access this data from within the interface functions. For example, to access the data from within the integrand_cost function, the following can be done: adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[ CINDEX(1) ]; 61 adouble x2 = states[ CINDEX(2) ]; adouble u = controls[ CINDEX(1) ]; Mydata* md = (Mydata*) workspace->problem->user_data; double c1 = md->c1; double c2 = md->c2; double c3 = md->c3; return ( c1*x1*x1 + c2*x2*x2 + c3*u*u ); } Specifying algorithm options Algorithm options and parameters can be specified as follows: algorithm.nlp_method algorithm.scaling algorithm.defect_scaling algorithm.derivatives algorithm.collocation_method algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.print_level algorithm.jac_sparsity_ratio algorithm.hess_sparsity_ratio algorithm.hessian algorithm.mesh_refinement algorithm.ode_tolerance algorithm.mr_max_iterations algorithm.mr_min_extrapolation_points algorithm.mr_initial_increment algorithm.mr_kappa algorithm.mr_M1 algorithm.switch_order algorithm.mr_max_increment_factor = = = = = = = = = = = = = = = = = = = = STRING; STRING; STRING; STRING; STRING; INTEGER; REAL; INTEGER; REAL; REAL; STRING; STRING; REAL; INTEGER; INTEGER; INTEGER; REAL; INTEGER; INTEGER; REAL; Note that: • algorithm.nlp_method takes the (only) option “IPOPT” (default). • algorithm.scaling takes the options “automatic” (default) or “user”. 62 • algorithm.defect_scaling takes the options “state-based” (default) or “jacobian-based”. • algorithm.derivatives takes the options “automatic” (default) or “numerical”. • algorithm.collocation_method takes the options “Legendre” (default), “Chebyshev”, “trapezoidal”, or “Hermite-Simpson”. • algorithm.diff_matrix takes the options “standard” (default), “reducedroundoff”, or “central-differences”. • algorithm.print_level takes the values 1 (default), which causes PSOPT and the NLP solver to print information on the screen, or 0 to supress all output. • algorithm.nlp_tolerance is a real positive number that is used as a tolerance to check convergence of the NLP solver (default 10−6 ). • algorithm.jac_sparsity_ratio is a real number in the interval (0,1] which indicates the maximum Jacobian density, which is ratio of nonzero elements to the total number of elements of the NLP constraint Jacobian matrix (default 0.5). • algorithm.hess_sparsity_ratio is a real number in the interval (0,1] which indicates the maximum Hessian density, ratio of nonzero elements to the total number of elements of the NLP Hessian matrix (default 0.2). • algorithm.hessian takes the options “reduced-memory” or “exact”. The “exact” option is only used together with the IPOPT NLP solver. • algorithm.nsteps_error_integration is an integer number that gives the number of integration steps to be taken within each interval when calculating the relative ODE error. The default value is 10. • algorithm.mesh_refinement takes the values “manual” (default) or “automatic”. • algorithm.ode_tolerance is a small real value that is used as one of the stopping criteria for mesh refinement. If the maximum relative ODE error falls below this value, the mesh refinement iterations are terminated. The default value is 10−3 . • algorithm.mr_max_iterations is a positive integer with the maximum number of mesh refinement iterations (default 7). 63 • algorithm.mr_min_extrapolation_points is the minimum number points to use to calculate the regression that is employed to extrapolate the number of nodes. This is only used if a global collocation method is employed (default 2). • algorithm.mr_initial_increment is a positive integer with the initial increment in the number of nodes. This is only used if a global collocation method is employed (default 10). • algorithm.mr_kappa is a positive real number used by the local mesh refinement algorithm (default 0.1). • algorithm.mr_M1 is a positive integer used by the local mesh refinement algorithm (default 5). • algorithm.switch_order is a positive integer indicating the local mesh refinement iteration after which the order is switched from 2 (trapezoidal) to 4 (Hermite-Simpson). If the entered value is zero, then the order is not switched and the collocation method specified through the option algorithm.collocation_method is used in all mesh refinement iterations. This option only applies if a local collocation method is specified (default 2). • algorithm.mr_max_increment_factor is a positive real number in the range (0, 1] used by the mesh refinement algorithms (default 0.4). Calling PSOPT Once everything is ready, then the psopt algorithm can be called as follows: psopt(problem, solution, algorithm); Error checking PSOPT will set solution.error flag to “true” if an run time error is caught. This flag can be checked for errors so that appropriate action can be taken once PSOPT returns. A diagostic message will be printed on the screen. The diagnostic message can also be recovered from solution.error message. Moreover, the error message is printed to file error message.txt. PSOPT checks automatically many of the user supplied parameters and will return an error if an inconsisetency is found. The following example shows a call to PSOPT , followed by error checking (in this case, the program exits with code 1 if the error flag is true). psopt(problem, solution, algorithm); if (solution.error_flag) { 64 exit(1); } Postprocessing the results The psopt() function returns the results of the optimisation within the solution data structure. The results may then be post-processed. For example, to save the time, control and state vectors of the first phase, the user may use the following commands: DMatrix x = solution.get_states_in_phase(1); DMatrix u = solution.get_controls_in_phase(1); DMatrix t = solution.get_time_in_phase(1); x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); Plotting with the GNUplot interface If the software GNUplot is available in the system where PSOPT is being run, then the user may employ the plot(), multiplot(), surf(), plot3() and polar() functions, which constitute a simple interface to GNUplot implemented within the PSOPT library. The prototype of the plot() function is as follows: void plot(DMatrix& x, DMatrix& y, const string& title, char* xlabel, char* ylabel, char* legend=NULL, char* terminal=NULL, char* output=NULL); where x is a column or row vector with n elements and y is a matrix with one either row or column dimension equal to n. xlabel is a string with the label for the x-axis, ylabel is a string with the label for y-axis, legend is a string with the legends for each curve that is plotted, separated by commas. terminal is a string with the GNUplot terminal to be used (see Table 2.2), and output is a string with the filename to be used for the output, if any. 65 The function is overloaded, such that the user may plot together curves generated from different x, y pairs, up to three pairs. The additional prototypes are as follows: void plot(DMatrix& x1, DMatrix& y1, DMatrix& x2, DMatrix& y2, const string& title, char* xlabel, char* ylabel, char* legend=NULL, char* terminal=NULL, char* output=NULL); void plot(DMatrix& x1, DMatrix& y1, DMatrix& x2, DMatrix& y2, DMatrix& x3, DMatrix& y3, const string& title, char* xlabel, char* ylabel, char* legend=NULL, char* terminal=NULL, char* output=NULL); For example, if the user wishes to display a plot of the control trajectories of a system with two control variables which have been stored in DMatrix object “u”, and assuming that the corresponding time vector has been stored in DMatrix object “t”, then an example of the syntax to call the plot()function is: plot(t,u,"Control variable","time (s)", "u", "u1 u2"); It is also possible to save plots to graphical files supported by GNUplot. For example, to save the above plot to an encapsulated postscript file (instead of displaying it), the command is as follows: plot(t,u,"Control variable", "time (s)", "u", "u1 u2", "postscript eps", "filename.eps"); The function spplot() allows to plot one or more curves together with one or more sets of isolated points (without joining the dots). This can be useful, for example, to compare how an estimated continuous variable compares with experimental data points. The prototype is as follows: 66 void spplot(DMatrix& x1, DMatrix& y1, DMatrix& x2, DMatrix& y2, const string& title, char* xlabel, char* ylabel, char* legend=NULL, char* terminal=NULL, char* output=NULL); where x1 is a column or row vector with n1 elements and y1 is a matrix with one either row or column dimension equal to n1 . The pair (x1, y1) is used to generate curve(s). x2 is a column or row vector with n2 elements and y2 is a matrix with one either row or column dimension equal to n2 . The pair (x2, y2) is used to plot data points. For example, if the user wishes to display a curve on the basis of the pair (t , y1) and on the same plot compare with experimental points stored in the pair (te , ye), then an example of the syntax to call the spplot()function is: plot(t,y,te, ye, "Data fit for y","time (s)", "u", "y ye"); The multiplot() function allows the user to plot on a single window an array of sub-plots, having one curve per subplot. The function prototype is as follows: void multiplot(DMatrix& x, DMatrix& y, const string& title, char* xlabel, char* ylabel, char* legend, int nrows=0, int ncols=0, char* terminal=NULL, char* output=NULL ) ; where x is a column or row vector with n elements and y is a matrix with one either row or column dimension equal to n, xlabel should be a string with the common label for the x-axis of all subplots, ylabel should be a string with the labels for all y-axes of all subplots, separated by spaces, nrows is the number of rows of the array of subplots, ncols is the number of columns of the array of subplots. If nrows and ncols are not provided, then the array 67 of subplots has a single column. Note that the product nrows*ncols should be equal to n, which is the number of curves to be plotted. For example, if the user wishes to display an array of subplots of the state trajectories of a system with four state variables which have been stored in DMatrix object “y”, and assuming that the corresponding time vector has been stored in DMatrix object “t”, then an example of the syntax to call the multiplot()function is: multiplot(t,y,"State variables","time (s)", "y1 y2 y3 y4", "y1 y2 y3 y4"); In the above case, a 4 × 1 array of sub-plots is produced. If a 2 × 2 array of sub-plots is required, then the following command can be used: multiplot(t,y,"State variables","time (s)", "y1 y2 y3 y4", "y1 y2 y3 y4", 2, 2); The function surf() plots the colored parametric surface defined by three matrix arguments. The prototype of the surf() function is as follows: void surf(DMatrix& x, DMatrix& y, DMatrix& z, const string& title, char* xlabel, char* ylabel, char* zlabel, char* terminal=NULL, char* output=NULL, char* view=NULL); Here view is a character string with two constants , (e.g. “50,60”), where rot_x is an angle in the interval [0, 180] degrees, and rot_y is an angle in the interval [0, 360] degrees. This is used to set the viewing angle of the surface plot. For example, if the user wishes to display a surface plot of a N × M matrix Z with respect to the 1 × N vector X and the 1 × M vector Y , stored, respectively, in DMatrix objects “z”, “x” and “y”, then an example of the syntax to call the surf()function is: surf(x, y, z, "Title", "x-label", "y-label", "z-label"); 68 The function plot3() plots a 3D parametric curve defined by three vector arguments. The prototype of the plot3() function is as follows: void plot3(DMatrix& x, DMatrix& y, DMatrix& z, const string& title, char* xlabel, char* ylabel, char* zlabel, char* terminal=NULL, char* output=NULL char* view = NULL); Here view is a character string with two constants , (e.g. “50,60”), where rot_x is an angle in the interval [0, 180] degrees, and rot_y is an angle in the interval [0, 360] degrees. This is used to set the viewing angle of the 3D plot. For example, if the user wishes to display a 3D parametric curve of a 1 × N vector Z with respect to the 1 × N vector X and the 1 × M vector Y , stored, respectively, in DMatrix objects “z”, “x” and “y”, then an example of the syntax to call the plot3()function is: plot3(x, y, z, "Title", "x-label", "y-label", "z-label"); The function polar() plots a polar curve defined by two vector arguments. The prototype of the polar() function is as follows: void polar(DMatrix& theta, DMatrix& r, const string& title, char* legend=NULL, char* terminal=NULL, char* output=NULL); For example, if the user wishes to display a polar plot using a of a 1 × N vector θ (the angle values in radians), and a 1×N vector r (the corresponding values of the radius), stored, respectively, in DMatrix objects “theta” and “r”, then an example of the syntax to call the polar()function is: polar(theta, r, "Title"); 69 Terminal Description postscript eps pdf Jpeg Png latex Encapsulated postscript Adobe portable document format (pdf) jpg graphical format png graphical format LaTeX graphical code Table 2.2: Some of the available GNUplot output graphical formats The polar() function is overloaded so that the user may plot together up to three different polar curves. The additional prototypes are given below. For two polar curves: void polar(DMatrix& theta, DMatrix& r, DMatrix& theta2, DMatrix& r2, const string& title, char* legend=NULL, char* terminal=NULL, char* output=NULL); For three polar curves. void polar(DMatrix& theta, DMatrix& r, DMatrix& theta2, DMatrix& r2, DMatrix& theta3, DMatrix& r3, const string& title, char* legend=NULL, char* terminal=NULL, char* output=NULL); Some common GNUplot terminals (graphical formats) are given in Table 2.2. See the GNUplot documentation for further details on the keywords needed to specify different graphical formats. http://www.gnuplot.info/documentation.html 70 2.3 Specifying a parameter estimation problem To use the parameter estimation facilities implemented in PSOPT for problems where the observation function is defined, and where there is a set of observed data at given sampling points (see section 1.8). The user needs to specify, for each phase, the number of observed variables and the number of sampling points: problem.phases(iphase).nobserved problem.phases(iphase).nsamples = INTEGER; = INTEGER; where nobserved is the number of simultaneous measurements taking place at each sampling node, and nsamples is the total number of sampling nodes. The above parameters should be entered before calling the function psopt_level2_setup(problem, algorithm). After this, additional information may be entered: problem.phases(iphase).observation_nodes problem.phases(iphase).observations problem.phases(iphase).residual_weights = DMatrix; = DMatrix; = DMatrix; where observation_nodes is a 1× nsamples matrix, observations is a nobserved × nsamples matrix, residual_weights is a 1× nobserved \times nsamples matrix. The residual_weights matrix is by default full of ones. If parameter estimation data for a particular phase is saved in a text file with the column format specified below, then an auxiliary function, which is described below, can be used to load the data: < Time > < Obs. # 1> < Weight # 1> ... < Weight # n> where each column is separated by either tabs or spaces, the first column contains the time stamps of the samples, the second column contains the observations of the first variable, the third column contains the weights for each observation of the first variable, and so on. It is then possible to load observation nodes, observations, and residual weights and assign them to the appropriate fields of the problem structure by using the function load_parameter_estimation_data, whose prototype is given below. void load_parameter_estimation_data() ( Prob& problem, int iphase, char* filename ); Note that the user should not register the problem.end_point_cost or the problem.integrand_cost functions, but the user needs to register problem.observation_function. The prototype of this function is as follows: 71 void observation_function( adouble* observations, adouble* states, adouble* controls, adouble* parameters, adouble& time_k, int k, adouble* xad, int iphase ); where on output the function should return the array of observed variables corresponding to sampling index k at sampling instant time_k. The rest of the interface is the same as for general optimal control problems. 2.4 Automatic scaling If the user specifies the option algorithm.scaling as “automatic”, then PSOPT will calculate scaling factors as follows. 1. Scaling factors for controls, states, static parameters, and time, are computed based on the user supplied bounds for these variables. For finite bounds, the variables are scaled such that their original value multipled by the scaling factor results in a number within the range [−1, 1]. If any of the bounds is greater or equal than the constant inf, then the variable is scaled to lie within the intervals, [−inf, 1], [1, inf] or [−inf, inf].The constant inf is defined in the include file psopt.h as 1 × 1019 . 2. Scaling factors for all constraints (except for the differential defect constraints) are computed as follows. The scaling factor for the i-th constraint is the reciprocal of the norm of the i-th row of the Jacobian of the constraints (Betts, 2001). If the computed norm is zero, then the scaling factor is set to 1. 3. The scaling factors of each differential defect constraint is by default equal to the scaling factor of the corresponding state by default (Betts, 2001). However, if algorithm.defect_scaling is set to “jacobianbased”, then the scaling factors of the differential defect constraints are computed as is done for the other constraints. 4. The scaling factor for the objective function is the reciprocal of the norm of the gradient of the objective function evaluated at the initial guess. If the norm of the objective function at the initial guess is zero, then the scaling factor of the objective function is set to one. 72 2.5 Differentiation Users are encouraged to use, whenever possible, the automatic differentiation facilities provided by the ADOL-C library. The use of automatic derivatives is the default behaviour, but it may be specified explicitly by setting the derivatives option to “automatic”. PSOPT uses the ADOL-C drivers for sparsity determination, Jacobian and gradient evaluation. Automatic derivatives are more accurate than numerical derivatives as they are free of truncation errors. Moreover, PSOPT works faster when using automatic derivatives. There may be cases, however, where it is preferrable or necessary to use numerical derivatives. If the user specifies the option algorithm.derivatives as “numerical”, then PSOPT will calculate the derivatives required by the nonlinear programming algorithm as follows. 1. If IPOPT is being used for optimization, then the Jacobian of the constraints is computed by using sparse finite differences, such that groups of variables are perturbed simultaneously [13]. It is assumed that the Jacobian of the constraint function G(y) is divided into constant and variable terms as follows: ∂G(y) ∂g(y) =A+ ∂y ∂y (2.7) where matrices A and ∂g(y)/∂y do not have non-zero elements with the same indices. The constant part A of the constraint Jacobian is estimated first, and only the variable part of the jacobian ∂g(y)/∂y is estimated by sparse finite differences. The gradient of the objective function is computed by perturbing one variable at a time. Normally the central difference formula is used, but if the perturbed variable is at (or very close to) one of its bounds, then the forward or backward difference formulas are employed. 2.6 Generation of initial guesses If no guesses are supplied by the user, then PSOPT computes the initial guess for the unspecified decision variables as follows. Each variable is assumed to be constant and equal to the mean value of its bounds, provided none of the bounds is defined as inf or -inf. If only one of the bounds is inf or -inf, then the variable is initialized with the value of the other bound. If the upper and lower bounds are inf and -inf, respectively, then the variable is initialized at zero. The variables that are initialized automatically for each phase include: the control variables, the state variables, the static parameters, the initial time, and the final time. 73 The user may also compute initial guesses for the state variables by propagating the differential equations associated with the problem. Two auxiliary functions are provided for this purpose. See section 2.10 for more details. 2.7 Evaluating the discretization error PSOPT evaluates the discretization error using a method adopted from [3]. Define the error in the differential equation as a function of time: ˙ − f [x̃(t), ũ(t), p, t] (t) = x̃(t) where x̃ is an interpolated value of the state vector given the grid point values of the state vector, x̃ is an estimate of the derivative of the state vector given the state vector interpolant, and ũ is an interpolated value of the control vector given the grid points values of the control vector. The type of interpolation used depends on the collocation method employed. For Legendre and Chebyshev methods, the interpolation done by the Lagrange interpolant. For Trapezoidal and Hermite-Simpson methods and central difference methods, cubic spline interpolation is used. The absolute local error corresponding to state i on a particular interval t ∈ [tk , tk+1 ], is defined as follows: Z tk+1 ηi,k = |i (t)|dt tk where the integral is computed using the composite Simpson method. The default number of integration steps for each interval is 10, but this can be changed by means of the input parameter algorithm.nsteps_error_integration. The relative local error is defined as: k = max i where ηi,k wi + 1 N wi = max |x̃i,k |, |x̃˙ i,k | k=1 After each PSOPT run, the sequence k for each phase is available through the solution structure as follows: epsilon = solution.get_relative_local_error_in_phase(iphase) where epsilon is a DMatrix object. The error sequence can be analysed by the user to assess the quality of the discretization. This information may be useful to aid the mesh refinement process. Additionally, the maximum value of the sequence k for each phase is printed in the solution summary at the end of an execution. 74 2.8 2.8.1 Mesh refinement Manual mesh refinement Manual mesh refinement, which is the default option, is performed by interpolating a previous solution based on n1 nodes, into a new mesh based on n2 nodes, where n2 > n1 , and using the interpolated solution as an initial guess for a new optimization. If global collocation is being used, PSOPT employs Lagrange polynomials to perform the interpolation associated with mesh refinement. If local collocation is being used, PSOPT employs cubic splines to perform the interpolation. The variables which are interpolated include the controls, states and Lagrange multipliers associated with the differential defect constraints, which are related to the co-states. The other decision variables (start and final times, and static parameters) do not need to be interpolated. To perform mesh refinement, the user must supply the desired sequence of grid points (or nodes) for each phase through a string constant with the values separated by commas which is assigned to problem.phases(iphase).nodes The number of mesh refinements to be carried out is one less than the amount of nodes to be tried. Note that the number of nodes to be tried must be the same in all phases (but not necessarily their value). For example to try the sequence 10, 20 and 50 nodes in phase iphase, then the following command specifies that: problem.phases(iphase).nodes = "[10, 20, 50]"; If the user wishes to try only a single grid size (with no mesh refinement), this is specified by providing a single value as follows: problem.phases(iphase).nodes = INTEGER; In problems with more than one phase, the length of the node sequence to be tried needs to be the same in each phase, but the actual grid sizes need not be the same betweeen phases. 2.8.2 Automatic mesh refinement with pseudospectral grids If a global collocation method is being used and algorithm.mesh_refinement is set to "automatic", then, mesh refinement is carried out as described below. PSOPT will compute the maximum discretization error (i,m) for every phase i at every mesh refinement iteration m, as described in Section 2.7. 75 The method is based on a nonlinear least squares fit of the maximum discretization error for each phase with respect to the mesh size: ŷ i = ϕ1 θ1 + θ2 where ŷ i is an estimate of log((i) ), ϕ1 = log(N ), θ1 and θ2 are parameters which are estimated based on the mesh refinement history. This is equivalent to modelling the dependency of (i) with respect to the number of nodes Ni as follows: (i) = C 1 Nim where m = −θ1 , C = exp(θ2 ). This dependency relates to the upper bound on the L2 norm of the interpolation error given in [10]. Given a desired tolerance max , this approximation is applied when the discretization error has been reduced for at least two iterations to find an extrapolated number of nodes which reduces the discretization error by a factor of 0.25. The user specifies an initial number of nodes for each phase, as follows: problem.phases(iphase).nodes = INTEGER. The user may also specify values for the following parameters which control the mesh refinement procedure. The default values are those shown: Maximum discretization error, max : algorithm.ode_tolerance = 1.e-3; Maximum increment factor, F : algorithm.mr_max_increment_factor = 0.4; Maximum number of mesh refinement interations, mmax : algorithm.mr_max_iterations = 7; Minimum number of extrapolation points: algorithm.mr_min_extrapolation_points = 3; Initial increment for the number of nodes, ∆N0 : algorithm.mr_initial_increment = 10; The mesh refinement algorithm is decribed below. 1. Set the iteration index m = 1. 2. If m > mmax , terminate. 76 3. Solve the nonlinear programming problem for the current mesh, and find the maximum discretization error (i,m) for each phase i. 4. If (i,m) < max for all phases, terminate. 5. The increment in the number of nodes in each phase i, denoted by ∆Ni , is computed as follows: (a) If m < mmin then ∆Ni = ∆N0 (b) if (i,m) has increased in the last two iterations, then ∆Ni = 5 (c) if (i,m) has decreased in at least the last two iterations, compute the parameters θ1 and θ2 by solving a least squares problem based on the monotonic part of the mesh refinement history, then ∆Ni is computed as follows: yd − θ2 ∆Ni = max int exp − Ni , ∆Nmax θ1 where yd = max(log(0.25(i,m) ), log(0.99max )), and ∆Nmax = F Ni where F is the maximum increment factor. 6. Increment the number of nodes in the mesh for each phase: Ni ← Ni + ∆Ni 7. Set m ← m + 1, and go back to step 2. 2.8.3 Automatic mesh refinement with local collocation If a local collocation method (trapezoidal, Hermite-Simpson) is being used, and algorithm.mesh_refinement is set to "automatic", then, mesh refinement is carried out as described below. PSOPT will compute the discretization error (i,m) for every phase i at every mesh refinement iteration m, as described in Section 2.7. The method is based on the mesh refinement algorithm described by Betts [3]. If the current discretization method is trapezoidal, then the order p = 2, otherwise if the current method is Hermite-Simpson, then p = 4. Conversely, if p changes from 2 to 4, then the discretization method is changed from trapezoidal to Hermite-Simpson. The user specifies an initial number of nodes for each phase, as follows: problem.phases(iphase).nodes = INTEGER. The user may also specify values for the following parameters which control the mesh refinement procedure. The default values are those shown: Maximum discretization error, max : 77 algorithm.ode_tolerance = 1.e-3; Minimum increment factor, κ: algorithm.mr_kappa = 0.1; Maximum increment factor, ρ: algorithm.mr_max_increment_factor = 0.4; Maximum number of mesh refinement interations, mmax : algorithm.mr_max_iterations = 7; Maximum nodes to add within a single interval, M1 : algorithm.mr_M1 = 5; Define M 0 = min(M1 , κNi ) + 1, where Ni is the current number of nodes in phase i. The local mesh refinement algorithm is as follows. 1. Set the iteration index m = 1. 2. If m > mmax , terminate. 3. Solve the nonlinear programming problem for the current mesh, and (i,m) find the discretization error k for each interval k and each phase i. (i,m) 4. If maxk k iterations. < max for all phases, terminate the mesh refinement 5. Select the primary order for the new mesh: (a) If p < 4 and α ≤ 2¯ (i,m) , where ¯(i,m) is the average discretization error in phase i. (b) Otherwise, if p < 4 and i > 2, then set p = 4. 6. Estimate the order reduction. The current and previous grid are compared and the order reduction rk is computed for each interval in each phase. The order reduction is computed from: rk = max[0, min(nint(r̂k ), p)] where r̂k = p + 1 − θk /ηk 1 + Ik where nint() is the nearest integer function, Ik is the number of points being added to interval k, ηk is the estimated discretization error within interval k of the old grid, after the subdivision, and θk is the discretization error on the old grid before the subdivision. 78 7. Construct the new mesh. (a) Compute the interval α with maximum error within phase i: (i,m) (i) α = max k k (b) Terminate step 7 if i. M 0 nodes have been added, and (i) ii. the error is below the tolerance in each phase: α < max and Iα = 0, or (i) iii. the predicted error is well below the tolerance α < κmax and 0 ≤ Iα < M1 , or iv. ρ(Ni − 1) nodes have been added, or v. M1 nodes have been added to a single interval. (c) Add one node to interval α, so that Iα ← Iα + 1. (d) Update the predicted error for interval α using p−rk +1 1 α ← α 1 + Ik (e) Return to step 7(a). 8. Set m ← m + 1 and go back to step 2. 2.8.4 LATEX code generation LATEX code is generated automatically producing a table with a summary of information about the mesh refinement process. It may be useful to include this summary in publications that incorporate results generated with PSOPT . A file named mesh_statistics_$$$.tex is automatically created, unless algorithm.print_level is set to zero, where $$$ represents the characters of problem.outfilename which occur to the left of the file extension point “.”. To include the generated table in a LATEX document, simply use the command: \input{mesh_statistics_$$$.tex} The generated table includes a caption associated with the problem name as set through problem.name, as well as a label which is generated by concatenating the string "mesh_stats_" with the characters of problem.outfilename which occur to the left of the file extension point “.”. The caption and label can easily be changed to suit the user requirements by editing or renaming the generated file. A key to the abbreviations used in the file is also printed. The abbreviations for the discretization methods used are described in Table 2.3 79 Abbreviation LGL-ST LGL-RR LGL-CD CGL-ST CGL-RR CGL-CD TRP H-S Description LGL nodes with standard differentiation matrix given by equation (1.12) LGL nodes with reduced round-off differentiation matrix given by equation (1.24) LGL nodes with reduced central-differences differentiation matrix given by equation (1.24) CLG nodes with standard differentiation matrix given by equation (1.19) LGL nodes with reduced round-off differentiation matrix given by equation (1.24) LGL nodes with reduced central-differences differentiation matrix given by equation (1.24) Trapezoidal discretization, see equation (1.50) Hermite-Simpson discretization, see equation (1.51) Table 2.3: Description of the abbreviations used for the discretization methods which are shown in the automatically generated LATEX table 2.9 Implementing multi-segment problems Sometimes, it is useful for computational or other reasons to define a multisegment problem. A multi-segment problem is an optimal control problem with multiple sequential phases that has the same dynamics and path constraints in each phase. The multi-segment facilities implemented in PSOPT allow the user to specify multi-segment problems in an easier way than defining a multi-phase problem. Special functions are called automatically to patch consecutive segments and ensure state and time continuity across the segment boundaries. To specify a multi-segment problem the it is necessary to create a data structure of the type MSdata (in addition to the problem, algorithm and solution structures) and assign values to its elements as follows: MSdata msdata; msdata.nsegments msdata.nstates msdata.ncontrols msdata.nparameters msdata.npath msdata.n_initial_events msdata.n_final_events msdata.nodes = = = = = = = = INTEGER; INTEGER; INTEGER; INTEGER; INTEGER; INTEGER; INTEGER; INTEGER OR STRING; 80 msdata.continuous_controls = BOOLEAN If it is desired to enforce control continuity across the segment boundaries, then set msdata.continuous_controls to true. By default the controls are allowed to be discontinuous across the segment boundaries. The number of nodes per segment can be speficied as follows (note that it is possible to create grids with segments that have different number of nodes): • as a single value (e.g. 30), such that the same number of nodes is employed in each segment. • If algorithm.mesh_refinement is set to "manual", a character string can be entered with the node sequence to be tried per segment as part of a manual mesh refinement strategy (e.g. “[30, 50, 60]” ). Here the number of values corresponds to the number of mesh refinement iterations to be performed. It is assumed that the same node sequence is tried for each segment. If algorithm.mesh_refinement is set to "automatic", then only the first value of the specified sequence is used to start the automatic mesh refinement iterations. • If algorithm.mesh_refinement is set to "manual", a matrix can be entered, such that each row of the matrix corresponds to the node sequence to be tried in the corresponding segment (a character string can be used to enter the matrix, e.g. “[30, 50, 60; 10, 15, 20; 5, 10, 15]”, noting the semicolons that separate the rows). Here the number of rows corresponds to the number of segments, and the number of columns corresponds to the number of manual mesh refinement iterations to be performed. If algorithm.mesh_refinement is set to "automatic", then only the first value of the specified sequence for each segment is used to start the automatic mesh refinement iterations. After this, the following function should be called: multi_segment_setup(problem, algorithm, msdata); The upper and lower bounds on the relevant event times of the problem (start time for each segment, and end time for the last segment) can be entered as follows: problem.bounds.lower.times = DMATRIX OR STRING; problem.bounds.upper.times = DMATRIX OR STRING; where entered value specifies the time bounds in the following order: (1) (Np ) (2) [t0 , t0 , . . . , t0 81 (Np ) , tf ] At this point, the bound information for segment 1 (phase 1) can be entered (bounds for states, controls, event constraints, path constraints, and parameters), as described in section 2.2.6. This should be followed by the bound information for the event constraints of the last phase or segment. After entering the bound information, the auxiliary function auto_phase_bounds should be called as follows: auto_phase_bounds(problem); The initial guess for the solution can be specified by a call to the function auto_phase_guess. See section 2.10. See section 3.30 for an example on the use of the multi-segment facilities available within PSOPT . 2.10 Other auxiliary functions available to the user PSOPT implements a number of auxiliary functions to help the user define optimal control problems. Most (but not all) of these functions are suitable for use with automatic differentiation. All the functions can also be used with numerical differentiation. See the examples section for further details on the use of these functions. 2.10.1 cross function This function takes two arrays of adoubles x and y, each of dimension 3, and returns in array z (also of dimension 3) the result of the vector cross product of x and y. The prototype of the function is as follows: void cross(adouble* x, adouble* y, adouble* z); 2.10.2 dot function This function takes two arrays of adoubles x and y, each of dimension n, and returns the dot product of x and y. The prototype of the function is as follows: adouble dot(adouble* x, adouble* y, int n); 2.10.3 get delayed state function This function allows the user to implement DAE’s with delayed states. Use only in single-phase problems. Its prototype is as follows: 82 void get_delayed_state(adouble* delayed_state, int state_index, int iphase, adouble& time, double delay, adouble* xad); The function parameters are as follows: • delayed_state: on output, the variable pointed by this pointer contrains the value of the delayed state. • state_index: is the index of the state vector whose delayed value is to be found (starting from 1). • iphase: is the phase index (starting from 1). • time: is the value of the current instant of time within the phase. • delay: is the value of the delay. • xad: is the vector of scaled decision variables. 2.10.4 get delayed control function This function allows the user to implement DAE’s with delayed controls. Use only in single-phase problems. Its prototype is as follows: void get_delayed_control(adouble* delayed_control, int control_index, int iphase, adouble& time, double delay, adouble* xad); • delayed_control: on output, the variable pointed by this pointer contrains the value of the delayed state. • control_index: is the index of the control vector whose delayed value is to be found (starting from 1). • iphase: is the phase index (starting from 1). • time: is the value of the current instant of time within the phase. • delay: is the value of the delay. • xad: is the vector of scaled decision variables. 83 2.10.5 get interpolated state function This function allows the user to obtain interpolated values of the state at arbitrary values of time within a phase. Its prototype is as follows: void get_interpolated_state(adouble* interp_state, int state_index, int iphase, adouble& time, adouble* xad); • interp_state: on output, the variable pointed by this pointer contrains the value of the interpolated state. • state_index: is the index of the state vector whose interpolated value is to be found (starting from 1). • iphase: is the phase index (starting from 1). • time: is the value of the current instant of time within the phase. • xad: is the vector of scaled decision variables. 2.10.6 get interpolated control function This function allows the user to obtain interpolated values of the control at arbitrary values of time within a phase. Its prototype is as follows: void get_interpolated_control(adouble* interp_control, int control_index, int iphase, adouble& time, adouble* xad); • interp_control: on output, the variable pointed by this pointer contains the value of the interpolated control. • control_index: is the index of the control vector whose interpolated value is to be found (starting from 1). • iphase: is the phase index (starting from 1). • time: is the value of the current instant of time within the phase. • xad: is the vector of scaled decision variables. 84 2.10.7 get control derivative function This function allows the user to obtain the value of the derivative of a specified control variable at arbitrary values of time within a phase. Its prototype is as follows: void get_control_derivative(adouble* control_derivative, int control_index, int iphase, adouble& time, adouble* xad) • control_derivative: on output, the variable pointed by this pointer contains the value of the control derivative. • control_index: is the index of the control vector whose interpolated value is to be found (starting from 1). • iphase: is the phase index (starting from 1). • time: is the value of the current instant of time within the phase. • xad: is the vector of scaled decision variables. 2.10.8 get state derivative function This function allows the user to obtain the value of the derivative of a specified state variable at arbitrary values of time within a phase. Its prototype is as follows: void get_state_derivative(adouble* control_derivative, int state_index, int iphase, adouble& time, adouble* xad) • state_derivative: on output, the variable pointed by this pointer contains the value of the state derivative. • state_index: is the index of the state vector whose interpolated value is to be found (starting from 1). • iphase: is the phase index (starting from 1). • time: is the value of the current instant of time within the phase. • xad: is the vector of scaled decision variables. 85 2.10.9 get initial states function This function allows the user to obtain the values of the states at the initial time of a phase. Its prototype is as follows: void get_initial_states(adouble* states, adouble* xad, int iphase); • states: on output, this array contains the values of the initial states within the specified phase. • iphase: is the phase index (starting from 1). • xad: is the vector of scaled decision variables. 2.10.10 get final states function This function allows the user to obtain the values of the states at the final time of a given phase. Its prototype is as follows: void get_initial_states(adouble* states, adouble* xad, int iphase); • states: on output, this array contains the values of the final states within the specified phase. • iphase: is the phase index (starting from 1). • xad: is the vector of scaled decision variables. 2.10.11 get initial controls function This function allows the user to obtain the values of the controls at the initial time of a phase. Its prototype is as follows: void get_initial_controls(adouble* controls, adouble* xad, int iphase); • controls: on output, this array contains the values of the initial controls within the specified phase. • iphase: is the phase index (starting from 1). • xad: is the vector of scaled decision variables. 86 2.10.12 get final controls function This function allows the user to obtain the values of the controls at the final time of a given phase. Its prototype is as follows: void get_initial_controls(adouble* controls, adouble* xad, int iphase); • controls: on output, this array contains the values of the final controls within the specified phase. • iphase: is the phase index (starting from 1). • xad: is the vector of scaled decision variables. 2.10.13 get initial time function This function allows the user to obtain the value of the initial time of a given phase. Its prototype is as follows: adouble get_initial_time(adouble* xad, int iphase); • iphase: is the phase index (starting from 1). • xad: is the vector of scaled decision variables. • The function returns the value of the initial time within the specified phase as an adouble type. 2.10.14 get final time function This function allows the user to obtain the value of the final time of a given phase. Its prototype is as follows: adouble get_final_time(adouble* xad, int iphase); • iphase: is the phase index (starting from 1). • xad: is the vector of scaled decision variables. • The function returns the value of the final time within a phase as an adouble type. 87 2.10.15 auto link function This function allows the user to automatically link two phases by generating suitable state and time continuity constraints. It is assumed that the number of states in the two phases being linked is the same. The function is intended to be called from within the user supplied linkages function. Each call to auto link generates an additional number of linkage constraints given by the number of states being linked plus one. The function prototype is as follows: void auto_link(adouble* linkages, int* index, adouble* xad, int iphase_a, int iphase_b); • linkages: on output, this is the updated array of linkage constraint values. • index: on input, the variable pointed to by this pointer contains the next value of the linkages array to be updated. On output, this value is updated to be used in the next call to the auto_link function. The first time the function is called, the value should be 0. • xad: is the vector of scaled decision variables. • iphase_a: is the phase index (starting from 1) of one phase to be linked. • iphase_b: is the phase index (starting from 1) of the other phase to be linked. 2.10.16 auto link 2 function This function works in a simular way as the auto link function, but it also forces the control variables to be continuous at the boundaries. It requires a match in the number of states and in the number of controls between the phases being linked. Each call to auto link 2 generates an additional number of linkage constraints given by the number of states plus the number of controls, plus one. The function prototype is as follows: void auto_link_2(adouble* linkages, int* index, adouble* xad, int iphase_a, int iphase_b); 88 • linkages: on output, this is the updated array of linkage constraint values. • index: on input, the variable pointed to by this pointer contains the next value of the linkages array to be updated. On output, this value is updated to be used in the next call to the auto_link_2 function. The first time the function is called, the value should be 0. • xad: is the vector of scaled decision variables. • iphase_a: is the phase index (starting from 1) of one phase to be linked. • iphase_b: is the phase index (starting from 1) of the other phase to be linked. 2.10.17 auto phase guess function This function allows the user to automatically specify the initial guess in a multi-segment problem. The function prototype is as follows: void auto_phase_guess(Prob& problem, DMatrix& controls, DMatrix& states, DMatrix& param, DMatrix& time); so that the controls, states, time and static parameters are specified as if the problem was single-phase. 2.10.18 linear interpolation function This function interpolates a point defined function using classical linear interpolation. The function is not suitable for automatic differentiation, so it should only be used with numerical differentiation. This is useful when the problem involves tabular data. The function prototype is as follows: void linear_interpolation(adouble* y, adouble& x, DMatrix& pointx, DMatrix& pointy, int npoints); • y: on output, the variable pointed to by this pointer contains the interpolated function value. 89 • x: is the value of the independent variable for which the interpolated function value is sought. • pointx: is the DMatrix object of independent data points. • pointy: is a DMatrix object of dependent data points. • npoints: is the number of points in the data objects pointx and pointy. 2.10.19 smoothed linear interpolation function This function interpolates a point defined function using a smoothed linear interpolation. The method used avoids joining sharp corners between adjacent linear segments. Instead, smooothed pulse functions are used to join the segments. The function is suitable for automatic differentiation. This is useful when the problem involves tabular data. The function prototype is as follows: void smoothed_linear_interpolation(adouble* y, adouble& x, DMatrix& pointx, DMatrix& pointy, int npoints); • y: on output, the variable pointed to by this pointer contains the interpolated function value. • x: is the value of the independent variable for which the interpolated function value is sought. • pointx: is the DMatrix object of independent data points. • pointy: is a DMatrix object of dependent data points. • npoints: is the number of points in the data objects pointx and pointy. 2.10.20 spline interpolation function This function interpolates a point defined function using cubic spline interpolation. The function is not suitable for automatic differentiation, so it should only be used with numerical differentiation. This is useful when the problem involves tabular data. The function prototype is as follows: void spline_interpolation( adouble* y, adouble& x, 90 DMatrix& pointx, DMatrix& pointy, int npoints); • y: on output, the variable pointed to by this pointer contains the interpolated function value. • x: is the value of the independent variable for which the interpolated function value is sought. • pointx: is the DMatrix object of independent data points. • pointy: is a DMatrix object of dependent data points. • npoints: is the number of points in the data objects pointx and pointy. 2.10.21 bilinear interpolation function The function interpolates functions of two variables on a regular grid using the classical bilinear interpolation method. This is useful when the problem involves tabular data. The function prototype is as follows. void bilinear_interpolation(adouble* adouble& adouble& DMatrix& DMatrix& DMatrix& z, x, y, X, Y, Z) • z: on output the adouble variable pointed to by this pointer contains the interpolated function value. • The adouble pair of variables (x, y) represents the point at which the interpolated value of the function is returned. • X: is a vector (DMatrix object) of dimension nxpoints × 1. • Y: is a vector (DMatrix object) of dimension nypoints × 1. • Z: is a matrix (DMatrix object) of dimensions nxpoints × nypoints. Each element Z(i,j) corresponds to the pair ( X(i), Y(j) ) The function does not deal with sparse data. This function does not allow the use of automatic differentiation, so it should only be used with numerical differentiation. 91 2.10.22 smooth bilinear interpolation function The function interpolates functions of two variables on a regular grid using the a smoothed bilinear interpolation method which allows the use of automatic differentiation. This is useful when the problem involves tabular data. The function prototype is as follows. void smooth_bilinear_interpolation(adouble* z, adouble& x, adouble& y, DMatrix& X, DMatrix& Y, DMatrix& Z) • z: on output the adouble variable pointed to by this pointer contains the interpolated function value. • The adouble pair of variables (x, y) represents the point at which the interpolated value of the function is returned. • X: is a vector (DMatrix object) of dimension nxpoints × 1. • Y: is a vector (DMatrix object) of dimension nypoints × 1. • Z: is a matrix (DMatrix object) of dimensions nxpoints × nypoints. Each element Z(i,j) corresponds to the pair ( X(i), Y(j) ) The function does not deal with sparse data. 2.10.23 spline 2d interpolation function The function interpolates functions of two variables on a regular grid using the a cubic spline interpolation method. This is useful when the problem involves tabular data. The function prototype is as follows. void spline_2d_interpolation(adouble* z, adouble& x, adouble& y, DMatrix& X, DMatrix& Y, DMatrix& Z) • z: on output the adouble variable pointed to by this pointer contains the interpolated function value. • The adouble pair of variables (x, y) represents the point at which the interpolated value of the function is returned. 92 • X: is a vector (DMatrix object) of dimension nxpoints × 1. • Y: is a vector (DMatrix object) of dimension nypoints × 1. • Z: is a matrix (DMatrix object) of dimensions nxpoints × nypoints. Each element Z(i,j) corresponds to the pair ( X(i), Y(j) ) The function does not deal with sparse data. This function does not allow the use of automatic differentiation, so it should only be used with numerical differentiation. 2.10.24 smooth heaviside function This function implements a smooth version of the Heaviside function H(x), defined as H(x) = 1, x > 0, H(x) = 0 otherwise. The approximation is implemented as follows: H(x) ≈ 0.5(1 + tanh(x/a)) (2.8) where a > 0 is a small real number. The function prototype is as follows: adouble smooth_heaviside(adouble x, double a); 2.10.25 smooth sign function This function implements a smooth version of the function sign(x), defined as sign(x) = 1, x > 0, sign(x) = −1, x < 0, and sign(0) = 0. The approximation is implemented as follows: sign(x) ≈ tanh(x/a) (2.9) where a > 0 is a small real number. The function prototype is as follows: adouble smooth_sign(adouble x, double a); See the examples section for further details on usage of this function. 2.10.26 smooth fabs function This function implements a smooth version of the absolute value function |x|. The approximation is implemented as follows: p |x| ≈ x2 + a2 (2.10) where a > 0 is a small real number. The function prototype is as follows: adouble smooth_fabs(adouble x, double a); 93 2.10.27 integrate function The integrate function computes the numerical quadrature Q of a scalar function g over the a single phase as a function of states, controls, static parameters and time. Z Q= (i) tf (i) t0 g[x(i) (t), u(i) (t), p(i) , t]dt (2.11) The integration is done using the Gauss-Lobatto method. This is useful, for example, to incorporate constraints involving integrals over a phase, which can be included as additional event constraints: Z Ql ≤ (i) tf (i) t0 g[x(i) (t), u(i) (t), p(i) , t]dt ≤ Qu (2.12) Function integrate has the following prototype: adouble integrate( adouble (*integrand)(adouble*, adouble*, adouble*, adouble&, adouble*,int), adouble* xad, int iphase ) • integrand: this is a pointer to the function to be integrated. • xad: is the vector of scaled decision variables. • iphase: is the phase index (starting from 1). • The function returns the value of the integral as an adouble type. The user needs to implement separately the integrand function, which must have the prototype: adouble integrand( adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase) • states: this is an array of instantaneous states. 94 • controls: is an array of instantaneous controls. • parameters: is an array of static parameter values. • time: is the value of the current instant of time within the phase. • xad: is the vector of scaled decision variables. • iphase: is the phase index (starting from 1). • the function must return the value of the integrand function given the supplied parameters as an adouble type. 2.10.28 product ad functions There are two versions of this function. The first version has the prototype: void product_ad(const DMatrix& A, const adouble* x, int nx, adouble* y); This function multiplies a constant matrix stored in DMatrix object A by adouble vector stored in array x, which has length nx, and returns the result in adouble array y. The second version has the prototype: void product_ad(adouble* Apr, adouble* Bpr, int na, int ma, int nb, int mb, adouble* ABpr); This function multiplies the (na × nb) matrix stored column by column in adouble array Apr, by the (nb × mb) matrix stored column by column in adouble array Bpr. The result is stored (column by column) in adouble array ABpr. 2.10.29 sum ad function This function adds a matrix or vector stored columnwise in adouble array a, to a matrix or vector of the same dimensions stored columnwise in adouble array b. Both arrays are assumed to have a total of n elements. The result is returned in adouble array c. The function prototype is as follows. 95 void sum_ad(const adouble* a, const adouble*b, int n, adouble* c); 2.10.30 subtract ad function This function subtracts a matrix or vector stored columnwise in adouble array a, to a matrix or vector of the same dimensions stored columnwise in adouble array b. Both arrays are assumed to have a total of n elements. The result is returned in adouble array c. The function prototype is as follows. void subtract_ad(const adouble* a, const adouble*b, int n, adouble* c); 2.10.31 inverse ad function This function computes the inverse of an n ×n square matrix stored columnwise in adouble array a. The result is returned in adouble array ainv, also using columnwise storage. The function prototype is as follows. void inverse_ad(adouble* a, int n, adouble* ainv) 2.10.32 rk4 propagate function This function may be used to generate an initial guess for the state trajectory by propagating the dynamics using 4th order Runge-Kutta integration. Note that no bounds are considered on states or controls and that any path constraints specified in function dae() are ignored. The user needs to specify a control trajectory and the corresponding time vector. The function prototype is as follows: void rk4_propagate( void (*dae)(), DMatrix& control_trajectory, DMatrix& time_vector, DMatrix& initial_state, DMatrix& parameters, Prob & problem, int iphase, DMatrix& state_trajectory); • dae is a pointer to the problem’s dae function; 96 • control_trajectory is a DMatrix object of dimensions problem.phases(iphase).ncontrols × M with the initial guess for the controls. • time_vector is a DMatrix object of dimensions 1 × M with the time instants that correspond to each element of control_trajectory. • initial_state is a DMatrix object of dimensions problem.phases(iphase).nstates × 1 with the value of the initial state vector. • parameters is a DMatrix object of dimensions problem.phases(iphase).nparameters × 1 with given values for the static parameters. • problem is a Prob structure. • iphase is the phase index. • state_trajectory is a DMatrix object with dimensions problem.phases(iphase).nstates × M which on output contains the result of the of the propagation. The values of the states correspond to the time vector time_vector. 2.10.33 rkf propagate function This function may be used to generate an initial guess for the state trajectory by propagating the dynamics using the Runge-Kutta-Fehlberg method with variable step size and relative local truncation error within a given tolerance. Note that no bounds are considered on states or controls and that any path constraints specified in function dae() are ignored. The user needs to specify a control trajectory and the corresponding time vector, as well as minimum and maximum values for the integration step size, and a tolerance. Note that the function throws an error if the minimum step size is violated. The function prototype is as follows: void rkf_propagate( void (*dae)(), DMatrix& control_trajectory, DMatrix& time_vector, DMatrix& initial_state, DMatrix& parameters, double tolerance, double hmin, double hmax, 97 Prob & problem, int iphase, DMatrix& state_trajectory, DMatrix& new_time_vector, DMatrix& new_control_trajectory); • dae is a pointer to the problem’s dae function; • control_trajectory is a DMatrix object of dimensions problem.phases(iphase).ncontrols × M with the initial guess for the controls. • time_vector is a DMatrix object of dimensions 1 × M with the time instants that correspond to each element of control_trajectory. • initial_state is a DMatrix object of dimensions problem.phases(iphase).nstates × 1 with the value of the initial state vector. • parameters is a DMatrix object of dimensions problem.phases(iphase).nparameters × 1 with given values for the static parameters. • tolerance is a positive value for the tolerance against which the maximum relative error in the state vector is compared. • hmin is the minimum integration step size. • hmax is the maximum integration step size. • problem is a Prob structure. • iphase is the phase index. • state_trajectory is a DMatrix object with dimensions problem.phases(iphase).nstates × M which on output contains the result of the of the propagation. The values of the states correspond to the time vector new_time_vector. • new_time_vector is a DMatrix object with dimensions 1 × N which on output contains the time values of the propagation. • new_control_trajectory is a DMatrix object with dimensions problem.phases(iphase).nstates × N which on output containts interpolated values of the control trajectory corresponding to the time vector new_time_vector. Linear interpolation is employed. 98 2.10.34 resample trajectory function This function resamples a trajectory given new values of the time vector using natural cubic spline interpolation. void resample_trajectory(DMatrix& DMatrix& DMatrix& DMatrix& Y, t, Ydata, tdata ) • Y is, on output, a DMatrix object with dimensions ny × N with the interpolated values of the dependent variable. • t is a DMatrix object of dimensions 1 × N with the values of the independent variable at which the interpolated values are required. The elements of this vector should be monotonically increasing, i.e. t(j+1) > t(j). The following restrictions should be satisfied: t(1) ≥ tdata(1), and t(N) ≤ tdata(M). • Ydata is a DMatrix object of dimensions ny × M with the data values of the dependent variable. • tdata is a DMatrix object of dimensions 1 × M with the data values of the independent variable. The elements of this vector should be monotonically increasing, i.e. t(j+1) > t(j). 2.11 Pre-defined constants The following constants are defined within the header file psopt.h: • pi: defined as 3.141592653589793; • inf: defined as 1 × 1019 . 2.12 Standard output PSOPT will by default produce output information on the screen as it runs. PSOPT will produce a short file with a summary of information named with the string provided in algorithm.outfilelname. This file contains the problem name, the total CPU time spent, the NLP solver used, the optimal value of the objective function, the values of the endpoint cost function and cost integrals, the initial and final time, the maximum discretization error, and the output string from the NLP solver. Additionally, every time a PSOPT excutable is run, it will produce a file named psopt_solution_$$$.txt ($$$ represents the characters of 99 problem.outfilename which occur to the left of the file extension point “.”). This file contains the problem name, time stamps, a summary of the algorithm options used, and results obtained, the final grid points, the final control variables, the final state variables, the final static parameter values. The file also contains a summary of all constraints functions associated with the NLP problem, including their final scaled value, bounds, and scaling factor used; a summary of the final NLP decision variables, including their final unscaled values, bounds and scaling factors used; and a summary of the mesh refinement process. An indication is given at the end of a constraint line, or decision variable line, if a scaled constraint function or scaled decision variable is within algorithm.nlp_tolerance of one of its bounds, or if a scaled constraint function or scaled decision variable has violated one of its bounds by more than algorithm.nlp_tolerance. For parameter estimation problems this file also contains the covariance matrix of the parameter vector, and the 95% confidence interval for each estimated parameter. LATEX code to produce a table with a summary of the mesh refinement process is also automatically generated as described in section 2.8.4. If algorithm.print_level is set to zero, then no output is produced. 2.13 Implementing your own problem A template C++ file named user.cxx is provided in the directory psopt-4.0.0 /PSOPT/examples/user This file can be modified by the user to implement their own problem. and an executable can then be built easily. 2.13.1 Building the user code from Linux After modifying the user.cxx code, open a command prompt and cd to the base directory of the PSOPT installation. Then simply type $ cd PSOPT/examples/user $ make user If no compilation errors occur, an executable named user should be created in the directory psopt-4.0.0 /PSOPT/examples/user. 100 Chapter 3 Examples of using PSOPT The following examples have been mostly selected from the literature such that their solution can be compared with published results by consulting the references provided. 3.1 Alp rider problem Consider the following optimal control problem, which is known in the literature as the Alp rider problem [3]. It is known as Alp rider because the minimum of the objective function forces the states to ride the path constraint. Minimize the cost functional Z 20 J= (100(x21 + x22 + x23 + x24 ) + 0.01(u21 + u22 ))dt (3.1) 0 subject to the dynamic constraints ẋ1 ẋ2 ẋ3 ẋ4 = = = = −10x1 + u1 + u2 −2x2 + u1 + 2u2 −3x3 + 5x4 + u1 − u2 5x3 − 3x4 + u1 + 3u2 (3.2) the path constraint x21 +x22 +x23 +x24 −3p(t, 3, 12)−3p(t, 6, 10)−3p(t, 10, 16)−8p(t, 15, 4)−0.01 ≤ 0 (3.3) 101 2 where the exponential peaks are p(t, a, b) = e−b(t−a) , and the boundary conditions are given by: x1 (0) x2 (0) x3 (0) x4 (0) x1 (20) x2 (20) x3 (20) x4 (20) = = = = = = = = 2 1 2 1 2 3 1 −2 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// alpine_rider.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Alp rider problem //////////////// //////// Last modified: 09 February 2009 //////////////// //////// Reference: Betts (2001) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which//////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define auxiliary function //////////////////////// ////////////////////////////////////////////////////////////////////////// adouble pk( adouble t, double a, double b) { return exp(-b*(t-a)*(t-a)); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[CINDEX(1)]; adouble x2 = states[CINDEX(2)]; adouble x3 = states[CINDEX(3)]; adouble x4 = states[CINDEX(4)]; adouble u1 = controls[CINDEX(1)]; adouble u2 = controls[CINDEX(2)]; adouble L; 102 (3.4) L = 100.0*( x1*x1 + x2*x2 + x3*x3 + x4*x4 ) + 0.01*( u1*u1 + u2*u2 ); return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[CINDEX(1)]; adouble x2 = states[CINDEX(2)]; adouble x3 = states[CINDEX(3)]; adouble x4 = states[CINDEX(4)]; adouble u1 = controls[CINDEX(1)]; adouble u2 = controls[CINDEX(2)]; adouble t = time; derivatives[CINDEX(1)] derivatives[CINDEX(2)] derivatives[CINDEX(3)] derivatives[CINDEX(4)] = -10*x1 + u1 + u2; = -2*x2 + u1 + 2*u2; = -3*x3 + 5*x4 + u1 - u2; = 5*x3 - 3*x4 + u1 + 3*u2; path[0] = x1*x1 + x2*x2 + x3*x3 + x4*x4 - 3*pk(t,3,12) - 3*pk(t,6,10) - 3*pk(t,10,6) - 8*pk(t,15,4) - 0.01; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble adouble adouble adouble adouble e[ e[ e[ e[ e[ e[ e[ e[ x1i x2i x3i x4i x1f x2f x3f x4f = = = = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) CINDEX(8) initial_states[ CINDEX(1) ]; initial_states[ CINDEX(2) ]; initial_states[ CINDEX(3) ]; initial_states[ CINDEX(4) ]; final_states[ CINDEX(1) ]; final_states[ CINDEX(2) ]; final_states[ CINDEX(3) ]; final_states[ CINDEX(4) ]; ] ] ] ] ] ] ] ] = = = = = = = = x1i; x2i; x3i; x4i; x1f; x2f; x3f; x4f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { 103 //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Alp rider problem"; = "alpine.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 4; 2; 8; 1; = "[120]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states = "[-4.0, -4.0, -4.0, -4.0]"; problem.phases(1).bounds.upper.states = "[ 4.0, 4.0, 4.0, 4.0]"; problem.phases(1).bounds.lower.controls = "[ -500.0, -500 ]"; problem.phases(1).bounds.upper.controls = "[ problem.phases(1).bounds.lower.events = 500.0, 500 ]"; "[2.0, 1.0, 2.0, 1.0, 2.0, 3.0, 1.0, -2.0]"; problem.phases(1).bounds.upper.events = problem.phases(1).bounds.lower.events; problem.phases(1).bounds.upper.path = 100.0; problem.phases(1).bounds.lower.path = 0.0; problem.phases(1).bounds.lower.StartTime = 0.0; problem.phases(1).bounds.upper.StartTime = 0.0; problem.phases(1).bounds.lower.EndTime = 20.0; problem.phases(1).bounds.upper.EndTime = 20.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// 104 int nnodes = problem.phases(1).nodes(1); DMatrix x_guess x_guess(1,colon()) x_guess(2,colon()) x_guess(3,colon()) x_guess(4,colon()) = = = = = zeros(4,nnodes); linspace(2,1,nnodes); linspace(2,3,nnodes); linspace(2,1,nnodes); linspace(1,-2,nnodes); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(2,nnodes); = x_guess; = linspace(0.0,20.0,nnodes+1); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.jac_sparsity_ratio algorithm.collocation_method algorithm.diff_matrix algorithm.mesh_refinement algorithm.mr_max_increment_factor algorithm.defect_scaling = = = = = = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; 0.20; "Legendre"; "central-differences"; "automatic"; 0.3; "jacobian-based"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x = solution.get_states_in_phase(1); DMatrix u = solution.get_controls_in_phase(1); DMatrix t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x(1,colon()),problem.name+": state", "time (s)", "state","x1"); plot(t,x(2,colon()),problem.name+": state", "time (s)", "state","x2"); plot(t,x(3,colon()),problem.name+": state", "time (s)", "state","x3"); plot(t,x(4,colon()),problem.name+": state", "time (s)", "state","x4"); plot(t,u(1,colon()),problem.name+": control","time (s)", "control", "u1"); plot(t,u(2,colon()),problem.name+": control","time (s)", "control", "u2"); plot(t,x(1,colon()),problem.name+": state x1", "time (s)", "state","x1", "pdf", "alpine_state1.pdf"); plot(t,x(2,colon()),problem.name+": state x2", "time (s)", "state","x2", "pdf", "alpine_state2.pdf"); 105 plot(t,x(3,colon()),problem.name+": state x3", "time (s)", "state","x2", "pdf", "alpine_state3.pdf"); plot(t,x(4,colon()),problem.name+": state x4", "time (s)", "state","x2", "pdf", "alpine_state4.pdf"); plot(t,u(1,colon()),problem.name+": control u1","time (s)", "control", "u1", "pdf", "alpine_control1.pdf"); plot(t,u(2,colon()),problem.name+": control u1","time (s)", "control", "u2", "pdf", "alpine_control2.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the box below and shown in Figures 3.1-3.4 and Figures 3.5-3.6, which contain the elements of the state and the control, respectively. Table ?? shows the mesh refinement history for this problem. PSOPT results summary ===================== Problem: Alp rider problem CPU time (seconds): 1.151346e+02 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:28:52 2019 Optimal (unscaled) cost function value: 2.026847e+03 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 2.026847e+03 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.000000e+01 Phase 1 maximum relative local error: 2.990169e-03 NLP solver reports: The problem has been solved! 3.2 Brachistochrone problem Consider the following optimal control problem. Minimize the cost functional J = tf (3.5) 106 Alp rider problem: state x1 2 x1 state 1.5 1 0.5 0 0 5 10 15 20 time (s) Figure 3.1: State x1 (t) for the Alp rider problem Alp rider problem: state x2 x2 3.5 3 2.5 state 2 1.5 1 0.5 0 -0.5 0 5 10 15 time (s) Figure 3.2: State x2 (t) for the Alp rider problem 107 20 Alp rider problem: state x3 x2 2 1 state 0 -1 -2 -3 -4 0 5 10 15 20 time (s) Figure 3.3: State x3 (t) for the Alp rider problem Alp rider problem: state x4 x2 1 0.5 state 0 -0.5 -1 -1.5 -2 0 5 10 15 time (s) Figure 3.4: State x4 (t) for the Alp rider problem 108 20 Alp rider problem: control u1 500 u1 400 300 control 200 100 0 -100 -200 -300 0 5 10 15 20 time (s) Figure 3.5: Control u1 (t) for the Alp rider problem Alp rider problem: control u1 u2 0 -50 control -100 -150 -200 -250 -300 -350 0 5 10 15 time (s) Figure 3.6: Control u2 (t) for the Alp rider problem 109 20 subject to the dynamic constraints ẋ = v sin(θ) ẏ = v cos(θ) v̇ = g cos(θ) (3.6) and the boundary conditions x(0) y(0) v(0) x(tf ) y(tf ) = = = = = 0 0 0 2 2 (3.7) where g = 9.8. A version of this problem was originally formulated by Johann Bernoulli in 1696 and is referred to as the Brachistochrone problem. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// brac1.cxx /////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Brachistochrone problem //////////////// //////// Last modified: 04 January 2009 //////////////// //////// Reference: Bryson and Ho (1975) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return tf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; 110 adouble x = states[ CINDEX(1) ]; adouble y = states[ CINDEX(2) ]; adouble v = states[ CINDEX(3) ]; adouble theta = controls[ CINDEX(1) ]; xdot = v*sin(theta); ydot = v*cos(theta); vdot = 9.8*cos(theta); derivatives[ CINDEX(1) ] = xdot; derivatives[ CINDEX(2) ] = ydot; derivatives[ CINDEX(3) ] = vdot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x0 = initial_states[ CINDEX(1) ]; adouble y0 = initial_states[ CINDEX(2) ]; adouble v0 = initial_states[ CINDEX(3) ]; adouble xf = final_states[ CINDEX(1)]; adouble yf = final_states[ CINDEX(2)]; e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) ] ] ] ] ] = = = = = x0; y0; v0; xf; yf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Brachistochrone Problem"; problem.outfilename = "brac1.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// 111 ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 3; 1; 5; 0; = "[40]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states problem.phases(1).bounds.upper.states = "[ 0; 0; 0]"; = "[20; 20; 20]"; problem.phases(1).bounds.lower.controls problem.phases(1).bounds.upper.controls = 0.0; = 2*pi; problem.phases(1).bounds.lower.events problem.phases(1).bounds.upper.events = "[0, = "[0, 0, 0, problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.0; = 10.0; 0, 0, 2, 2, 2]"; 2]"; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// set scaling factors (optional) //////////////////////// //////////////////////////////////////////////////////////////////////////// // // // // // problem.phases(1).scale.controls problem.phases(1).scale.states problem.phases(1).scale.events problem.phases(1).scale.defects problem.phases(1).scale.time = = = = = 1.0*ones(1,1); 1.0*ones(3,1); 1.0*ones(5,1); 1.0*ones(3,1); 1.0; // problem.scale.objective = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(3,20); x0(1,colon()) = linspace(0.0,1.0, 20); x0(2,colon()) = linspace(0.0,1.0, 20); x0(3,colon()) = linspace(0.0,1.0, 20); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = ones(1,20); = x0; = linspace(0.0, 2.0, 20); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; 112 // algorithm.hessian = "exact"; algorithm.collocation_method = "Legendre"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); if (solution.error_flag) exit(0); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix DMatrix DMatrix DMatrix DMatrix x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); H = solution.get_dual_hamiltonian_in_phase(1); lambda = solution.get_dual_costates_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); lambda.Save("p.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": states", "time (s)", "states", "x y v"); plot(t,u,problem.name + ": control", "time (s)", "control", "u"); plot(t,H,problem.name + ": Hamiltonian", "time (s)", "H", "H"); plot(t,lambda,problem.name + ": costates", "time (s)", "lambda", "lambda_1 lambda_2 lambda_3"); plot(t,x,problem.name + ": states", "time (s)", "states", "x y v", "pdf", "brac1_states.pdf"); plot(t,u,problem.name + ": control", "time (s)", "control", "u", "pdf", "brac1_control.pdf"); plot(t,H,problem.name + ": Hamiltonian", "time (s)", "H", "H", "pdf", "brac1_hamiltonian.pdf"); plot(t,lambda,problem.name + ": costates", "time (s)", "lambda", "lambda_1 lambda_2 lambda_3", "pdf", "brac1_costates.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the box below and shown in Figures 3.7, 3.8, which contain the elements of the state, and the control respectively. PSOPT results summary 113 Brachistochrone Problem: states x y v 6 5 states 4 3 2 1 0 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 time (s) Figure 3.7: States for brachistochrone problem ===================== Problem: Brachistochrone Problem CPU time (seconds): 1.794096e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:06:01 2019 Optimal (unscaled) cost function value: 8.247591e-01 Phase 1 endpoint cost function value: 8.247591e-01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 8.247591e-01 Phase 1 maximum relative local error: 2.774874e-07 NLP solver reports: The problem has been solved! 3.3 Breakwell problem Consider the following optimal control problem, which is known in the literature as the Breakwell problem [8]. The problem benefits from having an analytical solution, which is reported (with some errors) in the book by 114 Brachistochrone Problem: control 1.2 u 1 control 0.8 0.6 0.4 0.2 0 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 time (s) Figure 3.8: Control for brachistochrone problem Bryson and Ho (1975). Minimize the cost functional. Z tf u(t)2 dt J= (3.8) 0 subject to the dynamic constraints ẋ = v v̇ = u (3.9) x(t) ≤ l (3.10) the state dependent constraint where l = 0.1, tf = 1. and the boundary conditions x(0) v(0) x(tf ) v(tf ) = = = = 0 1 0 −1 (3.11) The analytical solution of the problem (valid for 0 ≤ l ≤ 1/6) is given by: 2 0 ≤ t ≤ 3l − 3l (1 − 3lt ), 0, 3l ≤ t ≤ 1 − 3l u(t) = 2 − 3l (1 − 1−t ), 1 − 3l ≤ t ≤ 1 3l 115 (3.12) t 3 l 1 − 1 − , 0 ≤ t ≤ 3l 3l l, x(t) = 3l ≤ t ≤ 1 − 3l 3 l 1 − 1 − 1−t , 1 − 3l ≤ t ≤ 1 3l 2 0 ≤ t ≤ 3l 1 − 3lt , 0, 3l ≤ t ≤ 1 − 3l v(t) = 1 − 1−t 2 , 1 − 3l ≤ t ≤ 1 3l 2 0 ≤ t ≤ 3l 9l2 , 0, 3l ≤ t ≤ 1 − 3l λx (t) = 2 − 9l2 , 1 − 3l ≤ t ≤ 1 2 0 ≤ t ≤ 3l 3l (1 − 3lt ), 0, 3l ≤ t ≤ 1 − 3l λv (t) = 2 1−t 3l (1 − 3l ), 1 − 3l ≤ t ≤ 1 (3.13) (3.14) (3.15) (3.16) where λx (t) and λv (t) are the costates. The analytical optimal value of the objective function is J = 4/(9l) = 4.4444444. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// bryson_max_range.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Bryson maximum range problem //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: Bryson and Ho (1975) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which//////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return (0); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble u = controls[0]; return 0.5*u*u; } 116 ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, vdot; double g = 1.0; double a = 0.5*g; adouble x = states[ CINDEX(1) ]; adouble v = states[ CINDEX(2) ]; adouble u = controls[ CINDEX(1) ]; xdot = v; vdot = u; derivatives[ CINDEX(1) ] = xdot; derivatives[ CINDEX(2) ] = vdot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble e[ e[ e[ e[ x0 v0 xf vf = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) initial_states[ CINDEX(1) ]; initial_states[ CINDEX(2) ]; final_states[ CINDEX(1) ]; final_states[ CINDEX(2) ]; ] ] ] ] = = = = x0; v0; xf; vf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Breakwell Problem"; = "breakwell.txt"; 117 //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 2; 1; 4; 0; = "[200]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double xL vL xU vU = = = = -2.0; -2.0; 0.1; 2.0; double uL = -10.0; double uU = 10.0; double double double double x0 v0 xf vf = = = = 0.0; 1.0; 0.0; -1.0; problem.phases(1).bounds.lower.states(1) = xL; problem.phases(1).bounds.lower.states(2) = vL; problem.phases(1).bounds.upper.states(1) = xU; problem.phases(1).bounds.upper.states(2) = vU; problem.phases(1).bounds.lower.controls(1) = uL; problem.phases(1).bounds.upper.controls(1) = uU; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) = = = = x0; v0; xf; vf; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) = = = = x0; v0; xf; vf; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// 118 problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix x_guess = zeros(nstates,nnodes); x_guess(1,colon()) = x0*ones(1,nnodes); x_guess(2,colon()) = v0*ones(1,nnodes); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(ncontrols,nnodes); = x_guess; = linspace(0.0,1.0,nnodes); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // // // algorithm.nlp_iter_max = 1000; algorithm.nlp_tolerance = 1.e-6; algorithm.nlp_method = "IPOPT"; algorithm.scaling = "automatic"; algorithm.derivatives = "automatic"; algorithm.hessian = "exact"; algorithm.mesh_refinement = "automatic"; algorithm.collocation_method = "Hermite-Simpson"; algorithm.diff_matrix = "central-differences"; algorithm.defect_scaling = "jacobian-based"; algorithm.nlp_tolerance = 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix muE; x u t lambda H muE = = = = = = solution.get_states_in_phase(1); solution.get_controls_in_phase(1); solution.get_time_in_phase(1); solution.get_dual_costates_in_phase(1); solution.get_dual_hamiltonian_in_phase(1); solution.get_dual_events_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Compute the analytical solution, which is valid when the ////// /////////// state constraint x<=l, with 0<=l<=1/6. In this case l=0.1 ///// /////////// The analytical solution is given (with some errors) in ///// /////////// the book by Bryson and Ho (1975), pages 121-122. ///// //////////////////////////////////////////////////////////////////////////// double l = 0.1; double t1 = 3*l; double t2 = 1.0-3*l; int nn = length(t); DMatrix ua(1,nn), xa(2,nn), pa(2,nn); for(int i=1;i <=nn;i++) { 119 if (t(i) =t1 && t(i) =t2) { ua(1,i) = -2.0/(3*l)*(1.0-(1.0-t(i))/(3.0*l)); xa(1,i) = l*(1.0 - pow( (1.0-(1.0-t(i))/(3.0*l)), 3.0)); xa(2,i) = -pow(1.0-(1.0-t(i))/(3.0*l), 2.0) ; pa(1,i) = -2.0/(9.0*l*l); pa(2,i) = 2.0/(3.0*l)*(1.0-(1.0-t(i))/(3*l)); } } //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); lambda.Save("p.dat"); H.Save("H.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,t,xa,problem.name+": states", "time (s)", "states","x v xa va"); plot(t,u,t,ua,problem.name+": controls","time (s)", "control", "u ua"); plot(t,lambda,t,pa, problem.name+": costates","time (s)", "costates", "l_x l_v la_x la_v"); plot(t,H,problem.name+": Hamiltonian","time (s)", "H", "H"); plot(t,x,t,xa,problem.name+": states", "time (s)", "states","x v xa va", "pdf", "breakwell_states.pdf"); plot(t,u,t,ua,problem.name+": control","time (s)", "control", "u ua", "pdf", "breakwell_control.pdf"); plot(t,lambda,t,pa, problem.name+": costates","time (s)", "costates", "l_x l_v la_x la_v", "pdf", "breakwell_costates.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the following box and shown in Figures 3.9 and 3.10, which contain the elements of the state and the control, respectively, and Figure 3.11 which shows the costates. The figures include curves with the analytical solution for each variable, which is very close to the computed solution. 120 Breakwell Problem: states x v xa va 1 states 0.5 0 -0.5 -1 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.9: States for Breakwell problem PSOPT results summary ===================== Problem: Breakwell Problem CPU time (seconds): 3.840152e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:08:09 2019 Optimal (unscaled) cost function value: 4.444441e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 4.444441e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 8.294815e-06 NLP solver reports: The problem has been solved! 3.4 Bryson-Denham problem Consider the following optimal control problem, which is known in the literature as the Bryson-Denham problem [7]. Minimize the cost functional 121 Breakwell Problem: control 0 u ua -1 -2 control -3 -4 -5 -6 -7 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.10: Control for Breakwell problem Breakwell Problem: costates l_x l_v la_x la_v 20 15 10 costates 5 0 -5 -10 -15 -20 0 0.2 0.4 0.6 0.8 time (s) Figure 3.11: Costates for Breakwell problem 122 1 J = x3 (tf ) (3.17) subject to the dynamic constraints ẋ1 = x2 ẋ2 = u ẋ3 = 21 u2 (3.18) 0 ≤ x1 ≤ 1/9 (3.19) the state bound and the boundary conditions x1 (0) x2 (0) x3 (0) x1 (tf ) x2 (tf ) = = = = = 0 1 0 0 −1 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Bryson-Denham problem //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: GPOPS Handbook //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x3f = final_states[ CINDEX(3) ]; return x3f; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// 123 (3.20) /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace ) { adouble x1 = states[CINDEX(1)]; adouble x2 = states[CINDEX(2)]; adouble x3 = states[CINDEX(3)]; adouble u = controls[CINDEX(1)]; derivatives[ CINDEX(1) ] = x2; derivatives[ CINDEX(2) ] = u; derivatives[ CINDEX(3) ] = u*u/2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1) ]; adouble x20 = initial_states[ CINDEX(2) ]; adouble x30 = initial_states[ CINDEX(3) ]; adouble x1f = final_states[ CINDEX(1) ]; adouble x2f = final_states[ CINDEX(2) ]; e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) ] ] ] ] ] = = = = = x10; x20; x30; x1f; x2f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Bryson-Denham Problem"; = "bryden.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Declare problem level constants & do level 1 setup /////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; 124 psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 3; 1; 5; 0; = "[10, 50]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) = 0.0; = -10.0; = -10.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) = 1.0/9.0; = 10.0; = 10.0; problem.phases(1).bounds.lower.controls(1) = -10.0; problem.phases(1).bounds.upper.controls(1) = 10.0; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) = = = = = 0.0; 1.0; 0.0; 0.0; -1.0; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) problem.phases(1).bounds.upper.events(5) = = = = = 0.0; 1.0; 0.0; 0.0; -1.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = = = = 0.0; 0.0; 0.0; 50.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(3,10); x0(1,colon()) = linspace(0.0, 0.0, 10); x0(2,colon()) = linspace(1.0,-1.0, 10); x0(3,colon()) = linspace(0.0, 0.0, 10); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,10); = x0; = linspace(0.0, 0.5, 10); 125 //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t lambda H = = = = = solution.get_states_in_phase(1); solution.get_controls_in_phase(1); solution.get_time_in_phase(1); solution.get_dual_costates_in_phase(1); solution.get_dual_hamiltonian_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); lambda.Save("lambda.dat"); H.Save("H.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "x1 x2 x3"); plot(t,u, problem.name ,"time (s)", "control", "u"); plot(t,lambda, problem.name ,"time (s)", "lambda", "1 2 3"); plot(t,x,problem.name, "time (s)", "states", "x1 x2 x3", "pdf", "bryden_states.pdf"); plot(t,u, problem.name ,"time (s)", "control", "u", "pdf", "bryden_control.pdf"); plot(t,lambda, problem.name ,"time (s)", "lambda", "1 2 3", "pdf", "bryden_lambda.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the following box and shown in Figures 3.12 and 3.13, which contain the elements of the state and the control, respectively. PSOPT results summary 126 Bryson-Denham Problem 4 x1 x2 x3 3 states 2 1 0 -1 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 time (s) Figure 3.12: States for Bryson Denham problem ===================== Problem: Bryson-Denham Problem CPU time (seconds): 2.550521e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:31:41 2019 Optimal (unscaled) cost function value: 3.999539e+00 Phase 1 endpoint cost function value: 3.999539e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 6.474067e-01 Phase 1 maximum relative local error: 6.431444e-05 NLP solver reports: The problem has been solved! 3.5 Bryson’s maximum range problem Consider the following optimal control problem, which is known in the literature as the Bryson’s maximum range problem [7]. Minimize the cost functional J = x(tf ) (3.21) 127 Bryson-Denham Problem u -1 control -2 -3 -4 -5 -6 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 time (s) Figure 3.13: Control for Bryson Denham problem subject to the dynamic constraints ẋ = vu1 ẏ = vu2 v̇ = a − gu2 (3.22) u21 + u22 = 1 (3.23) the path constraint and the boundary conditions x(0) y(0) v(0) y(tf ) = = = = 0 0 0 0.1 (3.24) where tf = 2, g = 1 and a = 0.5g. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// bryson_max_range.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Bryson maximum range problem //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: Bryson and Ho (1975) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// 128 ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which//////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x = final_states[0]; return (-x); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; double g = 1.0; double a = 0.5*g; adouble x = states[ CINDEX(1) ]; adouble y = states[ CINDEX(2) ]; adouble v = states[ CINDEX(3) ]; adouble u1 = controls[ CINDEX(1) ]; adouble u2 = controls[ CINDEX(2) ]; xdot = v*u1; ydot = v*u2; vdot = a-g*u2; derivatives[ CINDEX(1) ] = xdot; derivatives[ CINDEX(2) ] = ydot; derivatives[ CINDEX(3) ] = vdot; path[ CINDEX(1) ] = (u1*u1) + (u2*u2); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble adouble e[ e[ e[ e[ x0 y0 v0 xf yf = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) initial_states[ CINDEX(1) ]; initial_states[ CINDEX(2) ]; initial_states[ CINDEX(3) ]; final_states[ CINDEX(1) ]; final_states[ CINDEX(2) ]; ] ] ] ] = = = = x0; y0; v0; yf; 129 } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Bryson Maximum Range Problem"; = "brymr.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 3; 2; 4; 1; = "[20]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double xL yL vL xU yU vU = = = = = = double double double double u1L u2L u1U u2U -10.0; -10.0; -10.0; 10.0; 10.0; 10.0; = = = = -10.0; -10.0; 10.0; 10.0; double x0 = 0.0; 130 double y0 = 0.0; double v0 = 0.0; double yf = 0.1; problem.phases(1).bounds.lower.states(1) = xL; problem.phases(1).bounds.lower.states(2) = yL; problem.phases(1).bounds.lower.states(3) = vL; problem.phases(1).bounds.upper.states(1) = xU; problem.phases(1).bounds.upper.states(2) = yU; problem.phases(1).bounds.upper.states(3) = vU; problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.upper.controls(2) = = = = u1L; u2L; u1U; u2U; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) = = = = x0; y0; v0; yf; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) = = = = x0; y0; v0; yf; problem.phases(1).bounds.upper.path(1) = 1.0; problem.phases(1).bounds.lower.path(1) = 1.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 2.0; = 2.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix x_guess = zeros(nstates,nnodes); x_guess(1,colon()) = x0*ones(1,nnodes); x_guess(2,colon()) = y0*ones(1,nnodes); x_guess(3,colon()) = v0*ones(1,nnodes); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(ncontrols,nnodes); = x_guess; = linspace(0.0,2.0,nnodes); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling = = = = 1000; 1.e-4; "IPOPT"; "automatic"; 131 // algorithm.derivatives = "automatic"; algorithm.mesh_refinement = "automatic"; algorithm.collocation_method = "trapezoidal"; algorithm.defect_scaling = "jacobian-based"; algorithm.ode_tolerance = 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t lambda H = = = = = solution.get_states_in_phase(1); solution.get_controls_in_phase(1); solution.get_time_in_phase(1); solution.get_dual_costates_in_phase(1); solution.get_dual_hamiltonian_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); lambda.Save("lambda.dat"); H.Save("H.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name+": states", "time (s)", "states","x y v"); plot(t,u,problem.name+": controls","time (s)", "controls", "u_1 u_2"); plot(t,x,problem.name+": states", "time (s)", "states","x y v", "pdf", "brymr_states.pdf"); plot(t,u,problem.name+": controls","time (s)", "controls", "u_1 u_2", "pdf", "brymr_controls.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the box below and shown in Figures 3.14 and 3.15, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Bryson Maximum Range Problem CPU time (seconds): 7.225706e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Mon Feb 25 05:31:12 2019 132 Bryson Maximum Range Problem: states x y v 1.5 states 1 0.5 0 0 0.5 1 1.5 2 time (s) Figure 3.14: States for Bryson’s maximum range problem Optimal (unscaled) cost function value: -1.712319e+00 Phase 1 endpoint cost function value: -1.712319e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.000000e+00 Phase 1 maximum relative local error: 1.170448e-04 NLP solver reports: The problem has been solved! 3.6 Catalytic cracking of gas oil Consider the following optimization problem, which involves finding optimal static parameters subject to dynamic constraints [15]. Minimize J= 21 X (y1 (ti ) − ym,1 (i))2 + (y2 (ti ) − ym,2 (i))2 (3.25) i=1 subject to the dynamic constraints ẏ1 = −(θ1 + θ3 )y12 ẏ2 = θ1 y12 − θ2 y2 133 (3.26) Bryson Maximum Range Problem: controls u_1 u_2 1 controls 0.5 0 -0.5 -1 0 0.5 1 1.5 2 time (s) Figure 3.15: Controls for Bryson’s maximum range problem the parameter constraint θ1 ≥ 0 θ2 ≥ 0 (3.27) θ3 ≥ 0 Note that, given the nature of the problem, the parameter estimation facilities of PSOPT are used in this example. In this case, the observations function is simple: g(x(t), u(t), p, t) = [y1 y2 ]T The PSOPT code that solves this problem is shown below. The code includes the values of the measurement vectors ym,1 , and ym,2 , as well as the vector of sampling instants θi , i = 1, . . . , 21. ////////////////////////////////////////////////////////////////////////// ////////////////// cracking.cxx //////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Catalytic Cracking of Gas Oil //////////////// //////// Last modified: 15 January 2009 //////////////// //////// Reference: Users guide for DIRCOL //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// 134 #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the observation function ////////// ////////////////////////////////////////////////////////////////////////// void observation_function( adouble* adouble* adouble* adouble* observations, states, adouble* controls, parameters, adouble& time, int k, xad, int iphase, Workspace* workspace) { observations[ CINDEX(1) ] = states[ CINDEX(1) ]; observations[ CINDEX(2) ] = states[ CINDEX(2) ]; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble y1 = states[CINDEX(1)]; adouble y2 = states[CINDEX(2)]; adouble theta1 = parameters[ CINDEX(1) ]; adouble theta2 = parameters[ CINDEX(2) ]; adouble theta3 = parameters[ CINDEX(3) ]; derivatives[CINDEX(1)] = -(theta1 + theta3)*y1*y1; derivatives[CINDEX(2)] = theta1*y1*y1 - theta2*y2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { // No events } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { DMatrix y1meas, y2meas, tmeas; // Measured values of y1 y1meas = "[1.0,0.8105,0.6208,0.5258,0.4345,0.3903,0.3342,0.3034, \ 0.2735,0.2405,0.2283,0.2071,0.1669,0.153,0.1339,0.1265, \ 0.12,0.099,0.087,0.077,0.069]"; // Measured values of y2 y2meas = "[0.0,0.2,0.2886,0.301,0.3215,0.3123,0.2716,0.2551,0.2258, \ 0.1959,0.1789,0.1457,0.1198,0.0909,0.0719,0.0561,0.046, \ 0.028,0.019,0.014,0.01]"; // Sampling instants tmeas = "[0.0,0.025,0.05,0.075,0.1,0.125,0.15,0.175,0.2,0.225,0.25, \ 135 0.3,0.35,0.4,0.45,0.5,0.55,0.65,0.75,0.85,0.95]"; //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Catalytic cracking of gas oil"; = "cracking.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = 2; problem.phases(1).ncontrols = 0; problem.phases(1).nevents = 0; problem.phases(1).npath = 0; problem.phases(1).nparameters = 3; problem.phases(1).nodes = "[80]"; problem.phases(1).nobserved = 2; problem.phases(1).nsamples = 21; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// //////////// Enter estimation information //////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).observation_nodes problem.phases(1).observations problem.phases(1).residual_weights = tmeas; = (y1meas && y2meas); = ones(2,21); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, p, t; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = problem.phases(1).bounds.lower.states(2) = 0.0; 0.0; problem.phases(1).bounds.upper.states(1) = problem.phases(1).bounds.upper.states(2) = 2.0; 2.0; problem.phases(1).bounds.lower.parameters(1) problem.phases(1).bounds.lower.parameters(2) problem.phases(1).bounds.lower.parameters(3) problem.phases(1).bounds.upper.parameters(1) problem.phases(1).bounds.upper.parameters(2) problem.phases(1).bounds.upper.parameters(3) problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = = = = = = 0.0; 0.0; 0.0; 20.0; 20.0; 20.0; = 0.0; = 0.0; 136 problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.95; = 0.95; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; problem.observation_function = & observation_function; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix state_guess(2, 40); state_guess(1,colon()) = linspace(1.0,0.069, 40); state_guess(2,colon()) = linspace(0.30,0.01, 40); problem.phases(1).guess.states problem.phases(1).guess.time problem.phases(1).guess.parameters = state_guess; = linspace(0.0, 0.95, 40); = zeros(3,1); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// // algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.jac_sparsity_ratio = "IPOPT"; = "automatic"; = "automatic"; = "Hermite-Simpson"; = 1000; = 1.e-4; = 0.52; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); t = solution.get_time_in_phase(1); p = solution.get_parameters_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); t.Save("t.dat"); p.Print("Estimated parameters"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "y1 y2"); plot(t,x,problem.name, "time (s)", "states", "y1 y2", "pdf", "cracking_states.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the box below and shown in Figure 3.16, which shows the states of the system. The optimal parameters 137 found were: θ1 = 11.40825702 θ2 = 8.123367918 (3.28) θ3 = 1.668727477 PSOPT results summary ===================== Problem: Catalytic cracking of gas oil CPU time (seconds): 5.681664e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:06:49 2019 Optimal (unscaled) cost function value: 4.326398e-03 Phase 1 endpoint cost function value: 4.326398e-03 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 9.500000e-01 Phase 1 maximum relative local error: 6.513072e-10 NLP solver reports: The problem has been solved! 3.7 Catalyst mixing problem Consider the following optimal control problem, which attempts to determine the optimal mixing policy of two catalysts along the length of a tubular plug flow reactor involving several reactions [38]. The catalyst mixing problem is a typical bang-singular-bang problem. Minimize the cost functional J = −1 + x1 (tf ) + x2 (tf ) (3.29) subject to the dynamic constraints ẋ1 = u(10x2 − x1 ) ẋ2 = u(x1 − 10x2 ) − (1 − u)x2 (3.30) the boundary conditions x1 (0) = 1 x2 (0) = 0 x1 (tf ) ≤ 0.95 138 (3.31) Catalytic cracking of gas oil y1 y2 1 0.8 states 0.6 0.4 0.2 0 0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 time (s) Figure 3.16: States for catalytic cracking of gas oil problem and the box constraints: 0.9 ≤ x1 (t) ≤ 1.0 0 ≤ x2 (t) ≤ 0.1 0 ≤ u(t) ≤ 1 (3.32) where tf = 1. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// catmix.cxx ////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Catalyst mixing problem //////////////// //////// Last modified: 26 January 2009 //////////////// //////// Reference: Vassiliadis (1999) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x1f = final_states[ CINDEX(1) ]; adouble x2f = final_states[ CINDEX(2) ]; 139 return -(1.0-x1f-x2f); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; adouble x1 = states[ CINDEX(1) ]; adouble x2 = states[ CINDEX(2) ]; adouble u = controls[ CINDEX(1) ]; derivatives[ CINDEX(1) ] = u*(10*x2-x1); derivatives[ CINDEX(2) ] = u*(x1-10*x2) - (1-u)*x2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1)]; adouble x20 = initial_states[ CINDEX(2) ]; adouble x1f = final_states[ CINDEX(1) ]; e[ CINDEX(1) ] = x10; e[ CINDEX(2) ] = x20; e[ CINDEX(3) ] = x1f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Catalyst mixing roblem"; = "catmix.txt"; 140 //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 2; 1; 3; 0; = 40; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = 0.9; problem.phases(1).bounds.lower.states(2) = 0.0; problem.phases(1).bounds.upper.states(1) = 1.0; problem.phases(1).bounds.upper.states(2) = 0.1; problem.phases(1).bounds.lower.controls(1) = 0.0; problem.phases(1).bounds.upper.controls(1) = 1.0; problem.phases(1).bounds.lower.events(1) = 1.0; problem.phases(1).bounds.lower.events(2) = 0.0; problem.phases(1).bounds.lower.events(3) = 0.0; problem.phases(1).bounds.upper.events(1) = 1.0; problem.phases(1).bounds.upper.events(2) = 0.0; problem.phases(1).bounds.upper.events(3) = 0.95; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix u0(1,40); DMatrix x0(3,40); 141 DMatrix t0 = linspace(0.0, 1.0, 40); x0(1,colon()) = ones(1,40) - 0.085*t0; x0(2,colon()) = 0.05*t0; u0 = ones(1,40)- t0; problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = u0; = x0; = t0; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); lambda = solution.get_dual_costates_in_phase(1); H = solution.get_dual_hamiltonian_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); lambda.Save("lambda.dat"); H.Save("H.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": states", "time (s)", "states", "x1 x2"); plot(t,u,problem.name + ": control", "time (s)", "control", "u"); plot(t,x,problem.name + ": states", "time (s)", "states", "x1 x2", "pdf", "catmix_states.pdf"); plot(t,u,problem.name + ": control", "time (s)", "control", "u", "pdf", "catmix_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.17 and 3.18, which contain the elements of the state and the control, respectively. 142 Catalyst mixing roblem: states 1 x1 x2 0.8 states 0.6 0.4 0.2 0 0 0.2 0.4 0.6 0.8 time (s) Figure 3.17: States for catalyist mixing problem PSOPT results summary ===================== Problem: Catalyst mixing roblem CPU time (seconds): 1.268127e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:06:22 2019 Optimal (unscaled) cost function value: -4.805320e-02 Phase 1 endpoint cost function value: -4.805320e-02 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 1.092831e-04 NLP solver reports: The problem has been solved! 143 1 Catalyst mixing roblem: control 1 u 0.8 control 0.6 0.4 0.2 0 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.18: Control for catalyst mixing problem 3.8 Coulomb friction Consider the following optimal control problem, which consists of a system that exhibits Coulomb friction [29]. Minimize the cost: J = tf (3.33) subject to the dynamic constraints q̈1 = (−(k1 − k2 )q1 + k2 q2 − µsign(q̇1 ) + u1 )/m1 q̈2 = (k2 q1 − k2 q2 − µsign(q̇2 ) + u2 )/m2 (3.34) and the boundary conditions q1 (0) q̇1 (0) q2 (0) q̇2 (0) q1 (tf ) q̇1 (tf ) q2 (tf ) q̇2 (tf ) = 0 = = = = = = = 0 −1 0 −2 1 0 2 (3.35) where k1 = 0.95, k2 =0.85, µ = 1.0, m1 =1.1, m2 =1.2. The PSOPT code that solves this problem is shown below. 144 ////////////////////////////////////////////////////////////////////////// ////////////////// coulomb.cxx ////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Coulumb friction problem //////////////// //////// Last modified: 04 January 2009 //////////////// //////// Reference: Driessen and Sadegh (2000) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 /////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser /////////////// //////// General Public License (LGPL) /////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return tf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble q1 = states[ CINDEX(1) ]; adouble q1dot = states[ CINDEX(2) ]; adouble q2 = states[ CINDEX(3) ]; adouble q2dot = states[ CINDEX(4) ]; adouble u1 = controls[ CINDEX(1) ]; adouble u2 = controls[ CINDEX(2) ]; double double double double double k1 k2 mu m1 m2 = = = = = 0.95; 0.85; 1.0; 1.1; 1.2; double epsilon = 0.01; derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = q1dot; ( (-k1-k2)*q1+k2*q2-mu*smooth_sign(q1dot,epsilon)+u1 )/m1; q2dot; ( k2*q1-k2*q2-mu*smooth_sign(q2dot,epsilon)+u2 )/m2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { 145 adouble adouble adouble adouble q1_0 q1dot_0 q2_0 q2dot_0 = = = = initial_states[ initial_states[ initial_states[ initial_states[ adouble adouble adouble adouble q1_f q1dot_f q2_f q2dot_f = = = = final_states[ final_states[ final_states[ final_states[ e[ e[ e[ e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) CINDEX(8) ] ] ] ] ] ] ] ] = = = = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; ]; ]; ]; ]; q1_0; q1dot_0; q2_0; q2dot_0; q1_f; q1dot_f; q2_f; q2dot_f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Coulomb friction problem"; problem.outfilename = "coulomb.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 4; 2; 8; 0; = 40; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double q1_0 = 0.0; 146 double double double double double double double dotq1_0 q2_0 = dotq2_0 q1_f = dotq1_f q2_f = dotq2_f = -1.0; 0.0; -2.0; 1.0; 0.0; 2.0; 0.0; = = = problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) = = = = -2.0; -20.0; -2.0; -20.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) = = = = 2.0; 20.0; 2.0; 20.0; problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.upper.controls(2) = -4.0; = -4.0; = 4.0; = 4.0; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) problem.phases(1).bounds.lower.events(7) problem.phases(1).bounds.lower.events(8) = = = = = = = = q1_0; dotq1_0; q2_0; dotq2_0; q1_f; dotq1_f; q2_f; dotq2_f; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) problem.phases(1).bounds.upper.events(5) problem.phases(1).bounds.upper.events(6) problem.phases(1).bounds.upper.events(7) problem.phases(1).bounds.upper.events(8) = = = = = = = = q1_0; dotq1_0; q2_0; dotq2_0; q1_f; dotq1_f; q2_f; dotq2_f; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.8; = 4.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(4,40); x0(1,colon()) x0(2,colon()) x0(3,colon()) x0(4,colon()) = = = = linspace(q1_0,q1_f, 40); linspace(dotq1_0, dotq1_f, 40); linspace(q2_0, q2_f, 40); linspace(dotq2_0, dotq2_f, 40); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(2,40); = x0; = linspace(0.0, 4.0,40); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// 147 algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); if (solution.error_flag) exit(0); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix q12 = x(1,colon()) && x(3,colon()); plot(t,q12,problem.name + ": states q1 and q2", "time (s)", "states", "q1 q2"); plot(t,u,problem.name + ": controls", "time (s)", "control", "u1 u2"); plot(t,q12,problem.name + ": states q1 and q2", "time (s)", "states", "q1 q2", "pdf", "coulomb_states.pdf"); plot(t,u,problem.name + ": controls", "time (s)", "controls", "u1 u2", "pdf", "coulomb_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT summarised in the box below and shown in Figures 3.19 and 3.20, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Coulomb friction problem CPU time (seconds): 1.905840e+00 NLP solver used: IPOPT PSOPT release number: 4.0 148 Coulomb friction problem: states q1 and q2 2 q1 q2 1.5 states 1 0.5 0 0 0.5 1 1.5 2 2.5 time (s) Figure 3.19: States for Coulomb friction problem Date and time of this run: Thu Feb 21 17:28:32 2019 Optimal (unscaled) cost function value: 2.104992e+00 Phase 1 endpoint cost function value: 2.104992e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.104992e+00 Phase 1 maximum relative local error: 7.858415e-03 NLP solver reports: The problem has been solved! 3.9 DAE index 3 parameter estimation problem Consider the following parameter estimation problem, which involves a differentialalgebraic equation of index 3 with four differential states and one algebraic state [37]. The dynamics consists of the differential equations ẋ1 (t) = x3 (t) ẋ2 (t) = x4 (t) ẋ3 (t) = λ(t)x1 (t) ẋ4 (t) = λ(t)x2 (t) 149 (3.36) Coulomb friction problem: controls u1 u2 4 3 2 controls 1 0 -1 -2 -3 -4 0 0.5 1 1.5 2 2.5 time (s) Figure 3.20: Controls for Coulomb friction problem and the algebraic equation 0 = L2 − x1 (t)2 − x2 (t)2 (3.37) where xj (t), j = 1, . . . , 4 are the differential states, λ(t) is an algebraic state (note that algebraic states are treated as control variables), and L is a parameter to be estimated. The observations function is given by: y1 = x1 (3.38) y2 = x2 And the following least squares objective is minimised: J= ns X (y1 (tk ) − ŷ1 (tk ))2 + (y2 (tk ) − ŷ2 (tk ))2 k=1 where ns = 20, t1 = 0.5 and t20 = 10.0. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// ident1.cxx //////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: DAE Index 3 //////////////// //////// Last modified: 07 June 2011 //////////////// 150 (3.39) //////// Reference: Schittkowski (2002) //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2011 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the observation function ////////// ////////////////////////////////////////////////////////////////////////// void observation_function( adouble* adouble* adouble* adouble* observations, states, adouble* controls, parameters, adouble& time, int k, xad, int iphase, Workspace* workspace) { observations[ CINDEX(1) ] = states[ CINDEX(1) ]; observations[ CINDEX(2) ] = states[ CINDEX(2) ]; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { // Variables adouble x1, x2, x3, x4, L, OMEGA, LAMBDA; adouble dx1, dx2, dx3, dx4; // Differential states x1 = states[CINDEX(1)]; x2 = states[CINDEX(2)]; x3 = states[CINDEX(3)]; x4 = states[CINDEX(4)]; // Algebraic variables LAMBDA = controls[CINDEX(1)]; // Parameters L = parameters[CINDEX(1)]; // Differential equations dx1 = x3; dx2 = x4; dx3 = LAMBDA*x1; dx4 = LAMBDA*x2; derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = dx1; dx2; dx3; dx4; // algebraic equation path[ CINDEX(1) ] = L*L - x1*x1 - x2*x2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, 151 int iphase, Workspace* workspace) { // no events return; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = = "DAE Index 3"; "dae_i3.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = problem.phases(1).ncontrols = problem.phases(1).nevents = problem.phases(1).npath = problem.phases(1).nparameters problem.phases(1).nodes problem.phases(1).nobserved problem.phases(1).nsamples 4; 1; 0; 1; = 1; = "[10,20,30]"; = 2; = 20; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// //////////// Load data for parameter estimation //////////// //////////////////////////////////////////////////////////////////////////// int iphase = 1; load_parameter_estimation_data(problem, iphase, "dae_i3.dat"); problem.phases(1).observation_nodes.Print("observation nodes"); problem.phases(1).observations.Print("observations"); problem.phases(1).residual_weights.Print("weights"); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, p, t; 152 //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) = = = = -2.0; -2.0; -2.0; -2.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) = = = = 2.0; 2.0; 2.0; 2.0; problem.phases(1).bounds.lower.controls(1) = -10.0; problem.phases(1).bounds.upper.controls(1) = 10.0; problem.phases(1).bounds.lower.parameters(1) problem.phases(1).bounds.upper.parameters(1) problem.phases(1).bounds.lower.path(1) problem.phases(1).bounds.upper.path(1) = 0.0; = 5.0; = 0.0; = 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.5; = 0.5; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 10.0; = 10.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; problem.observation_function = & observation_function; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes = (int) problem.phases(1).nsamples; DMatrix state_guess(4, nnodes); DMatrix control_guess(1,nnodes); DMatrix param_guess(1,1); state_guess(1,colon()) state_guess(2,colon()) state_guess(3,colon()) state_guess(4,colon()) = = = = problem.phases(1).observations(1,colon()); problem.phases(1).observations(2,colon()); ones(1,nnodes); ones(1,nnodes); control_guess(1,colon()) = zeros(1,nnodes); param_guess = 0.5; problem.phases(1).guess.states problem.phases(1).guess.time problem.phases(1).guess.parameters problem.phases(1).guess.controls = = = = state_guess; problem.phases(1).observation_nodes; param_guess; control_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.jac_sparsity_ratio = = = = = "IPOPT"; "automatic"; "automatic"; "Legendre"; 0.50; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); 153 //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t p = = = = solution.get_states_in_phase(1); solution.get_controls_in_phase(1); solution.get_time_in_phase(1); solution.get_parameters_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); p.Print("Estimated parameter"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix tm; DMatrix ym; tm = problem.phases(1).observation_nodes; ym = problem.phases(1).observations; plot(t,x(1,colon()),tm,ym(1,colon()),problem.name, "time (s)", "state x1", "x1 yhat1"); plot(t,x(2,colon()),tm,ym(2,colon()),problem.name, "time (s)", "state x2", "x2 yhat2"); plot(t,u,problem.name, "time (s)", "algebraic state u", "u"); plot(t,x(1,colon()),tm,ym(1,colon()),problem.name, "time (s)", "state x1", "x1 yhat1", "pdf", "x1.pdf"); plot(t,x(2,colon()),tm,ym(2,colon()),problem.name, "time (s)", "state x2", "x2 yhat2", "pdf", "x2.pdf"); plot(t,u,problem.name, "time (s)", "algebraic state lambda", "lambda", "pdf", "lambda.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT summarised in the box below and shown in Figures 3.21 and 3.22, which compare the observations with the estimated outputs, and 3.23, which shows the algebraic state. The exact solution to the problem is L = 1 and λ(t) = −1. The numerical solution obtained is L = 1.000000188 and λ(t) = −0.999868. The 95% confidence interval for the estimated parameter is [0.9095289, 1.090471] PSOPT results summary ===================== Problem: DAE Index 3 CPU time (seconds): 1.046000e+01 NLP solver used: IPOPT 154 DAE Index 3 x1 yhat1 1 state x1 0.5 0 -0.5 -1 0 1 2 3 4 5 6 7 8 9 10 time (s) Figure 3.21: State x1 and observations Optimal (unscaled) cost function value: 1.965352e+01 Phase 1 endpoint cost function value: 1.965352e+01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 5.000000e-01 Phase 1 final time: 1.000000e+01 Phase 1 maximum relative local error: 8.341290e-08 NLP solver reports: The problem solved! 3.10 Delayed states problem 1 Consider the following optimal control problem, which consists of a linear system with delays in the state equations [29]. Minimize the cost functional: J = x3 (tf ) (3.40) subject to the dynamic constraints ẋ1 = x2 (t) ẋ2 = −10x1 (t) − 5x2 (t) − 2x1 (t − τ ) − x2 (t − τ ) + u(t) ẋ3 = 0.5(10x21 (t) + x22 (t) + u2 (t)) 155 (3.41) DAE Index 3 x2 yhat2 1 state x2 0.5 0 -0.5 -1 0 1 2 3 4 5 6 7 8 9 10 time (s) Figure 3.22: State x2 and observations DAE Index 3 lambda -0.285 algebraic state lambda -0.29 -0.295 -0.3 -0.305 -0.31 0 1 2 3 4 5 6 7 time (s) Figure 3.23: Algebraic state λ(t) 156 8 9 10 and the boundary conditions x1 (0) = 1 x2 (0) = 1 x3 (0) = 0 (3.42) where tf = 5 and τ = 0.25. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// delay1.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Time delay problem //////////////// //////// Last modified: 09 January 2009 //////////////// //////// Reference: Luus (2002) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x3f = final_states[CINDEX(3)]; return x3f; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1delayed, x2delayed; double tau = 0.25; adouble x1 = states[CINDEX(1)]; adouble x2 = states[CINDEX(2)]; adouble x3 = states[CINDEX(3)]; get_delayed_state( &x1delayed, 1, iphase, time, tau, xad, workspace); get_delayed_state( &x2delayed, 2, iphase, time, tau, xad, workspace); adouble u = controls[CINDEX(1)]; derivatives[CINDEX(1)] = x2; derivatives[CINDEX(2)] = -10*x1-5*x2-2*x1delayed-x2delayed+u; 157 // [uncomment the line below for approximate solution] // derivatives[CINDEX(2)] = (-12*x1+(2*tau-6)*x2+u)/(1-tau); derivatives[CINDEX(3)] = 0.5*(10*x1*x1+x2*x2+u*u); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[CINDEX(1)]; adouble x20 = initial_states[CINDEX(2)]; adouble x30 = initial_states[CINDEX(3)]; e[CINDEX(1)] = x10; e[CINDEX(2)] = x20; e[CINDEX(3)] = x30; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Time delay problem 1"; = "delay1.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath = = = = 3; 1; 3; 0; problem.phases(1).nodes = "[30]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// 158 /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double x1L x2L x3L x1U x2U x3U = = = = = = -100.0; -100.0; -100.0; 100.0; 100.0; 100.0; double uL = -100.0; double uU = 100.0; problem.phases(1).bounds.lower.states(1) = x1L; problem.phases(1).bounds.lower.states(2) = x2L; problem.phases(1).bounds.lower.states(3) = x3L; problem.phases(1).bounds.upper.states(1) = x1U; problem.phases(1).bounds.upper.states(2) = x2U; problem.phases(1).bounds.upper.states(3) = x3U; problem.phases(1).bounds.lower.controls(1) = uL; problem.phases(1).bounds.upper.controls(1) = uU; problem.phases(1).bounds.lower.events(1) = 1.0; problem.phases(1).bounds.lower.events(2) = 1.0; problem.phases(1).bounds.lower.events(3) = 0.0; problem.phases(1).bounds.upper.events(1) = 1.0; problem.phases(1).bounds.upper.events(2) = 1.0; problem.phases(1).bounds.upper.events(3) = 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 5.0; = 5.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(3,60); x0(1,colon()) = linspace(1.0,1.0, 60); x0(2,colon()) = linspace(1.0,1.0, 60); x0(3,colon()) = linspace(0.0,0.0, 60); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,60); = x0; = linspace(0.0,5.0, 60); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; 159 // algorithm.collocation_method algorithm.mesh_refinement = "Hermite-Simpson"; = "automatic"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "x1 x2 x3"); plot(t,u,problem.name, "time (s)", "control", "u"); plot(t,x,problem.name, "time (s)", "states", "x1 x2 x3", "pdf", "delay1_states.pdf"); plot(t,u,problem.name, "time (s)", "control", "u", "pdf", "delay1_controls.pdf"); } The output from PSOPT summarised in the box below and shown in Figures 3.24 and 3.25, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Time delay problem 1 CPU time (seconds): 1.624518e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:59:52 2019 Optimal Phase 1 Phase 1 Phase 1 Phase 1 Phase 1 (unscaled) cost function value: 2.526875e+00 endpoint cost function value: 2.526875e+00 integrated part of the cost: 0.000000e+00 initial time: 0.000000e+00 final time: 5.000000e+00 maximum relative local error: 1.848000e-02 160 Time delay problem 1 x1 x2 x3 2.5 2 1.5 states 1 0.5 0 -0.5 -1 -1.5 0 1 2 3 4 5 time (s) Figure 3.24: States for time delay problem 1 NLP solver reports: 3.11 The problem has been solved! Dynamic MPEC problem Consider the following optimal control problem, which involves special handling of a system with a discontinuous right hand side [4]. Minimize the cost functional: Z 2 J = [y(2) − 5/3]2 + y 2 (t)dt (3.43) 0 subject to ẏ = 2 − sgn(y) (3.44) y(0) = −1 (3.45) and the boundary condition Note that there is no control variable, and the analytical solution of this problem satisfies ẏ(t) = 3, 0 ≤ t ≤ 1/3, and ẏ(t) = 1, 1/3 ≤ t ≤ 2. In order to handle the discontinuous right hand side, the problem is converted into the following equivalent problem, which has three algebraic (control) variables. This type of problem is known in the literature as a 161 Time delay problem 1 u 0 control -0.1 -0.2 -0.3 -0.4 -0.5 0 1 2 3 4 5 time (s) Figure 3.25: Control for time delay problem 1 dynamic MPEC problem. Z 2 2 J = [y(2) − 5/3] + y 2 (t) + ρ {p(t)[s(t) + 1] + q(t)[1 − s(t)]} dt (3.46) 0 subject to ẏ = 2 − sgn(y) 0 = −y(t) − p(t) + q(t) (3.47) y(0) = −1 (3.48) −1 ≤ s(t) ≤ 1, 0 ≤ p(t), 0 ≤ q(t). (3.49) the boundary condition and the bounds: The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// bryson_max_range.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Dynamic MPEC problem //////////////// //////// Last modified: 27 May 2011 //////////////// //////// Reference: Betts (2010) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// 162 //////// This is part of the PSOPT software library, which//////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* adouble* adouble* { adouble y_f = final_states[ initial_states, adouble* final_states, parameters,adouble& t0, adouble& tf, xad, int iphase, Workspace* workspace) CINDEX(1) ]; return pow( y_f - (5.0/3.0) , 2.0); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble y = states[ CINDEX(1) ]; adouble s = controls[ CINDEX(1) ]; adouble p = controls[ CINDEX(2) ]; adouble q = controls[ CINDEX(3) ]; adouble retval; double rho = 1.e3; retval = y*y + rho*( p*(s+1.0)+ q*(1.0-s)); return retval; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble y = states[ CINDEX(1) ]; adouble ydot; adouble s = controls[ CINDEX(1) ]; adouble p = controls[ CINDEX(2) ]; adouble q = controls[ CINDEX(3) ]; ydot = 2.0 -s; derivatives[ CINDEX(1) ] = ydot; path[ CINDEX(1) ] = -y - p + q; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble y0 = initial_states[ CINDEX(1) ]; 163 e[ CINDEX(1) ] = y0; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Dynamic MPEC problem"; = "mpec.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 1; 3; 1; 1; = "[20]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix y, controls, s, p, q, t; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double y0 = -1.0; problem.phases(1).bounds.lower.states(1) = -10.0; problem.phases(1).bounds.upper.states(1) = 10.0; problem.phases(1).bounds.lower.controls(1) = -1.0; problem.phases(1).bounds.lower.controls(2) = 0.0; problem.phases(1).bounds.lower.controls(3) = 0.0; 164 problem.phases(1).bounds.upper.controls(1) = 1.0; problem.phases(1).bounds.upper.controls(2) = inf; problem.phases(1).bounds.upper.controls(3) = inf; problem.phases(1).bounds.lower.events(1) = y0; problem.phases(1).bounds.upper.events(1) = y0; problem.phases(1).bounds.upper.path(1) = 0.0; problem.phases(1).bounds.lower.path(1) = 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 2.0; = 2.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix x_guess = zeros(nstates,nnodes); x_guess(1,colon()) = y0*ones(1,nnodes); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(ncontrols,nnodes); = x_guess; = linspace(0.0,2.0,nnodes); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// // algorithm.nlp_iter_max = 1000; algorithm.nlp_tolerance = 1.e-4; algorithm.nlp_method = "IPOPT"; algorithm.scaling = "automatic"; algorithm.derivatives = "automatic"; algorithm.mesh_refinement = "automatic"; algorithm.collocation_method = "trapezoidal"; algorithm.defect_scaling = "jacobian-based"; algorithm.ode_tolerance = 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// y controls t s p q = = = = = = solution.get_states_in_phase(1); solution.get_controls_in_phase(1); solution.get_time_in_phase(1); controls(1,colon()); controls(2,colon()); controls(3,colon()); 165 //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// y.Save("y.dat"); controls.Save("controls.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,y,problem.name+": state", "time (s)", "state","y"); plot(t,s,problem.name+": algebraic variable s","time (s)", "s", "s"); plot(t,p,problem.name+": algebraic variable p","time (s)", "p", "p"); plot(t,q,problem.name+": algebraic variable q","time (s)", "q", "q"); plot(t,y,problem.name+": state", "time (s)", "state","y", "pdf", "y.pdf"); plot(t,s,problem.name+": algebraic variable s","time (s)", "s", "s", "pdf", "s.pdf"); plot(t,p,problem.name+": algebraic variable p","time (s)", "p", "p", "pdf", "p.pdf"); plot(t,q,problem.name+": algebraic variable q","time (s)", "q", "q", "pdf", "q.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT summarised in the box below and shown in Figures 3.26, 3.27, 3.28, and 3.29. PSOPT results summary ===================== Problem: Dynamic MPEC problem CPU time (seconds): 4.031824e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:00:52 2019 Optimal (unscaled) cost function value: 1.653464e+00 Phase 1 endpoint cost function value: 3.770062e-07 Phase 1 integrated part of the cost: 1.653464e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.000000e+00 Phase 1 maximum relative local error: 1.842153e-06 NLP solver reports: The problem has been solved! 166 Dynamic MPEC problem: state y 1.5 1 state 0.5 0 -0.5 -1 0 0.5 1 1.5 2 time (s) Figure 3.26: State y for dynamic MPEC problem 3.12 Geodesic problem This problem is about calculating the geodesic curve 1 that joins two points on Earth using optimal control. The problem is posed in the form of estimating the shortest fligh path for an airliner to fly from New Yorks’s JFK to London’s LHR airport. The formulation is as follows. Find the trajectories for the elevation and azimuth angles θ(t) and φ(t) ∈ [0, tf ] to minimize the cost functional Z tf p J= ẋ2 + ẏ 2 + ż 2 dt (3.50) 0 subject to the dynamic constraints ẋ = V sin(θ) cos(φ) ẏ = V sin(θ) sin(φ) ż = V cos(θ) The path constraint, which corresponds to the Earth’s spheroid x2 y 2 z 2 + 2 + 2 − 1.0 = 0 a2 a b 1 2 See http://mathworld.wolfram.com/Geodesic.html See http://mathworld.wolfram.com/Ellipsoid.html 167 (3.51) 2 shape: (3.52) Dynamic MPEC problem: algebraic variable s s 1 s 0.5 0 -0.5 -1 0 0.5 1 1.5 2 time (s) Figure 3.27: Algebraic variable s for dynamic MPEC problem Dynamic MPEC problem: algebraic variable p 1 p 0.8 p 0.6 0.4 0.2 0 0 0.5 1 1.5 2 time (s) Figure 3.28: Algebraic variable p for dynamic MPEC problem 168 Dynamic MPEC problem: algebraic variable q q 1.6 1.4 1.2 q 1 0.8 0.6 0.4 0.2 0 0 0.5 1 1.5 2 time (s) Figure 3.29: Algebraic variable q for dynamic MPEC problem the boundary conditions, which correspond to the geographical coordinates of LHR (51.4700◦ N, 0.4543◦ W) and JFK (40.6413◦ N, 73.7781◦ W) x(0) y(0) z(0) x(tf ) y(tf ) z(tf ) = = = = = = x0 y0 z0 xf yf zf (3.53) and the control bounds 0 ≤ θ(t) ≤ π 0 ≤ φ(t) ≤ 2π (3.54) where x, y, z are the Cartesian coordinates (in km) with origin on the centre of Earth, t is time in hours, V = 900 km/h corresponds to the cruising speed of a typical airliner, a = 6384 km is the Earth’s semi-major axis, and b = 6353 km is the Earth’s semi-minor axis, which is the length of the Earth’s axis of rotation from the north pole to the south pole. For simplicity, the altitude of the aircraft is neglected. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// geodesic.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// 169 //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Geodesic calculation problem //////////////// //////// Last modified: 22 February 2019 //////////////// //////// Reference: PROPT User’s Guide //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2019 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" typedef struct { double V; double a; double b; } Constants; ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase,Workspace* workspace) { return 0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { Constants* C = (Constants*) workspace->user_data; double V = C->V; adouble theta = controls[ CINDEX(1) ]; adouble phi = controls[ CINDEX(2) ]; // These are adouble dxdt adouble dydt adouble dzdt the components of the velocity vector in spherical coordinates. = V*sin(theta)*cos(phi); = V*sin(theta)*sin(phi); = V*cos(theta); // The integrand is the norm of the speed vector adouble L = sqrt( pow(dxdt,2.0) + pow(dydt,2.0)+pow(dzdt,2.0) ); return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { Constants* C = (Constants*) workspace->user_data; adouble x adouble y adouble z = states[ CINDEX(1) ]; = states[ CINDEX(2) ]; = states[ CINDEX(3) ]; double V = C->V; // Speed double a = C->a; // Semi-major axis double b = C->b; // Semi-minor axis // These are the angles of the velocity vector in spherical coordinates adouble theta = controls[ CINDEX(1) ]; 170 adouble phi = controls[ CINDEX(2) ]; // Simple kinematic equations of motion in spherical coordinates adouble dxdt = V*sin(theta)*cos(phi); adouble dydt = V*sin(theta)*sin(phi); adouble dzdt = V*cos(theta); derivatives[ CINDEX(1) ] = dxdt; derivatives[ CINDEX(2) ] = dydt; derivatives[ CINDEX(3) ] = dzdt; // This is the geodesic constraint to stay on the surface of the spheroid path[ CINDEX(1) ] = x*x/(a*a) + y*y/(a*a) + z*z/(b*b) - 1.0; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble adouble adouble e[ e[ e[ e[ e[ e[ x0 y0 z0 xf yf zf = = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) initial_states[ initial_states[ initial_states[ final_states[ final_states[ final_states[ ] ] ] ] ] ] = = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(1) CINDEX(2) CINDEX(3) ]; ]; ]; ]; ]; ]; x0; y0; z0; xf; yf; zf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Geodesic problem"; = "geodesic.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases = 1; 171 problem.nlinkages = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = 3; = 2; = 6; = 1; = "[30]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// Constants* C = new Constants; problem.user_data = (void*) C; C->V = 900.00; // Speed in km/h C->a = 6384.0; // Earth’s semi-major axis in km C->b = 6353.0; // Earth’s semi-minor axis in km double a = C->a; double b = C->b; double double double double double double xL yL zL xU yU zU = -a; = -a; = -b; = a; = a; = b; double double double double thetaL thetaU phiL phiU = 0.0; = pi; = 0.0; = 2.0*pi; // Coordinates of LHR: 51.4700 N, 0.4543 W double lat_lhr = 51.74*pi/180.0; double lon_lhr = 0.4543*pi/180.0; // Coordinates of JFK: 40.6413 N, 73.7781 W double lat_jfk = 40.6413*pi/180.0; double lon_jfk = 73.7781*pi/180.0; // Below, theta=0 corresponds to 90 deg latitude north, growing positive towards the south // while phi=0 corresponds to 0 longitude, growing positive towards the east. double theta0 = pi/2.0 - lat_jfk; double phi0 = 2.0*pi - lon_jfk; // Initial elevation angle, for JFK in New York // Initial azimuth angle, for JFK in New York double thetaf = pi/2.0 - lat_lhr; // Final elevation angle, for LHR in London double phif = 2.0*pi - lon_lhr; // Final azimuth angle, for LHR in London // Here we calculate initial and final Cartesian coordinates using // the parametric equations of the ellipsoid. double double double double double double x0 y0 z0 xf yf zf = = = = = = a*sin(theta0)*cos(phi0); a*sin(theta0)*sin(phi0); b*cos(theta0); a*sin(thetaf)*cos(phif); a*sin(thetaf)*sin(phif); b*cos(thetaf); problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) = = = = = = problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.upper.controls(2) xL; yL; zL; xU; yU; zU; = = = = thetaL; thetaU; phiL; phiU; 172 problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) = = = = = = x0; y0; z0; xf; yf; zf; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) problem.phases(1).bounds.upper.events(5) problem.phases(1).bounds.upper.events(6) = = = = = = x0; y0; z0; xf; yf; zf; problem.phases(1).bounds.lower.path(1) = 0.0; problem.phases(1).bounds.upper.path(1) = 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 3.0; // lower bound in hours = 10.0; // upper bound in hours //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates DMatrix u_guess = DMatrix x_guess = DMatrix time_guess = = 30; = problem.phases(1).ncontrols; = problem.phases(1).nstates; zeros(ncontrols,nnodes); zeros(nstates,nnodes); linspace(0.0,7.0,nnodes); u_guess(1,colon()) = linspace(theta0,thetaf,nnodes); u_guess(2,colon()) = linspace(phi0,phif,nnodes); for (int i = 1;i<= nnodes;i++) { x_guess(1,i) = a*sin(u_guess(1,i))*cos(u_guess(2,i)); x_guess(2,i) = a*sin(u_guess(1,i))*sin(u_guess(2,i)); x_guess(3,i) = b*cos(u_guess(1,i)); } problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = u_guess; = x_guess; = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max = 1000; algorithm.nlp_tolerance = 1.e-4; algorithm.nlp_method = "IPOPT"; algorithm.scaling = "automatic"; algorithm.derivatives = "automatic"; algorithm.collocation_method = "trapezoidal"; algorithm.mesh_refinement = "automatic"; //////////////////////////////////////////////////////////////////////////// 173 /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix states DMatrix controls DMatrix t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); DMatrix x = states(1,colon()); DMatrix y = states(2,colon()); DMatrix z = states(3,colon()); DMatrix theta = controls(1,colon()); DMatrix phi = controls(2,colon()); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); y.Save("y.dat"); z.Save("z.dat"); theta.Save("theta.dat"); phi.Save("phi.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot3(x(1,colon()), y(1,colon()), z(1,colon()), "Geodesic problem", "x", "y", "z", NULL, NULL, "30,97"); plot3(x(1,colon()), y(1,colon()), z(1,colon()), "Geodesic problem", "x", "y", "z", "pdf", "trajectory.pdf", "30,97"); plot(t,states,problem.name, "time (s)", "states", "x y z"); plot(t,controls,problem.name, "time (s)", "controls", "theta phi"); plot(t,states,problem.name, "time (s)", "states", "x y z", "pdf", "geodesic_states.pdf"); plot(t,controls,problem.name, "time (s)", "controls", "theta phi", "pdf", "geodesic_controls.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.30, 3.31 and 3.32, which show the flight path, the elements of the state vector, and the elements of the control vector, respectively. Note that PSOPT predicts that the length of the shortest flightpath is 5,540.4 km, and the flight time is 6 hours 9 min. 174 PSOPT results summary ===================== Problem: Geodesic problem CPU time (seconds): 1.405730e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Fri Feb 22 08:38:37 2019 Optimal (unscaled) cost function value: 5.540469e+03 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 5.540469e+03 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 6.156077e+00 Phase 1 maximum relative local error: 1.530431e-04 NLP solver reports: The problem has been solved! 3.13 Goddard rocket maximum ascent problem Consider the following optimal control problem, which is known in the literature as the Goddard rocket maximum ascent problem [6]. Find tf and T (t) ∈ [t0 , tf ] to minimize the cost functional J = h(tf ) (3.55) subject to the dynamic constraints 1 v̇ = m (T − D) − g ḣ = v ṁ = − Tc (3.56) the boundary conditions: v(0) h(0) m(0) m(tf ) = = = = 0 1 1 0.6 (3.57) the state bounds: 0.0 ≤ v(t) ≤ 2.0 1.0 ≤ h(t) ≤ 2.0 0.6 ≤ m(t) ≤ 1.0 175 (3.58) 8000 London 6000 New York 4000 2000 z 0 -2000 -4000 -6000 -8000 -1 0 10 4 1 x -0.5 -1 10 4 y Figure 3.30: Flight path for geodesic problem Geodesic problem x y z 4000 states 2000 0 -2000 -4000 0 1 2 3 4 5 time (s) Figure 3.31: States for geodesic problem 176 6 1 0.5 0 7 Geodesic problem theta phi 1.6 controls 1.4 1.2 1 0.8 0 1 2 3 4 5 6 7 time (s) Figure 3.32: Controls for geodesic problem and the control bounds where 0 ≤ T (t) ≤ 3.5 (3.59) D = D0 v 2 exp(−βh) , g = 1/(h2 ) (3.60) D0 = 310, β = 500, and c = 0.5, 0.1 ≤ tf ≤ 1. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// goddard.cxx ///////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Goddard rocket maximum ascent //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: Bryson (1999) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) 177 { adouble hf = final_states[1]; return -hf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble vdot, hdot, mdot; adouble v = states[0]; adouble h = states[1]; adouble m = states[2]; adouble T = controls[0]; double c double D0 double beta = 0.5; = 310.0; = 500.0; adouble g adouble D = 1.0/(h*h); = D0*v*v*exp(-beta*h); vdot = 1.0/m*(T-D)-g; hdot = v; mdot = -T/c; derivatives[0] = vdot; derivatives[1] = hdot; derivatives[2] = mdot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble vi = initial_states[0]; adouble hi = initial_states[1]; adouble mi = initial_states[2]; adouble mf = final_states[2]; e[0] e[1] e[2] e[3] = = = = vi; hi; mi; mf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { 178 } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Goddard Rocket Maximum Ascent"; = "goddard.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Declare problem level constants & setup phases ////////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & setup PSOPT ///////////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 3; 1; 4; 0; = 20; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x,u,t; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double v_L h_L m_L v_U h_U m_U = = = = = = 0; 1.0; 0.6; 2.0; 2.0; 1.0; double T_L = 0.0; double T_U = 3.5; double double double double v_i h_i m_i m_f = = = = 0.0; 1.0; 1.0; 0.6; problem.phases(1).bounds.lower.states(1) = v_L; problem.phases(1).bounds.lower.states(2) = h_L; problem.phases(1).bounds.lower.states(3) = m_L; problem.phases(1).bounds.upper.states(1) = v_U; problem.phases(1).bounds.upper.states(2) = h_U; 179 problem.phases(1).bounds.upper.states(3) = m_U; problem.phases(1).bounds.lower.controls(1) = T_L; problem.phases(1).bounds.upper.controls(1) = T_U; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) = = = = v_i; h_i; m_i; m_f; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) = = = = v_i; h_i; m_i; m_f; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.1; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(3,20); x0(1,colon()) = linspace(v_i,v_i, 20); x0(2,colon()) = linspace(h_i,h_i, 20); x0(3,colon()) = linspace(m_i,m_i, 20); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = T_U*ones(1,20); = x0; = linspace(0.0, 15.0, 20); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.collocation_method algorithm.mesh_refinement algorithm.mr_max_iterations = = = = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; "trapezoidal"; "automatic"; 5; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// 180 /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "v h m"); plot(t,u,problem.name, "time (s)", "control", "T"); plot(t,x,problem.name, "time (s)", "states", "v h m", "pdf", "goddard_states.pdf"); plot(t,u,problem.name, "time (s)", "control", "T", "pdf", "goddard_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.33 and 3.34, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Goddard Rocket Maximum Ascent CPU time (seconds): 2.799138e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:15:41 2019 Optimal (unscaled) cost function value: -1.025336e+00 Phase 1 endpoint cost function value: -1.025336e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.605347e-01 Phase 1 maximum relative local error: 6.877099e-04 NLP solver reports: The problem has been solved! 3.14 Hang glider This problem is about the range maximisation of a hang glider in the presence of a specified thermal draft [4]. Find tf and CL (t), t ∈ [0, tf ], to min181 Goddard Rocket Maximum Ascent v h m 1 0.8 states 0.6 0.4 0.2 0 0 0.05 0.1 0.15 0.2 0.25 0.3 time (s) Figure 3.33: States for Goddard rocket problem Goddard Rocket Maximum Ascent 3.5 T 3 2.5 control 2 1.5 1 0.5 0 0 0.05 0.1 0.15 0.2 0.25 time (s) Figure 3.34: Control for Goddard rocket problem 182 0.3 imise, J = x(tf ) (3.61) subject to the dynamic constraints ẋ ẏ v̇x v̇y where = vx = vy 1 = m (−L sin η − D cos η) 1 = m (L cos η − D sin η − W ) (3.62) CD = C0 + kCL2 q vr = vx2 + vy2 1 D = CD ρSvr2 2 1 L = CL ρSvr2 2 x 2 X= − 2.5 R ua = uM (1 − X) exp(−X) (3.63) Vy = vy − ua Vy sin η = vr vx cos η = vr W = mg The control is bounded as follows: 0 ≤ CL ≤ 1.4 (3.64) and the following boundary conditions: x(0) = 0, x(tf ) = free y(0) = 1000, y(tf ) = 900 vx (0) = 13.227567500, vx (tf ) = 13.227567500 (3.65) vy (0) = −1.2875005200, vy (tf ) = −1.2875005200 With the following parameter values: uM = 2.5, m = 100.0 R = 100.0, S = 14, C0 = 0.034, ρ = 1.13 k = 0.069662, g = 9.80665 PSOPT code that solves this problem is shown below. 183 (3.66) ////////////////////////////////////////////////////////////////////////// //////////////// glider.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Hang glider problem //////////////// //////// Last modified: 11 July 2009 //////////////// //////// Reference: PROPT User Manual //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble xf = final_states[CINDEX(1)]; return -(xf); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble CL adouble adouble adouble adouble double double double double = controls[ CINDEX(1) ]; x = states[ CINDEX(1) ]; y = states[ CINDEX(2) ]; vx = states[ CINDEX(3) ]; vy = states[ CINDEX(4) ]; m uM C0 S = = = = 100.0, 2.5, 0.034, 14.0, g = 9.80665; R = 100.0; k = 0.069662; rho = 1.13; adouble sin_eta, cos_eta, D, L, CD, Vy, ua, X, vr, W; CD = C0 + k*CL*CL; vr = sqrt(vx*vx + vy*vy); D = 0.5*CD*rho*S*vr*vr; L = 0.5*CL*rho*S*vr*vr; X = pow(x/R - 2.5, 2.0); ua = uM*(1.0-X)*exp(-X); Vy = vy-ua; sin_eta = Vy/vr; cos_eta = vx/vr; 184 W = m*g; derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = vx; vy; 1.0/m*(-L*sin_eta - D*cos_eta ); 1.0/m*( L*cos_eta - D*sin_eta - W); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble x_i = initial_states[ CINDEX(1) ]; y_i = initial_states[ CINDEX(2) ]; vx_i = initial_states[ CINDEX(3) ]; vy_i = initial_states[ CINDEX(4) ]; adouble adouble adouble adouble x_f = final_states[ CINDEX(1) ]; y_f = final_states[ CINDEX(2) ]; vx_f = final_states[ CINDEX(3) ]; vy_f = final_states[ CINDEX(4) ]; e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] e[ CINDEX(5) ] e[ CINDEX(6) ] e[ CINDEX(7) ] = x_i; = y_i; = vx_i; = vy_i; = y_f; = vx_f; = vy_f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Hang glider problem"; = "hang_glider.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// 185 //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 4; 1; 7; 0; = "[30 40 50 80]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states = "[0.0 problem.phases(1).bounds.upper.states = "[1500.0 0.0 0.0 1100.0 15.0 -4.0]"; 4.0]"; problem.phases(1).bounds.lower.controls(1) = 0.0; problem.phases(1).bounds.upper.controls(1) = 1.4; problem.phases(1).bounds.lower.events="[0.0,1000.0,13.2275675,-1.28750052,900.00,13.2275675,-1.28750052 ]"; problem.phases(1).bounds.upper.events="[0.0,1000.0,13.2275675,-1.28750052,900.00,13.2275675,-1.28750052 ]"; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.1; = 200.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix state_guess DMatrix control_guess DMatrix time_guess = = = state_guess(1,colon()) state_guess(2,colon()) state_guess(3,colon()) state_guess(4,colon()) = = = = zeros(nstates,nnodes); 1.0*ones(ncontrols,nnodes); linspace(0.0,105.0,nnodes); linspace(0.0, 1250, nnodes); linspace(1000.0, 900.0, nnodes); 13.23*ones(1,nnodes); -1.288*ones(1,nnodes); problem.phases(1).guess.states = state_guess; problem.phases(1).guess.controls = control_guess; problem.phases(1).guess.time = time_guess; 186 //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix states, CL, t, x, y, speeds; states CL t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// states.Save("states.dat"); CL.Save("cL.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// x = states(1,colon()); y = states(2,colon()); speeds = (states(3,colon()) && states(4,colon())); plot(x,y,problem.name+": trajectory","x [m]", "y [m]", "traj"); plot(t,speeds,problem.name+": speeds","time (s)", "speeds [m/s]", "dxdt dydt"); plot(t,CL,problem.name+": control","time (s)", "control", "CL"); plot(x,y,problem.name+": trajectory","x [m]", "y [m]", "traj", "pdf", "traj.pdf"); plot(t,speeds,problem.name+": speeds","time (s)", "speeds [m/s]", "dxdt dydt", "pdf", "velocities.pdf"); plot(t,CL,problem.name+": control - lift coefficient","time (s)", "CL", "CL", "pdf", "control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.35, 3.36 and 3.37. 187 Hang glider problem: trajectory traj 1040 1020 1000 y [m] 980 960 940 920 900 880 860 0 200 400 600 800 1000 1200 1400 x [m] Figure 3.35: x − y trajectory for hang glider Hang glider problem: speeds dxdt dydt 12 10 speeds [m/s] 8 6 4 2 0 -2 0 10 20 30 40 50 60 70 80 time (s) Figure 3.36: Velocities for hang glider 188 90 100 Hang glider problem: control - lift coefficient CL 1.4 1.3 1.2 CL 1.1 1 0.9 0.8 0.7 0 10 20 30 40 50 60 70 80 90 100 time (s) Figure 3.37: Lift coefficient for hang glider problem 3.15 Hanging chain problem Consider the following optimal control problem, which includes an integral constraint. Minimize the cost functional Z tf q 2 J= x 1 + (ẋ) dt (3.67) 0 subject to the dynamic constraint ẋ = u (3.68) the integral constraint: Z tf s 1+ 0 dx dt 2 dt = 4 (3.69) the boundary conditions x(0) = 1 x(tf ) = 3 (3.70) and the bounds: −20 ≤ u(t) ≤ 20 −10 ≤ x(t) ≤ 10 (3.71) where tf = 1. The PSOPT code that solves this problem is shown below. 189 ////////////////////////////////////////////////////////////////////////// ////////////////// chain.cxx /////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Hanging chain problem //////////////// //////// Last modified: 29 January 2009 //////////////// //////// Reference: //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[ CINDEX(1) ]; adouble dxdt = controls[ CINDEX(1) ]; adouble L = x*sqrt(1.0+ pow(dxdt,2.0)); return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; adouble x = states[ CINDEX(1) ]; adouble dxdt = controls[ CINDEX(1) ]; derivatives[ CINDEX(1) ] = dxdt; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand of the integral constraint /////// //////////////////////////////////////////////////////////////////////////// adouble integrand( adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble G; adouble dxdt = controls[ CINDEX(1) ]; G = sqrt( 1.0 + pow(dxdt,2.0)); return G; } 190 //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x0 = initial_states[ CINDEX(1) ]; adouble xf = final_states[ CINDEX(1) ]; adouble Q; // Compute the integral to be constrained Q = integrate( integrand, xad, iphase, workspace ); e[ CINDEX(1) ] = x0; e[ CINDEX(2) ] = xf; e[ CINDEX(3) ] = Q; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Hanging chain problem"; problem.outfilename = "chain.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 1; 1; 3; 0; = "[20, 50]"; psopt_level2_setup(problem, algorithm); 191 //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = problem.phases(1).bounds.upper.states(1) = -10.0; 10.0; problem.phases(1).bounds.lower.controls(1) = -20.0; problem.phases(1).bounds.upper.controls(1) = 20.0; problem.phases(1).bounds.lower.events(1) = problem.phases(1).bounds.lower.events(2) = problem.phases(1).bounds.lower.events(3) = 1.0; 3.0; 4.0; problem.phases(1).bounds.upper.events(1) = 1.0; problem.phases(1).bounds.upper.events(2) = 3.0; problem.phases(1).bounds.upper.events(3) = 4.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = 2.0*ones(1,30); = linspace(1.0,3.0, 30); = linspace(0.0,1.0, 30); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); if (solution.error_flag) exit(0); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); 192 //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": state", "time (s)", "x", "x"); plot(t,u,problem.name + ": control", "time (s)", "u", "u"); plot(t,x,problem.name + ": state", "time (s)", "x", "x", "pdf", "chain_state.pdf"); plot(t,u,problem.name + ": control", "time (s)", "u", "u", "pdf", "chain_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the text box below and in Figure 3.38, which illustrates the shape of the hanging chain. PSOPT results summary ===================== Problem: Hanging chain problem CPU time (seconds): 2.032182e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:08:27 2019 Optimal (unscaled) cost function value: 5.068480e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 5.068480e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 2.362643e-06 NLP solver reports: The problem has been solved! 3.16 Heat difussion problem This example can be viewed as a simplified model for the heating of a probe in a kiln [3]. The dynamics are a spatially discretized form of a partial 193 Hanging chain problem: state 3 x 2.5 x 2 1.5 1 0.5 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.38: State for hanging chain problem differential equation, which is obtained by using the method of the lines. The problem is formulated on the basis of the state vector x = [x1 , . . . , xM ]T and the control vector u = [v1 , v2 , v3 ]T , as follows Z 1 T min J = (xN (t) − xd (t))2 + γv1 (t)2 dt 2 0 u(t) subject to the differential constraints " # x2 − x1 2 1 1 ẋ1 = q1 + 2 (a3 + a4 x1 )(x2 − 2x1 + v2 ) + a4 (a1 + a2 x1 ) δ 2δ " # xi+1 − xi−1 2 1 1 ẋi = qi + 2 (a3 + a4 xi )(xi+1 − 2xi + xi−1 ) + a4 (a1 + a2 xi ) δ 2δ for i = 2, . . . , M − 1 " # 1 1 v3 − xM −1 2 ẋM = qM + 2 (a3 + a4 xM )(v3 − 2xN + xM −1 ) + a4 (a1 + a2 xM ) δ 2δ the path constraints 0 = g(x1 − v1 ) − 0= 1 (a3 + a4 x1 )(x2 − v2 ) 2δ 1 (a3 + a + 4xM )(v3 − xM −1 ) 2δ the control bounds uL ≤ v1 ≤ uU 194 and the initial conditions for the states: xi (0) = 2 + cos(πzi ) where i−1 , i = 1, . . . , M M −1 xd (t) = 2 − eρt q(z, t) = ρ(a1 + 2a2 ) + π 2 (a3 + 2a4 ) eρt cos(πz) zi = − a4 π 2 e2πt + (2a4 π 2 + ρa2 )e2ρt cos2 (πz) qi ≡ q(zi , t), i = 1, . . . , M with the parameter values a1 = 4, a2 = 1, a3 = 4, a4 = −1, uU = 0.1, ρ = −1, T = 0.5, γ = 10−3 , g = 1, uL = −∞. A spatial discretization given by M = 10 was used. The problem was solved initially by using first 50 nodes, then the mesh was refined to 60 nodes, and an interpolation of the previous solution was employed as an initial guess for the new solution. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// heat.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Template ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Heat difussion process //////////////// //////// Last modified: 09 July 2009 //////////////// //////// Reference: Betts (2001) //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which //////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// #define N_DISCRETIZATION 10 adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { 195 int N = N_DISCRETIZATION; double gamma double rho = 1.0e-3; = -1.0; adouble yd = 2.0-exp(rho*time); adouble yN = states[ CINDEX(N) ]; adouble v1 = controls[ CINDEX(1) ]; return 0.5*( pow(yN-yd,2.0) + gamma*pow(v1,2.0) ); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble u; double double double double double double double a1 = 4.0; a2 = 1.0; a3 = 4.0; a4 = -1.0; rho = -1.0; T = 0.5; g = 1.0; int N = N_DISCRETIZATION; int i; double delta = 1.0/(N-1); adouble* y = states; adouble v1 adouble v2 adouble v3 = controls[ CINDEX(1) ]; = controls[ CINDEX(2) ]; = controls[ CINDEX(3) ]; adouble adouble adouble adouble y1 = y2 = yN = yNm1 adouble x1 adouble xN = = adouble q1 = adouble qN = y[CINDEX(1)]; y[CINDEX(2)]; y[CINDEX(N)]; = y[CINDEX(N-1)]; 0; 1; (rho*(a1+2*a2) + pi*pi*(a3+2*a4))*exp(rho*time)*cos(pi*x1) - a4*pi*pi*exp(2*rho*time) + (2*a4*pi*pi+rho*a2)*exp(2*rho*time)*pow(cos(pi*x1),2.0); (rho*(a1+2*a2) + pi*pi*(a3+2*a4))*exp(rho*time)*cos(pi*xN) - a4*pi*pi*exp(2*rho*time) + (2*a4*pi*pi+rho*a2)*exp(2*rho*time)*pow(cos(pi*xN),2.0); derivatives[ CINDEX(1) ] = 1.0/(a1+a2*y1)*(q1 + (1.0/pow(delta,2.0))*(a3+a4*y1)*(y2-2*y1+v2)+a4*pow( (y2-v2)/(2*delta), 2.0) ); for(i=2;i<=(N-1);i++) { adouble yi = y[CINDEX(i)]; adouble yim1 = y[CINDEX(i-1)]; adouble yip1 = y[CINDEX(i+1)]; adouble xi = ( (double) (i-1) ) /( (double) (N-1) ); adouble qi = (rho*(a1+2*a2) + pi*pi*(a3+2*a4))*exp(rho*time)*cos(pi*xi) - a4*pi*pi*exp(2*rho*time) + (2*a4*pi*pi+rho*a2)*exp(2*rho*time)*pow(cos(pi*xi),2.0); derivatives[CINDEX(i)]=1.0/(a1+a2*yi)*(qi + (1.0/pow(delta,2.0))* (a3+a4*yi)*(yip1-2*yi+yim1)+a4*pow( (yip1-yim1)/(2*delta), 2.0) ); } derivatives[ CINDEX(N) ] = 1.0/(a1+a2*yN)*(qN + (1.0/pow(delta,2.0))* (a3+a4*yN)*(v3-2*yN+yNm1)+a4*pow( (v3-yNm1)/(2*delta), 2.0) ); path[CINDEX(1)] = g*(y1-v1) - (1.0/(2*delta))*(a3+a4*y1)*(y2-v2); 196 path[CINDEX(2)] = (1.0/(2*delta))*(a3+a4*yN)*(v3-yNm1); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { int i; int N = N_DISCRETIZATION; for(i=1;i<= N; i++) { e[CINDEX(i)] = initial_states[CINDEX(i)]; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { int N = N_DISCRETIZATION; //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Heat diffusion process"; = "heat.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = N; 3; N; 2; = "[20 50 60]"; 197 psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// int i, j; problem.phases(1).bounds.lower.states = zeros(N,1); problem.phases(1).bounds.upper.states = 3.0*ones(N,1); problem.phases(1).bounds.lower.controls(1) = 0.0; problem.phases(1).bounds.lower.controls(2) = 0.0; problem.phases(1).bounds.lower.controls(3) = 0.0; problem.phases(1).bounds.upper.controls(1) = 0.1; problem.phases(1).bounds.upper.controls(2) = 3.0; problem.phases(1).bounds.upper.controls(3) = 3.0; for(i = 1; i<= N; i++ ) { double xi = ( (double) (i-1) ) /( (double) (N-1) ); double yI = 2.0+cos(pi*xi); problem.phases(1).bounds.lower.events(i) = yI; problem.phases(1).bounds.upper.events(i) = yI; } problem.phases(1).bounds.upper.path(1) problem.phases(1).bounds.upper.path(2) problem.phases(1).bounds.lower.path(1) problem.phases(1).bounds.lower.path(2) = = = = 0.0; 0.0; 0.0; 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.5; = 0.5; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates DMatrix DMatrix DMatrix DMatrix = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; state_guess control_guess param_guess time_guess = = = = zeros(nstates,nnodes); zeros(ncontrols,nnodes); zeros(0,0); linspace(0,0.5,nnodes); for(i = 1; i<= N; i++ ) { double xi = ( (double) (i-1) ) /( (double) (N-1) ); double yI = 2.0+cos(pi*xi); state_guess(i,colon()) = yI*ones(1,nnodes); } control_guess(1,colon()) = linspace(0.1,0.1, nnodes); control_guess(2,colon()) = state_guess(1,colon()); control_guess(3,colon()) = state_guess(N,colon()); 198 auto_phase_guess(problem, control_guess, state_guess, param_guess, time_guess); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.defect_scaling = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; "jacobian-based"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix y, u, t; y u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// y.Save("y.dat"); u.Save("u.dat"); t.Save("t.dat"); DMatrix x(N); for(i = 1; i<= N; i++ ) { x(i) = ( (double) (i-1) ) /( (double) (N-1) ); } //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,u(1,colon()),problem.name+": control","time (s)", "control", "u"); plot(t,u(1,colon()),problem.name+": control","time (s)", "control", "v1", "pdf", "heat_control.pdf"); plot(t,y(1,colon()),problem.name+": state","time (s)", "state", "x1"); plot(t,y(1,colon()),problem.name+": state","time (s)", "state", "x1", "pdf", "heat_state1.pdf"); plot(t,y(N,colon()),problem.name+": state","time (s)", "state", "xN"); plot(t,y(N,colon()),problem.name+": state","time (s)", "state", "xN", "pdf", "heat_stateN.pdf"); surf(x, t, y, problem.name, "x", "t", "h"); surf(x, t, y, problem.name, "z", "t", "x", "pdf", "heat_surf.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized the box below. Figure 3.39 shows the control variable v1 as a function of time. Figure 3.40 shows the 199 resulting temperature distribution. PSOPT results summary ===================== Problem: Heat diffusion process CPU time (seconds): 3.369917e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:52:38 2019 Optimal (unscaled) cost function value: 4.417826e-05 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 4.417826e-05 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 5.000000e-01 Phase 1 maximum relative local error: 1.629944e-05 NLP solver reports: The problem has been solved! Heat diffusion process: control v1 0.08 0.07 control 0.06 0.05 0.04 0.03 0 0.1 0.2 0.3 0.4 0.5 time (s) Figure 3.39: Optimal control distribution for the heat diffusion process 200 Heat diffusion process 3 2.5 x 2 1.5 1 0.5 0.4 0 0.3 0.2 0.4 0.2 t 0.1 0.6 0.8 z 1 0 Figure 3.40: Optimal temperature distribution for the heat diffusion process 3.17 Hypersensitive problem Consider the following optimal control problem, which is known in the literature as the hypesensitive optimal control problem [34]. Minimize the cost functional Z 1 tf 2 J= [x + u2 ]dt (3.72) 2 0 subject to the dynamic constraint ẋ = −x3 + u (3.73) x(0) = 1.5 x(tf ) = 1 (3.74) and the boundary conditions where tf = 50. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// hypersensitive.cxx ///////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Hypersensitive problem //////////////// 201 //////// Last modified: 05 January 2009 //////////////// //////// Reference: Rao and Mease (2000) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[0]; adouble u = controls[0]; adouble L = 0.5*( x*x + u*u ); return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[0]; adouble u = controls[0]; adouble xdot = -pow(x,3) + u; derivatives[0] = xdot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble xi = initial_states[0]; adouble xf = final_states[0]; e[0] = xi; e[1] = xf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } 202 //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Hypersensitive problem"; = "hyper.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 1; 1; 2; 0; = "[25, 50]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double xi = 1.5; double xf = 1.0; problem.phases(1).bounds.lower.states(1) = -50.0; problem.phases(1).bounds.upper.states(1) = 50.0; problem.phases(1).bounds.lower.controls(1) = -50.0; problem.phases(1).bounds.upper.controls(1) = 50.0; problem.phases(1).bounds.lower.events(1) = xi; problem.phases(1).bounds.upper.events(1) = xi; problem.phases(1).bounds.lower.events(2) = xf; problem.phases(1).bounds.upper.events(2) = xf; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 50.0; = 50.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; 203 problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,60); = linspace(1,1,60); = linspace(0.0,50.0,60); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance // // // // = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; algorithm.ps_method = "none"; algorithm.refinement_strategy = "TH"; algorithm.mr_tolerance = 1.e-8; algorithm.mr_max_iterations = 8; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x DMatrix u DMatrix t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": state", "time (s)", "state", "x"); plot(t,u,problem.name + ": control", "time (s)", "control", "u"); plot(t,x,problem.name + ": state", "time (s)", "state", "x", "pdf", "hyper_state.pdf"); plot(t,u,problem.name + ": control", "time (s)", "control", "u", "pdf", "hyper_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// 204 Hypersensitive problem: state x 1.4 1.2 state 1 0.8 0.6 0.4 0.2 0 0 10 20 30 40 50 time (s) Figure 3.41: State for hypersensitive problem The output from PSOPT is summarized the box below and shown in the following plots that contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Hypersensitive problem CPU time (seconds): 1.409098e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:00:11 2019 Optimal (unscaled) cost function value: 1.330826e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 1.330826e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 5.000000e+01 Phase 1 maximum relative local error: 5.728533e-04 NLP solver reports: The problem has been solved! 205 Hypersensitive problem: control u 2 control 1.5 1 0.5 0 0 10 20 30 40 50 time (s) Figure 3.42: Control for hypersensitive problem 3.18 Interior point constraint problem Consider the following optimal control problem, which involves a scalar system with an interior point constraint on the state [24]. Minimize the cost functional Z 1 [x2 + u2 ]dt J= (3.75) 0 subject to the dynamic constraint ẋ = u, (3.76) x(0) = 1, x(1) = 0.75, (3.77) the boundary conditions and the interior point constraint: x(0.75) = 0.9. (3.78) The problem is divided into two phases and the interior point constraint is accommodated as an event constraint at the end of the first phase. The PSOPT code that solves this problem is shown below. 206 ////////////////////////////////////////////////////////////////////////// ////////////////// interior_point.cxx ///////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Problem with interior point constraint //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: Miser Manual //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser /////////////// //////// General Public License (LGPL) /////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function //////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[0]; adouble u = controls[0]; adouble L = x*x + u*u; return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[0]; adouble u = controls[0]; adouble xdot = u; derivatives[0] = xdot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble xi = initial_states[0]; adouble xf = final_states[0]; adouble x075; if (iphase == 1) { xi = initial_states[0]; x075 = final_states[0]; e[0] = xi; e[1] = x075; } 207 if (iphase == 2) { xf = final_states[0]; e[0] = xf; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { int index = 0; auto_link(linkages, &index, xad, 1, 2, workspace ); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Problem with interior point constraint"; = "ipc.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 2; = 2; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 1; 1; 2; 0; = 20; problem.phases(2).nstates problem.phases(2).ncontrols problem.phases(2).nevents problem.phases(2).npath problem.phases(2).nodes = = = = 1; 1; 1; 0; = 20; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double xi = 1; double x075 = 0.9; double xf = 0.75; ///////////// Phase 1 bounds /////////////////// problem.phases(1).bounds.lower.states(1) = -1.0; 208 problem.phases(1).bounds.upper.states(1) = 1.0; problem.phases(1).bounds.lower.controls(1) = -1.0; problem.phases(1).bounds.upper.controls(1) = 1.0; problem.phases(1).bounds.lower.events(1) = xi; problem.phases(1).bounds.upper.events(1) = xi; problem.phases(1).bounds.lower.events(2) = x075; problem.phases(1).bounds.upper.events(2) = x075; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.75; = 0.75; /////////////// Phase 2 bounds ///////////////// problem.phases(2).bounds.lower.states(1) = -1.0; problem.phases(2).bounds.upper.states(1) = 1.0; problem.phases(2).bounds.lower.controls(1) = -1.0; problem.phases(2).bounds.upper.controls(1) = 1.0; problem.phases(2).bounds.lower.events(1) = xf; problem.phases(2).bounds.upper.events(1) = xf; problem.phases(2).bounds.lower.StartTime problem.phases(2).bounds.upper.StartTime = 0.75; = 0.75; problem.phases(2).bounds.lower.EndTime problem.phases(2).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// /////////////// Phase 1 initial guess ///////////////////////// problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = = = zeros(1,20); linspace(xi,x075, 20); linspace(0.0, 0.75 , 20); /////////////// Phase 2 initial guess ////////////////////////// problem.phases(2).guess.controls problem.phases(2).guess.states problem.phases(2).guess.time = = = zeros(1,20); linspace(x075,xf, 20); linspace(0.75, 1.0 , 20); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// 209 psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x=solution.get_states_in_phase(1) || solution.get_states_in_phase(2); u=solution.get_controls_in_phase(1)|| solution.get_controls_in_phase(2); t=solution.get_time_in_phase(1) || solution.get_time_in_phase(2); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": state", "time (s)", "state", "x"); plot(t,u,problem.name + ": control", "time (s)", "control", "u"); plot(t,x,problem.name + ": state", "time (s)", "state", "x", "pdf", "ipc_state.pdf"); plot(t,u,problem.name + ": control", "time (s)", "control", "u", "pdf", "ipc_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized the box below and shown in the following plots that contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Problem with interior point constraint CPU time (seconds): 8.136300e-01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:08:24 2019 Optimal Phase 1 Phase 1 Phase 1 Phase 1 (unscaled) cost function value: 9.205314e-01 endpoint cost function value: 0.000000e+00 integrated part of the cost: 6.607877e-01 initial time: 0.000000e+00 final time: 7.500000e-01 210 Problem with interior point constraint: state x 1 0.95 state 0.9 0.85 0.8 0.75 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.43: State for interior point constraint problem Phase 1 maximum relative local error: 2.352899e-08 Phase 2 endpoint cost function value: 0.000000e+00 Phase 2 integrated part of the cost: 2.597438e-01 Phase 2 initial time: 7.500000e-01 Phase 2 final time: 1.000000e+00 Phase 2 maximum relative local error: 6.986779e-09 NLP solver reports: The problem has been solved! 3.19 Isoperimetric constraint problem Consider the following optimal control problem, which includes an integral constraint. Minimize the cost functional Z tf J= x2 (t)dt (3.79) 0 subject to the dynamic constraint ẋ = − sin(x) + u (3.80) the integral constraint: Z tf u2 (t)dt = 10 0 211 (3.81) Problem with interior point constraint: control u 0.2 0.1 0 control -0.1 -0.2 -0.3 -0.4 -0.5 -0.6 -0.7 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.44: Control for interior point constraint problem the boundary conditions x(0) = 1 x(tf ) = 0 (3.82) and the bounds: −4 ≤ u(t) ≤ 4 −10 ≤ x(t) ≤ 10 (3.83) where tf = 1. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// isoperimetric.cxx /////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Isoperimetric problem //////////////// //////// Last modified: 29 January 2009 //////////////// //////// Reference: //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { 212 return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[0]; adouble L = x; return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; adouble x = states[ CINDEX(1) ]; adouble u = controls[ CINDEX(1) ]; derivatives[0] = -sin(x) + u; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand of the integral constraint /////// //////////////////////////////////////////////////////////////////////////// adouble integrand( adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble g; adouble u = controls[ CINDEX(1) ]; g = u*u ; return g; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x0 = initial_states[ CINDEX(1) ]; adouble xf = final_states[ CINDEX(1) ]; adouble Q; // Compute the integral to be constrained Q = integrate( integrand, xad, iphase, workspace ); e[ CINDEX(1) ] = x0; e[ CINDEX(2) ] = xf; e[ CINDEX(3) ] = Q; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { 213 // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Isoperimetric constraint problem"; problem.outfilename = "isoperimetric.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = problem.phases(1).ncontrols = problem.phases(1).nevents = problem.phases(1).npath = problem.phases(1).nodes = 50; 1; 1; 3; 0; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = -10; problem.phases(1).bounds.upper.states(1) = 10; problem.phases(1).bounds.lower.controls(1) = -4.0; problem.phases(1).bounds.upper.controls(1) = 4.0; problem.phases(1).bounds.lower.events(1) = problem.phases(1).bounds.lower.events(2) = problem.phases(1).bounds.lower.events(3) = 1.0; 0.0; 10.0; problem.phases(1).bounds.upper.events(1) = 1.0; problem.phases(1).bounds.upper.events(2) = 0.0; problem.phases(1).bounds.upper.events(3) = 10.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// 214 problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,30); = zeros(1,30); = linspace(0.0,1.0,30); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); if (solution.error_flag) exit(0); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": state", "time (s)", "x", "x"); plot(t,u,problem.name + ": control", "time (s)", "u", "u"); plot(t,x,problem.name + ": state", "time (s)", "x", "x", "pdf", "isop_state.pdf"); plot(t,u,problem.name + ": control", "time (s)", "u", "u", "pdf", "isop_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the text box below and in Figures 3.45 and 3.46, which show the optimal state and control, respec215 Isoperimetric constraint problem: state x 1 x 0.5 0 -0.5 -1 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.45: State for isoperimetric constraint problem tively. PSOPT results summary ===================== Problem: Isoperimetric constraint problem CPU time (seconds): 1.388097e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:16:14 2019 Optimal (unscaled) cost function value: -3.755058e-01 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: -3.755058e-01 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 3.106352e-05 NLP solver reports: The problem has been solved! 3.20 Lambert’s problem This example demonstrates the use of the PSOPT for a classical orbit determination problem, namely the determination of an orbit from two position 216 Isoperimetric constraint problem: control u 4 3 2 u 1 0 -1 -2 -3 -4 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.46: Control for isoperimetric constraint problem vectors and time (Lambert’s problem) [42]. The problem is formulated as follows. Find r(t) ∈ [0, tf ] and v(t) ∈ [0, tf ] to minimise: J =0 (3.84) subject to ṙ = v v̇ = −µ r ||r||3 (3.85) with the boundary conditions: r(0) = [15945.34E3, 0.0, 0.0]T r(tf ) = [12214.83899E3, 10249.46731E3, 0.0]T (3.86) where r = [x, y, z]T (m) is a cartesian position vector, and v = [vx , vz , vz ]T is the corresponding velocity vector, µ = GMe , G (m3 /(kg s2 )) is the universal gravitational constant and Me (kg) is the mass of Earth. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// lambert.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////// Title: Lambert problem //////////////// //////// Last modified: 27 December 2009 //////////////// //////// Reference: Vallado (2001), page 470 //////////////// 217 //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which //////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { // Define constants: double G = double Me = double mu 6.6720e-11; // Universal gravity constant [m^3/(kg s^2)] 5.9742e24; // Mass of earth;[kg] = G*Me; // [m^3/sec^2] ADMatrix r(3), v(3); // Extract individual variables r(1) = states[ CINDEX(1) ]; r(2) = states[ CINDEX(2) ]; r(3) = states[ CINDEX(3) ]; v(1) = states[ CINDEX(4) ]; v(2) = states[ CINDEX(5) ]; v(3) = states[ CINDEX(6) ]; ADMatrix rdd(3); adouble rr = sqrt( r(1)*r(1)+r(2)*r(2)+r(3)*r(3) ); adouble r3 = pow(rr,3.0); rdd(1) = -mu*r(1)/r3; rdd(2) = -mu*r(2)/r3; rdd(3) = -mu*r(3)/r3; derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ] ] ] ] ] ] = = = = = = v(1); v(2); v(3); rdd(1); rdd(2); rdd(3); } //////////////////////////////////////////////////////////////////////////// 218 /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble ri1 = initial_states[ CINDEX(1) ]; adouble ri2 = initial_states[ CINDEX(2) ]; adouble ri3 = initial_states[ CINDEX(3) ]; adouble rf1 = final_states[ CINDEX(1) ]; adouble rf2 = final_states[ CINDEX(2) ]; adouble rf3 = final_states[ CINDEX(3) ]; e[ CINDEX(1) ] = ri1; e[ CINDEX(2) ] = ri2; e[ CINDEX(3) ] = ri3; e[ CINDEX(4) ] = rf1; e[ CINDEX(5) ] = rf2; e[ CINDEX(6) ] = rf3; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // auto_link_multiple(linkages, xad, N_PHASES); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; MSdata msdata; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Lambert problem"; = "lambert.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols = 6; = 0; 219 problem.phases(1).nevents = 6; problem.phases(1).nparameters problem.phases(1).npath = 0; problem.phases(1).nodes = 6; = "[100]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double r1i = 15945.34e3; // m double r2i = 0.0; double r3i = 0.0; double r1f = 12214.83899e3; //m double r2f = 10249.46731e3; //m double r3f = 0.0; double TF = 76.0*60.0; // seconds problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) = = = = = = -10*max(r1i,r1f); -10*max(r2i,r2f); -10*max(r3i,r3f); 10*max(r1i,r1f); 10*max(r2i,r2f); 10*max(r3i,r3f); problem.phases(1).bounds.lower.states(4) problem.phases(1).bounds.lower.states(5) problem.phases(1).bounds.lower.states(6) problem.phases(1).bounds.upper.states(4) problem.phases(1).bounds.upper.states(5) problem.phases(1).bounds.upper.states(6) = = = = = = -10*max(r1i,r1f)/TF; -10*max(r1i,r1f)/TF;; -10*max(r1i,r1f)/TF;; 10*max(r1i,r1f)/TF; 10*max(r2i,r2f)/TF; 10*max(r3i,r3f)/TF; problem.phases(1).bounds.lower.events(1) = r1i; problem.phases(1).bounds.upper.events(1) = r1i; problem.phases(1).bounds.lower.events(2) = r2i; problem.phases(1).bounds.upper.events(2) = r2i; problem.phases(1).bounds.lower.events(3) = r3i; problem.phases(1).bounds.upper.events(3) = r3i; problem.phases(1).bounds.lower.events(4) = r1f; problem.phases(1).bounds.upper.events(4) = r1f; problem.phases(1).bounds.lower.events(5) = r2f; problem.phases(1).bounds.upper.events(5) = r2f; problem.phases(1).bounds.lower.events(6) = r3f; problem.phases(1).bounds.upper.events(6) = r3f; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = TF; = TF; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem names and units ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).name.states(1) problem.phases(1).name.states(2) problem.phases(1).name.states(3) problem.phases(1).name.states(4) problem.phases(1).name.states(5) problem.phases(1).name.states(6) = = = = = = problem.phases(1).units.states(1) problem.phases(1).units.states(2) problem.phases(1).units.states(3) problem.phases(1).units.states(4) problem.phases(1).units.states(5) problem.phases(1).units.states(6) "x "y "z "x "y "z = = = = = = position"; position"; position"; velocity"; velocity"; velocity"; "m"; "m"; "m"; "m"; "m/s"; "m/s"; 220 //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = 20; = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix x_guess = DMatrix time_guess = x_guess(1,colon()) x_guess(2,colon()) x_guess(3,colon()) x_guess(4,colon()) x_guess(5,colon()) x_guess(6,colon()) = = = = = = zeros(nstates,nnodes); linspace(0.0,TF,nnodes); linspace(r1i,r1f, nnodes); linspace(r2i,r2f,nnodes); linspace(r3i,r3f,nnodes); linspace(r1i,r1f,nnodes)/TF; linspace(r2i,r2f,nnodes)/TF; linspace(r3i,r3f, nnodes)/TF; problem.phases(1).guess.states problem.phases(1).guess.time = x_guess; = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.defect_scaling algorithm.collocation_method = = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; "jacobian-based"; "Hermite-Simpson"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t, xi, ui, ti; x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// 221 /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix r1 = x(1,colon()); DMatrix r2 = x(2,colon()); DMatrix r3 = x(3,colon()); DMatrix v1 = x(4,colon()); DMatrix v2 = x(5,colon()); DMatrix v3 = x(6,colon()); DMatrix vi(3), vf(3); vi(1) = v1(1); vi(2) = v2(1); vi(3) = v3(1); vf(1) = v1("end"); vf(2) = v2("end"); vf(3) = v3("end"); vi.Print("Initial velocity vector [m/s]"); vf.Print("Final velocity vector [m/s]"); plot(t,r1,problem.name+": x", "time (s)", "x","x"); plot(t,r2,problem.name+": y", "time (s)", "x","y"); plot(t,r3,problem.name+": z", "time (s)", "z","z"); plot(r1,r2,problem.name+": x-y", "x (m)", "y (m)","y"); plot(r1,r2,problem.name+": x-y trajectory", "x (m)", "y (m)","y", "pdf", "lambert_xy.pdf"); plot3(r1,r2,r3,problem.name+": trajectory","x (m)", "y (m)", "z (m)"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the text box below and in Figure 3.47, which show the trajectory from r(0) to r(tf ), respectively. PSOPT results summary ===================== Problem: Lambert problem CPU time (seconds): 2.985497e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:53:56 2019 Optimal (unscaled) cost function value: 0.000000e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 4.560000e+03 Phase 1 maximum relative local error: 3.440783e-05 NLP solver reports: The problem has been solved! 222 Lambert problem: x-y trajectory y 1e+07 8e+06 y (m) 6e+06 4e+06 2e+06 0 1.2e+07 1.3e+07 1.4e+07 1.5e+07 1.6e+07 1.7e+07 1.8e+07 x (m) Figure 3.47: Trajectory between the initial and final positions for Lambert’s problem The resulting initial and final velocity vectors are: v(0) = [2058.902605, 2915.961924, −6.878790137E − 13]T v(tf ) = [−3451.55505, 910.3192974, −6.878787164E − 13]T 3.21 (3.87) Lee-Ramirez bioreactor Consider the following optimal control problem, which is known in the literature as the Lee-Ramirez bioreactor [29, 36]. Find tf and u(t) ∈ [0, tf ] to minimize the cost functional Z tf J = −x1 (tf )x4 (tf ) + ρ[u̇1 (t)2 + u̇2 (t)2 ]dt (3.88) 0 223 subject to the dynamic constraints ẋ1 = u1 + u2 ; u1 + u2 x2 ; ẋ2 = g1 x2 − x1 u1 u1 + u2 ẋ3 = 100 − x3 − (g1 /0.51)x2 ; x1 x1 u1 + u2 x4 ; ẋ4 = Rf p x2 − x1 u2 u1 + u2 ẋ5 = 4 − x5 ; x1 x1 ẋ6 = −k1 x6 ; (3.89) ẋ7 = k2 (1 − x7 ). where tf = 10, ρ = 1/N , and N is the number of discretization nodes, k1 = 0.09x5 /(0.034 + x5 ); k2 = k1 ; g1 = (x3 /(14.35 + x3 (1.0 + x3 /111.5)))(x6 + 0.22x7 /(0.22 + x5 )); Rf p = (0.233x3 /(14.35 + x3 (1.0 + x3 /111.5)))((0.0005 + x5 )/(0.022 + x5 )); (3.90) the initial conditions: x1 (0) = 1 x2 (0) = 0.1 x3 (0) = 40 x4 (0) = 0 (3.91) x5 (0) = 0 x6 (0) = 1.0 x7 (0) = 0 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// bioreactor.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Template ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Lee-Ramirez bioreactor //////////////// //////// Last modified: 09 July 2009 //////////////// //////// Reference: PROPT User Manual //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" 224 ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x1tf = final_states[CINDEX(1)]; adouble x4tf = final_states[CINDEX(4)]; return -(x1tf*x4tf); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble u2 = controls[ CINDEX(2) ]; adouble dot_u1, dot_u2; double Q = 0; get_control_derivative( &dot_u1, 1, iphase, time, xad, workspace); get_control_derivative( &dot_u2, 2, iphase, time, xad, workspace); double rho = 0.1/get_number_of_nodes(*workspace->problem, iphase); return (Q*u2 + rho*(dot_u1*dot_u1 + dot_u2*dot_u2)); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble u1 = controls[ CINDEX(1) ]; adouble u2 = controls[ CINDEX(2) ]; adouble adouble adouble adouble adouble adouble adouble x1 x2 x3 x4 x5 x6 x7 = = = = = = = states[ states[ states[ states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ]; ]; ]; ]; ]; ]; ]; adouble k1 = 0.09*x5/(0.034 + x5); adouble k2 = k1; adouble g1 = (x3/(14.35 + x3*(1.0+x3/111.5)))*(x6 + 0.22*x7/(0.22+x5)); adouble Rfp = (0.233*x3/(14.35 + x3*(1.0+x3/111.5)))*((0.0005+x5)/(0.022+x5)); derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ] ] ] ] ] ] ] = = = = = = = u1 + u2; g1*x2 - (u1+u2)/x1*x2; 100*u1/x1 - (u1+u2)/x1*x3 - g1/0.51*x2; Rfp*x2 - (u1+u2)/x1*x4; 4*u2/x1 - (u1+u2)/x1*x5; -k1*x6; k2*(1-x7); 225 } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { int i; for(i=1;i<= 7; i++) { e[CINDEX(i)] = initial_states[CINDEX(i)]; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Lee-Ramirez bioreactor"; = "bioreactor.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 7; 2; 7; 0; = "[20 35 50]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// 226 /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// int i; problem.phases(1).bounds.lower.states = -10.0*ones(7,1); problem.phases(1).bounds.upper.states = "[10.0 10.0 50.0 10.0 10.0 10.0 10.0]"; problem.phases(1).bounds.lower.controls(1) = 0.0; problem.phases(1).bounds.lower.controls(2) = 0.0; problem.phases(1).bounds.upper.controls(1) = 1.0; problem.phases(1).bounds.upper.controls(2) = 1.0; problem.phases(1).bounds.lower.events = "[1.0 0.1 40.0 0.0 0.0 1.0 0.0]"; problem.phases(1).bounds.upper.events = "[1.0 0.1 40.0 0.0 0.0 1.0 0.0]"; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 10.0; = 10.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates DMatrix DMatrix DMatrix DMatrix = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; state_guess control_guess param_guess time_guess state_guess(1,colon()) state_guess(2,colon()) state_guess(3,colon()) state_guess(4,colon()) state_guess(5,colon()) state_guess(6,colon()) state_guess(7,colon()) = = = = = = = = = = = zeros(nstates,nnodes); zeros(ncontrols,nnodes); zeros(0,0); linspace(0,10.0,nnodes); 1.0*ones(1,nnodes); 0.1*ones(1,nnodes); 40.0*ones(1,nnodes); 0.0*ones(1,nnodes); 0.0*ones(1,nnodes); 1.0*ones(1,nnodes); 0.0*ones(1,nnodes); control_guess(1,colon()) = time_guess/30.0; control_guess(2,colon()) = zeros(1,nnodes); auto_phase_guess(problem, control_guess, state_guess, param_guess, time_guess); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling = = = = 1000; 1.e-5; "IPOPT"; "automatic"; 227 algorithm.derivatives algorithm.defect_scaling algorithm.diff_matrix = "automatic"; = "jacobian-based"; = "central-differences"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,u,problem.name+": control","time (s)", "control 1", "u1 u2"); x(3,colon()) = x(3,colon())/10.0; plot(t,x,problem.name+": state","time (s)", "state", "x1 x2 x3/10 x4 x5 x6 x7"); plot(t,u,problem.name+": control","time (s)", "control 1", "u1 u2", "pdf", "bioreactor_controls.pdf"); x(3,colon()) = x(3,colon())/10.0; plot(t,x,problem.name+": states","time (s)", "states", "x1 x2 x3/10 x4 x5 x6 x7", "pdf", "bioreactor_states.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.48 and 3.49, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Lee-Ramirez bioreactor CPU time (seconds): 7.344649e+01 228 Lee-Ramirez bioreactor: states x1 x2 x3/10 x4 x5 x6 x7 7 6 states 5 4 3 2 1 0 0 2 4 6 8 10 time (s) Figure 3.48: States for the Lee-Ramirez bioreactor problem NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:57:58 2019 Returned (unscaled) cost function value: -6.048409e+00 Phase 1 endpoint cost function value: -6.049249e+00 Phase 1 integrated part of the cost: 8.402028e-04 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+01 Phase 1 maximum relative local error 2.082149e-03 NLP solver reports: *** The problem FAILED! - see screen output 3.22 Li’s parameter estimation problem This is a parameter estimation problem with two parameters and three observed variables, which is presented b Li et. al [28]. The dynamic equations are given by: dx = M (t, p)x + f (t), t ∈ [0, π] dt with boundary condition: x(0) + x(π) = (1 + eπ ) [1, 1, 1]T 229 (3.92) Lee-Ramirez bioreactor: control u1 u2 0.6 0.5 control 1 0.4 0.3 0.2 0.1 0 0 2 4 6 8 10 time (s) Figure 3.49: Control for the Lee-Ramirez bioreactor problem where and p2 − p1 cos(p2 t) 0 p2 + p1 sin(p2 t) 0 p1 0 M (t, p) = −p2 + p1 sin(p2 t) 0 p2 + p1 cos(p2 t) (3.93) −1 + 19(cos(t) − sin(t)) −18 f (t) = 1 − 19(cos(t) + sin(t)) (3.94) and the observation functions are: g1 = x1 g2 = x2 (3.95) g3 = x3 The trajectories of the dynamic system is characterised by rapidly varying fast and slow components if the difference between the two parameters p1 and p2 is large, which may cause numerical problems to some ODE solvers. The estimation data set is generated by adding Gaussian noise with standard deviation 1 around the solution [x1 (t), x2 (t), x3 (t)]T = [et , et , et ]T , with N = 33 equidistant samples within the interval t = [0, π]. The true values of the parameters are p1 = 19 and p2 = 1. The weights of the three observations are the same and equal to one. The solution is found using Legendre discretisation with 40 grid points. The code that solves the problem is shown below. The estimated parameter values and their 95% confidence limits for ns = 129 samples are shown in 230 Table 3.22. Figure 3.50 shows the observations as well as the estimated values of variable x1 . ////////////////////////////////////////////////////////////////////////// ////////////////// param2.cxx //////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Parameter estimation for ODE with 2 parameters ////////// //////// Last modified: 31 Jan 2014 //////////////// //////// Reference: Li et al (2005) //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2014 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the observation function ////////// ////////////////////////////////////////////////////////////////////////// void observation_function( adouble* adouble* adouble* adouble* observations, states, adouble* controls, parameters, adouble& time, int k, xad, int iphase, Workspace* workspace) { int i; for (i=0; i<3; i++) { observations[ i ] = states[ i ]; } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { // Variables adouble x1, x2, x3, p1, p2, t; // Differential states x1 = states[CINDEX(1)]; x2 = states[CINDEX(2)]; x3 = states[CINDEX(3)]; // Parameters p1 = parameters[CINDEX(1)]; p2 = parameters[CINDEX(2)]; t= time; ADMatrix M(3,3), f(3,1), dxdt(3,1), x(3,1), y(3,1); x(1,1) = x1; x(2,1) = x2; x(3,1) = x3; M(1,1) = p2-p1*cos(p2*t); M(1,2) = 0.0; M(1,3) = p2 + p1*sin(p2*t); M(2,1) = 0.0; M(2,2) = p1; M(2,3) = 0.0; M(3,1) = -p2+p1*sin(p2*t); M(3,2) = 0.0; M(3,3) = p2 + p1*cos(p2*t); f(1,1) = exp(t)*(-1.0 + 19.0*( cos(t) - sin(t) ) ); f(2,1) = exp(t)*(-18.0); f(3,1) = exp(t)*(1.0 - 19.0*( cos(t) + sin(t) ) ); // Differential equations 231 product_ad(M, x, &y); sum_ad(y, f, &dxdt ); derivatives[CINDEX(1)] = dxdt(1,1); derivatives[CINDEX(2)] = dxdt(2,1); derivatives[CINDEX(3)] = dxdt(3,1); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { int i; double rhs = 1.0 + exp(pi); for (i=0; i<3; i++) { e[i] = initial_states[i] + final_states[i] - rhs; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = = "Parameter estimation for ODE with two parameters"; "param2.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = problem.phases(1).ncontrols = problem.phases(1).nevents = problem.phases(1).npath = problem.phases(1).nparameters 3; 0; 3; 0; = 2; 232 problem.phases(1).nodes problem.phases(1).nobserved problem.phases(1).nsamples = 40; = 3; = 129; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// //////////// Load data for parameter estimation //////////// //////////////////////////////////////////////////////////////////////////// int iphase = 1; load_parameter_estimation_data(problem, iphase, "param2.dat"); problem.phases(1).observation_nodes.Print("observation nodes"); problem.phases(1).observations.Print("observations"); problem.phases(1).residual_weights.Print("weights"); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, p, t; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = 0.0; problem.phases(1).bounds.lower.states(2) = 0.0; problem.phases(1).bounds.lower.states(3) = 0.0; problem.phases(1).bounds.upper.states(1) = 30.0; problem.phases(1).bounds.upper.states(2) = 30.0; problem.phases(1).bounds.upper.states(3) = 30.0; problem.phases(1).bounds.lower.events(1) = 0.0; problem.phases(1).bounds.lower.events(2) = 0.0; problem.phases(1).bounds.lower.events(3) = 0.0; problem.phases(1).bounds.upper.events(1) = 0.0; problem.phases(1).bounds.upper.events(2) = 0.0; problem.phases(1).bounds.upper.events(3) = 0.0; problem.phases(1).bounds.lower.parameters(1) problem.phases(1).bounds.lower.parameters(2) = 0.0; = 0.0; problem.phases(1).bounds.upper.parameters(1) problem.phases(1).bounds.upper.parameters(2) = 30.0; = 30.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = pi; = pi; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; problem.observation_function = & observation_function; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes = (int) problem.phases(1).nsamples; DMatrix state_guess(3, nnodes); DMatrix param_guess(2,1); state_guess(1,colon()) = linspace(1.0, exp(pi), nnodes ); state_guess(2,colon()) = linspace(1.0, exp(pi), nnodes ); state_guess(3,colon()) = linspace(1.0, exp(pi), nnodes ); 233 param_guess(1) = 19.0*1.5; param_guess(2) = 1.0*1.5; problem.phases(1).guess.states problem.phases(1).guess.time problem.phases(1).guess.parameters = state_guess; = linspace(0.0, pi, nnodes); = param_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // // algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.mesh_refinement algorithm.defect_scaling algorithm.nlp_iter_max algorithm.ode_tolerance = = = = "IPOPT"; "automatic"; "automatic"; "Legendre"; = "automatic"; = "jacobian-based"; = 1000; = 1.e-4; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); t = solution.get_time_in_phase(1); p = solution.get_parameters_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); t.Save("t.dat"); p.Print("Estimated parameters"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix tm; DMatrix ym; tm = problem.phases(1).observation_nodes; ym = problem.phases(1).observations; spplot(t,x(1,colon()),tm,ym(1,colon()),problem.name, "time (s)", "state x1", "x1 y1"); spplot(t,x(2,colon()),tm,ym(2,colon()),problem.name, "time (s)", "state x2", "x2 y2"); spplot(t,x(3,colon()),tm,ym(3,colon()),problem.name, "time (s)", "state x3", "x3 y3"); spplot(t,x(1,colon()),tm,ym(1,colon()),problem.name, "time (s)", "state x1", "x1 y1", "pdf", "x1.pdf"); spplot(t,x(2,colon()),tm,ym(2,colon()),problem.name, "time (s)", "state x2", "x2 y2", "pdf", "x2.pdf"); spplot(t,x(3,colon()),tm,ym(3,colon()),problem.name, "time (s)", "state x3", "x3 y3", "pdf", "x3.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// 234 Table 3.1: Estimated parameter values and 95 percent statistical confidence limits on estimated parameters Parameter Low Confidence Limit Value High Confidence Limit p1 1.907055e+01 1.907712e+01 1.908369e+01 p2 9.984900e-01 9.984990e-01 9.985080e-01 Parameter estimation for ODE with two parameters x1 y1 25 20 state x1 15 10 5 0 0 0.5 1 1.5 2 2.5 3 time (s) Figure 3.50: Observations and estimated state x1 (t) 235 3.5 3.23 Linear tangent steering problem Consider the following optimal control problem, which is known in the literature as the linear tangent steering problem [3]. Find tf and u(t) ∈ [0, tf ] to minimize the cost functional J = tf (3.96) subject to the dynamic constraints ẋ1 ẋ2 ẋ3 ẋ4 = = = = x2 a cos(u) x4 a sin(u) (3.97) = = = = = = = (3.98) the boundary conditions: x1 (0) x2 (0) x3 (0) x4 (0) x2 (tf ) x3 (tf ) x4 (tf ) 0 0 0 0 45.0 5.0 0.0 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// lts.cxx ///////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Linear tangent steering problem //////////////// //////// Last modified: 16 February 2009 //////////////// //////// Reference: Betts (2001) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return tf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// 236 adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble x1 x2 x3 x4 = = = = states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; adouble u = controls[ CINDEX(1) ]; double a = 100.0; derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = x2; a*cos(u); x4; a*sin(u); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1) ]; adouble x20 = initial_states[ CINDEX(2) ]; adouble x30 = initial_states[ CINDEX(3) ]; adouble x40 = initial_states[ CINDEX(4) ]; adouble x2f = final_states[ CINDEX(2) ]; adouble x3f = final_states[ CINDEX(3) ]; adouble x4f = final_states[ CINDEX(4) ]; e[ e[ e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ] ] ] ] ] ] ] = = = = = = = x10; x20; x30; x40; x2f; x3f; x4f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; 237 //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Linear Tangent Steering Problem"; problem.outfilename = "lts.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 4; 1; 7; 0; = "[10, 30]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) = = = = -100.0; -100.0; -100.0; -100.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) = = = = 100.0; 100.0; 100.0; 100.0; problem.phases(1).bounds.lower.controls(1) = -pi/2.0; problem.phases(1).bounds.upper.controls(1) = pi/2.0; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) problem.phases(1).bounds.lower.events(7) = = = = = = = 0.0; 0.0; 0.0; 0.0; 45.0; 5.0; 0.0; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) problem.phases(1).bounds.upper.events(5) problem.phases(1).bounds.upper.events(6) problem.phases(1).bounds.upper.events(7) = = = = = = = 0.0; 0.0; 0.0; 0.0; 45.0; 5.0; 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// 238 problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(3,10); x0(1,colon()) x0(2,colon()) x0(3,colon()) x0(3,colon()) = = = = linspace(0.0, 12.0, 10); linspace(0.0, 45.0, 10); linspace(0.0, 5.0, 10); linspace(0.0, 0.0, 10); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = ones(1,10); = x0; = linspace(0.0,1.0, 10); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); if (solution.error_flag) exit(0); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": states", "time (s)", "states", "x1 x2 x3 x4"); plot(t,u,problem.name + ": control", "time (s)", "control", "u"); plot(t,x,problem.name + ": states", "time (s)", "states", "x1 x2 x3 x4", "pdf", "lts_states.pdf"); plot(t,u,problem.name + ": control", "time (s)", "control", "u", "pdf", "lts_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// 239 The output from PSOPT is summarised in the box below and shown in Figures 3.51 and 3.52, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Linear Tangent Steering Problem CPU time (seconds): 1.188370e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:16:35 2019 Optimal (unscaled) cost function value: 5.545709e-01 Phase 1 endpoint cost function value: 5.545709e-01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 5.545709e-01 Phase 1 maximum relative local error: 1.603284e-07 NLP solver reports: The problem has been solved! 3.24 Low thrust orbit transfer The goal of this problem is to compute an optimal low thrust policy for an spacecraft to go from a standard space shuttle park orbit to a specified final orbit, while maximising the final weight of the spacecraft. The problem is described in detail by Betts [3]. The problem is formulated as follows. Find u(t) = [ur (t), uθ (t), uh (t)]T , t ∈ [0, tf ], the unknown throtle parameter τ , and the final time tf , such that the following objective function is minimised: J = −w(tf ) (3.99) subject to the dynamic constraints: ẏ = A(y)∆ + b ẇ = −T [1 + 0.01τ ]/Isp (3.100) the path constraint: ||u(t)||2 = 1 240 (3.101) Linear Tangent Steering Problem: states 45 x1 x2 x3 x4 40 35 states 30 25 20 15 10 5 0 0 0.1 0.2 0.3 0.4 0.5 0.6 time (s) Figure 3.51: States for the linear tangent steering problem Linear Tangent Steering Problem: control 1 u control 0.5 0 -0.5 -1 0 0.1 0.2 0.3 0.4 0.5 0.6 time (s) Figure 3.52: Control for the linear tangent steering problem 241 and the parameter bounds: τL ≤ τ ≤ 0 (3.102) where y = [p, f, g, h, k, L, w]T is the vector of modified equinoctial elements, w(t) is the weight of the spacecraft, Isp is the specific impulse of the engine, expressions for A(y) and b are given in [3], the disturbing acceleration ∆ is given by: ∆ = ∆g + ∆T (3.103) where ∆g is the gravitational disturbing acceleration due to the oblatness of Earth (given in [3]), and ∆T is the thurst acceleration, given by: g0 T [1 + 0.01τ ] u w where T is the maximum thrust, and g0 is the mass to weight conversion factor. The boundary conditions of the problem are given by: ∆T = p(tf ) = 40007346.015232 ft q f (tf )2 + g(tf )2 = 0.73550320568829 q h(tf )2 + k(tf )2 = 0.61761258786099 f (tf )h(tf ) + g(tf )k(tf ) = 0 g(tf )h(tf ) − k(tf )f (tf ) = 0 p(0) = 21837080.052835ft f (0) = 0 (3.104) g(0) = 0 h(0) = 0 h(0) = 0 k(0) = 0 L(0) = π (rad) w(0) = 1 (lb) and the values of the parameters are: g0 = 32.174 (ft/sec2 ), Isp = 450 (sec), T = 4.446618 × 10−3 (lb), µ = 1.407645794 × 1016 (ft3 /sec2 ), Re = 20925662.73 (ft), J2 = 1082.639 × 10−6 , J3 = −2.565 × 10−6 , J4 = −1.608 × 10−6 , τL = −50. An initial guess was computed by forward propagation from the initial conditions, assuming that the direction of the thrust vector is parallel to the cartesian velocity vector, such that the initial control input was computed as follows: u(t) = QTr 242 v ||v|| (3.105) where Qr is a matrix whose columns are the directions of the rotating radial frame: i h r (r×v)×r (r×v) Qr = ir iθ ih = ||r|| (3.106) ||r×v||||r|| ||r×v|| The problem was solved using local collocation (trapezoidal followed by Hermite-Simpson) with automatic mesh refinement. The PSOPT code that solves the problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// low_thrust.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Low thrust orbit transfer problem //////////////// //////// Last modified: 16 February 2009 //////////////// //////// Reference: Betts (2001) //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define auxiliary functions ////////// ////////////////////////////////////////////////////////////////////////// adouble legendre_polynomial( adouble x, int n) { // This function computes the value of the legendre polynomials // for a given value of the argument x and for n=0...5 only adouble retval=0.0; switch(n) { case 0: retval=1.0; break; case 1: retval= x; break; case 2: retval= 0.5*(3.0*pow(x,2)-1.0); break; case 3: retval= 0.5*(5.0*pow(x,3)- 3*x); break; case 4: retval= (1.0/8.0)*(35.0*pow(x,4) - 30.0*pow(x,2) + 3.0); break; case 5: retval= (1.0/8.0)*(63.0*pow(x,5) - 70.0*pow(x,3) + 15.0*x); break; default: error_message("legendre_polynomial(x,n) is limited to n=0...5"); } return retval; } adouble legendre_polynomial_derivative( adouble x, int n) { // This function computes the value of the legendre polynomial derivatives // for a given value of the argument x and for n=0...5 only. adouble retval=0.0; switch(n) { case 0: retval=0.0; break; case 1: retval= 1.0; break; case 2: retval= 0.5*(2.0*3.0*x); break; 243 case 3: retval= 0.5*(3.0*5.0*pow(x,2)-3.0); break; case 4: retval= (1.0/8.0)*(4.0*35.0*pow(x,3) - 2.0*30.0*x ); break; case 5: retval= (1.0/8.0)*(5.0*63.0*pow(x,4) - 3.0*70.0*pow(x,2) + 15.0); break; default: error_message("legendre_polynomial_derivative(x,n) is limited to n=0...5"); } return retval; } void compute_cartesian_trajectory(const DMatrix& x, DMatrix& xyz ) { int npoints = x.GetNoCols(); xyz.Resize(3,npoints); for(int i=1; i<=npoints;i++) { double double double double double double p f g h k L = = = = = = x(1,i); x(2,i); x(3,i); x(4,i); x(5,i); x(6,i); double double double double double q r alpha2 X s2 = 1.0 + f*cos(L) + g*sin(L); = p/q; = h*h - k*k; = sqrt( h*h + k*k ); = 1 + X*X; double r1 = r/s2*( cos(L) + alpha2*cos(L) + 2*h*k*sin(L)); double r2 = r/s2*( sin(L) - alpha2*sin(L) + 2*h*k*cos(L)); double r3 = 2*r/s2*( h*sin(L) - k*cos(L) ); xyz(1,i) = r1; xyz(2,i) = r2; xyz(3,i) = r3; } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { if (iphase == 1) { adouble w = final_states[CINDEX(7)]; return (-w); } else { return (0); } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, 244 adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { // Local integers int i, j; // Define constants: double Isp = 450.0; double mu = 1.407645794e16; double g0 = 32.174; double T = 4.446618e-3; double Re = 20925662.73; double J[5]; J[2] = 1082.639e-6; J[3] = -2.565e-6; J[4] = -1.608e-6; // // // // // [sec] [f2^2/sec^2] [ft/sec^2] [lb] [ft] // Extract individual variables adouble adouble adouble adouble adouble adouble adouble p f g h k L w = = = = = = = adouble* u adouble tau states[ states[ states[ states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ]; ]; ]; ]; ]; ]; ]; = controls; = parameters[ CINDEX(1) ]; // Define some dependent variables adouble adouble adouble adouble adouble q r alpha2 X s2 = 1.0 + f*cos(L) + g*sin(L); = p/q; = h*h - k*k; = sqrt( h*h + k*k ); = 1 + X*X; // r and v adouble r1 = r/s2*( cos(L) + alpha2*cos(L) + 2*h*k*sin(L)); adouble r2 = r/s2*( sin(L) - alpha2*sin(L) + 2*h*k*cos(L)); adouble r3 = 2*r/s2*( h*sin(L) - k*cos(L) ); adouble rvec[3]; rvec[ CINDEX(1) ] = r1; rvec[ CINDEX(2)] = r2; rvec[ CINDEX(3) ] = r3; adouble v1 = -(1.0/s2)*sqrt(mu/p)*( sin(L) + alpha2*sin(L) - 2*h*k*cos(L) + g - 2*f*h*k + alpha2*g); adouble v2 = -(1.0/s2)*sqrt(mu/p)*( -cos(L) + alpha2*cos(L) + 2*h*k*sin(L) - f + 2*g*h*k + alpha2*f); adouble v3 = (2.0/s2)*sqrt(mu/p)*(h*cos(L) + k*sin(L) + f*h + g*k); adouble vvec[3]; vvec[ CINDEX(1) ] = v1; vvec[ CINDEX(2)] = v2; vvec[ CINDEX(3) ] = v3; // compute Qr adouble ir[3], ith[3], ih[3]; adouble rv[3]; adouble rvr[3]; cross( rvec, vvec, rv ); cross( rv, rvec, rvr ); adouble norm_r = sqrt( dot(rvec, rvec, 3) ); adouble norm_rv = sqrt( dot(rv, rv, 3) ); for (i=0; i<3; i++) { ir[i] = rvec[i]/norm_r; ith[i] = rvr[i]/( norm_rv*norm_r ); ih[i] = rv[i]/norm_rv; } adouble Qr1[3], Qr2[3], Qr3[3]; for(i=0; i< 3; i++) 245 { // Columns of matrix Qr Qr1[i] = ir[i]; Qr2[i] = ith[i]; Qr3[i] = ih[i]; } // Compute in adouble en[3]; en[ CINDEX(1) ] = 0.0; en[ CINDEX(2) ]= 0.0; en[ CINDEX(3) ] = 1.0; adouble enir = dot(en,ir,3); adouble in[3]; for(i=0;i<3;i++) { in[i] = en[i] - enir*ir[i]; } adouble norm_in = sqrt( dot( in, in, 3 ) ); for(i=0;i<3;i++) { in[i] = in[i]/norm_in; } // Geocentric latitude angle: adouble sin_phi = adouble cos_phi = rvec[ CINDEX(3) ]/ sqrt( dot(rvec,rvec,3) ) ; sqrt(1.0- pow(sin_phi,2.0)); adouble deltagn = 0.0; adouble deltagr = 0.0; for (j=2; j<=4;j++) { adouble Pdash_j = legendre_polynomial_derivative( sin_phi, j ); adouble P_j = legendre_polynomial( sin_phi, j ); deltagn += -mu*cos_phi/(r*r)*pow(Re/r,j)*Pdash_j*J[j]; deltagr += -mu/(r*r)* (j+1)*pow( Re/r,j)*P_j*J[j]; } // Compute vector delta_g adouble delta_g[3]; for (i=0;i<3;i++) { delta_g[i] = deltagn*in[i] - deltagr*ir[i]; } // Compute vector DELTA_g adouble DELTA_g[3]; DELTA_g[ CINDEX(1) ] = dot(Qr1, delta_g,3); DELTA_g[ CINDEX(2) ] = dot(Qr2, delta_g,3); DELTA_g[ CINDEX(3) ] = dot(Qr3, delta_g,3); // Compute DELTA_T adouble DELTA_T[3]; for(i=0;i<3;i++) { DELTA_T[i] = g0*T*(1.0+0.01*tau)*u[i]/w; } // Compute DELTA adouble DELTA[3]; for(i=0;i<3;i++) { DELTA[i] = DELTA_g[i] + DELTA_T[i]; } adouble delta1= DELTA[ CINDEX(1) ]; adouble delta2= DELTA[ CINDEX(2) ]; adouble delta3= DELTA[ CINDEX(3) ]; // derivatives adouble pdot = adouble fdot = 2*p/q*sqrt(p/mu) * delta2; sqrt(p/mu)*sin(L) * delta1 + sqrt(p/mu)*(1.0/q)*((q+1.0)*cos(L)+f) * delta2 - sqrt(p/mu)*(g/q)*(h*sin(L)-k*cos(L)) * delta3; 246 adouble gdot = -sqrt(p/mu)*cos(L) * delta1 + sqrt(p/mu)*(1.0/q)*((q+1.0)*sin(L)+g) * delta2 + sqrt(p/mu)*(f/q)*(h*sin(L)-k*cos(L)) * delta3; adouble hdot = sqrt(p/mu)*s2*cos(L)/(2.0*q) * delta3; adouble kdot = sqrt(p/mu)*s2*sin(L)/(2.0*q) * delta3; adouble Ldot = sqrt(p/mu)*(1.0/q)*(h*sin(L)-k*cos(L))* delta3 + sqrt(mu*p)*pow( (q/p),2.); adouble wdot = -T*(1.0+0.01*tau)/Isp; derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ] ] ] ] ] ] ] = = = = = = = pdot; fdot; gdot; hdot; kdot; Ldot; wdot; path[ CINDEX(1) ] = pow( u[CINDEX(1)] , 2) + pow( u[CINDEX(2)], 2) + pow( u[CINDEX(3)], 2); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { int offset; adouble adouble adouble adouble adouble adouble adouble pti fti gti hti kti Lti wti = = = = = = = initial_states[ initial_states[ initial_states[ initial_states[ initial_states[ initial_states[ initial_states[ adouble adouble adouble adouble adouble adouble ptf ftf gtf htf ktf Ltf = = = = = = final_states[ final_states[ final_states[ final_states[ final_states[ final_states[ if (iphase==1) { e[ CINDEX(1) ] e[ CINDEX(2) ] e[ CINDEX(3) ] e[ CINDEX(4) ] e[ CINDEX(5) ] e[ CINDEX(6) ] e[ CINDEX(7) ] } = = = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; pti; fti; gti; hti; kti; Lti; wti; if (1 == 1) offset = 7; else offset = 0; if (iphase e[ offset e[ offset e[ offset e[ offset e[ offset } == 1 ) { + CINDEX(1) + CINDEX(2) + CINDEX(3) + CINDEX(4) + CINDEX(5) ] = ptf; ] = sqrt( ftf*ftf + gtf*gtf ); ] = sqrt( htf*htf + ktf*ktf ); ] = ftf*htf + gtf*ktf; ] = gtf*htf - ktf*ftf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // auto_link_multiple(linkages, xad, 1); } 247 //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; MSdata msdata; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Low thrust transfer problem"; = "lowthrust.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = 7; problem.phases(1).ncontrols = 3; problem.phases(1).nparameters problem.phases(1).nevents problem.phases(1).npath = 1; problem.phases(1).nodes = 1; = 12; = 80; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double tauL = -50.0; double tauU = 0.0; double double double double double double double pti fti gti hti kti Lti wti = = = = = = = 21837080.052835; 0.0; 0.0; -0.25396764647494; 0.0; pi; 1.0; double wtf_guess; double double double SISP = 450.0; DELTAV = 22741.1460; CM2W = 32.174; wtf_guess double double double double double double = wti*exp(-DELTAV/(CM2W*SISP)); ptf = 40007346.015232; event_final_9 = 0.73550320568829; event_final_10 = 0.61761258786099; event_final_11 = 0.0; event_final_12_upper = 0.0; event_final_12_lower = -10.0; 248 problem.phases(1).bounds.lower.parameters(1) = problem.phases(1).bounds.upper.parameters(1) = tauL; tauU; problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) problem.phases(1).bounds.lower.states(5) problem.phases(1).bounds.lower.states(6) problem.phases(1).bounds.lower.states(7) = = = = = = = 10.e6; -0.20; -0.10; -1.0; -0.20; pi; 0.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) problem.phases(1).bounds.upper.states(5) problem.phases(1).bounds.upper.states(6) problem.phases(1).bounds.upper.states(7) = = = = = = = 60.e6; 0.20; 1.0; 1.0; 0.20; 20*pi; 2.0; problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.lower.controls(3) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.upper.controls(2) problem.phases(1).bounds.upper.controls(3) problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) problem.phases(1).bounds.lower.events(7) = = = = = = = = = = = = = -1.0; -1.0; -1.0; 1.0; 1.0; 1.0; pti; fti; gti; hti; kti; Lti; wti; problem.phases(1).bounds.lower.events(8) = ptf; problem.phases(1).bounds.lower.events(9) = event_final_9; problem.phases(1).bounds.lower.events(10) = event_final_10; problem.phases(1).bounds.lower.events(11) = event_final_11; problem.phases(1).bounds.lower.events(12) = event_final_12_lower; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) problem.phases(1).bounds.upper.events(5) problem.phases(1).bounds.upper.events(6) problem.phases(1).bounds.upper.events(7) problem.phases(1).bounds.upper.events(8) problem.phases(1).bounds.upper.events(9) problem.phases(1).bounds.upper.events(10) problem.phases(1).bounds.upper.events(11) problem.phases(1).bounds.upper.events(12) = = = = = = = = = = = = pti; fti; gti; hti; kti; Lti; wti; ptf; event_final_9; event_final_10; event_final_11; event_final_12_upper; double EQ_TOL = 0.001; problem.phases(1).bounds.upper.path(1) = 1.0+EQ_TOL; problem.phases(1).bounds.lower.path(1) = 1.0-EQ_TOL; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 50000.0; = 100000.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// 249 //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = 141; DMatrix u_guess = DMatrix x_guess = DMatrix time_guess = = problem.phases(1).ncontrols; = problem.phases(1).nstates; zeros(ncontrols,nnodes); zeros(nstates,nnodes); linspace(0.0,86810.0,nnodes); DMatrix param_guess = -25.0*ones(1,1); u_guess.Load("U0.dat"); x_guess.Load("X0.dat"); time_guess.Load("T0.dat"); auto_phase_guess(problem, u_guess, x_guess, param_guess, time_guess); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.defect_scaling algorithm.jac_sparsity_ratio algorithm.collocation_method algorithm.mesh_refinement algorithm.mr_max_increment_factor = = = = = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; "jacobian-based"; 0.11; // 0.05; "trapezoidal"; "automatic"; 0.2; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); t = t/3600.0; DMatrix tau = solution.get_parameters_in_phase(1); tau.Print("tau"); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix x1 x2 x3 x4 x5 x6 x7 = = = = = = = x(1,colon())/1.e6; x(2,colon()); x(3,colon()); x(4,colon()); x(5,colon()); x(6,colon()); x(7,colon()); 250 DMatrix u1 = u(1,colon()); DMatrix u2 = u(2,colon()); DMatrix u3 = u(3,colon()); plot(t,x1,problem.name+": states", "time (h)", "p (1000000 ft)","p (1000000 ft)"); plot(t,x2,problem.name+": states", "time (h)", "f","f"); plot(t,x3,problem.name+": states", "time (h)", "g","g"); plot(t,x4,problem.name+": states", "time (h)", "h","h"); plot(t,x5,problem.name+": states", "time (h)", "k","k"); plot(t,x6,problem.name+": states", "time (h)", "L (rev)","L (rev)"); plot(t,x7,problem.name+": states", "time (h)", "w (lb)","w (lb)"); plot(t,u1,problem.name+": controls","time (h)", "ur", "ur"); plot(t,u2,problem.name+": controls","time (h)", "ut", "ut"); plot(t,u3,problem.name+": controls","time (h)", "uh", "uh"); plot(t,x1,problem.name+": states", "time (h)", "p (1000000 ft)","p (1000000 ft)", "pdf","lowthr_x1.pdf"); plot(t,x2,problem.name+": states", "time (h)", "f","f", "pdf","lowthr_x2.pdf"); plot(t,x3,problem.name+": states", "time (h)", "g","g", "pdf","lowthr_x3.pdf"); plot(t,x4,problem.name+": states", "time (h)", "h","h", "pdf","lowthr_x4.pdf"); plot(t,x5,problem.name+": states", "time (h)", "k","k", "pdf","lowthr_x5.pdf"); plot(t,x6,problem.name+": states", "time (h)", "L (rev)","L (rev)", "pdf","lowthr_x6.pdf"); plot(t,x7,problem.name+": states", "time (h)", "w (lb)","w (lb)", "pdf","lowthr_x7.pdf"); plot(t,u1,problem.name+": controls","time (h)", "ur", "ur", "pdf","lowthr_u1.pdf"); plot(t,u2,problem.name+": controls","time (h)", "ut", "ut", "pdf","lowthr_u2.pdf"); plot(t,u3,problem.name+": controls","time (h)", "uh", "uh", "pdf","lowthr_u3.pdf"); DMatrix r; compute_cartesian_trajectory(x,r); double ft2km = 0.0003048; r = r*ft2km; DMatrix rnew, tnew; tnew = linspace(0.0, t("end"), 1000); resample_trajectory( rnew, tnew, r, t ); plot3(rnew(1,colon()), rnew(2,colon()), rnew(3,colon()), "Low thrust transfer trajectory", "x (km)", "y (km)", "z (km)", NULL, NULL, "30,97"); plot3(rnew(1,colon()), rnew(2,colon()), rnew(3,colon()), "Low thrust transfer trajectory", "x (km)", "y (km)", "z (km)", "pdf", "trajectory.pdf", "30,97"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// 251 Table 3.2: Mesh refinement statistics: Low thrust transfer problem Iter DM M NV NC OE CE JE HE RHS max CPUa 1 2 3 4 CPUb - TRP TRP H-S H-S - 80 98 108 116 - 803 983 1404 1508 - 653 797 984 1056 - 1968 118 145 209 2440 362 119 146 210 837 181 110 141 185 617 0 0 0 0 0 57558 23205 47012 72660 200435 2.208e-03 2.263e-03 1.180e-03 3.514e-04 - 5.560e+00 4.060e+00 7.650e+00 1.119e+01 8.250e+00 3.671e+01 Key: Iter=iteration number, DM= discretization method, M=number of nodes, NV=number of vari- ables, NC=number of constraints, OE=objective evaluations, CE = constraint evaluations, JE = Jacobian evaluations, HE = Hessian evaluations, RHS = ODE right hand side evaluations, max = maximum relative ODE error, CPUa = CPU time in seconds spent by NLP algorithm, CPUb = additional CPU time in seconds spent by PSOPT //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.53, to 3.58 and 3.59 to 3.61, which contain the modified equinoctial elements and the controls, respectively. PSOPT results summary ===================== Problem: Low thrust transfer problem CPU time (seconds): 4.712761e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:09:27 2019 Optimal (unscaled) cost function value: -2.203414e-01 Phase 1 endpoint cost function value: -2.203414e-01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 8.684094e+04 Phase 1 maximum relative local error: 5.604141e-04 NLP solver reports: The problem has been solved! 3.25 Manutec R3 robot The DLR model 2 of the Manutec r3 robot, reported and validated by Otter and co-workers [31, 20], describes the motion of three links of the robot as a function of the control input signals of the robot drive: 252 Low thrust transfer problem: states p 50 p (1000000 ft) 45 40 35 30 25 0 5 10 15 20 25 time (h) Figure 3.53: Modified equinoctial element p Low thrust transfer problem: states f 0 -0.02 f -0.04 -0.06 -0.08 -0.1 -0.12 0 5 10 15 20 time (h) Figure 3.54: Modified equinoctial element f 253 25 Low thrust transfer problem: states g 0.7 0.6 0.5 g 0.4 0.3 0.2 0.1 0 0 5 10 15 20 25 time (h) Figure 3.55: Modified equinoctial element g Low thrust transfer problem: states h -0.25 -0.3 -0.35 h -0.4 -0.45 -0.5 -0.55 -0.6 0 5 10 15 20 time (h) Figure 3.56: Modified equinoctial element h 254 25 Low thrust transfer problem: states k 0.06 0.04 0.02 k 0 -0.02 -0.04 -0.06 -0.08 -0.1 0 5 10 15 20 25 time (h) Figure 3.57: Modified equinoctial element k Low thrust transfer problem: states L 50 L (rev) 40 30 20 10 0 5 10 15 20 time (h) Figure 3.58: Modified equinoctial element L 255 25 Low thrust transfer problem: controls ur 0.8 0.6 ur 0.4 0.2 0 -0.2 -0.4 0 5 10 15 20 25 time (h) Figure 3.59: Radial component of the thrust direction vector, ur Low thrust transfer problem: controls ut 1 ut 0.5 0 -0.5 -1 0 5 10 15 20 25 time (h) Figure 3.60: Tangential component of the thrust direction vector, ut 256 Low thrust transfer problem: controls uh 0.8 0.6 0.4 uh 0.2 0 -0.2 -0.4 -0.6 -0.8 -1 0 5 10 15 20 25 time (h) Figure 3.61: Normal component of the thrust direction vector, uh M(q(t))q̈(t) = V(q(t), q̇(t)) + G(q(t)) + Du(t) where q = [q1 (t), q2 (t), q3 (t)]T is the vector of relative angles between the links, the normalized torque controls are u(t) = [u1 (t), u2 (t), u3 (t)]T , D is a diagonal matrix with constant values, M(q) is a symmetric inertia matrix, V(q(t), q̇(t)) are the torques caused by coriolis and centrifugal forces, G(q(t)) are gravitational torques. The model is described in detail in [31] and is fully included in the code for this example3 . The example reported here consists of a minimum energy point to point trajectory, so that the objective is to find tf and u(t) = [u1 (t), u2 (t), u3 (t)]T , t ∈ [0, tf ] to minimise: Z tf J= u(t)T u(t)dt (3.107) 0 The boundary conditions associated with the problem are: T q(0) = 0 −1.5 0 T q̇(0) = 0 0 0 T q(tf ) = 1.0 −1.95 1.0 T q̇(tf ) = 0 0 0 (3.108) 3 Dr. Martin Otter from DLR, Germany, has kindly authorised the author to publish a translated form of subroutine R3M2SI as part of the PSOPT distribution. 257 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// manutec.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Minimum time control of a Manutec R3 robot /////// //////// Last modified: 25 January 2010 //////////////// //////// Reference: Chettibi et al (2007) //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" // Set option to: // // #define OBJ_OPTION 1 for minimum time problem 2 for minimum time with regularization 3 for minimum energy problem 3 typedef struct { double g; // Gravity constant double pl; // Point mass of load; double Ia1; // Moment of inertia of arm 1, element (3,3) DMatrix* Fc; // Voltage-force constant of motor DMatrix* r; // Gear ratio of motor DMatrix* Im; // Moment of inertia of motor DMatrix* m; // Mass of arm 2 and 3 DMatrix* L; // Length of arm 2 and 3 (inc. tool) DMatrix* com; // Center of mass coordinates of arm 2 and 3; DMatrix* Ia; // Moment of inertia arm 2 and 3 } CONSTANTS_; CONSTANTS_ CONSTANTS; typedef struct { adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* qdd_ad; F_ad; qd_ad; q_ad; kw; beta; afor; wabs; zeta; cosp; ator; sinp; rhicm; genvd ; mrel12 ; mrel22; rhrel2; workm3; workm6; works1; works2; workv3; workv6; cmhges; ihhges; inertc; inertg; inerth; workam; workas; wworkm; 258 adouble* wworkv; } VARS_; VARS_ VARS; void r3m2si(double *ml, adouble *aq, adouble *aqd, adouble *fgen, adouble *aqdd, VARS_ *vars); ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble retval; if (OBJ_OPTION==1 || OBJ_OPTION==2 ) retval = tf; else retval = 0.0; return retval; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble retval; adouble* u = controls; adouble* qdot = &states[3]; double rho; if (OBJ_OPTION==1) rho = 0.0; else if (OBJ_OPTION==2) rho = 1.e-5; else if (OBJ_OPTION==3) rho = 1.0; retval = rho*dot( u, u, 3 ); return (retval); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { int i; double g = DMatrix& Fc = DMatrix& r = DMatrix& Im = DMatrix& m = double pl = DMatrix& L = DMatrix& com= double Ia1= DMatrix& Ia = CONSTANTS.g; *CONSTANTS.Fc; *CONSTANTS.r; *CONSTANTS.Im; *CONSTANTS.m; CONSTANTS.pl; *CONSTANTS.L; *CONSTANTS.com; CONSTANTS.Ia1; *CONSTANTS.Ia; adouble* F = VARS.F_ad; adouble* qdd = VARS.qdd_ad; adouble* qd = VARS.qd_ad; 259 adouble* q = VARS.q_ad; double ml = pl; for(i=0;i<3;i++) { q[i] = states[i]; qd[i] = states[3+i]; F[i] = Fc(i+1)*controls[i]; } r3m2si(&ml, q, qd, F, qdd, &VARS); derivatives[CINDEX(1)] = derivatives[CINDEX(2)] = derivatives[CINDEX(3)] = derivatives[CINDEX(4)] derivatives[CINDEX(5)] derivatives[CINDEX(6)] qd[CINDEX(1)]; qd[CINDEX(2)]; qd[CINDEX(3)]; = = = qdd[CINDEX(1)]; qdd[CINDEX(2)]; qdd[CINDEX(3)]; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { int i; for (i=0; i< 6; i++ ) { e[ i ] = initial_states[ i ]; } for (i=0; i< 6; i++ ) { e[6 + i ] = final_states[ i ]; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { double g = 9.81; double pl= 0.0; double Ia1 = 1.16; DMatrix Fc(3,1); Fc = "[-126.0;252.0; 72.0]"; DMatrix r(3,1); r = "[-105; 210; 60]"; DMatrix Im(3,1); Im = "[1.3e-3; 1.3e-3; 1.3e-3]"; DMatrix m(2,1); m = "[56.5; 60.3]"; DMatrix L(2,1); L = "[0.5; 0.98]"; DMatrix com(2,2); DMatrix Ia(4,2); // // // // // // // // // // Gravity constant [m/s2] Point mass of load; [kg] Moment of inertia of arm 1, element (3,3) [kgm^2] Voltage-force constant of motor [N*m/V] Gear ratio of motor Moment of inertia of motor [kg*m^2] Mass of arm 2 and 3 [kg] Length of arm 2 and 3 (inc. tool) [m] Center of mass coordinates of arm 2 and 3; [m] Moment of inertia arm 2 and 3 [kg*m^2] com(1,1)=0.172; com(1,2)=0.028; com(2,1)=0.205; com(2,2)=0.202; Ia(1,1)=2.58; Ia(1,2)=11.0; Ia(2,1)=2.73; Ia(2,2)=8.0; Ia(3,1)=0.64; Ia(3,2)=0.80; 260 Ia(4,1)=-0.46; Ia(4,2)=0.50; Fc.Print("Fc"); r.Print("r"); Im.Print("Im"); m.Print("m"); L.Print("L"); com.Print("com"); Ia.Print("Ia"); CONSTANTS.g = g; CONSTANTS.pl = pl; CONSTANTS.Ia1 = Ia1; CONSTANTS.Fc = &Fc; CONSTANTS.r = &r; CONSTANTS.Im = ℑ CONSTANTS.m = &m; CONSTANTS.L = &L; CONSTANTS.com= &com; CONSTANTS.Ia = &Ia; VARS.qdd_ad VARS.qd_ad VARS.q_ad VARS.F_ad = = = = new new new new adouble[3]; adouble[3]; adouble[3]; adouble[3]; VARS.kw = new adouble[9]; VARS.beta = new adouble[18]; VARS.afor = new adouble[9]; VARS.wabs = new adouble[9]; VARS.zeta = new adouble[18]; VARS.cosp = new adouble[3]; VARS.ator = new adouble[9]; VARS.sinp = new adouble[3]; VARS.rhicm = new adouble[9]; VARS.genvd = new adouble[3]; VARS.mrel12 = new adouble[18]; VARS.mrel22 = new adouble[3]; VARS.rhrel2 = new adouble[3]; VARS.workm3 = new adouble[9]; VARS.workm6 = new adouble[180]; VARS.works1 = new adouble[12]; VARS.works2 = new adouble[6]; VARS.workv3 = new adouble[39]; VARS.workv6 = new adouble[24]; VARS.cmhges = new adouble[3]; VARS.ihhges = new adouble[9]; VARS.inertc = new adouble[27]; VARS.inertg = new adouble[108]; VARS.inerth = new adouble[27]; VARS.workam = new adouble[144]; VARS.workas = new adouble[40]; VARS.wworkm = new adouble[54]; VARS.wworkv = new adouble[9]; //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Manutec R3 robot problem"; = "manutec.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); 261 ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 6; 3; 12; 0; = "[20, 30, 40, 60, 80]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix qmax(1,3), qdmax(1,3), qddmax(1,3); // Joint position limits in rad qmax(1) = 2.97; qmax(2) = 2.01; qmax(3) = 2.86; // Joint angular velocity limits in rad/s qdmax(1) = 3.0; qdmax(2) = 1.5; qdmax(3) = 5.2; problem.phases(1).bounds.lower.states = problem.phases(1).bounds.upper.states = (-qmax || -qdmax); ( qmax || qdmax); DMatrix umax(1,3); // Control variable limits in V umax(1) = 7.5; umax(2) = 7.5; umax(3) = 7.5; problem.phases(1).bounds.lower.controls = -umax; problem.phases(1).bounds.upper.controls = umax; DMatrix qi(1,3), qf(1,3), qdi(1,3), qdf(1,3); // Initial joint positions in rad qi(1) = 0.0; qi(2) = -1.5; qi(3) = 0.0; // Final joint positions in rad qf(1) = 1.0; qf(2) = -1.95; qf(3) = 1.0; // Initial joint velocities in rad/s qdi = zeros(1,3); // Final joint velocities in rad/s qdf = zeros(1,3); problem.phases(1).bounds.lower.events = (qi || qdi || qf || qdf); problem.phases(1).bounds.upper.events = (qi || qdi || qf || qdf); problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime if (OBJ_OPTION==1 || OBJ_OPTION==2) { problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime } else { problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime } = 0.0; = 0.0; = 0.0; = 1.0; = 0.53; = 0.53; 262 //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix state_guess DMatrix control_guess DMatrix time_guess = = = state_guess(1,colon()) state_guess(2,colon()) state_guess(3,colon()) state_guess(4,colon()) state_guess(5,colon()) state_guess(6,colon()) = = = = = = zeros(nstates,nnodes); zeros(ncontrols,nnodes); linspace(0.0,0.53,nnodes); linspace( linspace( linspace( linspace( linspace( linspace( qi(1), qi(2), qi(3), qi(4), qi(5), qi(6), qf(1), qf(2), qf(3), qf(4), qf(5), qf(6), nnodes nnodes nnodes nnodes nnodes nnodes ); ); ); ); ); ); problem.phases(1).guess.states = state_guess; problem.phases(1).guess.controls = control_guess; problem.phases(1).guess.time = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.mesh_refinement algorithm.ode_tolerance algorithm.mr_max_iterations = 1000; = 1.e-6; = "IPOPT"; = "automatic"; = "automatic"; = "automatic"; = 1.e-5; = 5; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); DMatrix q = x( colon(1,3), colon() ); DMatrix qd= x( colon(4,6), colon() ); 263 //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,q,problem.name+": positions","t", "q (rad)", "q1 q2 q3"); plot(t,qd,problem.name+": velocities","t", "qdot (rad/s)", "qd1 qd2 qd3"); plot(t,u(1,colon()),problem.name+": control u1","time (s)", "control", "u1"); plot(t,u(2,colon()),problem.name+": control u2","time (s)", "control", "u2"); plot(t,u(3,colon()),problem.name+": control u3","time (s)", "control", "u3"); plot(t,q,problem.name+": positions","t", "q (rad)", "q1 q2 q3", "pdf", "positions.pdf"); plot(t,qd,problem.name+": velocities","t", "qdot (rad/s)", "qd1 qd2 qd3", "pdf", "velocities.pdf"); plot(t,u,problem.name+": controls","time (s)", "controls", "u1 u2 u3", "pdf", "controls.pdf"); } void r3m2si(double *ml, adouble *aq, adouble *aqd, adouble *fgen, adouble *aqdd, VARS_ *vars) { adouble mhges; adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* adouble* /* /* /* /* kw = vars->kw; beta = vars->beta; afor = vars->afor; wabs = vars->wabs; zeta = vars->zeta; cosp = vars->cosp; ator = vars->ator; sinp = vars->sinp; rhicm = vars->rhicm; genvd = vars->genvd; mrel12 = vars->mrel12; mrel22 = vars->mrel22; rhrel2 = vars->rhrel2; workm3 = vars->workm3; workm6 = vars->workm6; works1 = vars->works1; works2 = vars->works2; workv3 = vars->workv3; workv6 = vars->workv6; cmhges = vars->cmhges; ihhges = vars->ihhges; inertc = vars->inertc; inertg = vars->inertg; inerth = vars->inerth; workam = vars->workam; workas = vars->workas; wworkm = vars->wworkm; wworkv = vars->wworkv; Simulation model equations of the Manutec r3 robot (DFVLR model 2) */ This is a modified C++ tranlation of the original Fortran subroutine R3M2SI */ by Otter and co-workers, published here with kind persmission from Dr. Martin Otter */ DSL, Germany */ /* Procedure purpose: */ /* This subroutine calculates the generalized accelerations (AQDD) for */ /* the Manutec r3 robot of the DFVLR model 2. This model is based on */ /* the following assumptions: */ /* /* /* - The last 3 joints (joints 4,5,6) of the robot hand do not move. */ This corresponds to the case that the brakes in these joints are */ blocked (joints 4,5,6 are taken into account in model 1). */ /* - The robot consists of a base body (the environment), three arms */ 264 /* /* /* /* and three rotors. The rotors represent the inertia effects of the */ motors and of the wheels of the gear boxes. The rotors are */ embedded in the preceeding arms, e.g. the rotor 2, which drives */ arm2, is embedded in arm1. */ /* - Arms and rotors are considered to be rigid bodies. */ /* - Elasticity, backlash and friction in the gear boxes are neglected. */ /* /* /* /* /* /* /* /* /* - The motors are modelled as ideal voltage-torque converters */ without any dynamic effects. As input arguments of the subroutine */ the torques at the gear output (FGEN in ) must be given. If the */ voltage (U in ) at the input of the current regulator of the */ motor is given, FGEN must be calculated (before calling this */ subroutine) in the following way: */ FGEN(1) = -126.0*U(1) */ FGEN(2) = 252.0*U(2) */ FGEN(3) = 72.0*U(3) */ /* /* - At the robot’s tip a load mass (ML) is attached. It can range */ between 0 ... 15 kg. */ /* /* /* /* Given the actual value the derivatives of the in the joints FGEN(i), AQDD(i) are calculated ML of the load mass, the joint angles AQ(i), */ joint angles AQD(i), and the driving torques */ the second derivatives of the joint angles */ by this subroutine. */ /* Usage: */ /* CALL R3M2SI (ML, AQ, AQD, FGEN, AQDD) */ /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* /* ML : AQ : AQD : FGEN : AQDD : IN, DOUBLE, , 0<=ML<=15 */ Load mass. */ IN, DOUBLE(3), , -2.97 <= AQ(1) <= 2.97 (+/- 170 deg), */ -2.01 <= AQ(2) <= 2.01 (+/- 115 deg), */ -2.86 <= AQ(3) <= 2.86 (+/- 164 deg) */ (The given limits are hardware constraints by limit switches). */ Vector of generalized coordinates (relative angles between */ two contigous robot arms). */ AQ(1) is not used in this subroutine, because it is not */ needed to calculate AQDD. */ IN, DOUBLE(3), , */ -3.0 <= AQD(1) <= 3.0 (+/- 172 deg/sec), */ -1.5 <= AQD(2) <= 1.5 (+/- 86 deg/sec), */ -5.2 <= AQD(3) <= 5.2 (+/- 298 deg/sec) */ Derivative of AQ. */ IN, DOUBLE(3), , -945.0 <= FGEN(1) <= 945.0, */ -1890.0 <= FGEN(2) <= 1890.0, */ -540.0 <= FGEN(3) <= 540.0 */ Torque at the gear output. */ OUT, DOUBLE(3), */ Second derivative of AQ. */ /* Bibliography: */ /* A detailed description of the model, together with the body-data */ /* (mass, center of mass etc. of the arms and rotors) is given in */ /* Otter M., Tuerk S., Mathematical Model of the Manutec r3 Robot, */ /* (DFVLR Model No. 2), DFVLR - Oberpfaffenhofen, Institut fuer */ /* Dynamik der Flugsysteme, D-8031 Wessling, West Germany, */ /* corrected version april 1988. */ /* /* /* /* This subroutine was generated by the program MYROBOT. See */ Otter M., Schlegel S., Symbolic generation of efficient simulation */ codes for robots. 2nd European Simulation Multiconference, */ June 1-3, 1988, Nice. */ /* /* /* /* /* The underlying multibody algorithm is a modified version of */ Brandl H., Johanni R., Otter M., A very efficient algorithm for the */ simulation of robots and similar multibody systems without */ inversion of the mass matrix. IFAC/IFIP/IMACS International */ Symposium on Theory of Robots, december 3-5, 1986, Vienna. */ /* Remarks: */ /* - The limits given for the input variables are not checked in */ /* this subroutine. The user is responsible for proper data. */ /* /* /* - If a SINGLE PRECISION version of this subroutine is desired, just */ change all strings from DOUBLE PRECISION to REAL in the declaration */ part. */ /* Copyright: */ /* 1988 DFVLR - Institut fuer Dynamik der Flugsysteme */ /* Life cycle: */ 265 /* 1988 APR M. Otter, S. Tuerk (DFVLR) : specified. */ /* 1988 APR M. Otter (DFVLR) : generated. */ /* 1988 APR M. Otter, C. Schlegel, S. Tuerk (DFVLR): tested. */ /* ----------------------------------------------------------------------- */ /* /* Statistical information (MySymbol-Version 1.2) */ ============================================== */ /* /* 1. Number of Operations: */ ------------------------ */ /* /* /* /* /* /* /* /* Without simplifications Terms simplified Unnecessary statements removed + - | * | / | sin | cos | sqrt| -----|-----|-----|-----|-----|-----| 3180| 3702| 26| 3| 3| 0| -----|-----|-----|-----|-----|-----| 222| 247| 23| 3| 3| 0| -----|-----|-----|-----|-----|-----| | | | | | | 140| 159| 17| 2| 2| 0| /* /* 2. Storage information: */ ----------------------- */ /* /* /* /* /* /* /* /* /* /* /* /* /* /* INTEGER | | words | | --------|---------| available storage 100000 | 100.0 % | --------|---------| array-elements 1412 | 1.4 % | --------|---------| double-numbers 104 | 0.1 % | --------|---------| statement buffer 2552 | 2.6 % | +++++++++++++++++++++++++++++++++++++++ used storage 4068 | 4.1 % | --------|---------| free storage 95932 | 95.9 % | */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ */ /* ----------------------------------------------------------------------- */ /* cccccccccccccccccccccccc Procedural section cccccccccccccccccccccccccc */ /* Parameter adjustments */ --aqdd; --fgen; --aqd; --aq; /* /* /* /* /* /* Function Body */ workv3[14] = *ml * .98; workv3[17] = workv3[14] + 12.1806; mhges = *ml + 60.3; cmhges[0] = 1.6884 / mhges; cmhges[2] = workv3[17] / mhges; workv3[20] = workv3[14] * .98; ihhges[0] = workv3[20] + 7.8704812; ihhges[4] = workv3[20] + 8.1077564; sinp[1] = sin(aq[2]); cosp[1] = cos(aq[2]); wworkv[0] = mhges * cmhges[0]; wworkv[2] = mhges * cmhges[2]; wworkv[3] = cmhges[0] * wworkv[0]; wworkv[5] = cmhges[2] * wworkv[2]; wworkv[6] = wworkv[3] + wworkv[5]; wworkm[0] = wworkv[6] - wworkv[3]; wworkm[8] = wworkv[6] - wworkv[5]; wworkm[6] = -wworkv[0] * cmhges[2]; inertc[18] = ihhges[0] - wworkm[0]; inertc[22] = ihhges[4] - wworkv[6]; inertc[24] = -.0110568 - wworkm[6]; inertc[26] = .4372752 - wworkm[8]; sinp[2] = sin(aq[3]); cosp[2] = cos(aq[3]); ** The equations of motion have been generated with the algorithm */ ** of Brandl/Johanni/Otter (variant 6) */ ** Forward recursion -------------------------------------------------- */ -- Quantities of body 1 */ -- Quantities of body 2 */ wabs[4] = sinp[1] * aqd[1]; wabs[5] = cosp[1] * aqd[1]; workv3[1] = aqd[1] * aqd[2]; zeta[7] = cosp[1] * workv3[1]; zeta[8] = -sinp[1] * workv3[1]; kw[3] = aqd[2] * 4.9544125 - wabs[5] * 2.45219; 266 kw[4] = wabs[4] * 6.7759085; kw[5] = aqd[2] * -2.45219 + wabs[5] * 2.311496; -- Quantities of body 3 */ workv3[19] = -sinp[2] * cmhges[2]; workv3[20] = cosp[2] * cmhges[2]; workm3[4] = cosp[2] * inertc[22]; workm3[5] = sinp[2] * inertc[22]; workm3[7] = -sinp[2] * inertc[26]; workm3[8] = cosp[2] * inertc[26]; inertc[21] = -inertc[24] * sinp[2]; inertc[22] = workm3[4] * cosp[2] - workm3[7] * sinp[2]; inertc[24] *= cosp[2]; inertc[25] = workm3[4] * sinp[2] + workm3[7] * cosp[2]; inertc[26] = workm3[5] * sinp[2] + workm3[8] * cosp[2]; workv3[21] = mhges * cmhges[0]; workv3[22] = mhges * workv3[19]; workv3[23] = mhges * workv3[20]; workv3[24] = cmhges[0] * workv3[21]; workv3[25] = workv3[19] * workv3[22]; workv3[26] = workv3[20] * workv3[23]; workv3[27] = workv3[24] + workv3[25] + workv3[26]; inerth[18] = workv3[27] - workv3[24]; inerth[22] = workv3[27] - workv3[25]; inerth[26] = workv3[27] - workv3[26]; inerth[21] = -workv3[21] * workv3[19]; inerth[24] = -workv3[21] * workv3[20]; inerth[25] = -workv3[22] * workv3[20]; inerth[18] += inertc[18]; inerth[21] += inertc[21]; inerth[22] += inertc[22]; inerth[24] += inertc[24]; inerth[25] += inertc[25]; inerth[26] += inertc[26]; wabs[6] = aqd[3] + aqd[2]; workv3[1] = wabs[5] * aqd[3]; workv3[2] = -wabs[4] * aqd[3]; workv3[6] = wabs[4] * .5; workv3[7] = -aqd[2] * .5; workv3[9] = -wabs[5] * workv3[7]; workv3[10] = wabs[5] * workv3[6]; workv3[11] = aqd[2] * workv3[7] - wabs[4] * workv3[6]; rhicm[6] = mhges * cmhges[0]; rhicm[7] = mhges * workv3[19]; rhicm[8] = mhges * workv3[20]; kw[6] = inerth[18] * wabs[6] + inerth[21] * wabs[4] + inerth[24] * wabs[5] ; kw[7] = inerth[21] * wabs[6] + inerth[22] * wabs[4] + inerth[25] * wabs[5] ; kw[8] = inerth[24] * wabs[6] + inerth[25] * wabs[4] + inerth[26] * wabs[5] ; ator[3] = -wabs[4] * kw[5] + wabs[5] * kw[4]; ator[4] = -wabs[5] * kw[3] + aqd[2] * kw[5]; ator[5] = -aqd[2] * kw[4] + wabs[4] * kw[3]; ator[6] = -wabs[4] * kw[8] + wabs[5] * kw[7]; ator[7] = -wabs[5] * kw[6] + wabs[6] * kw[8]; ator[8] = -wabs[6] * kw[7] + wabs[4] * kw[6]; workv3[15] = wabs[4] * rhicm[8] - wabs[5] * rhicm[7]; workv3[16] = wabs[5] * rhicm[6] - wabs[6] * rhicm[8]; workv3[17] = wabs[6] * rhicm[7] - wabs[4] * rhicm[6]; afor[6] = -wabs[4] * workv3[17] + wabs[5] * workv3[16]; afor[7] = -wabs[5] * workv3[15] + wabs[6] * workv3[17]; /* ** Backward recursion ------------------------------------------------- */ /* -- Quantities of body 3 */ mrel22[2] = inerth[18] + 4.68; workv6[0] = ator[6] - inerth[21] * workv3[1] - inerth[24] * workv3[2] + rhicm[8] * workv3[10] - rhicm[7] * workv3[11]; workv6[1] = ator[7] - inerth[22] * workv3[1] - inerth[25] * workv3[2] rhicm[8] * workv3[9] + rhicm[6] * workv3[11]; workv6[2] = ator[8] - inerth[25] * workv3[1] - inerth[26] * workv3[2] + rhicm[7] * workv3[9] - rhicm[6] * workv3[10]; workv6[3] = afor[6] - rhicm[8] * workv3[1] + rhicm[7] * workv3[2] - mhges * workv3[9]; workv6[4] = afor[7] - rhicm[6] * workv3[2] - mhges * workv3[10]; rhrel2[2] = fgen[3] + workv6[0]; mrel12[12] = inerth[18] + .078 + rhicm[8] * .5; workv6[18] = workv6[0] - workv6[4] * .5; workv6[19] = workv6[1] + workv6[3] * .5; workm6[108] = inerth[18] - rhicm[8] * -.5; workm6[112] = -rhicm[8] + mhges * -.5; workm6[115] = inerth[22] + rhicm[8] * .5; workm6[117] = rhicm[8] + mhges * .5; inertg[36] = workm6[108] + 4.9544125 - workm6[112] * .5; inertg[43] = workm6[115] + 6.7759085 + workm6[117] * .5; /* 267 /* /* /* /* /* /* inertg[48] = inerth[24] - 2.45219 - rhicm[6] * .5; inertg[49] = inerth[25] - rhicm[7] * .5; inertg[50] = inerth[26] + 2.311496; inertg[60] = -11.5825 - rhicm[8] - mhges * .5; inertg[62] = rhicm[6] + 9.718; inertg[67] = -9.718 - rhicm[6]; beta[6] = ator[3] + workv6[18]; beta[7] = ator[4] + workv6[19]; beta[8] = ator[5] + workv6[2]; works2[0] = mrel12[12] / mrel22[2]; works2[1] = inerth[21] / mrel22[2]; works2[2] = inerth[24] / mrel22[2]; works2[4] = -rhicm[8] / mrel22[2]; works2[5] = rhicm[7] / mrel22[2]; inertg[36] -= mrel12[12] * works2[0]; inertg[42] = inerth[21] - mrel12[12] * works2[1]; inertg[43] -= inerth[21] * works2[1]; inertg[48] -= mrel12[12] * works2[2]; inertg[49] -= inerth[21] * works2[2]; inertg[50] -= inerth[24] * works2[2]; inertg[60] -= mrel12[12] * works2[4]; inertg[61] = -inerth[21] * works2[4]; inertg[62] -= inerth[24] * works2[4]; inertg[66] = rhicm[7] - mrel12[12] * works2[5]; inertg[67] -= inerth[21] * works2[5]; inertg[68] = -inerth[24] * works2[5]; beta[6] -= works2[0] * rhrel2[2]; beta[7] -= works2[1] * rhrel2[2]; beta[8] -= works2[2] * rhrel2[2]; -- Quantities of body 2 */ mrel22[1] = inertg[36] + 57.33; workv6[0] = beta[6] - inertg[42] * zeta[7] - inertg[48] * zeta[8]; workv6[1] = beta[7] - inertg[43] * zeta[7] - inertg[49] * zeta[8]; workv6[2] = beta[8] - inertg[49] * zeta[7] - inertg[50] * zeta[8]; rhrel2[1] = fgen[2] + workv6[0]; works1[8] = sinp[1] * inertg[42] + cosp[1] * inertg[48]; works1[11] = sinp[1] * inertg[60] + cosp[1] * inertg[66]; workv6[14] = sinp[1] * workv6[1] + cosp[1] * workv6[2]; workas[30] = cosp[1] + sinp[1]; workas[31] = cosp[1] - sinp[1]; workas[33] = workas[30] * workas[31]; workas[32] = cosp[1] * sinp[1]; workas[34] = workas[32] + workas[32]; workas[0] = inertg[50] + inertg[43]; workas[1] = inertg[50] - inertg[43]; workas[4] = workas[0] / 2.; workas[5] = workas[1] / 2.; workas[8] = workas[33] * workas[5] + workas[34] * inertg[49]; workam[115] = workas[4] + workas[8]; workas[20] = inertg[68] + inertg[61]; workas[21] = inertg[68] - inertg[61]; workas[24] = workas[20] / 2.; workas[25] = workas[21] / 2.; workas[23] = -inertg[62] - inertg[67]; workas[27] = workas[23] / 2.; workas[28] = workas[33] * workas[25] - workas[34] * workas[27]; workam[133] = workas[24] + workas[28]; inertg[14] = workam[115] + 1.16; works2[2] = works1[8] / mrel22[1]; works2[5] = works1[11] / mrel22[1]; inertg[14] -= works1[8] * works2[2]; inertg[32] = workam[133] - works1[8] * works2[5]; beta[2] = workv6[14] - works2[2] * rhrel2[1]; -- Quantities of body 1 */ mrel22[0] = inertg[14] + 14.3325; workv6[2] = beta[2] - inertg[32] * 9.81; rhrel2[0] = fgen[1] + workv6[2]; ** Forward recursion -------------------------------------------------- */ -- Quantities of body 1 */ genvd[0] = rhrel2[0] / mrel22[0]; -- Quantities of body 2 */ rhrel2[1] = rhrel2[1] - works1[8] * genvd[0] - works1[11] * 9.81; genvd[1] = rhrel2[1] / mrel22[1]; workv6[7] = zeta[7] + sinp[1] * genvd[0]; workv6[8] = zeta[8] + cosp[1] * genvd[0]; workv6[10] = sinp[1] * 9.81; workv6[11] = cosp[1] * 9.81; -- Quantities of body 3 */ rhrel2[2] = rhrel2[2] - mrel12[12] * genvd[1] - inerth[21] * workv6[7] inerth[24] * workv6[8] + rhicm[8] * workv6[10] - rhicm[7] * workv6[11]; genvd[2] = rhrel2[2] / mrel22[2]; aqdd[1] = genvd[0]; 268 Table 3.3: Mesh refinement statistics: Manutec R3 robot problem Iter DM M NV NC OE CE JE HE RHS max CPUa 1 2 3 4 CPUb - LGL-ST LGL-ST LGL-ST LGL-ST - 20 25 35 49 - 182 227 317 443 - 133 163 223 307 - 43 34 12 35 124 43 35 13 36 127 35 30 12 33 110 0 0 0 0 0 860 875 455 1764 3954 4.676e-05 3.636e-05 2.787e-05 7.655e-06 - 8.334e-01 3.836e-01 2.682e-01 1.356e+00 4.804e+00 7.645e+00 Key: Iter=iteration number, DM= discretization method, M=number of nodes, NV=number of vari- ables, NC=number of constraints, OE=objective evaluations, CE = constraint evaluations, JE = Jacobian evaluations, HE = Hessian evaluations, RHS = ODE right hand side evaluations, max = maximum relative ODE error, CPUa = CPU time in seconds spent by NLP algorithm, CPUb = additional CPU time in seconds spent by PSOPT aqdd[2] = genvd[1]; aqdd[3] = genvd[2]; return; } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.62, 3.63 and 3.64, which contain the elements of the position vector q(t), the velocity vector q̇(t), and the controls u(t), respectively. The mesh refinement process is described in Table 3.3. PSOPT results summary ===================== Problem: Manutec R3 robot problem CPU time (seconds): 7.645063e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:10:35 2019 Optimal (unscaled) cost function value: 2.040419e+01 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 2.040419e+01 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 5.300000e-01 Phase 1 maximum relative local error: 7.655210e-06 NLP solver reports: The problem has been solved! 269 Manutec R3 robot problem: positions q1 q2 q3 1 0.5 q (rad) 0 -0.5 -1 -1.5 -2 0 0.1 0.2 0.3 0.4 0.5 0.6 t Figure 3.62: States q1 , q2 and q3 for the Manutec R3 robot minimum energy problem Manutec R3 robot problem: velocities qd1 qd2 qd3 3 2.5 2 qdot (rad/s) 1.5 1 0.5 0 -0.5 -1 0 0.1 0.2 0.3 0.4 0.5 0.6 t Figure 3.63: States q̇1 , q̇2 and q̇3 for the Manutec R3 robot minimum energy problem 270 Manutec R3 robot problem: controls u1 u2 u3 6 4 controls 2 0 -2 -4 -6 0 0.1 0.2 0.3 0.4 0.5 0.6 time (s) Figure 3.64: Controls u1 , u2 and u3 for the Manutec R3 robot minimum energy problem 3.26 Minimum swing control for a container crane Consider the following optimal control problem [40], which seeks to minimise the load swing of a container crane, while the load is transferred from one location to another. Find u(t) ∈ [0, tf ] to minimize the cost functional Z tf 2 (3.109) J = 4.5 x3 (t) + x26 (t) dt 0 subject to the dynamic constraints ẋ1 ẋ2 ẋ3 ẋ4 ẋ5 ẋ6 = = = = = = 9x4 9x5 9x6 9(u1 + 17.2656x3 ) 9u2 − x92 [u1 + 27.0756x3 + 2x5 x6 ] (3.110) the boundary conditions x1 (0) x2 (0) x3 (0) x4 (0) x5 (0) x6 (0) = 0 x1 (tf ) = 22 x2 (tf ) = 0 x3 (tf ) = 0 x4 (tf ) = −1 x5 (tf ) = 0 x6 (tf ) 271 = 10 = 14 = 0 , = 2.5 = 0 = 0 (3.111) and the bounds −2.83374 ≤u1 (t) ≤ 2.83374, −0.80865 ≤u2 (t) ≤ 0.71265, −2.5 ≤x4 (t) ≤ 2.5, −1 ≤x5 (t) ≤ 1. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// crane.cxx ////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Minimum Swing Control for Container Crane //////////////// //////// Last modified: 06 February 2009 //////////////// //////// Reference: Teo and Goh //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x3 = states[CINDEX(3)]; adouble x6 = states[CINDEX(6)]; return 4.5*( pow(x3,2) + pow(x6,2) ); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; adouble adouble adouble adouble adouble adouble x1 x2 x3 x4 x5 x6 = = = = = = states[ states[ states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ]; ]; ]; ]; ]; ]; adouble u1 = controls[ CINDEX(1) ]; adouble u2 = controls[ CINDEX(2) ]; derivatives[ CINDEX(1) ] = 9*x4; 272 (3.112) derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ] ] ] ] ] = = = = = 9*x5; 9*x6; 9*(u1 + 17.2656*x3); 9*u2; -(9/x2)*(u1 + 27.0756*x3 + 2*x5*x6); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1) ]; adouble x20 = initial_states[ CINDEX(2) ]; adouble x30 = initial_states[ CINDEX(3) ]; adouble x40 = initial_states[ CINDEX(4) ]; adouble x50 = initial_states[ CINDEX(5) ]; adouble x60 = initial_states[ CINDEX(6) ]; adouble x1f = final_states[ CINDEX(1) ]; adouble x2f = final_states[ CINDEX(2) ]; adouble x3f = final_states[ CINDEX(3) ]; adouble x4f = final_states[ CINDEX(4) ]; adouble x5f = final_states[ CINDEX(5) ]; adouble x6f = final_states[ CINDEX(6) ]; e[ e[ e[ e[ e[ e[ e[ e[ e[ e[ e[ e[ CINDEX(1) ] CINDEX(2) ] CINDEX(3) ] CINDEX(4) ] CINDEX(5) ] CINDEX(6) ] CINDEX(7) ] CINDEX(8) ] CINDEX(9) ] CINDEX(10)] CINDEX(11)] CINDEX(12)] = = = = = = = = = = = = x10; x20; x30; x40; x50; x60; x1f; x2f; x3f; x4f; x5f; x6f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Minimum swing control for a container crane"; problem.outfilename = "crane.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// 273 //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = 6; = 2; = 12; = 0; "[40, 60, 80]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) problem.phases(1).bounds.lower.states(5) problem.phases(1).bounds.lower.states(6) = = = = = = -5.0; -5.0; -5.0; -2.5; -1.0; -5.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) problem.phases(1).bounds.upper.states(5) problem.phases(1).bounds.upper.states(6) = = = = = = 15.0; 25.0; 15.0; 2.5; 1.0; 15.0; problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.upper.controls(2) = = = = -2.83374; -0.80865; 2.83374; 0.71265; // Initial states problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) = = = = = = 0.0; 22.0; 0.0; 0.0; -1.0; 0.0; // Final states problem.phases(1).bounds.lower.events(7) = problem.phases(1).bounds.lower.events(8) = problem.phases(1).bounds.lower.events(9) = problem.phases(1).bounds.lower.events(10)= problem.phases(1).bounds.lower.events(11)= problem.phases(1).bounds.lower.events(12)= 10.0; 14.0; 0.0; 2.5; 0.0; 0.0; problem.phases(1).bounds.upper.events = problem.phases(1).bounds.lower.events; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; 274 //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(6,20); x0(1,colon()) x0(2,colon()) x0(3,colon()) x0(4,colon()) x0(5,colon()) x0(6,colon()) = = = = = = linspace(0.0,10.0, 20); linspace(22.0,14.0, 20); linspace(0.,0., 20); linspace(0.,2.5, 20); linspace(-1.0,0., 20); linspace(0.,0., 20); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(2, 20); = x0; = linspace(0.0, 1.0, 20); ; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.collocation_method = = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; "Legendre"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x DMatrix u DMatrix t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); DMatrix x13 = x(colon(1,3), colon() ); DMatrix x46 = x(colon(4,6), colon() ); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x13,problem.name + ": states x1, x2 and x3", "time (s)", "states", "x1 x2 x3"); plot(t,x46,problem.name + ": states x4, x5 and x6", "time (s)", "states", "x4 x5 x6"); plot(t,u,problem.name + ": controls", "time", "controls", "u1 u2"); plot(t,x13,problem.name + ": states x1, x2 and x3", "time (s)", "states", "x1 x2 x3", "pdf", "crane_states13.pdf"); plot(t,x46,problem.name + ": states x4, x5 and x6", "time (s)", "states", "x4 x5 x6", "pdf", "crane_states46.pdf"); plot(t,u,problem.name + ": controls", "time", "controls", "u1 u2", "pdf", "crane_controls.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// 275 //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.65, 3.66 and 3.67, which contain the elements of the state x1 to x3 , x4 to x6 , and the controls, respectively. PSOPT results summary ===================== Problem: Minimum swing control for a container crane CPU time (seconds): 2.236445e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:14:50 2019 Optimal (unscaled) cost function value: 5.151343e-03 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 5.151343e-03 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 1.091430e-04 NLP solver reports: The problem has been solved! 3.27 Minimum time to climb for a supersonic aircraft Consider the following optimal control problem, which finds the minimum time to climb to a given altitude for a supersonic aircraft [4]. Minimize the cost functional J = tf (3.113) subject to the dynamic constraints ḣ v̇ = v sin γ 1 = m [T (M, h) cos α − D] − γ̇ = ẇ = 1 mv [T (M, h) sin α −T (M,h) Isp µ (Re +h)h2 + L] + cos γ sin γ v (Re +h) − µ v(Re +h)2 i (3.114) where h is the altitude (ft), v is the velocity (ft/s), γ is the flight path angle (rad), w is the weight (lb), L is the lift force, D is the drag force (lb), T is 276 Minimum swing control for a container crane: states x1, x2 and x3 x1 x2 x3 20 states 15 10 5 0 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.65: States x1 , x2 and x3 for minimum swing crane control problem Minimum swing control for a container crane: states x4, x5 and x6 2.5 x4 x5 x6 2 1.5 states 1 0.5 0 -0.5 -1 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.66: States x4 , x5 and x6 for minimum swing crane control problem 277 Minimum swing control for a container crane: controls u1 u2 2.5 2 controls 1.5 1 0.5 0 -0.5 0 0.2 0.4 0.6 0.8 1 time Figure 3.67: Controls for minimum swing crane control problem the thrust (lb), M = v/c is the mach number, m = w/g0 (slug) is the mass, c(h) is the speed of sound (ft/s), Re is the radious of Earth, and µ is the gravitational constant. The control input α is the angle of attack (rad). The speed of sound is given by: √ c = 20.0468 θ (3.115) where θ = θ(h) is the atmospheric temperature (K). The aerodynamic forces are given by: 1 D = CD Sρv 2 2 1 L = CL Sρv 2 2 (3.116) where CL = cLα (M )α CD = cD0 (M ) + η(M )cLα (M )α2 (3.117) where CL and CD are aerodynamic lift and drag coefficients, S is the aerodynamic reference area of the aircraft, and ρ = ρ(h) is the air density. 278 The boundary conditions are given by: h(0) = 0 (ft), h(tf ) = 65600.0 (ft) v(0) = 424.260 (ft/s), v(tf ) = 968.148 (ft/s) (3.118) γ(0) = γ(tf ) = 0 (rad) w(0) = 42000.0 lb The parameter values are given by: S = 530 (ft2 ), Isp = 1600.0 (sec) µ = 0.14046539 × 1017 (ft3 /s2 ), (3.119) 2 g0 = 32.174 (ft/s ) Re = 20902900 (ft) The variables cLα (M ), cD0 (M ), η(M ) are interpolated from 1-D tabular data which is given in the code and also in [4], using spline interpolation, while the thrust T (M, h) is interpolated from 2-D tabular data given in the code and in [4], using 2D spline interpolation. The air density ρ and the atmospheric temperature θ were calculated using the US Standard Atmosphere Model 19764 , based on the standard temperature of 15 (deg C) at zero altitude and the standard air density of 1.22521 (slug/ft3 ) at zero altitude. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// climb.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Minimum time to climb of a supersonic aircraft /////////// //////// Last modified: 12 January 2009 //////////////// //////// Reference: GPOPS Manual //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// ///////// Declare an auxiliary structure to hold local constants //////// 4 see http://www.pdas.com/programs/atmos.f90 279 ////////////////////////////////////////////////////////////////////////// struct Constants { double g0; double S; double Re; double Isp; double mu; DMatrix* CLa_table; DMatrix* CD0_table; DMatrix* eta_table; DMatrix* T_table; DMatrix* M1; DMatrix* M2; DMatrix* h1; DMatrix* htab; DMatrix* ttab; DMatrix* ptab; DMatrix* gtab; }; typedef struct Constants Constants_; void atmosphere(adouble* alt,adouble* sigma,adouble* delta,adouble* theta, Constants_& CONSTANTS); void atmosphere_model(adouble* rho, adouble* M, adouble v, adouble h, Constants_& CONSTANTS); ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return tf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { Constants_& CONSTANTS = *( (Constants_ *) workspace->problem->user_data ); adouble alpha = controls[ CINDEX(1)]; // Angle of attack (rad) adouble adouble adouble adouble double double double double double h v gamma w g0 S Re Isp mu DMatrix& DMatrix& DMatrix& DMatrix& DMatrix& = = = = = = = = = states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; // // // // Altitude (ft) Velocity (ft/s) Flight path angle (rad) weight (lb) CONSTANTS.g0; CONSTANTS.S; CONSTANTS.Re; CONSTANTS.Isp; CONSTANTS.mu; M1 M2 h1 CLa_table CD0_table = = = = = *CONSTANTS.M1; *CONSTANTS.M2; *CONSTANTS.h1; *CONSTANTS.CLa_table; *CONSTANTS.CD0_table; 280 DMatrix& eta_table DMatrix& T_table = *CONSTANTS.eta_table; = *CONSTANTS.T_table; int lM1 = length(M1); adouble rho; adouble m = w/g0; adouble M; atmosphere_model( &rho, &M, v, h, CONSTANTS); adouble CL_a, CD0, eta, T; spline_interpolation( &CL_a, M, M1, CLa_table, lM1); spline_interpolation( &CD0, M, M1, CD0_table, lM1); spline_interpolation( &eta, M, M1, eta_table, lM1); spline_2d_interpolation(&T, M, h, M2, h1, T_table, workspace); // // // // smooth_linear_interpolation( &CL_a, M, M1, CLa_table, lM1); smooth_linear_interpolation( &CD0, M, M1, CD0_table, lM1); smooth_linear_interpolation( &eta, M, M1, eta_table, lM1); smooth_bilinear_interpolation(&T, M, h, M2, h1, T_table); // // // // linear_interpolation( &CL_a, M, M1, CLa_table, lM1); linear_interpolation( &CD0, M, M1, CD0_table, lM1); linear_interpolation( &eta, M, M1, eta_table, lM1); bilinear_interpolation(&T, M, h, M2, h1, T_table); adouble CL = CL_a*alpha; adouble CD = CD0 + eta*CL_a*alpha*alpha; adouble D = 0.5*CD*S*rho*v*v; adouble L = 0.5*CL*S*rho*v*v; adouble adouble adouble adouble hdot vdot gammadot wdot derivatives[ derivatives[ derivatives[ derivatives[ = = = = v*sin(gamma); 1.0/m*(T*cos(alpha)-D) - mu/pow(Re+h,2.0)*sin(gamma); (1.0/(m*v))*(T*sin(alpha)+L) + cos(gamma)*(v/(Re+h)-mu/(v*pow(Re+h,2.0))); -T/Isp; CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = hdot; vdot; gammadot; wdot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble h0 v0 gamma0 w0 = = = = initial_states[CINDEX(1)]; initial_states[CINDEX(2)]; initial_states[CINDEX(3)]; initial_states[CINDEX(4)]; adouble hf = final_states[CINDEX(1)]; adouble vf = final_states[CINDEX(2)]; adouble gammaf = final_states[CINDEX(3)]; e[ e[ e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ] ] ] ] ] ] ] = = = = = = = h0; v0; gamma0; w0; hf; vf; gammaf; } 281 /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Minimum time to climb for a supersonic aircraft"; problem.outfilename = "climb.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath = = = = 4; 1; 7; 0; problem.phases(1).nodes = "[30,60]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare an instance of Constants structure ///////////// //////////////////////////////////////////////////////////////////////////// Constants_ CONSTANTS; problem.user_data = (void*) &CONSTANTS; //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Initialize CONSTANTS and ////////////////////////////// /////////////////// declare local variables ////////////////////////////// //////////////////////////////////////////////////////////////////////////// CONSTANTS.g0 CONSTANTS.S CONSTANTS.Re = 32.174; // ft/s^2 = 530.0; // ft^2 = 20902900.0; // ft 282 CONSTANTS.Isp CONSTANTS.mu = 1600.00; //s = 0.14076539E17; // ft^3/s^2 DMatrix M1(1,9, 0.E0, .4E0, .8E0, .9E0, 1.E0, 1.2E0, 1.4E0, 1.6E0, 1.8E0); DMatrix M2(1,10, 0.E0, .2E0, .4E0, .6E0, .8E0, 1.E0, 1.2E0, 1.4E0, 1.6E0, 1.8E0); DMatrix h1(1,10, 0.E0, 5E3, 10.E3, 15.E3, 20.E3, 25.E3, 30.E3, 40.E3, 50.E3, 70.E3); DMatrix CLa_table(1,9, 3.44E0, 3.44E0, 3.44E0, 3.58E0, 4.44E0, 3.44E0, 3.01E0, 2.86E0, 2.44E0); DMatrix CD0_table(1,9, .013E0, .013E0, .013E0, .014E0, .031E0, 0.041E0, .039E0, .036E0, .035E0); DMatrix eta_table(1,9, .54E0, .54E0, .54E0, .75E0, .79E0, .78E0, .89E0, .93E0, .93E0); DMatrix 24200., 28000., 28300., 30800., 34500., 37900., 36100., 34300., 32500., 30700., T_table(10,10, 24000., 20300., 24600., 21100., 25200., 21900., 27200., 23800., 30300., 26600., 34300., 30400., 38000., 34900., 36600., 38500., 35200., 42100., 33800., 45700., 17300.,14500.,12200.,10200.,5700.,3400.,100., 18100.,15200.,12800.,10700.,6500.,3900.,200., 18700.,15900.,13400.,11200.,7300.,4400.,400., 20500.,17300.,14700.,12300.,8100.,4900.,800., 23200.,19800.,16800.,14100.,9400.,5600.,1100., 26800.,23300.,19800.,16800.,11200.,6800.,1400., 31300.,27300.,23600.,20100.,13400.,8300.,1700., 36100.,31600.,28100.,24200.,16200.,10000.,2200., 38700.,35700.,32000.,28100.,19300.,11900.,2900., 41300.,39800.,34600.,31100.,21700.,13300.,3100. ); DMatrix htab(1,8, 0.0, 11.0, 20.0, 32.0, 47.0, 51.0, 71.0, 84.852); DMatrix ttab(1,8, 288.15, 216.65, 216.65, 228.65, 270.65, 270.65, 214.65, 186.946); DMatrix ptab(1,8, 1.0, 2.233611E-1, 5.403295E-2, 8.5666784E-3, 1.0945601E-3, 6.6063531E-4, 3.9046834E-5, 3.68501E-6); DMatrix gtab(1,8, -6.5, 0.0, 1.0, 2.8, 0.0, -2.8, -2.0, 0.0); M1.Print("M1"); M2.Print("M2"); h1.Print("h1"); CLa_table.Print("CLa_table"); CD0_table.Print("CD0_table"); eta_table.Print("eta_table"); T_table.Print("T_table"); CONSTANTS.M1 CONSTANTS.M2 CONSTANTS.h1 CONSTANTS.CLa_table CONSTANTS.CD0_table CONSTANTS.eta_table CONSTANTS.T_table CONSTANTS.htab CONSTANTS.ttab CONSTANTS.ptab CONSTANTS.gtab = = = = = = = = = = = = = = = = = = &M1; &M2; &h1; &CLa_table; &CD0_table; &eta_table; &T_table; &htab; &ttab; &ptab; >ab; double double double double double double double h0 hf v0 vf gamma0 gammaf w0 0.0; 65600.0; 424.26; 968.148; 0.0; 0.0; 42000.0; double double double double double hmin = 0; hmax = 69000.0; vmin = 1.0; vmax = 2000.0; gammamin = -89.0*pi/180.0; // -89.0*pi/180.0; 283 double double double double double gammamax wmin wmax alphamin alphamax double double double double t0min t0max tfmin tfmax = = = = = 89.0*pi/180.0; // = 0.0; = 45000.0; = -20.0*pi/180.0; = 20.0*pi/180.0; 89.0*pi/180.0; 0.0; 0.0; 200.0; 500.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// int iphase = 0; problem.phase[iphase].bounds.lower.StartTime problem.phase[iphase].bounds.upper.StartTime = t0min; = t0max; problem.phase[iphase].bounds.lower.EndTime problem.phase[iphase].bounds.upper.EndTime = tfmin; = tfmax; problem.phase[iphase].bounds.lower.states(1) problem.phase[iphase].bounds.upper.states(1) problem.phase[iphase].bounds.lower.states(2) problem.phase[iphase].bounds.upper.states(2) problem.phase[iphase].bounds.lower.states(3) problem.phase[iphase].bounds.upper.states(3) problem.phase[iphase].bounds.lower.states(4) problem.phase[iphase].bounds.upper.states(4) = = = = = = = = hmin; hmax; vmin; vmax; gammamin; gammamax; wmin; wmax; problem.phase[iphase].bounds.lower.controls(1) = alphamin; problem.phase[iphase].bounds.upper.controls(1) = alphamax; // The following bounds fix the initial and final state conditions problem.phase[iphase].bounds.lower.events(1) problem.phase[iphase].bounds.upper.events(1) problem.phase[iphase].bounds.lower.events(2) problem.phase[iphase].bounds.upper.events(2) problem.phase[iphase].bounds.lower.events(3) problem.phase[iphase].bounds.upper.events(3) problem.phase[iphase].bounds.lower.events(4) problem.phase[iphase].bounds.upper.events(4) problem.phase[iphase].bounds.lower.events(5) problem.phase[iphase].bounds.upper.events(5) problem.phase[iphase].bounds.lower.events(6) problem.phase[iphase].bounds.upper.events(6) problem.phase[iphase].bounds.lower.events(7) problem.phase[iphase].bounds.upper.events(7) = = = = = = = = = = = = = = h0; h0; v0; v0; gamma0; gamma0; w0; w0; hf; hf; vf; vf; gammaf; gammaf; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes = problem.phases(1).nodes(1); DMatrix stateGuess(4,nnodes); stateGuess(1, stateGuess(2, stateGuess(3, stateGuess(4, colon()) colon()) colon()) colon()) = = = = linspace(h0,hf,nnodes); linspace(v0,vf,nnodes); linspace(gamma0,gammaf,nnodes); linspace(w0,0.8*w0,nnodes); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,nnodes); = stateGuess; = linspace(t0min, tfmax, nnodes); //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; 284 problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.mesh_refinement algorithm.mr_max_iterations algorithm.defect_scaling = "IPOPT"; = "automatic"; = "numerical"; = "trapezoidal"; = 1000; = 1.e-6; = "automatic"; = 4; = "jacobian-based"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t H = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); = solution.get_dual_hamiltonian_in_phase(1); DMatrix DMatrix DMatrix DMatrix h v gamma w = = = = x(1,colon()); x(2,colon()); x(3,colon()); x(4,colon()); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,h/1000.0,problem.name + ": altitude", "time (s)", "altitude (x1,000 ft)", "h"); plot(t,v/100.0,problem.name + ": velocity", "time (s)", "velocity (x100 ft/s)", "v"); plot(t,gamma*180/pi,problem.name + ": flight path angle", "time (s)", "gamma (deg)", "gamma"); plot(t,w/10000.0,problem.name + ": weight", "time (s)", "w (x10,000 lb)", "w"); plot(t,u*180/pi,problem.name + ": angle of attack", "time (s)", "alpha (deg)", "alpha"); plot(t,h/1000.0,problem.name + ": altitude", "time (s)", "altitude (x1,000 ft", "h", "pdf","climb_altitude.pdf"); plot(t,v/100.0,problem.name + ": velocity", "time (s)", "velocity (x100 ft/s)", "v", "pdf","climb_velocity.pdf"); plot(t,gamma*180/pi,problem.name + ": flight path angle", "time (s)", "gamma (deg)", "gamma", "pdf","climb_fpa.pdf"); plot(t,w/10000.0,problem.name + ": weight", "time (s)", "w (x10,000 lb)", "w", "pdf", "weight.pdf"); plot(t,u*180/pi,problem.name + ": angle of attack", "time (s)", "alpha (deg)", "alpha", "pdf", "alpha.pdf"); } void atmosphere(adouble* alt,adouble* sigma,adouble* delta,adouble* theta, Constants_& CONSTANTS) // US Standard Atmosphere Model 1976 // Adopted from original Fortran 90 code by Ralph Carmichael 285 // Fortran code located at: http://www.pdas.com/programs/atmos.f90 { /*! ------------------------------------------------------------------------! PURPOSE - Compute the properties of the 1976 standard atmosphere to 86 km. ! AUTHOR - Ralph Carmichael, Public Domain Aeronautical Software ! NOTE - If alt > 86, the values returned will not be correct, but they will ! not be too far removed from the correct values for density. ! The reference document does not use the terms pressure and temperature ! above 86 km. IMPLICIT NONE !============================================================================ ! A R G U M E N T S | !============================================================================ alt ! geometric altitude, km. sigma ! density/sea-level standard density delta ! pressure/sea-level standard pressure theta ! temperature/sea-level standard temperature */ /*!============================================================================ ! L O C A L C O N S T A N T S | !============================================================================ */ double REARTH = 6369.0; // radius of the Earth (km) double GMR = 34.163195; // hydrostatic constant int NTAB=8; // number of entries in the defining tables /*!============================================================================ ! L O C A L V A R I A B L E S | !============================================================================ */ int i,j,k; // counters adouble h; // geopotential altitude (km) adouble tgrad, tbase; // temperature gradient and base temp of this layer adouble tlocal; // local temperature adouble deltah; // height above base of this layer /*!============================================================================ ! L O C A L A R R A Y S ( 1 9 7 6 S T D. A T M O S P H E R E ) | !============================================================================ */ DMatrix& DMatrix& DMatrix& DMatrix& htab ttab ptab gtab = = = = *CONSTANTS.htab; *CONSTANTS.ttab; *CONSTANTS.ptab; *CONSTANTS.gtab; //!---------------------------------------------------------------------------h=(*alt)*REARTH/((*alt)+REARTH); //convert geometric to geopotential altitude i=1; j=NTAB; while (j<=i+1) { k=(i+j)/2; if (h < htab(k)) { j=k; } else { i=k; } } tgrad=gtab(i); tbase=ttab(i); deltah=h-htab(i); tlocal=tbase+tgrad*deltah; *theta=tlocal/ttab(1); if (tgrad == 0.0) { *delta=ptab(i)*exp(-GMR*deltah/tbase); } else { *delta=ptab(i)*pow(tbase/tlocal, GMR/tgrad); } // setting up for binary search // integer division // i will be in 1...NTAB-1 // temperature ratio // pressure ratio *sigma=(*delta)/(*theta); return; // density ratio } void atmosphere_model(adouble* rho, adouble* M, adouble v, adouble h, Constants_& CONSTANTS) { double feet2meter = 0.3048; double kgperm3_to_slug_per_feet3 = 0.062427960841/32.174049; adouble alt, sigma, delta, theta; 286 Iter Method Nodes NV NC OE CE JE HE RHS max CPU(sec) 1 2 3 - LGL-ST LGL-ST LGL-ST - 80 90 100 - 322 362 402 - 247 277 307 - 747 70 28 845 746 71 29 846 89 55 28 172 0 0 0 0 59680 6390 2900 68970 1.752e-03 1.706e-03 7.940e-04 - 8.020e+00 6.890e+00 4.810e+00 1.972e+01 Key: Iter=iteration number, NV=number of variables, NC=number of constraints, OE=objective evalu- ations, CE = constraint evaluations, JE = Jacobian evaluations, HE = Hessian evaluations, RHS = ODE right hand side evaluations, max = maximum relative ODE error, CPU(sec) = CPU time in seconds spent by nonlinear programming algorithm Table 3.4: Mesh refinement statistics: Minimum time to climb for a supersonic aircraft alt = h.value()*feet2meter/1000.0; // Call the standard atmosphere model 1976 atmosphere(&alt, &sigma, &delta, &theta, CONSTANTS); adouble rho1 = 1.22521 * sigma; // Multiply by standard density at zero altitude and 15 deg C. rho1 = rho1*kgperm3_to_slug_per_feet3; *rho = rho1; adouble T; adouble mach; double TempStandardSeaLevel = 288.15; // in K, or 15 deg C. T = theta*TempStandardSeaLevel; adouble a = 20.0468 * sqrt(T); // Speed of sound in m/s. a = a/feet2meter; // Speed of sound in ft/s mach = v/a; *M = mach; return; } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the box below and Figures 3.68, to 3.72. The results can be compared with those presented in [4]. Table ?? shows the mesh refinement history for this problem. PSOPT results summary ===================== Problem: Minimum time to climb for a supersonic aircraft CPU time (seconds): 3.563276e+02 NLP solver used: IPOPT PSOPT release number: 4.0 287 Minimum time to climb for a supersonic aircraft: altitude h 60 altitude (x1,000 ft 50 40 30 20 10 0 0 50 100 150 200 250 300 350 time (s) Figure 3.68: Altitude for minimum time to climb problem Date and time of this run: Thu Feb 21 15:51:19 2019 Optimal (unscaled) cost function value: 3.188143e+02 Phase 1 endpoint cost function value: 3.188143e+02 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 3.188143e+02 Phase 1 maximum relative local error: 1.665362e-03 NLP solver reports: The problem has been solved! 3.28 Missile terminal burn maneouvre This example illustrates the design of a missile trajectory to strike a specified target from given initial conditions in minimum time [39]. Figure 3.28 shows the variables associated with the dynamic model of the missile employed in this example, where γ is the flight path angle, α is the angle of attack, V is the missile speed, x is the longitudinal position, h is the altitude, D is the axial aerodynamic force, L is the normal aerodynamic force, and T is the thrust. The equations of motion of the missile are given by: 288 Minimum time to climb for a supersonic aircraft: velocity v 16 velocity (x100 ft/s) 14 12 10 8 6 0 50 100 150 200 250 300 350 time (s) Figure 3.69: Velocity for minimum time to climb problem Minimum time to climb for a supersonic aircraft: flight path angle gamma 35 30 25 gamma (deg) 20 15 10 5 0 -5 0 50 100 150 200 250 300 350 time (s) Figure 3.70: Flight path angle for minimum time to climb problem 289 Minimum time to climb for a supersonic aircraft: weight w 4.3 4.2 w (x10,000 lb) 4.1 4 3.9 3.8 3.7 3.6 0 50 100 150 200 250 300 350 time (s) Figure 3.71: Weight for minimum time to climb problem Minimum time to climb for a supersonic aircraft: angle of attack 6 alpha 4 alpha (deg) 2 0 -2 -4 -6 0 50 100 150 200 250 300 350 time (s) Figure 3.72: Angle of attack (α) for minimum time to climb problem 290 L normal aerodynamic force angle of attack T thrust V speed 90º D axial aerodynamic force 90º flight path angle x mg weight Figure 3.73: Ilustration of the variables associated with the missile model 291 T −D L g cos γ sin α + cos α − mg mV V L T −D cos α − sin α − g cos γ V̇ = m m ẋ = V cos γ γ̇ = ḣ = V sin γ where 1 D = Cd ρV 2 Sref 2 Cd = A1 α2 + A2 α + A3 1 L = Cl ρV 2 Sref 2 Cl = B 1 α + B 2 ρ = C 1 h2 + C 2 h + C 3 where all the model parameters are given in Table 3.5. The initial conditions for the state variables are: γ(0) = 0 V (0) = 272m/s x(0) = 0m h(0) = 30m The terminal conditions on the states are: γ(tf ) = −π/2 V (tf ) = 310m/s x(tf ) = 10000m h(tf ) = 0m The problem constraints are given by: 200 ≤V ≤ 310 1000 ≤T ≤ 6000 −0.3 ≤α ≤ 0.3 L ≤4 −4 ≤ mg h ≥ 30 (for x ≤ 7500m) h ≥ 0 (for x > 7500m) 292 Table 3.5: Parameters values of the missile model Parameter m g Sref A1 A2 A3 B1 B2 C1 C2 C3 Value 1005 9.81 0.3376 -1.9431 -0.1499 0.2359 21.9 0 3.312 × 10−9 −1.142 × 10−4 1.224 Units kg m/s2 m2 kg/m5 kg/m4 kg/m3 Note that the path constraints on the altitude are non-smooth. Given that non-smoothness causes problems with nonlinear programming, the constraints on the altitude were approximated by a single smooth constraint: H (x − 7500))h(t) + [1 − H (x − 7500)][h(t) − 30] ≥ 0 where H (z) is a smooth version of the Heaviside function, which is computed as follows: H (z) = 0.5(1 + tanh(z/)) where > 0 is a small number. The problem is solved by using automatic mesh refinement starting with 50 nodes. The final solution, which is found after six mesh refinement iterations, has 85 nodes. Figure 3.74 shows the missile altitude as a function of the longitudinal position. Figures 3.75 and 3.76 show, respectively, the missile speed and angle of attack as functions of time. The output from PSOPT is summarised in the box below. PSOPT results summary ===================== Problem: Missile problem CPU time (seconds): 4.394547e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:16:57 2019 293 Optimal (unscaled) cost function value: 4.091759e+01 Phase 1 endpoint cost function value: 4.091759e+01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 4.091759e+01 Phase 1 maximum relative local error: 4.925376e-04 NLP solver reports: The problem has been solved! Missile problem: altitude (m) h 1800 1600 1400 h (m) 1200 1000 800 600 400 200 0 0 2000 4000 6000 8000 10000 x (m) Figure 3.74: Missile altitude and a function of the longitudinal position 3.29 Moon lander problem Consider the following optimal control problem, which is known in the literature as the moon lander problem [36]. Find tf and T (t) ∈ [0, tf ] to minimize the cost functional Z tf J= T (t)dt (3.120) 0 subject to the dynamic constraints ḣ = v v̇ = −g + T /m ṁ = −T /E 294 (3.121) Missile problem: speed (m/s) V 320 310 V (m/s) 300 290 280 270 260 0 5 10 15 20 25 30 35 40 45 time (s) Figure 3.75: Missile speed as a function of time Missile problem: angle of attack (rad) 0.05 alpha alpha (rad) 0 -0.05 -0.1 -0.15 0 5 10 15 20 25 30 35 40 45 time (s) Figure 3.76: Missile angle of attack as a function of time 295 the boundary conditions: h(0) v(0) m(0) h(tf ) v(tf ) = = = = = 1 −0.783 1 0.0 0.0 (3.122) and the bounds 0 ≤ T (t) ≤ 1.227 −20 ≤ h(t) ≤ 20 −20 ≤ v(t) ≤ 20 0.01 ≤ m(t) ≤1 0 ≤ tf (3.123) ≤ 1000 where g = 1.0, and E = 2.349. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// moonlander.cxx ///////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Moonlander problem //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: PROPT Handbook //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble thrust = controls[0]; return thrust; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, 296 adouble* xad, int iphase, Workspace* workspace) { adouble altitude_dot, speed_dot, mass_dot; adouble altitude adouble speed adouble mass = states[0]; = states[1]; = states[2]; adouble thrust = controls[0]; double exhaust_velocity = 2.349; double gravity = 1.0; altitude_dot = speed; speed_dot = -gravity + thrust/mass; mass_dot = -thrust/exhaust_velocity; derivatives[0] = altitude_dot; derivatives[1] = speed_dot; derivatives[2] = mass_dot; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble altitude_i = initial_states[0]; adouble speed_i = initial_states[1]; adouble mass_i = initial_states[2]; adouble altitude_f = final_states[0]; adouble speed_f = final_states[1]; e[0] e[1] e[2] e[3] e[4] = = = = = altitude_i; speed_i; mass_i; altitude_f; speed_f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Moon Lander Problem"; = "moon.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); 297 ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 3; 1; 5; 0; = 70; psopt_level2_setup(problem, algorithm); int nodes = 70; //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double altitudeL = -20.0; speedL = -20.0; massL = 0.01; altitudeU = 20.0; speedU = 20.0; massU = 1.0; double thrustL double thrustU = 0.0; = 1.227; double altitude_i double speed_i double mass_i = 1.0; = -0.783; = 1.0; double altitude_f double speed_f = 0.0; = 0.0; problem.phases(1).bounds.lower.states(1) = altitudeL; problem.phases(1).bounds.lower.states(2) = speedL; problem.phases(1).bounds.lower.states(3) = massL; problem.phases(1).bounds.upper.states(1) = altitudeU; problem.phases(1).bounds.upper.states(2) = speedU; problem.phases(1).bounds.upper.states(3) = massU; problem.phases(1).bounds.lower.controls(1) = thrustL; problem.phases(1).bounds.upper.controls(1) = thrustU; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) = = = = = altitude_i; speed_i; mass_i; altitude_f; speed_f; problem.phases(1).bounds.upper.events = problem.phases(1).bounds.lower.events; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 0.0; = 1000.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// 298 /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix states_guess(3,nodes+1); states_guess(1,colon()) = linspace(altitude_i, altitude_f, nodes+1); states_guess(2,colon()) = linspace(speed_i, speed_f, nodes+1); states_guess(3,colon()) = linspace(mass_i, massL, nodes+1); problem.phases(1).guess.controls = 0.5*(thrustL+thrustU)*ones(1,nodes+1); problem.phases(1).guess.states = states_guess; problem.phases(1).guess.time = linspace(0.0, 1.5, nodes+1); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max = 1000; algorithm.nlp_tolerance = 1.e-6; algorithm.nlp_method = "IPOPT"; algorithm.scaling = "automatic"; algorithm.derivatives = "automatic"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); lambda = solution.get_dual_costates_in_phase(1); H = solution.get_dual_hamiltonian_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); lambda.Save("lambda.dat"); H.Save("H.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "altitude speed mass"); plot(t,u,problem.name, "time (s)", "control", "thrust"); plot(t,x,problem.name, "time (s)", "states", "altitude speed mass", "pdf", "moon_states.pdf"); plot(t,u,problem.name, "time (s)", "control", "thrust", "pdf", "moon_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.77 and 3.78, which contain the elements of the state and the control, respectively. 299 Moon Lander Problem altitude speed mass 1 states 0.5 0 -0.5 -1 0 0.2 0.4 0.6 0.8 1 1.2 1.4 time (s) Figure 3.77: States for moon lander problem PSOPT results summary ===================== Problem: Moon Lander Problem CPU time (seconds): 5.159037e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:54:38 2019 Optimal (unscaled) cost function value: 1.420378e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 1.420378e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.396972e+00 Phase 1 maximum relative local error: 5.386719e-05 NLP solver reports: The problem has been solved! 3.30 Multi-segment problem Consider the following optimal control problem, where the optimal control has a characteristic stepped shape [22]. Find u(t) ∈ [0, 3] to minimize the 300 Moon Lander Problem thrust 1.2 1 control 0.8 0.6 0.4 0.2 0 0 0.2 0.4 0.6 0.8 1 1.2 1.4 time (s) Figure 3.78: Control for moon lander problem cost functional Z 3 J= x(t)dt (3.124) 0 subject to the dynamic constraints ẋ = u (3.125) x(0) = 1 x(3) = 1 (3.126) the boundary conditions: and the bounds −1 ≤ u(t) ≤ 1 x(t) ≥ 0 The analytical optimal control is given by: −1, t ∈ [0, 1) 0, t ∈ [1, 2] u(t) = 1, t ∈ (2, 3] (3.127) (3.128) The problem has been solved using the multi-segment paradigm. Three (1) segments are defined in the code, such that the initial time is fixed at t0 = 0, (3) the final time is fixed at tf = 3, and the intermediate junction times are (1) (2) tf = 1, and tf = 2. The PSOPT code that solves this problem is shown below. 301 ////////////////////////////////////////////////////////////////////////// //////////////// steps.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Steps problem //////////////// //////// Last modified: 12 July 2009 //////////////// //////// Reference: Gong, Farhoo, and Ross (2008) //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x = states[ CINDEX(1) ]; return (x); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble u = controls[CINDEX(1)]; derivatives[ CINDEX(1) ] = u; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x1_i = initial_states[ CINDEX(1) ]; adouble x1_f = final_states[ CINDEX(1) ]; if ( iphase==1 ) { e[ CINDEX(1) ] = x1_i; } else if ( iphase==3 ) { 302 e[ CINDEX(1) ] = x1_f; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; MSdata msdata; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Steps problem"; = "steps.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// msdata.nsegments msdata.nstates msdata.ncontrols msdata.nparameters msdata.npath msdata.ninitial_events msdata.nfinal_events msdata.nodes = = = = = = = = 3; 1; 1; 0; 0; 1; 1; 20; // nodes per segment multi_segment_setup(problem, algorithm, msdata ); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.lower.states(1) = problem.phases(1).bounds.upper.states(1) = problem.phases(1).bounds.lower.events(1) = problem.phases(3).bounds.lower.events(1) = = -1.0; = 1.0; 0.0; 5.0; 1.0; 1.0; problem.phases(1).bounds.upper.events=problem.phases(1).bounds.lower.events; problem.phases(3).bounds.upper.events=problem.phases(3).bounds.lower.events; // // problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(3).bounds.lower.EndTime problem.phases(3).bounds.upper.EndTime = 3.0; = 3.0; problem.bounds.lower.times = "[0.0, 1.0, 2.0, 3.0]"; problem.bounds.upper.times = "[0.0, 1.0, 2.0, 3.0]"; 303 problem.bounds.lower.times = "[0.0, 1.0, 2.0, 3.0]"; problem.bounds.upper.times = "[0.0, 1.0, 2.0, 3.0]"; auto_phase_bounds(problem); //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates DMatrix DMatrix DMatrix DMatrix = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; state_guess control_guess time_guess param_guess; = = = zeros(nstates,nnodes); zeros(ncontrols,nnodes); linspace(0.0,3.0,nnodes); state_guess(1,colon()) = linspace(1.0, 1.0, nnodes); control_guess(1,colon()) = zeros(1,nnodes); auto_phase_guess(problem, control_guess, state_guess, param_guess, time_guess); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.hessian algorithm.mesh_refinement algorithm.ode_tolerance = = = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; "exact"; "automatic"; 1.e-5; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t, xi, ui, ti; x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); for(int i=2;i<=problem.nphases;i++) { xi = solution.get_states_in_phase(i); ui = solution.get_controls_in_phase(i); ti = solution.get_time_in_phase(i); x = x || xi; u = u || ui; t = t || ti; 304 } //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x(1,colon()),problem.name+": state","time (s)", "x", "x"); plot(t,u(1,colon()),problem.name+": control","time (s)", "u", "u"); plot(t,x(1,colon()),problem.name+": state","time (s)", "x", "x", "pdf", "steps_state.pdf"); plot(t,u(1,colon()),problem.name+": control","time (s)", "u", "u", "pdf", "steps_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.79 and 3.80, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Steps problem CPU time (seconds): 8.536780e-01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:24:35 2019 Optimal Phase 1 Phase 1 Phase 1 Phase 1 Phase 1 Phase 2 Phase 2 Phase 2 Phase 2 (unscaled) cost function value: 1.000010e+00 endpoint cost function value: 0.000000e+00 integrated part of the cost: 5.000034e-01 initial time: 0.000000e+00 final time: 1.000000e+00 maximum relative local error: 6.960834e-07 endpoint cost function value: 0.000000e+00 integrated part of the cost: 3.401579e-06 initial time: 1.000000e+00 final time: 2.000000e+00 305 Steps problem: state 1 x 0.9 0.8 0.7 x 0.6 0.5 0.4 0.3 0.2 0.1 0 0 0.5 1 1.5 2 2.5 3 time (s) Figure 3.79: State trajectory for the multi-segment problem Phase 2 maximum relative local error: 4.856300e-06 Phase 3 endpoint cost function value: 0.000000e+00 Phase 3 integrated part of the cost: 5.000034e-01 Phase 3 initial time: 2.000000e+00 Phase 3 final time: 3.000000e+00 Phase 3 maximum relative local error: 6.961454e-07 NLP solver reports: The problem has been solved! 3.31 Notorious parameter estimation problem Consider the following parameter estimation problem, which is known to be challenging to single-shooting methods because of internal instability of the differential equations [37]. Find p ∈ < to minimize J= 200 X (y1 (ti ) − ỹ1 (i))2 + (y2 (ti ) − ỹ2 (i))2 (3.129) i=1 subject to the dynamic constraints ẏ1 = y2 ẏ2 = µ2 y1 − (µ2 + p2 ) sin(pt) 306 (3.130) Steps problem: control u 1 u 0.5 0 -0.5 -1 0 0.5 1 1.5 2 2.5 3 time (s) Figure 3.80: Control trajectory for the multi-segment problem where µ = 60.0, y1 (0) = 0, y2 (0) = π. The parameter estimation facilities of PSOPT are used in this example. In this case, the observations function is: g(x(θk ), u(θk ), p, θk ) = [y1 (θk ) y2 (θk )]T The PSOPT code that solves this problem is shown below. The code includes the generation of the measurement vectors ỹ1 , and ỹ2 by adding Gaussian noise with standard deviation 0.05 to the exact solution of the problem with p = π, which is given by: y1 (t) = sin(πt) y2 (t) = π cos(πt) The code also defines the vector of sampling instants θi , i = 1, . . . , 200 as a uniform random samples in the interval [0, 1]. ////////////////////////////////////////////////////////////////////////// ////////////////// notorious.cxx /////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Bock’s notorious parameter estimation problem /////////// //////// Last modified: 08 April 2011 //////////////// //////// Reference: Schittkowskit (2002) //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// 307 ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the observation function ////////// ////////////////////////////////////////////////////////////////////////// void observation_function( adouble* adouble* adouble* adouble* observations, states, adouble* controls, parameters, adouble& time, int k, xad, int iphase, Workspace* workspace) { observations[ CINDEX(1) ] = states[ CINDEX(1) ]; observations[ CINDEX(2) ] = states[ CINDEX(2) ]; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[ CINDEX(1) ]; adouble x2 = states[ CINDEX(2) ]; adouble p adouble t = parameters[ CINDEX(1) ]; = time; double mu = 60.0; derivatives[CINDEX(1)] = derivatives[CINDEX(2)] = x2; mu*mu*x1 - (mu*mu + p*p)*sin(p*t); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { e[ 0 ] = e[ 1 ] = initial_states[0]; initial_states[1]; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { int nobs =200; // Generate true solution at sampling points and add noise 308 double sigma = 0.05; DMatrix theta, y1m, y2m, ym; theta = randu(1,nobs); sort(theta); y1m = sin( pi* theta ) + sigma*randn(1,nobs); y2m = pi*cos( pi*theta ) + sigma*randn(1,nobs); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Bocks notorious parameter estimation problem"; = "notorious.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = problem.phases(1).ncontrols = problem.phases(1).nevents = problem.phases(1).npath = problem.phases(1).nparameters problem.phases(1).nodes problem.phases(1).nobserved problem.phases(1).nsamples 2; 0; 2; 0; = 1; = "[80]"; = 2; = nobs; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// //////////// Enter estimation information //////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).observation_nodes problem.phases(1).observations = (theta); = (y1m && y2m); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = problem.phases(1).bounds.lower.states(2) = -10.0; -100.0; problem.phases(1).bounds.upper.states(1) = problem.phases(1).bounds.upper.states(2) = 10.0; 100.0; problem.phases(1).bounds.lower.parameters(1) = problem.phases(1).bounds.upper.parameters(1) = problem.phases(1).bounds.lower.events(1) = -10.0; 10.0; 0.0; 309 problem.phases(1).bounds.upper.events(1) = 0.0; problem.phases(1).bounds.lower.events(2) = problem.phases(1).bounds.upper.events(2) = pi; pi; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; problem.observation_function = & observation_function; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes = problem.phases(1).nodes(1); DMatrix state_guess; state_guess = zeros(2,nnodes); problem.phases(1).guess.states problem.phases(1).guess.time problem.phases(1).guess.parameters = state_guess; = linspace(0.0, 1.0, nnodes); = 0.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// // // algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.mesh_refinement algorithm.ode_tolerance = = = = = = "IPOPT"; "automatic"; "automatic"; "trapezoidal"; 200; 1.e-4; = "automatic"; = 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix states, x1, x2, p, t; //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// states t p x1 x2 = = = = = solution.get_states_in_phase(1); solution.get_time_in_phase(1); solution.get_parameters_in_phase(1); states(1,colon()); states(2,colon()); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// states.Save("states.dat"); t.Save("t.dat"); p.Print("Estimated parameter"); 310 Abs(p-pi).Print("Parameter error"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(theta,ym(1,colon()),t,x1,problem.name, "time (s)", "observed x1", "x1m x1"); plot(theta,ym(2,colon()),t,x2,problem.name, "time (s)", "observed x2", "x2m x2"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarized in the box below. The optimal parameter found was p = 3.141180, which is an approximation of π with an error of the order of 10−4 . The 95% confidence interval of the estimated parameter is [3.132363, 3.149998]. PSOPT results summary ===================== Problem: Bocks notorious parameter estimation problem CPU time (seconds): 1.848284e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:06:04 2019 Optimal (unscaled) cost function value: 9.310255e-01 Phase 1 endpoint cost function value: 9.310255e-01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 5.098817e-04 NLP solver reports: The problem has been solved! 3.32 Predator-prey parameter estimation problem This is a well knowon model that describes the behaviour of predator and prey species of an ecological system. The Letka-Volterra model system consist of two differential equations [37]. The dynamic equations are given by: ẋ1 = −p1 x1 + p2 x1 x2 ẋ2 = p3 x2 − p4 x1 x2 311 (3.131) with boundary condition: x1 (0) = 0.4 x2 (0) = 1 The observation functions are: g1 = x1 g2 = x2 (3.132) The measured data, with consists of ns = 10 samples over the interval t ∈ [0, 10], was constructed from simulations with parameter values [p1 , p2 , p3 , p4 ] = [1, 1, 1, 1] with added noise. The weights of both observations are the same and equal to one. The solution is found using local discretisation (trapezoidal, HermiteSimpson) and automatic mesh refinement, starting with 20 grid points with ODE tolerance 10−4 . The code that solves the problem is shown below. The estimated parameter values and their 95% confidence limits are shown in Table 3.32. Figure 3.81 shows the observations as well as the estimated values of variables x1 and x2 . The mesh statistics can be seen in Table 3.7 ////////////////////////////////////////////////////////////////////////// ////////////////// predator.cxx /////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Lotka-Volterra model parameter estimation //// ////////// //////// Last modified: 03 Jan 2014 //////////////// //////// Reference: Schittkowski (2002) //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2014 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the observation function ////////// ////////////////////////////////////////////////////////////////////////// void observation_function( adouble* adouble* adouble* adouble* observations, states, adouble* controls, parameters, adouble& time, int k, xad, int iphase, Workspace* workspace) { int i; for (i=0; i<2; i++) { observations[ i ] = states[ i ]; } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) 312 { // Variables adouble x1, x2, p1, p2, p3, p4; // Differential states x1 = states[CINDEX(1)]; x2 = states[CINDEX(2)]; // Parameters p1 = parameters[CINDEX(1)]; p2 = parameters[CINDEX(2)]; p3 = parameters[CINDEX(3)]; p4 = parameters[CINDEX(4)]; derivatives[CINDEX(1)] = -p1*x1 + p2*x1*x2; derivatives[CINDEX(2)] = p3*x2 - p4*x1*x2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { int i; for (i=0; i<2; i++) { e[i] = initial_states[i]; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = = "Predator-prey example"; "predator.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); 313 ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = problem.phases(1).ncontrols = problem.phases(1).nevents = problem.phases(1).npath = problem.phases(1).nparameters problem.phases(1).nodes problem.phases(1).nobserved problem.phases(1).nsamples 2; 0; 2; 0; = 4; = 20; = 2; = 10; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// //////////// Load data for parameter estimation //////////// //////////////////////////////////////////////////////////////////////////// int iphase = 1; load_parameter_estimation_data(problem, iphase, "predator.dat"); problem.phases(1).observation_nodes.Print("observation nodes"); problem.phases(1).observations.Print("observations"); problem.phases(1).residual_weights.Print("weights"); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, p, t; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = 0.0; problem.phases(1).bounds.lower.states(2) = 0.0; problem.phases(1).bounds.upper.states(1) = 3.0; problem.phases(1).bounds.upper.states(2) = 3.0; problem.phases(1).bounds.lower.events(1) = 0.4; problem.phases(1).bounds.lower.events(2) = 1.0; problem.phases(1).bounds.upper.events(1) = 0.4; problem.phases(1).bounds.upper.events(2) = 1.0; problem.phases(1).bounds.lower.parameters(1) = -1.0; problem.phases(1).bounds.lower.parameters(2) = -1.0; problem.phases(1).bounds.lower.parameters(3) = -1.0; problem.phases(1).bounds.lower.parameters(4) = -1.0; problem.phases(1).bounds.upper.parameters(1) = 5.0; problem.phases(1).bounds.upper.parameters(2) = 5.0; problem.phases(1).bounds.upper.parameters(3) = 5.0; problem.phases(1).bounds.upper.parameters(4) = 5.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 10.0; = 10.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; problem.observation_function = & observation_function; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// 314 int nnodes = (int) problem.phases(1).nsamples; DMatrix state_guess(3, nnodes); DMatrix param_guess(2,1); state_guess(1,colon()) = linspace(0.4, 0.4, nnodes ); state_guess(2,colon()) = linspace(1.0, 1.0, nnodes ); param_guess(1) = 0.8; param_guess(2) = 0.8; param_guess(3) = 1.5; param_guess(4) = 1.5; problem.phases(1).guess.states problem.phases(1).guess.time problem.phases(1).guess.parameters = state_guess; = linspace(0.0, 10.0, nnodes); = param_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.mesh_refinement algorithm.ode_tolerance = "IPOPT"; = "automatic"; = "automatic"; = "trapezoidal"; = "automatic"; = 1.e-4; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); t = solution.get_time_in_phase(1); p = solution.get_parameters_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); t.Save("t.dat"); p.Print("Estimated parameters"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix tm; DMatrix ym; tm = problem.phases(1).observation_nodes; ym = problem.phases(1).observations; spplot(t,x,tm,ym,problem.name, "time (s)", "state x1", "x1 x2 y1 y2"); spplot(t,x,tm,ym,problem.name, "time (s)", "state x1", "x1 x2 y1 y2", "pdf", "x1x2.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// 315 Table 3.6: Estimated parameter values and 95 percent statistical confidence limits on estimated parameters Parameter Low Confidence Limit Value High Confidence Limit p1 7.166429e-01 9.837490e-01 1.250855e+00 p2 7.573469e-01 9.803930e-01 1.203439e+00 p3 7.287846e-01 1.016900e+00 1.305015e+00 p4 6.914964e-01 1.022702e+00 1.353909e+00 Predator-prey example x1 x2 y1 y2 2 1.8 state x1 1.6 1.4 1.2 1 0.8 0.6 0.4 0 2 4 6 8 10 time (s) Figure 3.81: Observations y1 , y2 and estimated states x1 (t) and x2 (t) //////////////////////////////////////////////////////////////////////////// 3.33 Rayleigh problem with mixed state-control path constraints Consider the following optimal control problem, which involves a path constraint in which the control and the state appear explicitly [4]. Find u(t) ∈ [0, tf ] to minimize the cost functional Z J= tf x1 (t)2 + u(t)2 dt 0 316 (3.133) Table 3.7: Mesh refinement statistics: Predator-prey example Iter DM M NV NC OE CE JE HE RHS max CPUa 1 2 3 4 5 CPUb - TRP TRP H-S H-S H-S - 20 28 39 54 62 - 46 62 84 114 130 - 43 59 81 111 127 - 20 14 9 11 10 64 20 14 9 11 10 64 20 14 9 11 10 64 0 0 0 0 0 0 780 770 1035 1760 1840 6185 1.615e-02 8.919e-03 1.670e-03 1.114e-04 3.985e-05 - 4.000e-02 4.000e-02 4.000e-02 5.000e-02 5.000e-02 4.500e-01 6.700e-01 Key: Iter=iteration number, DM= discretization method, M=number of nodes, NV=number of vari- ables, NC=number of constraints, OE=objective evaluations, CE = constraint evaluations, JE = Jacobian evaluations, HE = Hessian evaluations, RHS = ODE right hand side evaluations, max = maximum relative ODE error, CPUa = CPU time in seconds spent by NLP algorithm, CPUb = additional CPU time in seconds spent by PSOPT subject to the dynamic constraints ẋ1 = x2 ẋ2 = −x1 + x2 (1.4 − px22 ) + 4u sin(θ) The path constraint: u+ x1 ≤0 6 (3.134) (3.135) and the boundary conditions: x1 (0) = −5 x2 (0) = −5 (3.136) where tf = 4.5, and p = 0.14. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Rayleigh problem //////////////// //////// Last modified: 11 July 2011 //////////////// //////// Reference: Betts (2010) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2011 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } 317 ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[CINDEX(1)]; adouble u = controls[CINDEX(1)]; return (u*u + x1*x1); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[CINDEX(1)]; adouble x2 = states[CINDEX(2)]; adouble u = controls[CINDEX(1)]; double p = 0.14; derivatives[ CINDEX(1) ] = x2; derivatives[ CINDEX(2) ] = -x1 + x2*(1.4-p*x2*x2) + 4.0*u; path[ CINDEX(1) ] = u + x1/6.0; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1) ]; adouble x20 = initial_states[ CINDEX(2) ]; e[ CINDEX(1) ] = x10; e[ CINDEX(2) ] = x20; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// 318 problem.name problem.outfilename = "Rayleigh problem"; = "rayleigh.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Declare problem level constants & do level 1 setup /////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 2; 1; 2; 1; = "[80]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H, mu; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) = -10.0; = -10.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) = 10.0; = 10.0; problem.phases(1).bounds.lower.controls(1) = -10.0; problem.phases(1).bounds.upper.controls(1) = 10.0; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) = -5.0; = -5.0; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) = -5.0; = -5.0; problem.phases(1).bounds.lower.path(1) = -100.0; problem.phases(1).bounds.upper.path(1) = 0.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = = = = 0.0; 0.0; 4.5; 4.5; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(2,30); x0(1,colon()) = -5.0*ones(1,30); x0(2,colon()) = -5.0*ones(1, 30); 319 problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,30); = x0; = linspace(0.0, 4.5, 30); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = = "IPOPT"; "automatic"; "automatic"; "Hermite-Simpson"; 1000; 1.e-10; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t lambda mu H = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); = solution.get_dual_costates_in_phase(1); = solution.get_dual_path_in_phase(1); = solution.get_dual_hamiltonian_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "x1 x2" ); plot(t,u,problem.name ,"time (s)", "control", "u" ); plot(t,lambda,problem.name ,"time (s)", "costates", "p1 p2"); plot(t,mu, problem.name,"time (s)", "mu", "mu"); plot(t,x,problem.name, "time (s)", "states", "x1 x2", "pdf", "rayleigh_states.pdf" ); plot(t,u,problem.name ,"time (s)", "control", "u", "pdf", "rayleigh_control.pdf" ); plot(t,lambda,problem.name ,"time (s)", "costates", "l1 l2", "pdf", "rayleigh_costates.pdf"); plot(t,mu, problem.name,"time (s)", "mu", "mu", "pdf", "rayleigh_mu.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.82, 3.83, 3.85, 3.85, 3.85, which show, respectively, the trajectories of the states, control, costates and path constraint multiplier. The results are comparable to those presented by [4]. PSOPT results summary 320 Rayleigh problem x1 x2 4 2 states 0 -2 -4 -6 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 time (s) Figure 3.82: States for Rayleigh problem ===================== Problem: Rayleigh problem CPU time (seconds): 1.295778e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:06:44 2019 Optimal (unscaled) cost function value: 4.477625e+01 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 4.477625e+01 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 4.500000e+00 Phase 1 maximum relative local error: 2.329140e-03 NLP solver reports: The problem has been solved! 3.34 Obstacle avoidance problem Consider the following optimal control problem, which involves finding an optimal trajectory for a particle to travel from A to B while avoiding two 321 Rayleigh problem u 1 0.5 control 0 -0.5 -1 -1.5 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 time (s) Figure 3.83: Optimal control for Rayleigh problem Rayleigh problem l1 l2 0 costates -2 -4 -6 -8 -10 0 0.5 1 1.5 2 2.5 3 3.5 time (s) Figure 3.84: Costates for Rayleigh problem 322 4 4.5 Rayleigh problem mu 25 20 mu 15 10 5 0 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 time (s) Figure 3.85: Path constraint multiplier for Rayleigh problem forbidden regions [36]. Find θ(t) ∈ [0, tf ] to minimize the cost functional tf Z J= ẋ(t)2 + ẏ(t)2 dt (3.137) 0 subject to the dynamic constraints ẋ = V cos(θ) ẏ = V sin(θ) (3.138) The path constraints: (x(t) − 0.4)2 + (y(t) − 0.5)2 ≥ 0.1 (x(t) − 0.8)2 + (y(t) − 1.5)2 ≥ 0.1, (3.139) and the boundary conditions: x(0) y(0) x(tf ) y(tf ) = = = = 0 0 1.2 1.6 (3.140) where tf = 1.0, and V = 2.138. The PSOPT code that solves this problem is shown below. 323 ////////////////////////////////////////////////////////////////////////// //////////////// obstacle.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Obstacle avoidance problem //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: PROPT User’s Guide //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase,Workspace* workspace) { return 0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { double V = 2.138; adouble theta = controls[ CINDEX(1) ]; adouble dxdt = V*cos(theta); adouble dydt = V*sin(theta); // adouble dxdt, dydt; // get_state_derivative( &dxdt, 1, iphase, time, xad); // get_state_derivative( &dydt, 2, iphase, time, xad); adouble L = return pow(dxdt,2.0) + pow(dydt,2.0); L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x adouble y = states[ CINDEX(1) ]; = states[ CINDEX(2) ]; adouble theta = controls[ CINDEX(1) ]; double V = 2.138; adouble dxdt = V*cos(theta); adouble dydt = V*sin(theta); derivatives[ CINDEX(1) ] = dxdt; derivatives[ CINDEX(2) ] = dydt; path[ CINDEX(1) ] = pow(x-0.4,2.0) + pow(y-0.5,2.0); 324 path[ CINDEX(2) ] = pow(x-0.8,2.0) + pow(y-1.5,2.0); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble e[ e[ e[ e[ x0 y0 xf yf = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) initial_states[ initial_states[ final_states[ final_states[ ] ] ] ] = = = = CINDEX(1) CINDEX(2) CINDEX(1) CINDEX(2) ]; ]; ]; ]; x0; y0; xf; yf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Obstacle avoidance problem"; = "obstacle.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = 2; = 1; = 4; = 2; = "[20]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// 325 /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double xL yL xU yU = = = = 0.0; 0.0; 2.0; 2.0; double thetaL = -10.0; double thetaU = 10.0; double double double double x0 y0 xf yf = = = = 0.0; 0.0; 1.2; 1.6; problem.phases(1).bounds.lower.states(1) = xL; problem.phases(1).bounds.lower.states(2) = yL; problem.phases(1).bounds.upper.states(1) = xU; problem.phases(1).bounds.upper.states(2) = yU; problem.phases(1).bounds.lower.controls(1) = thetaL; problem.phases(1).bounds.upper.controls(1) = thetaU; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) = = = = x0; y0; xf; yf; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) = = = = x0; y0; xf; yf; problem.phases(1).bounds.lower.path(1) = 0.1; problem.phases(1).bounds.upper.path(1) = 100.0; problem.phases(1).bounds.lower.path(2) = 0.1; problem.phases(1).bounds.upper.path(2) = 100.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = 30; DMatrix u_guess = DMatrix x_guess = DMatrix time_guess = = problem.phases(1).ncontrols; = problem.phases(1).nstates; zeros(ncontrols,nnodes); zeros(nstates,nnodes); linspace(0.0,1.0,nnodes); x_guess(1,colon()) = linspace(x0,xf,nnodes); x_guess(2,colon()) = linspace(y0,yf,nnodes); u_guess(1,colon()) = zeros(1,nnodes); problem.phases(1).guess.controls = u_guess; 326 problem.phases(1).guess.states problem.phases(1).guess.time = x_guess; = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max = 1000; algorithm.nlp_tolerance = 1.e-4; algorithm.nlp_method = "IPOPT"; algorithm.scaling = "automatic"; algorithm.derivatives = "automatic"; algorithm.collocation_method = "trapezoidal"; algorithm.mesh_refinement = "automatic"; algorithm.ode_tolerance = 1.0e-2; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix DMatrix DMatrix DMatrix DMatrix states theta t mu lambda = = = = = solution.get_states_in_phase(1); solution.get_controls_in_phase(1); solution.get_time_in_phase(1); solution.get_dual_path_in_phase(1); solution.get_dual_costates_in_phase(1); DMatrix x = states(1,colon()); DMatrix y = states(2,colon()); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("obstacle_x.dat"); y.Save("obstacle_y.dat"); theta.Save("obstacle_theta.dat"); t.Save("obstacle_t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix alpha = colon(0.0, pi/20, 2*pi); DMatrix xObs1 = sqrt(0.1)*cos(alpha) + 0.4; DMatrix yObs1 = sqrt(0.1)*sin(alpha) + 0.5; DMatrix xObs2 = sqrt(0.1)*cos(alpha) + 0.8; DMatrix yObs2 = sqrt(0.1)*sin(alpha) + 1.5; plot(x,y,xObs1,yObs1,xObs2,yObs2,problem.name+": x-y trajectory", "x", "y", "y obs1 obs2"); plot(x,y,xObs1,yObs1,xObs2,yObs2,problem.name+": x-y trajectory", "x", "y", "y obs1 obs2", "pdf", "obstacle_xy.pdf"); plot(t,theta, problem.name+": theta","t", "theta"); plot(t,mu, problem.name+": path constraint multipliers","t", "mu_1 mu_2"); plot(t,lambda, problem.name+": costates","t", "lambda_1 lambda_2"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in 327 Obstacle avoidance problem: x-y trajectory y obs1 obs2 1.6 1.4 1.2 y 1 0.8 0.6 0.4 0.2 0 0 0.2 0.4 0.6 0.8 1 1.2 x Figure 3.86: Optimal (x, y) trajectory for obstacle avoidance problem Figure 3.86, which illustrates the optimal (x, y) trajectory of the particle. PSOPT results summary ===================== Problem: Obstacle avoidance problem CPU time (seconds): 4.305481e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:17:27 2019 Optimal (unscaled) cost function value: 4.571044e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 4.571044e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 1.302550e-02 NLP solver reports: The problem has been solved! 328 3.35 Reorientation of an asymmetric rigid body Consider the following optimal control problem, which consists of the reorientation of an asymmetric rigid body in minimum time [4]. Find tf , û(t) = [u1 (t), u2 (t), u3 (t), q4 (t)]T to minimize the cost functional J = tf (3.141) subject to the dynamic constraints q̇1 = q̇2 = q̇3 = ω̇1 = ω̇2 = ω̇3 = 1 [ω1 q4 − ω2 q3 + ω3 q2 ] 2 1 [ω1 q3 + ω2 q4 − ω3 q1 ] 2 1 [−ω1 q2 + ω2 q1 + ω3 q4 ] 2 Iz − Iy u1 − ω2 ω3 Ix Ix u2 Ix − Iz − ω1 ω3 Iy Iy Iy − Ix u3 − ω1 ω2 Iz Iz (3.142) The path constraint: 0 = q12 + q22 + q32 + q42 − 1 (3.143) the boundary conditions: q1 (0) = 0, q2 (0) = 0, q3 (0) = 0, q4 (0) = 1.0 φ q1 (tf ) = sin , 2 q2 (tf ) = 0, q3 (tf ) = 0, φ q4 (tf ) = cos 2 ω1 (0) = 0, ω2 (0) = 0, ω3 (0) = 0, ω1 (tf ) = 0, ω2 (tf ) = 0, ω3 (tf ) = 0, 329 (3.144) where φ = 150 deg is the Euler axis rotation angle, q = [q1 , q2 , q3 , q4 ]T is the quarternion vector, ω = [ω1 , ω2 , ω3 ]T is the angular velocity vector, and u = [u1 , u2 , u3 ]T is the control vector. Note that in the implementation, variable q4 (t) is treated as an algebraic variable (i.e. as a control variable). The variable bounds and other parameters are given in the code. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// reorientation.cxx //////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Reorientation of an asymmetric rigid body /////////////// //////// Last modified: 03 July 2010 //////////////// //////// Reference: Fleming et al //////////////// //////// //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2010 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return tf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble u1 u2 u3 q4 = controls[ CINDEX(1) ]; = controls[ CINDEX(2) ]; = controls[ CINDEX(3) ]; = controls[ CINDEX(4) ]; adouble adouble adouble adouble adouble adouble q1 = states[ CINDEX(1) ]; q2 = states[ CINDEX(2) ]; q3 = states[ CINDEX(3) ]; omega1 = states[ CINDEX(4) ]; omega2 = states[ CINDEX(5) ]; omega3 = states[ CINDEX(6) ]; double Ix = 5621.0; double Iy = 4547.0; 330 double Iz = 2364.0; adouble adouble adouble adouble adouble adouble dq1 dq2 dq3 domega1 domega2 domega3 derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ derivatives[ = = = = = = 0.5*( omega1*q4 - omega2*q3 + omega3*q2 ); 0.5*( omega1*q3 + omega2*q4 - omega3*q1 ); 0.5*(-omega1*q2 + omega2*q1 + omega3*q4 ); u1/Ix - ((Iz-Iy)/Ix)*omega2*omega3; u2/Iy - ((Ix-Iz)/Iy)*omega1*omega3; u3/Iz - ((Iy-Ix)/Iz)*omega1*omega2; CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ] ] ] ] ] ] = = = = = = dq1; dq2; dq3; domega1; domega2; domega3; path[ CINDEX(1) ] = q1*q1 + q2*q2 + q3*q3 + q4*q4 - 1.0; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble adouble adouble q1i = initial_states[ CINDEX(1) ]; q2i = initial_states[ CINDEX(2) ]; q3i = initial_states[ CINDEX(3) ]; omega1i = initial_states[ CINDEX(4) ]; omega2i = initial_states[ CINDEX(5) ]; omega3i = initial_states[ CINDEX(6) ]; adouble initial_controls[4], final_controls[4], q4i, q4f; get_initial_controls( initial_controls, xad, iphase, workspace ); get_final_controls( final_controls , xad, iphase, workspace ); q4i = initial_controls[ CINDEX(4) ]; q4f = final_controls[ adouble adouble adouble adouble adouble adouble CINDEX(4) ]; q1f = final_states[ CINDEX(1) ]; q2f = final_states[ CINDEX(2) ]; q3f = final_states[ CINDEX(3) ]; omega1f = final_states[ CINDEX(4) ]; omega2f = final_states[ CINDEX(5) ]; omega3f = final_states[ CINDEX(6) ]; e[ e[ e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ] ] ] ] ] ] ] = q1i; = q2i; = q3i; = q4i; = omega1i; = omega2i; = omega3i; e[ e[ e[ e[ e[ e[ e[ CINDEX(8) CINDEX(9) CINDEX(10) CINDEX(11) CINDEX(12) CINDEX(13) CINDEX(14) ] ] ] ] ] ] ] = q1f; = q2f; = q3f; = q4f; = omega1f; = omega2f; = omega3f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// 331 void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Reorientation of a rigid body"; = "reorientation.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 6; 4; 14; 1; = "[60]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states = "[-1.0 problem.phases(1).bounds.upper.states = "[ 1.0 problem.phases(1).bounds.lower.controls = "[-50.0 problem.phases(1).bounds.upper.controls = "[ 50.0 -1.0 1.0 -1.0 1.0 -50.0 -50.0 50.0 50.0 -0.5 0.5 -1.0]"; 1.0]"; problem.phases(1).bounds.lower.path(1) = 0.000; problem.phases(1).bounds.upper.path(1) = 0.000; DMatrix q0, qf, omega0, omegaf; double phi = 150.0*(pi/180.0); q0(1) = 0.0; q0(2) = 0.0; q0(3)=0.0; q0(4)=1.0; qf(1) = sin(phi/2.0); qf(2) = 0.0; qf(3) = 0.0; qf(4) = cos(phi/2.0); omega0 = zeros(1,3); omegaf = zeros(1,3); 332 -0.5 0.5 -0.5]"; 0.5]"; problem.phases(1).bounds.lower.events = q0 || omega0 || qf || omegaf; problem.phases(1).bounds.upper.events = problem.phases(1).bounds.lower.events; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 25.0; = 35.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes int ncontrols int nstates = problem.phases(1).nodes(1); = problem.phases(1).ncontrols; = problem.phases(1).nstates; DMatrix state_guess DMatrix control_guess DMatrix time_guess = = = state_guess(1, colon() ) state_guess(2, colon() ) state_guess(3, colon() ) control_guess(4, colon() state_guess(4, colon() ) state_guess(5, colon() ) state_guess(6, colon() ) zeros(nstates,nnodes); 50.0*ones(ncontrols,nnodes); linspace(0.0,40,nnodes); = linspace( = linspace( = linspace( )=linspace( = linspace( = linspace( = linspace( q0(1), qf(1), nnodes ); q0(2), qf(2), nnodes ); q0(3), qf(3), nnodes ); q0(4), qf(4), nnodes ); omega0(1), omegaf(1), nnodes ); omega0(2), omegaf(2), nnodes ); omega0(3), omegaf(3), nnodes ); problem.phases(1).guess.states = state_guess; problem.phases(1).guess.controls = control_guess; problem.phases(1).guess.time = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.mesh_refinement algorithm.ode_tolerance = = = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; "trapezoidal"; "automatic"; 1.e-5; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix states, controls, t, q1, q2, q3, q4, omega, u, q; states controls t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); q1 = states(1,colon()); 333 q2 = states(2,colon()); q3 = states(3,colon()); q4 = controls(4, colon()); q = q1 && q2 && q3 && q4; omega = states(colon(4,6), colon()); u = controls(colon(1,3), colon()); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// states.Save("states.dat"); controls.Save("controls.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// multiplot(t,q,problem.name+": quarternion","time (s)", "q1 q2 q3 q4", "q1 q2 q3 q4", 2, 2); multiplot(t,u, problem.name+": controls", "time (s)", "u1 u2 u3", "u1 u2 u3"); multiplot(t,q,problem.name+": quarternion","time (s)", "q1 q2 q3 q4", "q1 q2 q3 q4", 2, 2, "pdf", "reorientation_q.pdf"); multiplot(t,u, problem.name+": controls", "time (s)", "u1 u2 u3", "u1 u2 u3", 3, 1, "pdf", "reorientation_u.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.87 to 3.88, which contain the elements of the quarternion vector q, and the control vector u = [u1 , u2 , u3 ]T , respectively. PSOPT results summary ===================== Problem: Reorientation of a rigid body CPU time (seconds): 7.367546e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:23:02 2019 Optimal Phase 1 Phase 1 Phase 1 (unscaled) cost function value: 2.863041e+01 endpoint cost function value: 2.863041e+01 integrated part of the cost: 0.000000e+00 initial time: 0.000000e+00 334 Reorientation of a rigid body: quarternion Reorientation of a rigid body: quarternion 1 0 q1 q3 q1 -0.2 0.6 0.4 -0.3 -0.4 0.2 -0.5 0 -0.6 0 5 10 15 20 25 30 0 5 10 15 20 25 30 time (s) time (s) Reorientation of a rigid body: quarternion Reorientation of a rigid body: quarternion 0.3 0.25 0.2 0.15 0.1 0.05 0 q2 q4 q2 q3 -0.1 0.8 0 5 10 15 20 25 30 q4 1 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0 time (s) 5 10 15 20 25 30 time (s) Figure 3.87: Quarternion vector elements for the reorientation problem Phase 1 final time: 2.863041e+01 Phase 1 maximum relative local error: 1.772329e-05 NLP solver reports: The problem has been solved! 3.36 Shuttle re-entry problem Consider the following optimal control problem, which is known in the literature as the shuttle re-entry problem [3]. Find tf , α(t) and β(t) ∈ [0, tf ] to minimize the cost functional J =− 180 θ(tf ) π (3.145) subject to the dynamic constraints ḣ φ̇ ṁ v̇ γ̇ ψ̇ = v sin(γ) = vr cos(γ) sin(ψ)/ cos(θ) = vr cos(γ) cos(ψ) D = −m − g sin(γ) L = mv cos(β) + cos(γ)( vr − vg ) 1 v = mv cos(γ) L sin(β) + r cos(θ) cos(γ) sin(ψ) sin(θ) 335 (3.146) u1 Reorientation of a rigid body: controls 40 20 0 -20 -40 u1 0 5 10 15 20 25 30 time (s) u2 Reorientation of a rigid body: controls 40 20 0 -20 -40 u2 0 5 10 15 20 25 30 time (s) u3 Reorientation of a rigid body: controls 40 20 0 -20 -40 u3 0 5 10 15 20 25 30 time (s) Figure 3.88: Control vector elements for the reorientation problem the boundary conditions: h(0) φ(0) θ(0) v(0) γ(0) h(tf ) v(tf ) γ(tf ) = = = = = = = = 260000.0 −0.6572 0.0 25600.0 −0.0175 80000.0 2500.0 −0.0873 (3.147) The variable bounds and other parameters are given in the code. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// shuttle_reentry1.cxx ////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Shuttle reentry problem //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: Betts (2001) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser /////////////// //////// General Public License (LGPL) /////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" 336 ////////////////////////////////////////////////////////////////////////// ////////////////// Define some local macros ////////////////////// ////////////////////////////////////////////////////////////////////////// #define #define #define #define #define #define H_INDX 1 PHI_INDX 2 THETA_INDX 3 V_INDX 4 GAMMA_INDX 5 PSI_INDX 6 #define ALPHA_INDX #define BETA_INDX 2 #define DEG2RAD(x) 1 (3.141592653589793*(x)/180.0) ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble theta = final_states[THETA_INDX-1]; return (-theta*180/3.141592653589793); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble alpha, beta; adouble adouble adouble adouble adouble adouble alt lon lat vel gamma azi = = = = = = states[H_INDX-1]; states[PHI_INDX-1]; states[THETA_INDX-1]; states[V_INDX-1]; states[GAMMA_INDX-1]; states[PSI_INDX-1]; alpha = controls[ALPHA_INDX-1]; beta = controls[BETA_INDX-1]; double double double double double double double double double double double double double double pi cr2d weight cm2w cea mu rho0 href cl0 cl1 cd0 cd1 cd2 sref = = = = = = = = = = = = = = 3.141592653589793; 180.0/pi; 203000.0; 32.174; 20902900.0; 0.14076539e17; 0.002378; 23800.0; -0.20704; 0.029244; 0.07854; -6.1592e-3; 6.21408e-4; 2690.0; 337 double mass = weight/cm2w; adouble sgamma = sin(gamma); adouble cgamma = cos(gamma); adouble sazi = sin(azi); adouble cazi = cos(azi); adouble sbeta = sin(beta); adouble cbeta = cos(beta); adouble slat = sin(lat); adouble clat = cos(lat); adouble alphad adouble radius adouble grav = cr2d*alpha; = cea+alt; = mu/pow(radius,2); adouble adouble adouble adouble adouble adouble adouble rhodns dynp subl subd drag lift vrelg = = = = = = = adouble adouble adouble adouble adouble adouble d_alt_dt d_lon_dt d_lat_dt d_vel_dt d_gamma_dt d_azi_dt rho0*exp(-alt/href); 0.5*(rhodns*pow(vel,2)); cl0+cl1*alphad; cd0+((cd1+cd2*alphad)*alphad); (dynp*subd)*sref; (dynp*subl)*sref; (vel/radius)-(grav/vel); = = = = = = vel*sgamma; ((vel*cgamma)*sazi)/(radius*clat); ((vel*cgamma)*cazi)/radius; (-(drag/mass)-(grav*sgamma)); ((lift*cbeta)/(mass*vel))+(cgamma*vrelg); ((lift*sbeta)/((mass*vel)*cgamma)) + ((vel*cgamma)*(sazi*slat)/(radius*clat)); derivatives[H_INDX-1] = d_alt_dt ; derivatives[PHI_INDX-1] = d_lon_dt ; derivatives[THETA_INDX-1] = d_lat_dt ; derivatives[V_INDX-1] = d_vel_dt ; derivatives[GAMMA_INDX-1] = d_gamma_dt ; derivatives[PSI_INDX-1] = d_azi_dt ; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble h0 = initial_states[H_INDX-1]; adouble phi0 = initial_states[PHI_INDX-1]; adouble theta0 = initial_states[THETA_INDX-1]; adouble v0 = initial_states[V_INDX-1]; adouble gamma0 = initial_states[GAMMA_INDX-1]; adouble psi0 = initial_states[PSI_INDX-1]; adouble hf adouble vf adouble gammaf = final_states[H_INDX-1]; = final_states[V_INDX-1]; = final_states[GAMMA_INDX-1]; e[H_INDX-1] = h0; e[PHI_INDX-1] = phi0; e[THETA_INDX-1] = theta0; e[V_INDX-1] = v0; e[GAMMA_INDX-1] = gamma0; e[PSI_INDX-1] = psi0; e[6] e[7] e[8] = hf; = vf; = gammaf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { 338 // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Shuttle re-entry problem"; = "shuttle.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = 6; problem.phases(1).ncontrols = 2; problem.phases(1).nevents = 9; problem.phases(1).npath = 0; problem.phases(1).nodes problem.phases(1).zero_cost_integrand = "[60 80]"; = true; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double double double double double double double hL hU phiL phiU vL vU thetaL thetaU gammaL gammaU psiL psiU = = = = = = = = = = = = 0.0; 300000.0; DEG2RAD(-45.0); DEG2RAD( 45.0); 1000.0 ; 40000.0 ; DEG2RAD(-89.0) ; DEG2RAD( 89.0) ; DEG2RAD(-89.0) ; DEG2RAD(89.0) ; DEG2RAD(-180.0); DEG2RAD(180.0) ; double double double double alphaL alphaU betaL betaU = = = = DEG2RAD(-89.0) DEG2RAD( 89.0) DEG2RAD(-90.0) DEG2RAD( 1.0) double qU double qL double double double double h0 v0 phi0 gamma0 ; ; ; ; = 70.0; = -INF; = = = = 260000.0 ; 25600.0 ; DEG2RAD(-0.5*75.3153) ; DEG2RAD(-1.0); 339 double theta0 double psi0 = = 0.0 ; DEG2RAD(90.0) ; double hf double vf double gammaf = 80000.0 ; = 2500.0 ; = DEG2RAD(-5.0); double pi = 3.141592653589793; problem.phases(1).bounds.lower.states(H_INDX) = hL; problem.phases(1).bounds.lower.states(PHI_INDX) = phiL; problem.phases(1).bounds.lower.states(THETA_INDX) = thetaL; problem.phases(1).bounds.lower.states(V_INDX) = vL; problem.phases(1).bounds.lower.states(GAMMA_INDX) = gammaL; problem.phases(1).bounds.lower.states(PSI_INDX) = psiL; problem.phases(1).bounds.upper.states(H_INDX) = hU; problem.phases(1).bounds.upper.states(PHI_INDX) = phiU; problem.phases(1).bounds.upper.states(THETA_INDX) = thetaU; problem.phases(1).bounds.upper.states(V_INDX) = vU; problem.phases(1).bounds.upper.states(GAMMA_INDX) = gammaU; problem.phases(1).bounds.upper.states(PSI_INDX) = psiU; problem.phases(1).bounds.lower.controls(ALPHA_INDX) = alphaL; problem.phases(1).bounds.upper.controls(ALPHA_INDX) = alphaU; problem.phases(1).bounds.lower.controls(BETA_INDX) = betaL; problem.phases(1).bounds.upper.controls(BETA_INDX) = betaU; problem.phases(1).bounds.lower.events(H_INDX) = h0; problem.phases(1).bounds.lower.events(PHI_INDX) = phi0; problem.phases(1).bounds.lower.events(THETA_INDX) = theta0; problem.phases(1).bounds.lower.events(V_INDX) = v0; problem.phases(1).bounds.lower.events(GAMMA_INDX) = gamma0; problem.phases(1).bounds.lower.events(PSI_INDX) = psi0; problem.phases(1).bounds.lower.events(7) = hf; problem.phases(1).bounds.lower.events(8) = vf; problem.phases(1).bounds.lower.events(9) = gammaf; problem.phases(1).bounds.upper.events(H_INDX) = h0; problem.phases(1).bounds.upper.events(PHI_INDX) = phi0; problem.phases(1).bounds.upper.events(THETA_INDX) = theta0; problem.phases(1).bounds.upper.events(V_INDX) = v0; problem.phases(1).bounds.upper.events(GAMMA_INDX) = gamma0; problem.phases(1).bounds.upper.events(PSI_INDX) = psi0; problem.phases(1).bounds.upper.events(7) = hf; problem.phases(1).bounds.upper.events(8) = vf; problem.phases(1).bounds.upper.events(9) = gammaf; problem.phases(1).bounds.lower.path(1) problem.phases(1).bounds.upper.path(1) = qL; = qU; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 100.0; = 4000.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nodes = (int) problem.phases(1).nodes(1); 340 DMatrix u_guess = ( zeros(1,nodes+1) && (DEG2RAD(1.0)*ones(1,nodes+1) ) ); DMatrix x_guess = zeros(6,nodes+1); DMatrix time_guess; x_guess(H_INDX,colon()) = linspace(h0, hf, nodes+1); x_guess(PHI_INDX,colon()) = -0.5*DEG2RAD(90.0)*ones(1,nodes+1); x_guess(THETA_INDX,colon()) = DEG2RAD(-89.0)*ones(1,nodes+1); x_guess(V_INDX,colon()) = linspace(v0,vf, nodes+1); x_guess(GAMMA_INDX,colon()) = linspace(gamma0, gammaf, nodes+1); x_guess(PSI_INDX,colon()) = linspace(pi/2, -pi/2, nodes+1); time_guess = linspace(0.0, 1000.0, nodes+1); problem.phases(1).guess.controls = u_guess; problem.phases(1).guess.states = x_guess; problem.phases(1).guess.time = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.scaling algorithm.derivatives algorithm.collocation_method algorithm.mesh_refinement = "IPOPT"; = 1000; = 5e-6; = "automatic"; = "automatic"; = "trapezoidal"; = "automatic"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = solution.get_states_in_phase(1); u = solution.get_controls_in_phase(1); t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix h phi theta v gamma psi alpha beta = = = = = = = = x(1,colon()); x(2,colon()); x(3,colon()); x(4,colon()); x(5,colon()); x(6,colon()); u(1,colon()); u(2,colon()); plot(t,x(1,colon()),problem.name, "time (s)", "x1","altitude"); plot(t,u(1,colon()),problem.name,"time (s)", "alpha"); plot(t,u(2,colon()),problem.name,"time (s)", "beta"); plot(t,h, problem.name+": altitude", "time (s)", "h (ft)", "altitude","pdf", "shutt_alt.pdf" ); plot(t,phi, problem.name+": longitude", "time (s)", "phi (rad)", "longitude","pdf", "shutt_lon.pdf" ); plot(t,theta, problem.name+": latitude", "time (s)", "theta (rad)", "latitude","pdf", "shutt_lat.pdf" ); 341 plot(t,v, problem.name+": velocity", "time (s)", "v (ft/s)", "velocity","pdf", "shutt_vel.pdf" ); plot(t,gamma, problem.name+": flight path angle", "time (s)", "gamma (rad)", "fpa","pdf", "shutt_fpa.pdf" ); plot(t,psi, problem.name+": azimuth", "time (s)", "psi (rad)", "azi","pdf", "shutt_azi.pdf" ); plot(t,alpha, problem.name+": alpha", "time (s)", "alpha (rad)", "alpha","pdf", "shutt_alpha.pdf" ); plot(t,beta, problem.name+": beta", "time (s)", "beta (rad)", "beta","pdf", "shutt_beta.pdf" ); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.89 to 3.96, which contain the elements of the state and the control vectors. PSOPT results summary ===================== Problem: Shuttle re-entry problem CPU time (seconds): 1.522294e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:55:18 2019 Optimal (unscaled) cost function value: -3.414119e+01 Phase 1 endpoint cost function value: -3.414119e+01 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.008466e+03 Phase 1 maximum relative local error: 6.337462e-04 NLP solver reports: The problem has been solved! 3.37 Singular control problem Consider the following optimal control problem, whose solution is known to have a singular arc [29, 36]. Find u(t), t ∈ [0, 1] to minimize the cost 342 Shuttle re-entry problem: altitude altitude 260000 240000 220000 200000 h (ft) 180000 160000 140000 120000 100000 80000 0 500 1000 1500 2000 2500 time (s) Figure 3.89: Altitude h(t) for the shuttle re-entry problem Shuttle re-entry problem: longitude longitude 0.6 0.4 phi (rad) 0.2 0 -0.2 -0.4 -0.6 0 500 1000 1500 2000 2500 time (s) Figure 3.90: Longitude φ(t) for the shuttle re-entry problem 343 Shuttle re-entry problem: latitude latitude 0.5 theta (rad) 0.4 0.3 0.2 0.1 0 0 500 1000 1500 2000 2500 time (s) Figure 3.91: Latitude θ(t) for the shuttle re-entry problem Shuttle re-entry problem: velocity velocity 25000 v (ft/s) 20000 15000 10000 5000 0 500 1000 1500 2000 2500 time (s) Figure 3.92: Velocity v(t) for the shuttle re-entry problem 344 Shuttle re-entry problem: flight path angle fpa 0 gamma (rad) -0.02 -0.04 -0.06 -0.08 0 500 1000 1500 2000 2500 time (s) Figure 3.93: Flight path angle γ(t) for the shuttle re-entry problem Shuttle re-entry problem: azimuth azi 1.4 1.2 psi (rad) 1 0.8 0.6 0.4 0.2 0 500 1000 1500 2000 2500 time (s) Figure 3.94: Azimuth ψ(t) for the shuttle re-entry problem 345 Shuttle re-entry problem: alpha alpha 0.315 0.31 alpha (rad) 0.305 0.3 0.295 0.29 0.285 0.28 0.275 0 500 1000 1500 2000 2500 time (s) Figure 3.95: Angle of attack α(t) for the shuttle re-entry problem Shuttle re-entry problem: beta beta 0 -0.2 beta (rad) -0.4 -0.6 -0.8 -1 -1.2 0 500 1000 1500 2000 2500 time (s) Figure 3.96: Bank angle β(t) for the shuttle re-entry problem 346 functional Z J= 1 [x21 + x22 + 0.0005(x2 + 16x5 − 8 − 0.1x3 u2 )2 ]dt (3.148) 0 subject to the dynamic constraints ẋ1 = x2 ẋ2 = −x3 u + 16t − 8 ẋ3 = u (3.149) x1 (0) = 0 x2 (0) = −1 √ 5 x3 (0) = (3.150) −4 ≤ u(t) ≤ 10 (3.151) the boundary conditions: and the control bounds The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// singular5.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Singular problem //////////////// //////// Last modified: 09 January 2009 //////////////// //////// Reference: Luus (2002) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[ CINDEX(1) ]; adouble x2 = states[ CINDEX(2) ]; adouble x3 = states[ CINDEX(3) ]; 347 adouble u = controls[ CINDEX(1) ]; adouble t = time; adouble L; L = x1*x1 + x2*x2 + 0.0005*pow(x2+16*t-8.0-0.1*x3*u*u,2.0); return L; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[ CINDEX(1) ]; adouble x2 = states[ CINDEX(2) ]; adouble x3 = states[ CINDEX(3) ]; adouble u = controls[ CINDEX(1) ]; adouble t = time; derivatives[ CINDEX(1) ] = x2; derivatives[ CINDEX(2) ] = -x3*u + 16*t - 8.0; derivatives[ CINDEX(3) ] = u; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1) ]; adouble x20 = initial_states[ CINDEX(2) ]; adouble x30 = initial_states[ CINDEX(3) ]; e[ CINDEX(1) ] = x10; e[ CINDEX(2) ] = x20; e[ CINDEX(3) ] = x30; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; MSdata msdata; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Singular control 5"; = "sing5.txt"; 348 //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = 3; = 1; = 3; = 0; = "[80]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = -inf; problem.phases(1).bounds.lower.states(2) = -inf; problem.phases(1).bounds.lower.states(3) = -inf; problem.phases(1).bounds.upper.states(1) = inf; problem.phases(1).bounds.upper.states(2) = inf; problem.phases(1).bounds.upper.states(3) = inf; problem.phases(1).bounds.lower.controls(1) = problem.phases(1).bounds.upper.controls(1) = 0.0; 10.0; problem.phases(1).bounds.lower.events(1) = 0.0; problem.phases(1).bounds.lower.events(2) = -1.0; problem.phases(1).bounds.lower.events(3) = -sqrt(5.0); problem.phases(1).bounds.upper.events(1) = 0.0; problem.phases(1).bounds.upper.events(2) = -1.0; problem.phases(1).bounds.upper.events(3) = -sqrt(5.0); problem.bounds.lower.times = "[0.0, problem.bounds.upper.times = "[0.0, 1.0]"; 1.0]"; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix state_guess(3,20), control_guess(1,20), param_guess, time_guess; state_guess(1,colon())= linspace(0.0, 0.0, 20); state_guess(2,colon())= linspace(-1.0, -1.0, 20); state_guess(3,colon())= linspace(-sqrt(5.0),-sqrt(5.0), 20); control_guess = zeros(1,20); time_guess = linspace(0.0, 1.0, 20); auto_phase_guess(problem, control_guess, state_guess, param_guess, time_guess); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// 349 // // // // algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.defect_scaling algorithm.collocation_method algorithm.mesh_refinement algorithm.ode_tolerance algorithm.mr_max_iterations = = = = = = 1000; 1.e-7; "IPOPT"; "automatic"; "automatic"; "jacobian-based"; = "trapezoidal"; = "automatic"; = 5.e-3 ; = 3; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t, xi, ui, ti; x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name,"time (s)", "x"); plot(t,u,problem.name,"time (s)", "u"); plot(t,x,problem.name,"time (s)", "x","x1 x2 x3", "pdf","sing5_states.pdf"); plot(t,u,problem.name,"time (s)", "u","u", "pdf","sing5_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.97 and 3.98, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Singular control 5 CPU time (seconds): 6.808369e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:01:49 2019 350 Singular control 5 x1 x2 x3 1.5 1 0.5 x 0 -0.5 -1 -1.5 -2 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.97: States for singular control problem Optimal (unscaled) cost function value: 1.192562e-01 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 1.192562e-01 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 2.503060e-04 NLP solver reports: The problem has been solved! 3.38 Time varying state constraint problem Consider the following optimal control problem, which involves a time varying state constraint [40]. Find u(t) ∈ [0, 1] to minimize the cost functional Z J= 1 [x21 (t) + x22 (t) + 0.005u2 (t)]dt (3.152) 0 subject to the dynamic constraints ẋ1 = x2 ẋ2 = −x2 + u 351 (3.153) Singular control 5 10 u 9 8 7 u 6 5 4 3 2 1 0 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.98: Control for singular control problem the boundary conditions: x1 (0) = 0 x2 (0) = −1 (3.154) x2 ≤ 8(t − 0.5)2 − 0.5 (3.155) and the path constraint The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// statecon1.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Time varying state constraint problem //////////////// //////// Last modified: 06 February 2009 //////////////// //////// Reference: Teo et. al (1991) //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, 352 adouble* xad, int iphase, Workspace* workspace) { adouble retval = 0.0; return retval; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the running (Lagrange) cost function //////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[0]; adouble x2 = states[1]; adouble u = controls[0]; return ( x1*x1 + x2*x2 + 0.005*u*u ); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; adouble x1 = states[0]; adouble x2 = states[1]; adouble t = time; adouble u = controls[0]; derivatives[0] = x2; derivatives[1] = -x2 + u; path[0] = -( 8.0*((t-0.5)*(t-0.5)) -0.5 - x2 ); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[0]; adouble x20 = initial_states[1]; e[0] = x10; e[1] = x20; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// 353 //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Time varying state constraint problem"; problem.outfilename= "stc1.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 2; 1; 2; 1; = "[20 50]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) = -2.0; problem.phases(1).bounds.lower.states(2) = -2.0; problem.phases(1).bounds.upper.states(1) = 2.0; problem.phases(1).bounds.upper.states(2) = 2.0; problem.phases(1).bounds.lower.controls(1) = -20.0; problem.phases(1).bounds.upper.controls(1) = 20.0; problem.phases(1).bounds.lower.events(1) = 0.0; problem.phases(1).bounds.lower.events(2) = -1.0; problem.phases(1).bounds.upper.events(1) = 0.0; problem.phases(1).bounds.upper.events(2) = -1.0; problem.phases(1).bounds.upper.path(1) = 0.0; problem.phases(1).bounds.lower.path(1) = -100.0; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// 354 /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(2,20); = zeros(2,20); = linspace(0.0,1.0, 20); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max = 1000; algorithm.nlp_tolerance = 1.e-4; algorithm.nlp_method ="IPOPT"; = "automatic"; = "automatic"; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x = solution.get_states_in_phase(1); DMatrix u = solution.get_controls_in_phase(1); DMatrix t = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// // Generate points to plot the constraint boundary DMatrix x2c = 8.0*((t-0.5)^2.0) -0.5*ones(1,length(t)); plot(t,x,t,x2c,"states","time (s)", "x", "x1 x2 constraint"); plot(t,u,"control","time (s)", "u"); plot(t,x,t,x2c,"states","time (s)", "x", "x1 x2 constraint", "pdf", "stc1_states.pdf"); plot(t,u,"control","time (s)", "u", "u", "pdf","stc1_control.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.99 and 3.100, which contain the elements of the states with the boundary of the constraint on x2 , and the control, respectively. PSOPT results summary ===================== Problem: Time varying state constraint problem CPU time (seconds): 1.874042e+00 355 states x1 x2 constraint 1.5 1 x 0.5 0 -0.5 -1 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.99: States for time-varying state constraint problem NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:07:14 2019 Optimal (unscaled) cost function value: 1.698182e-01 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 1.698182e-01 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 3.150532e-05 NLP solver reports: The problem has been solved! 3.39 Two burn orbit transfer The goal of this problem is to compute a trajectory for an spacecraft to go from a standard space shuttle park orbit to a geosynchronous final orbit. It is assumed that the engines operate over two short periods during the mission, and it is desired to compute the timing and duration of the burn periods, as well as the instantaneous direction of the thrust during these two periods, to maximise the final weight of the spacecraft. The problem is described in detail by Betts [3]. The mission then involves four phases: coast, burn, coast and burn. The problem is formulated as follows. Find u(t) = 356 control u 14 12 10 u 8 6 4 2 0 -2 0 0.2 0.4 0.6 0.8 1 time (s) Figure 3.100: Control for time-varying state constraint problem (1) (2) (3) (4) (1) (2) (3) (4) [θ(t), φ(t)]T , t ∈ [tf , tf ] and t ∈ [tf , tf ], and the instants tf , tf , tf , tf such that the following objective function is minimised: J = −w(tf ) (3.156) subject to the dynamic constraints for phases 1 and 3: ẏ = A(y)∆g + b (3.157) the following dynamic constraints for phases 2 and 4: ẏ = A(y)∆ + b ẇ = −T /Isp (3.158) and the following linkages between phases (1) (2) (2) (3) (3) (4) y(tf ) = y(t0 ) y(tf ) = y(t0 ) y(tf ) = y(t0 ) (1) (2) (2) (3) (3) (4) (3.159) tf = t0 tf = t0 tf = t0 (2) (4) w(tf ) = w(t0 ) 357 where y = [p, f, g, h, k, L, w]T is the vector of modified equinoctial elements, w is the spacecraft weight, Isp is the specific impulse of the engine, T is the maximum thrust, expressions for A(y) and b are given in [3]. the disturbing acceleration is ∆ = ∆g + ∆T , where ∆g is the gravitational disturbing acceleration due to the oblatness of Earth (given in [3]), and ∆T is the thurst acceleration, given by: Ta cos θ cos φ ∆T = Qr Qv Ta cos θ sin φ (3.160) Ta sin θ where Ta (t) = g0 T /w(t), g0 is a constant, θ is the pitch angle and φ is the yaw angle of the thurst, matrix Qv is given by: v v×r v v×r Qv = (3.161) , , × ||v|| ||v × r|| ||v|| ||v × r|| matrix Qr is given by: h r Qr = ir iθ ih = ||r|| (r×v)×r ||r×v||||r|| (r×v) ||r×v|| i (3.162) The boundary conditions of the problem are given by: p(0) = 218327080.052835 f (0) = 0 g(0) = 0 h(0) = 0 h(0) = 0 k(0) = 0 L(0) = π (rad) (3.163) w(0) = 1 (lb) p(tf ) = 19323/σ + Re f (tf ) = 0 g(tf ) = 0 h(tf ) = 0 k(tf ) = 0 and the values of the parameters are: g0 = 32.174 (ft/sec2 ), Isp = 300 (sec), T = 1.2 (lb), µ = 1.407645794 × 1016 (ft3 /sec2 ), Re = 20925662.73 (ft), σ = 1.0/6076.1154855643, J2 = 1082.639 × 10−6 , J3 = −2.565 × 10−6 , J4 = −1.608 × 10−6 . 358 An initial guess was computed by forward propagation from the initial conditions, assuming the following guesses for the controls and burn periods [3]: T u(t) = 0.148637 × 10−2 , −9.08446 u(t) = −0.136658 × 10−2 , 49.7892 t ∈ [2840, 21650] t ∈ [21650, 21700] (3.164) The problem was solved using local collocation (trapezoidal followed by Hermite-Simpson) with automatic mesh refinement. The PSOPT code that solves the problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// twoburn.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// //////// Title: two burn orbit transfer problem //////////////// //////// Last modified: 07 February 2010 //////////////// //////// Reference: Betts (2001) //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2010 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which //////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define auxiliary functions ////////// ////////////////////////////////////////////////////////////////////////// adouble legendre_polynomial( adouble x, int n) { // This function computes the value of the legendre polynomials // for a given value of the argument x and for n=0...5 only adouble retval=0.0; switch(n) { case 0: retval=1.0; break; case 1: retval= x; break; case 2: retval= 0.5*(3.0*pow(x,2)-1.0); break; case 3: retval= 0.5*(5.0*pow(x,3)- 3*x); break; case 4: retval= (1.0/8.0)*(35.0*pow(x,4) - 30.0*pow(x,2) + 3.0); break; case 5: retval= (1.0/8.0)*(63.0*pow(x,5) - 70.0*pow(x,3) + 15.0*x); break; default: error_message("legendre_polynomial(x,n) is limited to n=0...5"); } return retval; } adouble legendre_polynomial_derivative( adouble x, int n) { // This function computes the value of the legendre polynomial derivatives // for a given value of the argument x and for n=0...5 only. adouble retval=0.0; 359 switch(n) { case 0: retval=0.0; break; case 1: retval= 1.0; break; case 2: retval= 0.5*(2.0*3.0*x); break; case 3: retval= 0.5*(3.0*5.0*pow(x,2)-3.0); break; case 4: retval= (1.0/8.0)*(4.0*35.0*pow(x,3) - 2.0*30.0*x ); break; case 5: retval= (1.0/8.0)*(5.0*63.0*pow(x,4) - 3.0*70.0*pow(x,2) + 15.0); break; default: error_message("legendre_polynomial_derivative(x,n) is limited to n=0...5"); } return retval; } void compute_cartesian_trajectory(const DMatrix& x, DMatrix& xyz ) { int npoints = x.GetNoCols(); xyz.Resize(3,npoints); for(int i=1; i<=npoints;i++) { double double double double double double p f g h k L = = = = = = x(1,i); x(2,i); x(3,i); x(4,i); x(5,i); x(6,i); double double double double double q r alpha2 X s2 = 1.0 + f*cos(L) + g*sin(L); = p/q; = h*h - k*k; = sqrt( h*h + k*k ); = 1 + X*X; double r1 = r/s2*( cos(L) + alpha2*cos(L) + 2*h*k*sin(L)); double r2 = r/s2*( sin(L) - alpha2*sin(L) + 2*h*k*cos(L)); double r3 = 2*r/s2*( h*sin(L) - k*cos(L) ); xyz(1,i) = r1; xyz(2,i) = r2; xyz(3,i) = r3; } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { if (iphase == 4) { adouble w = final_states[CINDEX(7)]; return (-w); } else { return (0); } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } 360 ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { // Local integers int i, j; // Define constants: double Isp = 300.0; // [sec] double mu = 1.407645794e16; // [f2^2/sec^2] double g0 = 32.174; // [ft/sec^2] double Re = 20925662.73; // [ft] double T = 1.2; // [lb] double CM2W = 32.174; double J[5]; J[2] = 1082.639e-6; J[3] = -2.565e-6; J[4] = -1.608e-6; // Extract individual variables adouble adouble adouble adouble adouble adouble adouble p = f = g = h = k = L = w; adouble* u states[ states[ states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ]; ]; ]; ]; ]; ]; = controls; // Define some dependent variables adouble adouble adouble adouble adouble q r alpha2 X s2 = 1.0 + f*cos(L) + g*sin(L); = p/q; = h*h - k*k; = sqrt( h*h + k*k ); = 1 + X*X; // r and v adouble r1 = r/s2*( cos(L) + alpha2*cos(L) + 2*h*k*sin(L)); adouble r2 = r/s2*( sin(L) - alpha2*sin(L) + 2*h*k*cos(L)); adouble r3 = 2*r/s2*( h*sin(L) - k*cos(L) ); adouble rvec[3]; rvec[ CINDEX(1) ] = r1; rvec[ CINDEX(2)] = r2; rvec[ CINDEX(3) ] = r3; adouble v1 = -(1.0/s2)*sqrt(mu/p)*( sin(L) + alpha2*sin(L) - 2*h*k*cos(L) + g - 2*f*h*k + alpha2*g); adouble v2 = -(1.0/s2)*sqrt(mu/p)*( -cos(L) + alpha2*cos(L) + 2*h*k*sin(L) - f + 2*g*h*k + alpha2*f); adouble v3 = (2.0/s2)*sqrt(mu/p)*(h*cos(L) + k*sin(L) + f*h + g*k); adouble vvec[3]; vvec[ CINDEX(1) ] = v1; vvec[ CINDEX(2)] = v2; vvec[ CINDEX(3) ] = v3; // compute Qr adouble ir[3], ith[3], ih[3]; adouble rv[3]; adouble rvr[3]; cross( rvec, vvec, rv ); cross( rv, rvec, rvr ); adouble norm_r = sqrt( dot(rvec, rvec, 3) ); adouble norm_rv = sqrt( dot(rv, rv, 3) ); for (i=0; i<3; i++) { ir[i] = rvec[i]/norm_r; ith[i] = rvr[i]/( norm_rv*norm_r ); 361 ih[i] = rv[i]/norm_rv; } adouble Qr1[3], Qr2[3], Qr3[3]; for(i=0; i< 3; i++) { // Columns of matrix Qr Qr1[i] = ir[i]; Qr2[i] = ith[i]; Qr3[i] = ih[i]; } adouble Qv1[3], Qv2[3], Qv3[3]; adouble norm_v = sqrt( dot(vvec, vvec,3) ); for(i=0;i<3;i++) { Qv1[i] = vvec[i]/norm_v; } adouble vr[3]; cross( vvec, rvec, vr); adouble norm_vr = sqrt( dot(vr, vr,3) ); for(i=0;i<3;i++) { Qv2[i] = vr[i]/norm_vr; } cross( Qv1, Qv2, Qv3 ); // Compute in adouble en[3]; en[ CINDEX(1) ] = 0.0; en[ CINDEX(2) ]= 0.0; en[ CINDEX(3) ] = 1.0; adouble enir = dot(en,ir,3); adouble in[3]; for(i=0;i<3;i++) { in[i] = en[i] - enir*ir[i]; } adouble norm_in = sqrt( dot( in, in, 3 ) ); for(i=0;i<3;i++) { in[i] = in[i]/norm_in; } // Geocentric latitude angle: adouble sin_phi = adouble cos_phi = rvec[ CINDEX(3) ]/ sqrt( dot(rvec,rvec,3) ) ; sqrt(1.0- pow(sin_phi,2.0)); adouble deltagn = 0.0; adouble deltagr = 0.0; for (j=2; j<=4;j++) { adouble Pdash_j = legendre_polynomial_derivative( sin_phi, j ); adouble P_j = legendre_polynomial( sin_phi, j ); deltagn += -mu*cos_phi/(r*r)*pow(Re/r,j)*Pdash_j*J[j]; deltagr += -mu/(r*r)* (j+1)*pow( Re/r,j)*P_j*J[j]; } // Compute vector delta_g adouble delta_g[3]; for (i=0;i<3;i++) { delta_g[i] = deltagn*in[i] - deltagr*ir[i]; } // Compute vector DELTA_g adouble DELTA_g[3]; DELTA_g[ CINDEX(1) ] = dot(Qr1, delta_g,3); DELTA_g[ CINDEX(2) ] = dot(Qr2, delta_g,3); DELTA_g[ CINDEX(3) ] = dot(Qr3, delta_g,3); 362 // Compute DELTA_T adouble DELTA_T[3]; if (iphase == 1 || iphase==3) { for(i=0;i<3;i++) { DELTA_T[i] = 0.0; } } else { adouble Qr[9], Qrt[9], Qv[9], Tvel[3]; for(i=0;i<3;i++) { Qr[i] = Qr1[i]; Qr[3+i] = Qr2[i]; Qr[6+i] = Qr3[i]; Qv[i] = Qv1[i]; Qv[3+i] = Qv2[i]; Qv[6+i] = Qv3[i]; } transpose_ad(Qr, 3, 3, Qrt ); adouble theta = u[ CINDEX(1) ]; adouble phi = u[ CINDEX(2) ]; adouble Tvec[3]; w = states[CINDEX(7)]; adouble mass = w/CM2W; adouble Tacc = T/mass; Tvec[ CINDEX(1) ] = Tacc*cos(theta)*cos(phi); Tvec[ CINDEX(2) ] = Tacc*cos(theta)*sin(phi); Tvec[ CINDEX(3) ] = Tacc*sin(theta); product_ad( Qv, Tvec, 3, 3, 3, 1, Tvel ); product_ad(Qrt, Tvel, 3, 3, 3, 1, DELTA_T ); } // Compute DELTA adouble DELTA[3]; for(i=0;i<3;i++) { DELTA[i] = DELTA_g[i] + DELTA_T[i]; } adouble delta1= DELTA[ CINDEX(1) ]; adouble delta2= DELTA[ CINDEX(2) ]; adouble delta3= DELTA[ CINDEX(3) ]; // derivatives adouble pdot = adouble fdot = adouble gdot = adouble hdot = adouble kdot = adouble Ldot = 2*p/q*sqrt(p/mu) * sqrt(p/mu)*sin(L) * delta1 + sqrt(p/mu)*(1.0/q)*((q+1.0)*cos(L)+f) * - sqrt(p/mu)*(g/q)*(h*sin(L)-k*cos(L)) * delta3; -sqrt(p/mu)*cos(L) * delta1 + sqrt(p/mu)*(1.0/q)*((q+1.0)*sin(L)+g) * + sqrt(p/mu)*(f/q)*(h*sin(L)-k*cos(L)) * delta3; sqrt(p/mu)*s2*cos(L)/(2.0*q) * delta3; sqrt(p/mu)*s2*sin(L)/(2.0*q) * delta3; sqrt(p/mu)*(1.0/q)*(h*sin(L)-k*cos(L))* delta3 + sqrt(mu*p)*pow( adouble wdot; if (iphase==2 || iphase==4) { wdot = -T/Isp; } else { wdot = 0.0; } derivatives[ CINDEX(1) ] = pdot; derivatives[ CINDEX(2) ] = fdot; 363 delta2; delta2 delta2 (q/p),2.); derivatives[ CINDEX(3) ] = gdot; derivatives[ CINDEX(4) ] = hdot; derivatives[ CINDEX(5) ] = kdot; derivatives[ CINDEX(6) ] = Ldot; if (iphase==2 || iphase==4) { derivatives[ CINDEX(7) ] = wdot; } } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble pti = initial_states[ CINDEX(1) adouble fti = initial_states[ CINDEX(2) adouble gti = initial_states[ CINDEX(3) adouble hti = initial_states[ CINDEX(4) adouble kti = initial_states[ CINDEX(5) adouble Lti = initial_states[ CINDEX(6) adouble wti; if (iphase==2) { wti = initial_states[ CINDEX(7) ]; } adouble adouble adouble adouble adouble adouble ptf ftf gtf htf ktf Ltf = = = = = = final_states[ final_states[ final_states[ final_states[ final_states[ final_states[ if (iphase==1) { e[ CINDEX(1) ] e[ CINDEX(2) ] e[ CINDEX(3) ] e[ CINDEX(4) ] e[ CINDEX(5) ] e[ CINDEX(6) ] } = = = = = = CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; ]; pti; fti; gti; hti; kti; Lti; if (iphase==2) { e[ CINDEX(1) ] = wti; } if (iphase == e[ CINDEX(1) e[ CINDEX(2) e[ CINDEX(3) e[ CINDEX(4) e[ CINDEX(5) } 4) { ] = ptf; ] = ftf; ] = gtf; ] = htf; ] = ktf; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* { // Numeber of linkages: // Boundary of phases 1,2: 6 state continuity + 1 time // Boundary of phases 2,3: 6 state continuity + 1 time // Boundary of phases 3,4: 6 state continuity + 1 time // 1 extra linkage w(tf2) = w(ti4) // Total: 22 linkage constraints workspace) continuity continuity continuity adouble xf[7], xi[7], t0a, t0b, tfa, tfb, wtf2, wti4; // Linking phases 1 and 2 364 get_final_states( xf, xad, 1, workspace get_initial_states( xi, xad, 2, workspace tfa = get_final_time( xad, 1, workspace t0b = get_initial_time( xad, 2, workspace ); ); ); ); linkages[ linkages[ linkages[ linkages[ linkages[ linkages[ linkages[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) ] ] ] ] ] ] ] = = = = = = = xf[ xf[ xf[ xf[ xf[ xf[ t0b CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) - tfa; ] ] ] ] ] ] - xi[ xi[ xi[ xi[ xi[ xi[ ]; ]; ]; ]; ]; ]; // Linking phases 2 and 3 get_final_states( get_initial_states( tfa = get_final_time( t0b = get_initial_time( xf, xad, 2, xi, xad, 3, xad, 2, xad, 3, workspace workspace workspace workspace ); ); ); ); wtf2 = xf[ CINDEX(7) ]; linkages[ linkages[ linkages[ linkages[ linkages[ linkages[ linkages[ CINDEX(8) ] CINDEX(9) ] CINDEX(10) ] CINDEX(11) ] CINDEX(12) ] CINDEX(13) ] CINDEX(14) ] = = = = = = = xf[ xf[ xf[ xf[ xf[ xf[ t0b CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) - tfa; ] ] ] ] ] ] - xi[ xi[ xi[ xi[ xi[ xi[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ]; ]; ]; ]; ]; ]; // Linking phases 3 and 4 get_final_states( xf, get_initial_states( xi, tfa = get_final_time( t0b = get_initial_time( xad, xad, xad, xad, 3, 4, 3, 4, workspace workspace workspace workspace ); ); ); ); wti4 = xi[ CINDEX(7) ]; linkages[ linkages[ linkages[ linkages[ linkages[ linkages[ linkages[ CINDEX(15) CINDEX(16) CINDEX(17) CINDEX(18) CINDEX(19) CINDEX(20) CINDEX(21) ] ] ] ] ] ] ] = = = = = = = xf[ xf[ xf[ xf[ xf[ xf[ t0b CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) - tfa; ] ] ] ] ] ] - xi[ xi[ xi[ xi[ xi[ xi[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) ]; ]; ]; ]; ]; ]; // Linking the weight at the end of phase 2 with the weight at the beginning of phase 4 linkages[ CINDEX(22) ] = wtf2 - wti4; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; MSdata msdata; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Two burn transfer problem"; = "twoburn.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// 365 //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 4; = 22; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = 6; problem.phases(1).ncontrols = 0; problem.phases(1).nparameters problem.phases(1).nevents problem.phases(1).npath = 0; problem.phases(1).nodes problem.phases(2).nstates = 7; problem.phases(2).ncontrols = 2; problem.phases(2).nparameters problem.phases(2).nevents problem.phases(2).npath = 0; problem.phases(2).nodes problem.phases(3).nstates = 6; problem.phases(3).ncontrols = 0; problem.phases(3).nparameters problem.phases(3).nevents problem.phases(3).npath = 0; problem.phases(3).nodes problem.phases(4).nstates = 7; problem.phases(4).ncontrols = 2; problem.phases(4).nparameters problem.phases(4).nevents problem.phases(4).npath = 0; problem.phases(4).nodes = 0; = 6; = 10; = 0; = 1; = 10; = 0; = 0; = 10; = 0; = 5; = 10; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double double double double double double pti fti gti hti kti Lti wti = = = = = = = 21837080.052835; 0.0; 0.0; -0.25396764647494; 0.0; pi; 1.0; SISP = 300.0; DELTAV2 = 8000; DELTAV4 = 3000; CM2W = 32.174; double sigma = 1.0/6076.1154855643; double Re = 20925662.73; double wtf2 double wtf4 = wti*exp(-DELTAV2/(CM2W*SISP)); = wtf2*exp(-DELTAV4/(CM2W*SISP)); double double double double double 19323/sigma + Re; 0.0; 0.0; 0.0; 0.0; ptf ftf gtf htf ktf = = = = = double D2R = pi/180.0; // BOUNDS FOR PHASE 1 problem.phases(1).bounds.lower.states(1) = 10.e6; problem.phases(1).bounds.lower.states(2) = -1; 366 problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) problem.phases(1).bounds.lower.states(5) problem.phases(1).bounds.lower.states(6) = = = = -1; -1; -1; pi; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) problem.phases(1).bounds.upper.states(5) problem.phases(1).bounds.upper.states(6) = = = = = = 2e8; 1; 1; 1; 1; 30*pi; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) = = = = = = pti; fti; gti; hti; kti; Lti; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) problem.phases(1).bounds.upper.events(5) problem.phases(1).bounds.upper.events(6) = = = = = = pti; fti; gti; hti; kti; Lti; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 2000.0; = 3000.0; // BOUNDS FOR PHASE 2 problem.phases(2).bounds.lower.states(1) problem.phases(2).bounds.lower.states(2) problem.phases(2).bounds.lower.states(3) problem.phases(2).bounds.lower.states(4) problem.phases(2).bounds.lower.states(5) problem.phases(2).bounds.lower.states(6) problem.phases(2).bounds.lower.states(7) = = = = = = = 10.e6; -1; -1; -1; -1; pi; 0.0; problem.phases(2).bounds.upper.states(1) problem.phases(2).bounds.upper.states(2) problem.phases(2).bounds.upper.states(3) problem.phases(2).bounds.upper.states(4) problem.phases(2).bounds.upper.states(5) problem.phases(2).bounds.upper.states(6) problem.phases(2).bounds.upper.states(7) = = = = = = = 2.e8; 1; 1; 1; 1; 30*pi; 2.0; problem.phases(2).bounds.lower.controls(1) problem.phases(2).bounds.lower.controls(2) problem.phases(2).bounds.upper.controls(1) problem.phases(2).bounds.upper.controls(2) problem.phases(2).bounds.lower.events(1) problem.phases(2).bounds.upper.events(1) = = = = -pi; -pi; pi; pi; = wti; = wti; problem.phases(2).bounds.lower.StartTime problem.phases(2).bounds.upper.StartTime = 2000; = 3000; problem.phases(2).bounds.lower.EndTime problem.phases(2).bounds.upper.EndTime = 2100; = 3100; // BOUNDS FOR PHASE 3 problem.phases(3).bounds.lower.states(1) problem.phases(3).bounds.lower.states(2) problem.phases(3).bounds.lower.states(3) problem.phases(3).bounds.lower.states(4) problem.phases(3).bounds.lower.states(5) problem.phases(3).bounds.lower.states(6) = = = = = = 10.e6; -1; -1; -1; -1; pi; 367 problem.phases(3).bounds.upper.states(1) problem.phases(3).bounds.upper.states(2) problem.phases(3).bounds.upper.states(3) problem.phases(3).bounds.upper.states(4) problem.phases(3).bounds.upper.states(5) problem.phases(3).bounds.upper.states(6) = = = = = = 2.e8; 1.0; 1.0; 1.0; 1.0; 30*pi; problem.phases(3).bounds.lower.StartTime problem.phases(3).bounds.upper.StartTime = 2100; = 3100; problem.phases(3).bounds.lower.EndTime problem.phases(3).bounds.upper.EndTime = 21600; = 21800; // BOUNDS FOR PHASE 4 problem.phases(4).bounds.lower.states(1) problem.phases(4).bounds.lower.states(2) problem.phases(4).bounds.lower.states(3) problem.phases(4).bounds.lower.states(4) problem.phases(4).bounds.lower.states(5) problem.phases(4).bounds.lower.states(6) problem.phases(4).bounds.lower.states(7) = = = = = = = 10.e6; -1; -1; -1; -1; pi; 0.0; problem.phases(4).bounds.upper.states(1) problem.phases(4).bounds.upper.states(2) problem.phases(4).bounds.upper.states(3) problem.phases(4).bounds.upper.states(4) problem.phases(4).bounds.upper.states(5) problem.phases(4).bounds.upper.states(6) problem.phases(4).bounds.upper.states(7) = = = = = = = 2.e8; 1; 1; 1; 1; 30*pi; 2.0; problem.phases(4).bounds.lower.controls(1) problem.phases(4).bounds.lower.controls(2) problem.phases(4).bounds.upper.controls(1) problem.phases(4).bounds.upper.controls(2) = = = = -pi; -pi; pi; pi; problem.phases(4).bounds.lower.events(1) problem.phases(4).bounds.lower.events(2) problem.phases(4).bounds.lower.events(3) problem.phases(4).bounds.lower.events(4) problem.phases(4).bounds.lower.events(5) = = = = = ptf; ftf; gtf; htf; ktf; problem.phases(4).bounds.upper.events(1) problem.phases(4).bounds.upper.events(2) problem.phases(4).bounds.upper.events(3) problem.phases(4).bounds.upper.events(4) problem.phases(4).bounds.upper.events(5) = = = = = ptf; ftf; gtf; htf; ktf; problem.phases(4).bounds.lower.StartTime problem.phases(4).bounds.upper.StartTime = 21600; = 21800; problem.phases(4).bounds.lower.EndTime problem.phases(4).bounds.upper.EndTime = 21650; = 21900; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int nnodes; int ncontrols; int nstates; int iphase; DMatrix x_guess, u_guess, time_guess, param_guess, xini, xfinal; // Phase 1 nnodes = problem.phases(1).nodes(1); 368 nstates iphase = 1; x_guess = time_guess = = problem.phases(1).nstates; zeros(nstates,nnodes); linspace(0.0,2690,nnodes); xini.Resize(6,1); xini(1)= pti; xini(2)=fti;xini(3)=gti;xini(4)=hti;xini(5)=kti;xini(6)=Lti; rk4_propagate( dae, u_guess, time_guess, xini, param_guess, problem, iphase, x_guess, NULL); tra(x_guess).Print("x_guess(iphase=1)"); xfinal = x_guess(colon(),nnodes); problem.phases(1).guess.states = x_guess; problem.phases(1).guess.time = time_guess; // Phase 2 nnodes = problem.phases(2).nodes(1); nstates = problem.phases(2).nstates; ncontrols = problem.phases(2).ncontrols; iphase = 2; u_guess = x_guess = time_guess = zeros(ncontrols,nnodes); zeros(nstates,nnodes); linspace(2690,2840,nnodes); xini.Resize(7,1); xini(1)= xfinal(1); xini(2)=xfinal(2);xini(3)=xfinal(3);xini(4)=xfinal(4);xini(5)=xfinal(5);xini(6)=xfinal(6); xini(7)= wti; u_guess(1,colon()) = 0.148637e-2*D2R*ones(1,nnodes); u_guess(2,colon()) = -9.08446*D2R*ones(1,nnodes); rk4_propagate( dae, u_guess, time_guess, xini, param_guess, problem, iphase, x_guess, NULL); tra(x_guess).Print("x_guess(iphase=2)"); xfinal = x_guess(colon(),nnodes); double wtf2__ = xfinal(7); problem.phases(2).guess.states = x_guess; problem.phases(2).guess.controls = u_guess; problem.phases(2).guess.time = time_guess; // Phase 3 nnodes = problem.phases(3).nodes(1); nstates = problem.phases(3).nstates; iphase = 3; x_guess = time_guess = zeros(nstates,nnodes); linspace(2840,21650,nnodes); xini.Resize(6,1); xini(1)= xfinal(1); xini(2)=xfinal(2);xini(3)=xfinal(3);xini(4)=xfinal(4);xini(5)=xfinal(5);xini(6)=xfinal(6); rk4_propagate( dae, u_guess, time_guess, xini, param_guess, problem, iphase, x_guess, NULL); tra(x_guess).Print("x_guess(iphase=3)"); xfinal = x_guess(colon(),nnodes); problem.phases(3).guess.states = x_guess; problem.phases(3).guess.time = time_guess; // Phase 4 nnodes = problem.phases(4).nodes(1); nstates = problem.phases(4).nstates; ncontrols = problem.phases(4).ncontrols; iphase = 4; 369 u_guess = x_guess = time_guess = zeros(ncontrols,nnodes); zeros(nstates,nnodes); linspace(21650,21700,nnodes); u_guess(1,colon()) = -0.136658e-2*D2R*ones(1,nnodes); u_guess(2,colon()) = 49.7892*D2R*ones(1,nnodes); xini.Resize(7,1); xini(1)= xfinal(1); xini(2)=xfinal(2);xini(3)=xfinal(3);xini(4)=xfinal(4);xini(5)=xfinal(5);xini(6)=xfinal(6); xini(7)= wtf2__; rk4_propagate( dae, u_guess, time_guess, xini, param_guess, problem, iphase, x_guess, NULL); tra(x_guess).Print("x_guess(iphase=4)"); problem.phases(4).guess.states = x_guess; problem.phases(4).guess.controls = u_guess; problem.phases(4).guess.time = time_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// // algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.defect_scaling algorithm.jac_sparsity_ratio algorithm.collocation_method algorithm.diff_matrix algorithm.mesh_refinement algorithm.mr_max_iterations algorithm.ode_tolerance = = = = = = = = 1000; 1.e-6; "IPOPT"; "automatic"; "automatic"; "jacobian-based"; 0.11; "trapezoidal"; = "central-differences"; = "automatic"; = 5; = 1.0e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, t, w2, xi, w4, ti; x t = solution.get_states_in_phase(1); = solution.get_time_in_phase(1); w2 w2 = solution.get_states_in_phase(2); = w2(7,colon()); w4 w4 = solution.get_states_in_phase(4); = w4(7,colon()); for(int i=2;i<=problem.nphases;i++) { xi = solution.get_states_in_phase(i); ti = solution.get_time_in_phase(i); xi = xi(colon(1,6),colon()); x = x || xi; t = t || ti; } DMatrix DMatrix DMatrix DMatrix u_phase2 u_phase4 t2 t4 = = = = solution.get_controls_in_phase(2); solution.get_controls_in_phase(4); solution.get_time_in_phase(2); solution.get_time_in_phase(4); 370 //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// // // // x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// double R2D = 180/pi; DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix DMatrix x1 = x(1,colon())/1.e6; x2 = x(2,colon()); x3 = x(3,colon()); x4 = x(4,colon()); x5 = x(5,colon()); x6 = x(6,colon()); x7 = x(7,colon()); theta_phase2; theta_phase4; phi_phase2; phi_phase4; r; theta_phase2 = u_phase2(1,colon())*R2D; phi_phase2 = u_phase2(2,colon())*R2D; theta_phase4 = u_phase4(1,colon())*R2D; phi_phase4 = u_phase4(2,colon())*R2D; compute_cartesian_trajectory(x,r); r.Save("r.dat"); double ft2km = 0.0003048; r = r*ft2km; plot(t2,theta_phase2,problem.name+": thrust theta phase 2","time (s)", "theta (deg)", "theta"); plot(t2,phi_phase2,problem.name+": thrust phi phase 2","time (s)", "phi (deg)", "phi"); plot(t4,theta_phase4,problem.name+": thrust theta phase 4","time (s)", "theta (deg)", "theta"); plot(t4,phi_phase4,problem.name+": thrust phi phase 4","time (s)", "phi (deg)", "phi"); plot(t2,theta_phase2,problem.name+": "pdf", "theta2.pdf"); thrust pitch angle phase 2","time (s)", "theta (deg)", "theta", plot(t2,phi_phase2,problem.name+": thrust angle phase 2","time (s)", "phi (deg)", "phi", "pdf", "phi2.pdf"); plot(t4,theta_phase4,problem.name+": thrust pitch angle phase 4","time (s)", "theta (deg)", "theta", "pdf", "theta4.pdf"); plot(t4,phi_phase4,problem.name+": thrust yaw angle phase 4","time (s)", "phi (deg)", "phi", "pdf", "phi4.pdf"); plot3(r(1,colon()), r(2,colon()), r(3,colon()), "Two burn trasnfer trajectory", "x (km)", "y (km)", "z (km)", NULL, NULL, "30,110"); plot3(r(1,colon()), r(2,colon()), r(3,colon()), "Two burn transfer trajectory", "x (km)", "y (km)", "z (km)", "pdf", "trajectory.pdf", "30,110"); plot(r(1,colon()), r(2,colon()), "x (km)", "y (km)"); "Two burn trajectory - projection on the equatorial plane", } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// 371 The output from PSOPT is summarised in the box below. The controls during the burn periods are shown Figures 3.101 to 3.104, which show the control variables during phases 2 and 4, and Figure 3.105, which shows the trajectory in cartesian co-ordinates. PSOPT results summary ===================== Problem: Two burn transfer problem CPU time (seconds): 8.818389e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 15:55:48 2019 Optimal (unscaled) cost function value: -2.367249e-01 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.609964e+03 Phase 1 maximum relative local error: 1.385002e-06 Phase 2 endpoint cost function value: 0.000000e+00 Phase 2 integrated part of the cost: 0.000000e+00 Phase 2 initial time: 2.609964e+03 Phase 2 final time: 2.751426e+03 Phase 2 maximum relative local error: 1.225041e-06 Phase 3 endpoint cost function value: 0.000000e+00 Phase 3 integrated part of the cost: 0.000000e+00 Phase 3 initial time: 2.751426e+03 Phase 3 final time: 2.163415e+04 Phase 3 maximum relative local error: 1.174820e-05 Phase 4 endpoint cost function value: -2.367249e-01 Phase 4 integrated part of the cost: 0.000000e+00 Phase 4 initial time: 2.163415e+04 Phase 4 final time: 2.168351e+04 Phase 4 maximum relative local error: 5.521144e-06 NLP solver reports: The problem has been solved! 372 Iter Method Nodes NV NC OE CE JE HE RHS max CPUa 1 2 3 4 5 CPUb - TRAPZ TRAPZ H-S H-S H-S - 40 56 76 104 133 - 308 428 650 888 1132 - 298 402 532 714 902 - 637 25 38 46 66 812 636 26 39 47 67 815 20 23 37 41 57 178 0 0 0 0 0 0 48336 2808 8580 14288 26197 100209 4.942e-02 4.129e-03 1.568e-04 2.222e-05 8.212e-06 - 2.400e-01 3.600e-01 1.000e+00 1.590e+00 2.880e+00 3.786e+01 4.393e+01 Key: Iter=iteration number, NV=number of variables, NC=number of constraints, OE=objective evalu- ations, CE = constraint evaluations, JE = Jacobian evaluations, HE = Hessian evaluations, RHS = ODE right hand side evaluations, max = maximum relative ODE error, CPUa = CPU time in seconds spent by NLP algorithm, CPUb = additional CPU time in seconds spent by PSOPT Table 3.8: Mesh refinement statistics: Two burn transfer problem Two burn transfer problem: thrust pitch angle phase 2 1 theta 0.9 0.8 theta (deg) 0.7 0.6 0.5 0.4 0.3 0.2 2600 2620 2640 2660 2680 2700 2720 2740 time (s) Figure 3.101: Pitch angle during phase 2 373 2760 Two burn transfer problem: thrust angle phase 2 -6.5 phi -7 phi (deg) -7.5 -8 -8.5 -9 -9.5 2600 2620 2640 2660 2680 2700 2720 2740 2760 time (s) Figure 3.102: Yaw angle during phase 2 Two burn transfer problem: thrust pitch angle phase 4 0.08 theta 0.06 theta (deg) 0.04 0.02 0 -0.02 -0.04 21630 21635 21640 21645 21650 21655 21660 21665 21670 21675 time (s) Figure 3.103: Pitch angle during phase 4 374 21680 21685 Two burn transfer problem: thrust yaw angle phase 4 phi 50 45 phi (deg) 40 35 30 25 21630 21635 21640 21645 21650 21655 21660 21665 21670 21675 21680 21685 time (s) Figure 3.104: Yaw angle during phase 4 Two burn transfer trajectory 0.001 0.0005 0 -0.0005 -0.001 z (km) -7000 -6000 -5000 -4000 -3000 -1 -2000 -0.5 0 -1000 0.5 1 y (km) 0 Figure 3.105: Two burn transfer trajectory 375 x (km) 3.40 Two link robotic arm Consider the following optimal control problem [29]. Find tf , and u(t) ∈ [0, tf ] to minimize the cost functional J = tf (3.165) subject to the dynamic constraints ẋ1 = 9.0 3.0 sin(x3 )( 4.0 cos(x3 )x21 +2∗x22 )+ 4.0 (u1 −u2 )− 2.0 cos(x3 )u2 3.0 31.0 9.0 + 2 36.0 4.0 sin (x3 ) ẋ2 = 7.0 ∗x21 + 9.0 cos(x3 )x22 )− 7.0 u + 3.0 cos(x3 )(u1 −u2 )) −(sin(x3 )∗( 2.0 4.0 3.0 2 2.0 31.0 9.0 + 2 36.0 (3.166) 4.0 sin (x3 ) ẋ3 = x2 − x1 ẋ4 = x1 the boundary conditions: x1 (0) x2 (0) x3 (0) x4 (0) = 0 x1 (tf ) = 0 x2 (tf ) = 0.5 x3 (tf ) = 0.0 x4 (tf ) = 0 = 0 = 0.5 = 0.522 (3.167) The control bounds: −1 ≤u1 (t) ≤ 1 −1 ≤u2 (t) ≤ 1 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// twolinkarm.cxx ////////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Two link arm problem //////////////// //////// Last modified: 04 January 2009 //////////////// //////// Reference: PROPT users guide //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { 376 (3.168) return tf; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble xdot, ydot, vdot; adouble adouble adouble adouble x1 x2 x3 x4 = = = = states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; adouble u1 = controls[ CINDEX(1) ]; adouble u2 = controls[ CINDEX(2) ]; adouble num1 = sin(x3)*( (9.0/4.0)*cos(x3)*x1*x1+2*x2*x2 ) + (4.0/3.0)*(u1-u2)-(3.0/2.0)*cos(x3)*u2; adouble num2 = -(sin(x3)*((7.0/2.0)*x1*x1+(9.0/4.0)*cos(x3)*x2*x2) -(7.0/3.0)*u2+(3.0/2.0)*cos(x3)*(u1-u2)); adouble den = 31.0/36.0 + 9.0/4.0*pow(sin(x3),2); derivatives[ derivatives[ derivatives[ derivatives[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = num1/den; num2/den; x2 - x1; x1; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x10 = initial_states[ CINDEX(1) ]; adouble x20 = initial_states[ CINDEX(2) ]; adouble x30 = initial_states[ CINDEX(3) ]; adouble x40 = initial_states[ CINDEX(4) ]; adouble x1f = final_states[ CINDEX(1) ]; adouble x2f = final_states[ CINDEX(2) ]; adouble x3f = final_states[ CINDEX(3) ]; adouble x4f = final_states[ CINDEX(4) ]; e[ e[ e[ e[ e[ e[ e[ e[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) CINDEX(5) CINDEX(6) CINDEX(7) CINDEX(8) ] ] ] ] ] ] ] ] = = = = = = = = x10; x20; x30; x40; x1f; x2f; x3f; x4f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // No linkages as this is a single phase problem 377 } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name = "Two link robotic arm"; problem.outfilename = "twolink.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup ///////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = = = = 4; 2; 8; 0; = 40; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t; DMatrix lambda, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) = = = = -2.0; -2.0; -2.0; -2.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) = = = = 2.0; 2.0; 2.0; 2.0; problem.phases(1).bounds.lower.controls(1) = -1.0; problem.phases(1).bounds.lower.controls(2) = -1.0; problem.phases(1).bounds.upper.controls(1) = 1.0; problem.phases(1).bounds.upper.controls(2) = 1.0; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) problem.phases(1).bounds.lower.events(5) problem.phases(1).bounds.lower.events(6) = = = = = = 0.0; 0.0; 0.5; 0.0; 0.0; 0.0; 378 problem.phases(1).bounds.lower.events(7) = 0.5; problem.phases(1).bounds.lower.events(8) = 0.522; problem.phases(1).bounds.upper.events = problem.phases(1).bounds.lower.events; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 10.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x0(4,40); x0(1,colon()) x0(2,colon()) x0(3,colon()) x0(4,colon()) = = = = linspace(0.0,0.0, 40); linspace(0.0,0.0, 40); linspace(0.5,0.5, 40); linspace(0.522,0.522, 40); problem.phases(1).guess.controls problem.phases(1).guess.states problem.phases(1).guess.time = zeros(1,40); = x0; = linspace(0.0, 3.0, 40); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x u t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name + ": states", "time (s)", "states", "x1 x2 x3 x4"); 379 plot(t,u,problem.name + ": controls", "time (s)", "controls", "u1 u2"); plot(t,x,problem.name + ": states", "time (s)", "states", "x1 x2 x3 x4", "pdf", "twolinkarm_states.pdf"); plot(t,u,problem.name + ": controls", "time (s)", "controls", "u1 u2", "pdf", "twolinkarm_controls.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.106 and 3.107, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Two link robotic arm CPU time (seconds): 1.080000e+00 NLP solver used: IPOPT Optimal (unscaled) cost function value: 2.988662e+00 Phase 1 endpoint cost function value: 2.988662e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 2.988662e+00 Phase 1 maximum relative local error: 3.815633e-04 NLP solver reports: The problem solved! 3.41 Two-phase path tracking robot Consider the following two-phase optimal control problem, which consists of a robot following a specified path [38, 36]. Find u(t) ∈ [0, 2] to minimize the cost functional Z 2 J= [100(x1 − x1,ref )2 + 100(x2 − x2,ref )2 (3.169) 0 2 2 +500(x3 − x3,ref ) + 500(x4 − x4,ref ) ]dt 380 Two link robotic arm: states 0.001 x1 x2 x3 x4 states 0.0005 0 -0.0005 -0.001 0 0.5 1 1.5 2 2.5 3 time (s) Figure 3.106: States for two-link robotic arm problem Two link robotic arm: controls 0.001 u1 u2 controls 0.0005 0 -0.0005 -0.001 0 0.5 1 1.5 2 2.5 time (s) Figure 3.107: Controls for two link robotic arm problem 381 3 subject to the dynamic constraints ẋ1 ẋ2 ẋ3 ẋ4 = = = = x3 x4 u1 u2 (3.170) the boundary conditions: x1 (0) x2 (0) x3 (0) x4 (0) = 0 x1 (2) = 0 x2 (2) = 0.5 x3 (2) = 0.0 x4 (2) = 0.5 = 0.5 = 0 = 0.5 (3.171) where the reference signals are given by: t 1 (0 ≤ t < 1), (1 ≤ t ≤ 2) 2 2 t−1 = 0 (0 ≤ t < 1), (1 ≤ t ≤ 2) 2 1 = (0 ≤ t < 1), 0 (1 ≤ t ≤ 2) 2 1 = 0 (0 ≤ t < 1), (1 ≤ t ≤ 2) 2 x1,ref = x2,ref x3,ref x4,ref (3.172) The PSOPT code that solves this problem is shown below. The first phase covers the period t ∈ [0, 1], while the second phase covers the period t ∈ [1, 2]. ////////////////////////////////////////////////////////////////////////// ////////////////// twophase_robot.cxx //////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Two phase path tracking robot //////////////// //////// Last modified: 09 January 2009 //////////////// //////// Reference: PROPT Users Guide //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// 382 ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble retval; adouble x1, x2, x3, x4; adouble x1ref, x2ref, x3ref, x4ref; double w1, w2, w3, w4; w1 w2 w3 w4 x1 x2 x3 x4 = = = = = = = = 100.0; 100.0; 500.0; 500.0; states[0]; states[1]; states[2]; states[3]; if (iphase==1) { x1ref = time/2; x2ref = 0.0; x3ref = 0.5; x4ref = 0.0; } if (iphase==2) { x1ref = 0.5; x2ref = (time-1.0)/2.0; x3ref = 0.0; x4ref = 0.5; } retval = w1*pow(x1-x1ref,2)+w2*pow(x2-x2ref,2)+w3*pow(x3-x3ref,2)+w4*pow(x4-x4ref,2); return retval; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x3, x4, u1, u2; x3 = states[2]; x4 = states[3]; u1 = controls[0]; u2 = controls[1]; derivatives[0] derivatives[1] derivatives[2] derivatives[3] = = = = x3; x4; u1; u2; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, 383 adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x1i, x2i, x3i, x4i; adouble x1f, x2f, x3f, x4f; if (iphase == 1) { x1i x2i x3i x4i = = = = e[0] e[1] e[2] e[3] initial_states[0]; initial_states[1]; initial_states[2]; initial_states[3]; = = = = x1i; x2i; x3i; x4i; } else if (iphase == 2) { x1f x2f x3f x4f = = = = e[0] e[1] e[2] e[3] final_states[0]; final_states[1]; final_states[2]; final_states[3]; = = = = x1f; x2f; x3f; x4f; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { int index = 0; auto_link( linkages, &index, xad, 1, 2, workspace ); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Two phase path tracking robot"; = "twophro.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 2; = 5; 384 psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup /////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath = = = = 4; 2; 4; 0; problem.phases(2).nstates problem.phases(2).ncontrols problem.phases(2).nevents problem.phases(2).npath = = = = 4; 2; 4; 0; problem.phases(1).nodes problem.phases(2).nodes = 30; = 30; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double x1i x2i x3i x4i = = = = 0.0; 0.0; 0.5; 0.0; double double double double x1f x2f x3f x4f = = = = 0.5; 0.5; 0.0; 0.5; // Phase 0 bounds problem.phases(1).bounds.lower.states(1) problem.phases(1).bounds.lower.states(2) problem.phases(1).bounds.lower.states(3) problem.phases(1).bounds.lower.states(4) = = = = -10.0; -10.0; -10.0; -10.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) = = = = 10.0; 10.0; 10.0; 10.0; problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.upper.controls(2) = -10.0; = 10.0; = -10.0; = 10.0; problem.phases(1).bounds.lower.events(1) problem.phases(1).bounds.lower.events(2) problem.phases(1).bounds.lower.events(3) problem.phases(1).bounds.lower.events(4) = = = = x1i; x2i; x3i; x4i; problem.phases(1).bounds.upper.events(1) problem.phases(1).bounds.upper.events(2) problem.phases(1).bounds.upper.events(3) problem.phases(1).bounds.upper.events(4) = = = = x1i; x2i; x3i; x4i; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; // Phase 1 bounds problem.phases(2).bounds.lower.states(1) problem.phases(2).bounds.lower.states(2) problem.phases(2).bounds.lower.states(3) problem.phases(2).bounds.lower.states(4) = = = = -10.0; -10.0; -10.0; -10.0; problem.phases(2).bounds.upper.states(1) = 10.0; problem.phases(2).bounds.upper.states(2) = 10.0; 385 problem.phases(2).bounds.upper.states(3) = 10.0; problem.phases(2).bounds.upper.states(4) = 10.0; problem.phases(2).bounds.lower.controls(1) problem.phases(2).bounds.upper.controls(1) problem.phases(2).bounds.lower.controls(2) problem.phases(2).bounds.upper.controls(2) = -10.0; = 10.0; = -10.0; = 10.0; problem.phases(2).bounds.lower.events(1) problem.phases(2).bounds.lower.events(2) problem.phases(2).bounds.lower.events(3) problem.phases(2).bounds.lower.events(4) = = = = x1f; x2f; x3f; x4f; problem.phases(2).bounds.upper.events(1) problem.phases(2).bounds.upper.events(2) problem.phases(2).bounds.upper.events(3) problem.phases(2).bounds.upper.events(4) = = = = x1f; x2f; x3f; x4f; problem.phases(2).bounds.lower.StartTime problem.phases(2).bounds.upper.StartTime = 1.0; = 1.0; problem.phases(2).bounds.lower.EndTime problem.phases(2).bounds.upper.EndTime = 2.0; = 2.0; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int iphase; DMatrix u0(2,30); DMatrix x0(4,30); DMatrix time_guess0 DMatrix time_guess1 = linspace(0.0, 1.0 , 30); = linspace(1.0, 2.0 , 30); iphase = 1; u0 = zeros(2,30+1); x0(1,colon()) x0(2,colon()) x0(3,colon()) x0(4,colon()) = = = = linspace(x1i,(x1i+x1f)/2, linspace(x2i,(x2i+x2f)/2, linspace(x3i,(x3i+x3f)/2, linspace(x4i,(x4i+x4f)/2, 30); 30); 30); 30); problem.phases(iphase).guess.controls = u0; problem.phases(iphase).guess.states = x0; problem.phases(iphase).guess.time = time_guess0; iphase = 2; u0 = zeros(2,30+1); x0(1,colon()) x0(2,colon()) x0(3,colon()) x0(4,colon()) = = = = linspace((x1i+x1f)/2, linspace((x2i+x2f)/2, linspace((x3i+x3f)/2, linspace((x4i+x4f)/2, x1f, x2f, x3f, x4f, 30); 30); 30); 30); problem.phases(iphase).guess.controls = u0; problem.phases(iphase).guess.states = x0; problem.phases(iphase).guess.time = time_guess1; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives = "IPOPT"; = "automatic"; = "automatic"; 386 algorithm.hessian algorithm.nlp_iter_max algorithm.nlp_tolerance = "exact"; = 1000; = 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix xphase1 = solution.get_states_in_phase(1); DMatrix uphase1 = solution.get_controls_in_phase(1); DMatrix tphase1 = solution.get_time_in_phase(1); DMatrix xphase2 = solution.get_states_in_phase(2); DMatrix uphase2 = solution.get_controls_in_phase(2); DMatrix tphase2 = solution.get_time_in_phase(2); DMatrix x = (xphase1 || xphase2); DMatrix u = (uphase1 || uphase2); DMatrix t = (tphase1 || tphase2); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states"); plot(t,u,problem.name, "time (s)", "control"); plot(t,x,problem.name+": states", "time (s)", "states", "x1 x2 x3 x4", "pdf", "twophro_states.pdf"); plot(t,u,problem.name+": controls", "time (s)", "controls", "u1 u2", "pdf", "twophro_controls.pdf" ); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.108 and 3.109, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Two phase path tracking robot CPU time (seconds): 1.759431e+00 NLP solver used: IPOPT PSOPT release number: 4.0 387 Two phase path tracking robot: states 0.5 x1 x2 x3 x4 0.4 states 0.3 0.2 0.1 0 0 0.5 1 1.5 2 time (s) Figure 3.108: States for two-phase path tracking robot problem Date and time of this run: Thu Feb 21 17:07:41 2019 Optimal (unscaled) cost function value: 1.042568e+00 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 5.212840e-01 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 3.443287e-04 Phase 2 endpoint cost function value: 0.000000e+00 Phase 2 integrated part of the cost: 5.212840e-01 Phase 2 initial time: 1.000000e+00 Phase 2 final time: 2.000000e+00 Phase 2 maximum relative local error: 3.443298e-04 NLP solver reports: The problem has been solved! 3.42 Two-phase Schwartz problem Consider the following two-phase optimal control problem [36]. Find u(t) ∈ [0, 2.9] to minimize the cost functional J = 5(x1 (tf )2 + x2 (tf )2 ) 388 (3.173) Two phase path tracking robot: controls u1 u2 10 controls 5 0 -5 -10 0 0.5 1 1.5 2 time (s) Figure 3.109: Control for two phase path tracking robot problem subject to the dynamic constraints ẋ1 = x2 ẋ2 = u − 0.1(1 + 2x21 )x2 (3.174) the boundary conditions: x1 (0) = 1 x2 (0) = 1 (3.175) and the constraints for t < 1: 2 1−9(x1 − 1) − x2 − 0.4 0.3 2 −0.8 ≤ x2 ≤0 (3.176) −1 ≤ u ≤ 1 The PSOPT code that solves this problem is shown below. The problem has been divided into two phases. The first phase covers the period t ∈ [0, 1], while the second phase covers the period t ∈ [1, 2.9]. ////////////////////////////////////////////////////////////////////////// ////////////////// twophase_schwartz.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Two phase Schwartz problem //////////////// 389 //////// Last modified: 09 January 2009 //////////////// //////// Reference: PROPT Users Guide //////////////// //////// (See PSOPT handbook for full reference) /////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble retval; adouble x1f, x2f; // Phase 1 cost if ( iphase == 1) { retval = 0.0; } // Phase 2 cost if( iphase == 2 ) { x1f = final_states[0]; x2f = final_states[1]; retval = 5*(x1f*x1f + x2f*x2f); } return retval; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { adouble x1 = states[0]; adouble x2 = states[1]; adouble u = controls[0]; derivatives[0] = x2; derivatives[1] = u-0.1*(1+2*x1*x1)*x2; if(iphase==1) { path[0] = 1.0 - 9.0*((x1-1.0)*(x1-1.0))-((x2-0.4)*(x2-0.4))/(0.3*0.3); } 390 } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble x1i = initial_states[0]; adouble x2i = initial_states[1]; if (iphase==1) { e[0] = x1i; e[1] = x2i; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { int index = 0; auto_link( linkages, &index, xad, 1, 2, workspace ); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Two phase Schwartz problem"; = "twophsc.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 2; = 3; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath = = = = 2; 1; 2; 1; problem.phases(2).nstates problem.phases(2).ncontrols problem.phases(2).nevents problem.phases(2).npath = = = = 2; 1; 0; 0; problem.phases(1).nodes problem.phases(2).nodes = 40; = 40; 391 psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// double double double double double double double double double x1L = -20.0; x2L_phase1 = -0.8; x2L_phase2 = -10.0; uL = -1.0; x1U = 10.0; x2U = 10.0; uU = 1.0; hL = -100.0; hU = 0.0; // Phase 0 bounds problem.phases(1).bounds.lower.states(1) = x1L; problem.phases(1).bounds.lower.states(2) = x2L_phase1; problem.phases(1).bounds.upper.states(1) = x1U; problem.phases(1).bounds.upper.states(2) = x2U; problem.phases(1).bounds.lower.controls(1) = uL; problem.phases(1).bounds.upper.controls(1) = uU; problem.phases(1).bounds.lower.events(1) = 1.0; problem.phases(1).bounds.lower.events(2) = 1.0; problem.phases(1).bounds.upper.events(1) = 1.0; problem.phases(1).bounds.upper.events(2) = 1.0; problem.phases(1).bounds.lower.path(1) = hL; problem.phases(1).bounds.upper.path(1) = hU; problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = 1.0; = 1.0; // Phase 1 bounds problem.phases(2).bounds.lower.states(1) = x1L; problem.phases(2).bounds.lower.states(2) = x2L_phase2; problem.phases(2).bounds.upper.states(1) = x1U; problem.phases(2).bounds.upper.states(2) = x2U; problem.phases(2).bounds.lower.controls(1) = -50.0; problem.phases(2).bounds.upper.controls(1) = 50.0; problem.phases(2).bounds.lower.StartTime problem.phases(2).bounds.upper.StartTime = 1.0; = 1.0; problem.phases(2).bounds.lower.EndTime problem.phases(2).bounds.upper.EndTime = 2.9; = 2.9; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// int iphase; DMatrix u0(1,40); DMatrix x0(2,40); 392 DMatrix time_guess0 DMatrix time_guess1 = linspace(0.0, 1.0 , 40); = linspace(1.0, 2.9 , 40); iphase = 1; u0 = zeros(1,40); x0(1,colon()) = linspace(1.0,1.0, 40); x0(2,colon()) = linspace(1.0,1.0, 40); problem.phases(iphase).guess.controls = u0; problem.phases(iphase).guess.states = x0; problem.phases(iphase).guess.time = time_guess0; iphase = 2; u0 = zeros(1,40); x0(1,colon()) = linspace(1.0,1.0, 40); x0(2,colon()) = linspace(1.0,1.0, 40); problem.phases(iphase).guess.controls = u0; problem.phases(iphase).guess.states = x0; problem.phases(iphase).guess.time = time_guess1; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.nlp_iter_max algorithm.nlp_tolerance = = = = = "IPOPT"; "automatic"; "automatic"; 1000; 1.e-6; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ///////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix xphase1 = solution.get_states_in_phase(1); DMatrix uphase1 = solution.get_controls_in_phase(1); DMatrix tphase1 = solution.get_time_in_phase(1); DMatrix xphase2 = solution.get_states_in_phase(2); DMatrix uphase2 = solution.get_controls_in_phase(2); DMatrix tphase2 = solution.get_time_in_phase(2); DMatrix x = (xphase1 || xphase2); DMatrix u = (uphase1 || uphase2); DMatrix t = (tphase1 || tphase2); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,x,problem.name, "time (s)", "states", "x1 x2"); plot(t,u,problem.name, "time (s)", "control", "u"); plot(t,x,problem.name, "time (s)", "states", "x1 x2", "pdf", "twophsc_states.pdf"); plot(t,u,problem.name, "time (s)", "control", "u", "pdf", "twophsc_control.pdf"); 393 } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.110 and 3.111, which contain the elements of the state and the control, respectively. PSOPT results summary ===================== Problem: Two phase Schwartz problem CPU time (seconds): 1.668786e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 17:25:06 2019 Optimal (unscaled) cost function value: 4.640127e-15 Phase 1 endpoint cost function value: 0.000000e+00 Phase 1 integrated part of the cost: 0.000000e+00 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 1.000000e+00 Phase 1 maximum relative local error: 5.734565e-06 Phase 2 endpoint cost function value: 4.640127e-15 Phase 2 integrated part of the cost: 0.000000e+00 Phase 2 initial time: 1.000000e+00 Phase 2 final time: 2.900000e+00 Phase 2 maximum relative local error: 9.827730e-05 NLP solver reports: The problem has been solved! 3.43 Vehicle launch problem This problem consists of the launch of a space vehicle. See [33, 2] for a full description of the problem. Only a brief description is given here. The flight of the vehicle can be divided into four phases, with dry masses ejected from the vehicle at the end of phases 1, 2 and 3. The final times of phases 1, 2 and 3 are fixed, while the final time of phase 4 is free. The optimal control 394 Two phase Schwartz problem 2 x1 x2 1.5 1 states 0.5 0 -0.5 -1 -1.5 0 0.5 1 1.5 2 2.5 3 time (s) Figure 3.110: States for two-phase Schwartz problem Two phase Schwartz problem u 2 1 control 0 -1 -2 -3 -4 -5 -6 0 0.5 1 1.5 2 2.5 time (s) Figure 3.111: Control for two-phase Schwartz problem 395 3 problem is to find the control, u, that minimizes the cost function J = −m(4) (tf ) (3.177) In other words, it is desired to maximise the vehicle mass at the end of phase 4. The dynamics are given by: ṙ = v µ T D r+ u+ 3 krk m m T ṁ = − g0 Isp v̇ = − (3.178) T T where r(t) = x(t) y(t) z(t) is the position, v = vx (t) vy (t) vz (t) is the Cartesian ECI velocity, µ is the gravitational parameter, T is the vacuum thrust, m is the mass, g0 is the acceleration due to gravity at sea level, T is the thrust Isp is the specific impulse of the engine, u = ux uy uz T direction, and D = Dx Dy Dz is the drag force, which is given by: 1 D = − CD Aref ρkvrel kvrel (3.179) 2 where CD is the drag coefficient, Aref is the reference area, ρ is the atmospheric density, and vrel is the Earth relative velocity, where vrel is given as vrel = v − ω × r (3.180) where ω is the angular velocity of the Earth relative to inertial space. The atmospheric density is modeled as follows ρ = ρ0 exp[−h/h0 ] (3.181) where ρ0 is the atmospheric density at sea level, h = krk−Re is the altitude, Re is the equatorial radius of the Earth, and h0 is the density scale height. The numerical values for these constants can be found in the code. The vehicle starts on the ground at rest (relative to the Earth) at time t0 , so that the initial conditions are T r(t0 ) = r0 = 5605.2 0 3043.4 km T (3.182) v(t0 ) = v0 = 0 0.4076 0 km/s m(t0 ) = m0 = 301454 kg The terminal constraints define the target transfer orbit, which is defined in orbital elements as af = 24361.14 km, ef = 0.7308, if = 28.5 deg, (3.183) Ωf = 269.8 deg, ωf = 130.5 deg 396 There is also a path constraint associated with this problem: ||u||2 = 1 (3.184) The following linkage constraints force the position and velocity to be continuous and also account for discontinuity in the mass state due to the ejections at the end of phases 1, 2 and 3: r(p) (tf ) − r(p+1) (t0 ) = 0, v(p) (tf ) − v(p+1) (t0 ) = 0, (p) m(p) (tf ) − mdry − m(p+1) (t0 ) = 0 (p = 1, . . . , 3) where the superscript (p) represents the phase number. The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// ////////////////// launch.cxx ////////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example //////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Multiphase vehicle launch //////////////// //////// Last modified: 05 January 2009 //////////////// //////// Reference: GPOPS Manual //////////////// //////// (See PSOPT handbook for full reference) //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" ////////////////////////////////////////////////////////////////////////// /////////////////// Declare auxiliary functions //////////////////////// ////////////////////////////////////////////////////////////////////////// void oe2rv(DMatrix& oe, double mu, DMatrix* ri, DMatrix* vi); void rv2oe(adouble* rv, adouble* vv, double mu, adouble* oe); ////////////////////////////////////////////////////////////////////////// ///////// Declare an auxiliary structure to hold local constants /////// ////////////////////////////////////////////////////////////////////////// struct Constants { DMatrix* omega_matrix; double mu; double cd; double sa; double rho0; double H; double Re; double g0; double thrust_srb; double thrust_first; double thrust_second; double ISP_srb; double ISP_first; double ISP_second; }; typedef struct Constants Constants_; ////////////////////////////////////////////////////////////////////////// 397 (3.185) /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble retval; adouble mass_tf = final_states[6]; if (iphase < 4) retval = 0.0; if (iphase== 4) retval = -mass_tf; return retval; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { return 0.0; } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { int j; Constants_& CONSTANTS = *( (Constants_ *) workspace->problem->user_data ); adouble* x = states; adouble* u = controls; adouble r[3]; r[0]=x[0]; r[1]=x[1]; r[2]=x[2]; adouble v[3]; v[0]=x[3]; v[1]=x[4]; v[2]=x[5]; adouble m = x[6]; double T_first, T_second, T_srb, T_tot, m1dot, m2dot, mdot; adouble rad = sqrt( dot( r, r, 3) ); DMatrix& omega_matrix = *CONSTANTS.omega_matrix; adouble vrel[3]; for (j=0;j<3;j++) vrel[j] = v[j] - omega_matrix(j+1,1)*r[0] -omega_matrix(j+1,2)*r[1] - omega_matrix(j+1,3)*r[2]; adouble speedrel = sqrt( dot(vrel,vrel,3) ); adouble altitude = rad-CONSTANTS.Re; adouble rho = CONSTANTS.rho0*exp(-altitude/CONSTANTS.H); double a1 = CONSTANTS.rho0*CONSTANTS.sa*CONSTANTS.cd; adouble a2 = a1*exp(-altitude/CONSTANTS.H); adouble bc = (rho/(2*m))*CONSTANTS.sa*CONSTANTS.cd; adouble bcspeed = bc*speedrel; adouble Drag[3]; for(j=0;j<3;j++) Drag[j] = - (vrel[j]*bcspeed); adouble muoverradcubed = (CONSTANTS.mu)/(pow(rad,3)); 398 adouble grav[3]; for(j=0;j<3;j++) grav[j] = -muoverradcubed*r[j]; if (iphase==1) { T_srb = 6*CONSTANTS.thrust_srb; T_first = CONSTANTS.thrust_first; T_tot = T_srb+T_first; m1dot = -T_srb/(CONSTANTS.g0*CONSTANTS.ISP_srb); m2dot = -T_first/(CONSTANTS.g0*CONSTANTS.ISP_first); mdot = m1dot+m2dot; } else if (iphase==2) { T_srb = 3*CONSTANTS.thrust_srb; T_first = CONSTANTS.thrust_first; T_tot = T_srb+T_first; m1dot = -T_srb/(CONSTANTS.g0*CONSTANTS.ISP_srb); m2dot = -T_first/(CONSTANTS.g0*CONSTANTS.ISP_first); mdot = m1dot+m2dot; } else if (iphase==3) { T_first = CONSTANTS.thrust_first; T_tot = T_first; mdot = -T_first/(CONSTANTS.g0*CONSTANTS.ISP_first); } else if (iphase==4) { T_second = CONSTANTS.thrust_second; T_tot = T_second; mdot = -T_second/(CONSTANTS.g0*CONSTANTS.ISP_second); } adouble Toverm = T_tot/m; adouble thrust[3]; for(j=0;j<3;j++) thrust[j] = Toverm*u[j]; adouble rdot[3]; for(j=0;j<3;j++) rdot[j] = v[j]; adouble vdot[3]; for(j=0;j<3;j++) derivatives[0] derivatives[1] derivatives[2] derivatives[3] derivatives[4] derivatives[5] derivatives[6] = = = = = = = vdot[j] = thrust[j]+Drag[j]+grav[j]; rdot[0]; rdot[1]; rdot[2]; vdot[0]; vdot[1]; vdot[2]; mdot; path[0] = dot( controls, controls, 3); } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { Constants_& CONSTANTS = *( (Constants_ *) workspace->problem->user_data ); adouble rv[3]; rv[0]=final_states[0]; rv[1]=final_states[1]; rv[2]=final_states[2]; adouble vv[3]; vv[0]=final_states[3]; vv[1]=final_states[4]; vv[2]=final_states[5]; adouble oe[6]; int j; if(iphase==1) { // These events are related to the initial state conditions in phase 1 for(j=0;j<7;j++) e[j] = initial_states[j]; } if (iphase==4) { // These events are related to the final states in phase 4 399 rv2oe( rv, vv, CONSTANTS.mu, oe ); for(j=0;j<5;j++) e[j]=oe[j]; } } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { double double double double double double m_tot_first m_prop_first m_dry_first m_tot_srb m_prop_srb m_dry_srb = = = = = = 104380.0; 95550.0; m_tot_first-m_prop_first; 19290.0; 17010.0; m_tot_srb-m_prop_srb; int index=0; auto_link(linkages, linkages[index-2]-= auto_link(linkages, linkages[index-2]-= auto_link(linkages, linkages[index-2]-= &index, xad, 1, 2, workspace ); 6*m_dry_srb; &index, xad, 2, 3, workspace ); 3*m_dry_srb; &index, xad, 3, 4, workspace ); m_dry_first; } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Multiphase vehicle launch"; = "launch.txt"; //////////////////////////////////////////////////////////////////////////// /////////////////// Declare an instance of Constants structure ///////////// //////////////////////////////////////////////////////////////////////////// Constants_ CONSTANTS; problem.user_data = (void*) &CONSTANTS; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 4; = 24; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates = 7; 400 problem.phases(1).ncontrols = 3; problem.phases(1).nevents = 7; problem.phases(1).npath = 1; problem.phases(2).nstates problem.phases(2).ncontrols problem.phases(2).nevents problem.phases(2).npath = = = = 7; 3; 0; 1; problem.phases(3).nstates problem.phases(3).ncontrols problem.phases(3).nevents problem.phases(3).npath = = = = 7; 3; 0; 1; problem.phases(4).nstates problem.phases(4).ncontrols problem.phases(4).nevents problem.phases(4).npath = = = = 7; 3; 5; 1; problem.phases(1).nodes problem.phases(2).nodes problem.phases(3).nodes problem.phases(4).nodes = = = = "[5, "[5, "[5, "[5, 15]"; 15]"; 15]"; 15]"; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Declare DMatrix objects to store results ////////////// //////////////////////////////////////////////////////////////////////////// DMatrix x, u, t, H; //////////////////////////////////////////////////////////////////////////// /////////////////// Initialize CONSTANTS and ////////////////////////////// /////////////////// declare local variables ////////////////////////////// //////////////////////////////////////////////////////////////////////////// double omega = 7.29211585e-5; // Earth rotation rate (rad/s) DMatrix omega_matrix(3,3); omega_matrix(1,1) = 0.0; omega_matrix(1,2) = -omega; omega_matrix(2,1) = omega; omega_matrix(2,2) = 0.0; omega_matrix(3,1) = 0.0; omega_matrix(3,2) = 0.0; omega_matrix(1,3)=0.0; omega_matrix(2,3)=0.0; omega_matrix(3,3)=0.0; CONSTANTS.omega_matrix = &omega_matrix; // Rotation rate matrix (rad/s) CONSTANTS.mu = 3.986012e14; // Gravitational parameter (m^3/s^2) CONSTANTS.cd = 0.5; // Drag coefficient CONSTANTS.sa = 4*pi; // Surface area (m^2) CONSTANTS.rho0 = 1.225; // sea level gravity (kg/m^3) CONSTANTS.H = 7200.0; // Density scale height (m) CONSTANTS.Re = 6378145.0; // Radius of earth (m) CONSTANTS.g0 = 9.80665; // sea level gravity (m/s^2) double lat0 = 28.5*pi/180.0; double x0 = CONSTANTS.Re*cos(lat0); double z0 = CONSTANTS.Re*sin(lat0); double y0 = 0; DMatrix r0(3,1, x0, y0, z0); DMatrix v0 = omega_matrix*r0; // Geocentric Latitude of Cape Canaveral // x component of initial position // z component of initial position double bt_srb = 75.2; double bt_first = 261.0; double bt_second = 700.0; double double double double double t0 t1 t2 t3 t4 = = = = = 0; 75.2; 150.4; 261.0; 961.0; double double double double double double double m_tot_srb m_prop_srb m_dry_srb m_tot_first m_prop_first m_dry_first m_tot_second = = = = = = = 19290.0; 17010.0; m_tot_srb-m_prop_srb; 104380.0; 95550.0; m_tot_first-m_prop_first; 19300.0; 401 double double double double double double double double double double double double m_prop_second m_dry_second m_payload thrust_srb thrust_first thrust_second mdot_srb ISP_srb mdot_first ISP_first mdot_second ISP_second = 16820.0; = m_tot_second-m_prop_second; = 4164.0; = 628500.0; = 1083100.0; = 110094.0; = m_prop_srb/bt_srb; = thrust_srb/(CONSTANTS.g0*mdot_srb); = m_prop_first/bt_first; = thrust_first/(CONSTANTS.g0*mdot_first); = m_prop_second/bt_second; = thrust_second/(CONSTANTS.g0*mdot_second); double af = 24361140.0; double ef = 0.7308; double incf = 28.5*pi/180.0; double Omf = 269.8*pi/180.0; double omf = 130.5*pi/180.0; double nuguess = 0; double cosincf = cos(incf); double cosOmf = cos(Omf); double cosomf = cos(omf); DMatrix oe(6,1, af, ef, incf, Omf, omf, nuguess); DMatrix rout(3,1); DMatrix vout(3,1); oe2rv(oe,CONSTANTS.mu, &rout, &vout); rout.Transpose(); vout.Transpose(); double double double double double double double double m10 m1f m20 m2f m30 m3f m40 m4f = = = = = = = = m_payload+m_tot_second+m_tot_first+9*m_tot_srb; m10-(6*mdot_srb+mdot_first)*t1; m1f-6*m_dry_srb; m20-(3*mdot_srb+mdot_first)*(t2-t1); m2f-3*m_dry_srb; m30-mdot_first*(t3-t2); m3f-m_dry_first; m_payload; CONSTANTS.thrust_srb CONSTANTS.thrust_first CONSTANTS.thrust_second CONSTANTS.ISP_srb CONSTANTS.ISP_first CONSTANTS.ISP_second double double double double rmin rmax vmin vmax = = = = = = = = = = thrust_srb; thrust_first; thrust_second; ISP_srb; ISP_first; ISP_second; -2*CONSTANTS.Re; -rmin; -10000.0; -vmin; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// int iphase; problem.bounds.lower.times = "[0, 75.2, 150.4, 261.0, 261.0]"; problem.bounds.upper.times = "[0, 75.2, 150.4 261.0, 961.0]"; // Phase 1 bounds iphase = 1; problem.phases(iphase).bounds.lower.states(1) problem.phases(iphase).bounds.upper.states(1) problem.phases(iphase).bounds.lower.states(2) problem.phases(iphase).bounds.upper.states(2) problem.phases(iphase).bounds.lower.states(3) problem.phases(iphase).bounds.upper.states(3) problem.phases(iphase).bounds.lower.states(4) problem.phases(iphase).bounds.upper.states(4) problem.phases(iphase).bounds.lower.states(5) problem.phases(iphase).bounds.upper.states(5) = = = = = = = = = = rmin; rmax; rmin; rmax; rmin; rmax; vmin; vmax; vmin; vmax; 402 problem.phases(iphase).bounds.lower.states(6) problem.phases(iphase).bounds.upper.states(6) problem.phases(iphase).bounds.lower.states(7) problem.phases(iphase).bounds.upper.states(7) = = = = vmin; vmax; m1f; m10; problem.phases(iphase).bounds.lower.controls(1) problem.phases(iphase).bounds.upper.controls(1) problem.phases(iphase).bounds.lower.controls(2) problem.phases(iphase).bounds.upper.controls(2) problem.phases(iphase).bounds.lower.controls(3) problem.phases(iphase).bounds.upper.controls(3) = -1.0; = 1.0; = -1.0; = 1.0; = -1.0; = 1.0; problem.phases(iphase).bounds.lower.path(1) problem.phases(iphase).bounds.upper.path(1) = 1.0; = 1.0; // The following bounds fix the initial state conditions in phase 0. problem.phases(iphase).bounds.lower.events(1) problem.phases(iphase).bounds.upper.events(1) problem.phases(iphase).bounds.lower.events(2) problem.phases(iphase).bounds.upper.events(2) problem.phases(iphase).bounds.lower.events(3) problem.phases(iphase).bounds.upper.events(3) problem.phases(iphase).bounds.lower.events(4) problem.phases(iphase).bounds.upper.events(4) problem.phases(iphase).bounds.lower.events(5) problem.phases(iphase).bounds.upper.events(5) problem.phases(iphase).bounds.lower.events(6) problem.phases(iphase).bounds.upper.events(6) problem.phases(iphase).bounds.lower.events(7) problem.phases(iphase).bounds.upper.events(7) = = = = = = = = = = = = = = r0(1); r0(1); r0(2); r0(2); r0(3); r0(3); v0(1); v0(1); v0(2); v0(2); v0(3); v0(3); m10; m10; = = = = = = = = = = = = = = rmin; rmax; rmin; rmax; rmin; rmax; vmin; vmax; vmin; vmax; vmin; vmax; m2f; m20; // Phase 2 bounds iphase = 2; problem.phases(iphase).bounds.lower.states(1) problem.phases(iphase).bounds.upper.states(1) problem.phases(iphase).bounds.lower.states(2) problem.phases(iphase).bounds.upper.states(2) problem.phases(iphase).bounds.lower.states(3) problem.phases(iphase).bounds.upper.states(3) problem.phases(iphase).bounds.lower.states(4) problem.phases(iphase).bounds.upper.states(4) problem.phases(iphase).bounds.lower.states(5) problem.phases(iphase).bounds.upper.states(5) problem.phases(iphase).bounds.lower.states(6) problem.phases(iphase).bounds.upper.states(6) problem.phases(iphase).bounds.lower.states(7) problem.phases(iphase).bounds.upper.states(7) problem.phases(iphase).bounds.lower.controls(1) problem.phases(iphase).bounds.upper.controls(1) problem.phases(iphase).bounds.lower.controls(2) problem.phases(iphase).bounds.upper.controls(2) problem.phases(iphase).bounds.lower.controls(3) problem.phases(iphase).bounds.upper.controls(3) = -1.0; = 1.0; = -1.0; = 1.0; = -1.0; = 1.0; problem.phases(iphase).bounds.lower.path(1) problem.phases(iphase).bounds.upper.path(1) = 1.0; = 1.0; // Phase 3 bounds iphase = 3; problem.phases(iphase).bounds.lower.states(1) problem.phases(iphase).bounds.upper.states(1) problem.phases(iphase).bounds.lower.states(2) problem.phases(iphase).bounds.upper.states(2) problem.phases(iphase).bounds.lower.states(3) problem.phases(iphase).bounds.upper.states(3) problem.phases(iphase).bounds.lower.states(4) problem.phases(iphase).bounds.upper.states(4) problem.phases(iphase).bounds.lower.states(5) problem.phases(iphase).bounds.upper.states(5) = = = = = = = = = = rmin; rmax; rmin; rmax; rmin; rmax; vmin; vmax; vmin; vmax; 403 problem.phases(iphase).bounds.lower.states(6) problem.phases(iphase).bounds.upper.states(6) problem.phases(iphase).bounds.lower.states(7) problem.phases(iphase).bounds.upper.states(7) = = = = vmin; vmax; m3f; m30; problem.phases(iphase).bounds.lower.controls(1) problem.phases(iphase).bounds.upper.controls(1) problem.phases(iphase).bounds.lower.controls(2) problem.phases(iphase).bounds.upper.controls(2) problem.phases(iphase).bounds.lower.controls(3) problem.phases(iphase).bounds.upper.controls(3) = -1.0; = 1.0; = -1.0; = 1.0; = -1.0; = 1.0; problem.phases(iphase).bounds.lower.path(1) problem.phases(iphase).bounds.upper.path(1) = 1.0; = 1.0; // Phase 4 bounds iphase = 4; problem.phases(iphase).bounds.lower.states(1) problem.phases(iphase).bounds.upper.states(1) problem.phases(iphase).bounds.lower.states(2) problem.phases(iphase).bounds.upper.states(2) problem.phases(iphase).bounds.lower.states(3) problem.phases(iphase).bounds.upper.states(3) problem.phases(iphase).bounds.lower.states(4) problem.phases(iphase).bounds.upper.states(4) problem.phases(iphase).bounds.lower.states(5) problem.phases(iphase).bounds.upper.states(5) problem.phases(iphase).bounds.lower.states(6) problem.phases(iphase).bounds.upper.states(6) problem.phases(iphase).bounds.lower.states(7) problem.phases(iphase).bounds.upper.states(7) = = = = = = = = = = = = = = rmin; rmax; rmin; rmax; rmin; rmax; vmin; vmax; vmin; vmax; vmin; vmax; m4f; m40; problem.phases(iphase).bounds.lower.controls(1) problem.phases(iphase).bounds.upper.controls(1) problem.phases(iphase).bounds.lower.controls(2) problem.phases(iphase).bounds.upper.controls(2) problem.phases(iphase).bounds.lower.controls(3) problem.phases(iphase).bounds.upper.controls(3) = -1.0; = 1.0; = -1.0; = 1.0; = -1.0; = 1.0; problem.phases(iphase).bounds.lower.path(1) problem.phases(iphase).bounds.upper.path(1) = 1.0; = 1.0; problem.phases(iphase).bounds.lower.events(1) problem.phases(iphase).bounds.lower.events(2) problem.phases(iphase).bounds.lower.events(3) problem.phases(iphase).bounds.lower.events(4) problem.phases(iphase).bounds.lower.events(5) problem.phases(iphase).bounds.upper.events(1) problem.phases(iphase).bounds.upper.events(2) problem.phases(iphase).bounds.upper.events(3) problem.phases(iphase).bounds.upper.events(4) problem.phases(iphase).bounds.upper.events(5) = = = = = = = = = = af; ef; incf; Omf; omf; af; ef; incf; Omf; omf; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// iphase = 1; problem.phases(iphase).guess.states = zeros(7,5); problem.phases(iphase).guess.states(1, problem.phases(iphase).guess.states(2, problem.phases(iphase).guess.states(3, problem.phases(iphase).guess.states(4, problem.phases(iphase).guess.states(5, problem.phases(iphase).guess.states(6, problem.phases(iphase).guess.states(7, colon()) colon()) colon()) colon()) colon()) colon()) colon()) = = = = = = = linspace( linspace( linspace( linspace( linspace( linspace( linspace( r0(1), r0(2), r0(3), v0(1), v0(2), v0(3), m10 , problem.phases(iphase).guess.controls = zeros(3,5); problem.phases(iphase).guess.controls(1,colon()) = ones( 1, 5); problem.phases(iphase).guess.controls(2,colon()) = zeros(1, 5); problem.phases(iphase).guess.controls(3,colon()) = zeros(1, 5); problem.phases(iphase).guess.time = linspace(t0,t1, 5); 404 r0(1), r0(2), r0(3), v0(1), v0(2), v0(3), m1f , 5); 5); 5); 5); 5); 5); 5); iphase = 2; problem.phases(iphase).guess.states = zeros(7,5); problem.phases(iphase).guess.states(1, problem.phases(iphase).guess.states(2, problem.phases(iphase).guess.states(3, problem.phases(iphase).guess.states(4, problem.phases(iphase).guess.states(5, problem.phases(iphase).guess.states(6, problem.phases(iphase).guess.states(7, colon()) colon()) colon()) colon()) colon()) colon()) colon()) = = = = = = = linspace( linspace( linspace( linspace( linspace( linspace( linspace( r0(1), r0(2), r0(3), v0(1), v0(2), v0(3), m20 , r0(1), r0(2), r0(3), v0(1), v0(2), v0(3), m2f , 5); 5); 5); 5); 5); 5); 5); problem.phases(iphase).guess.controls = zeros(3,5); problem.phases(iphase).guess.controls(1,colon()) = ones( 1, 5); problem.phases(iphase).guess.controls(2,colon()) = zeros(1, 5); problem.phases(iphase).guess.controls(3,colon()) = zeros(1, 5); problem.phases(iphase).guess.time = linspace(t1,t2, 5); iphase = 3; problem.phases(iphase).guess.states = zeros(7,5); problem.phases(iphase).guess.states(1, problem.phases(iphase).guess.states(2, problem.phases(iphase).guess.states(3, problem.phases(iphase).guess.states(4, problem.phases(iphase).guess.states(5, problem.phases(iphase).guess.states(6, problem.phases(iphase).guess.states(7, colon()) colon()) colon()) colon()) colon()) colon()) colon()) = = = = = = = linspace( linspace( linspace( linspace( linspace( linspace( linspace( rout(1), rout(1), rout(2), rout(2), rout(3), rout(3), vout(1), vout(1), vout(2), vout(2), vout(3), vout(3), m30 , m3f , 5); 5); 5); 5); 5); 5); 5); problem.phases(iphase).guess.controls = zeros(3,5); problem.phases(iphase).guess.controls(1,colon()) = zeros( 1, 5); problem.phases(iphase).guess.controls(2,colon()) = zeros( 1, 5); problem.phases(iphase).guess.controls(3,colon()) = ones( 1, 5); problem.phases(iphase).guess.time = linspace(t2,t3, 5); iphase = 4; problem.phases(iphase).guess.states = zeros(7,5); problem.phases(iphase).guess.states(1, problem.phases(iphase).guess.states(2, problem.phases(iphase).guess.states(3, problem.phases(iphase).guess.states(4, problem.phases(iphase).guess.states(5, problem.phases(iphase).guess.states(6, problem.phases(iphase).guess.states(7, colon()) colon()) colon()) colon()) colon()) colon()) colon()) = = = = = = = linspace( linspace( linspace( linspace( linspace( linspace( linspace( rout(1), rout(1), rout(2), rout(2), rout(3), rout(3), vout(1), vout(1), vout(2), vout(2), vout(3), vout(3), m40 , m4f , 5); problem.phases(iphase).guess.controls = zeros(3,5); problem.phases(iphase).guess.controls(1,colon()) = zeros( 1, 5); problem.phases(iphase).guess.controls(2,colon()) = zeros( 1, 5); problem.phases(iphase).guess.controls(3,colon()) = ones( 1, 5); problem.phases(iphase).guess.time = linspace(t3,t4, 5); //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_method algorithm.scaling algorithm.derivatives = "IPOPT"; = "automatic"; = "automatic"; 405 5); 5); 5); 5); 5); 5); // // algorithm.nlp_iter_max = 500; algorithm.mesh_refinement = "automatic"; algorithm.collocation_method = "trapezoidal"; algorithm.ode_tolerance = 1.e-5; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// x = (solution.get_states_in_phase(1) || solution.get_states_in_phase(2) || solution.get_states_in_phase(3) || solution.get_states_in_phase(4) ); u = (solution.get_controls_in_phase(1) || solution.get_controls_in_phase(2) || solution.get_controls_in_phase(3) || solution.get_controls_in_phase(4) ); t = (solution.get_time_in_phase(1) || solution.get_time_in_phase(2) || solution.get_time_in_phase(3) || solution.get_time_in_phase(4) ); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// x.Save("x.dat"); u.Save("u.dat"); t.Save("t.dat"); //////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// DMatrix r = x(colon(1,3),colon()); DMatrix v = x(colon(4,6),colon()); DMatrix altitude = Sqrt(sum(elemProduct(r,r)))/1000.0; DMatrix speed = Sqrt(sum(elemProduct(v,v))); plot(t,altitude,problem.name, "time (s)", "position (km)"); plot(t,speed,problem.name, "time (s)", "speed (m/s)"); plot(t,u,problem.name,"time (s)", "u"); plot(t,altitude,problem.name, "time (s)", "position (km)", "alt", "pdf", "launch_position.pdf"); plot(t,speed,problem.name, "time (s)", "speed (m/s)", "speed", "pdf", "launch_speed.pdf"); plot(t,u,problem.name,"time (s)", "u (dimensionless)", "u1 u2 u3", "pdf", "launch_control.pdf"); } //////////////////////////////////////////////////////////////////////////// ////////////////// Define auxiliary functions ////////////////////////////// //////////////////////////////////////////////////////////////////////////// void rv2oe(adouble* rv, adouble* vv, double mu, adouble* oe) { int j; adouble K[3]; K[0] = 0.0; K[1]=0.0; K[2]=1.0; adouble hv[3]; cross(rv,vv, hv); adouble nv[3]; cross(K, hv, nv); adouble n = sqrt( dot(nv,nv,3) ); 406 adouble h2 = dot(hv,hv,3); adouble v2 = dot(vv,vv,3); adouble r = sqrt(dot(rv,rv,3)); adouble ev[3]; for(j=0;j<3;j++) ev[j] = 1/mu *( (v2-mu/r)*rv[j] - dot(rv,vv,3)*vv[j] ); adouble p = h2/mu; adouble e adouble a adouble i = sqrt(dot(ev,ev,3)); // eccentricity = p/(1-e*e); // semimajor axis = acos(hv[2]/sqrt(h2)); // inclination #define USE_SMOOTH_HEAVISIDE double a_eps = 0.1; #ifndef USE_SMOOTH_HEAVISIDE adouble Om = acos(nv[0]/n); // RAAN if ( nv[1] < -DMatrix::GetEPS() ){ // fix quadrant Om = 2*pi-Om; } #endif #ifdef USE_SMOOTH_HEAVISIDE adouble Om = smooth_heaviside( (nv[1]+DMatrix::GetEPS()), a_eps )*acos(nv[0]/n) +smooth_heaviside( -(nv[1]+DMatrix::GetEPS()), a_eps )*(2*pi-acos(nv[0]/n)); #endif #ifndef USE_SMOOTH_HEAVISIDE adouble om = acos(dot(nv,ev,3)/n/e); // arg of periapsis if ( ev[2] < 0 ) { // fix quadrant om = 2*pi-om; } #endif #ifdef USE_SMOOTH_HEAVISIDE adouble om = smooth_heaviside( (ev[2]), a_eps )*acos(dot(nv,ev,3)/n/e) +smooth_heaviside( -(ev[2]), a_eps )*(2*pi-acos(dot(nv,ev,3)/n/e)); #endif #ifndef USE_SMOOTH_HEAVISIDE adouble nu = acos(dot(ev,rv,3)/e/r); // true anomaly if ( dot(rv,vv,3) < 0 ) { // fix quadrant nu = 2*pi-nu; } #endif #ifdef USE_SMOOTH_HEAVISIDE adouble nu = smooth_heaviside( dot(rv,vv,3), a_eps )*acos(dot(ev,rv,3)/e/r) +smooth_heaviside( -dot(rv,vv,3), a_eps )*(2*pi-acos(dot(ev,rv,3)/e/r)); #endif oe[0] oe[1] oe[2] oe[3] oe[4] oe[5] = = = = = = a; e; i; Om; om; nu; return; } void oe2rv(DMatrix& oe, double mu, DMatrix* ri, DMatrix* vi) { double a=oe(1), e=oe(2), i=oe(3), Om=oe(4), om=oe(5), nu=oe(6); double p = a*(1-e*e); double r = p/(1+e*cos(nu)); DMatrix rv(3,1); rv(1) = r*cos(nu); rv(2) = r*sin(nu); rv(3) = 0.0; DMatrix vv(3,1); vv(1) = -sin(nu); 407 vv(2) = e+cos(nu); vv(3) = 0.0; vv *= sqrt(mu/p); double cO = cos(Om), double co = cos(om), double ci = cos(i), sO = sin(Om); so = sin(om); si = sin(i); DMatrix R(3,3); R(1,1)= cO*co-sO*so*ci; R(1,2)= -cO*so-sO*co*ci; R(1,3)= R(2,1)= sO*co+cO*so*ci; R(2,2)= -sO*so+cO*co*ci; R(2,3)=-cO*si; R(3,1)= so*si; R(3,2)= co*si; R(3,3)= ci; sO*si; *ri = R*rv; *vi = R*vv; return; } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// = The output from PSOPT is summarised in the box below and shown in Figures 3.112, 3.113 and 3.114, which contain the trajectories of the altitude, speed and the elements of the control vector, respectively. PSOPT results summary ===================== Problem: Multiphase vehicle launch CPU time (seconds): 3.356515e+00 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:00:31 2019 Optimal Phase 1 Phase 1 Phase 1 Phase 1 Phase 1 Phase 2 Phase 2 Phase 2 Phase 2 Phase 2 Phase 3 Phase 3 Phase 3 Phase 3 (unscaled) cost function value: -7.529661e+03 endpoint cost function value: 0.000000e+00 integrated part of the cost: 0.000000e+00 initial time: 0.000000e+00 final time: 7.520000e+01 maximum relative local error: 1.173524e-06 endpoint cost function value: 0.000000e+00 integrated part of the cost: 0.000000e+00 initial time: 7.520000e+01 final time: 1.504000e+02 maximum relative local error: 1.301765e-06 endpoint cost function value: 0.000000e+00 integrated part of the cost: 0.000000e+00 initial time: 1.504000e+02 final time: 2.610000e+02 408 Multiphase vehicle launch alt altitude (km) 6550 6500 6450 6400 0 100 200 300 400 500 600 700 800 900 1000 time (s) Figure 3.112: Altitude for the vehicle launch problem Phase 3 maximum relative local error: 3.952037e-07 Phase 4 endpoint cost function value: -7.529661e+03 Phase 4 integrated part of the cost: 0.000000e+00 Phase 4 initial time: 2.610000e+02 Phase 4 final time: 9.241413e+02 Phase 4 maximum relative local error: 1.631374e-06 NLP solver reports: The problem has been solved! 3.44 Zero propellant maneouvre of the International Space Station This problem illustrates the use of PSOPT for solving an optimal control problem associated with the design of a zero propellant maneouvre for the international space station by means of control moment gyroscopes (CMGs). The example is based on the results presented in the thesis by Bhatt [5] and also reported by Bedrossian and co-workers [1]. The original 90 and 180 degree maneouvres were computed using DIDO, and they were actually implemented on the International Space Station on 5 November 2006 and 2 January 2007, respectively, resulting in savings for NASA of around US$1.5m in propellant costs. The dynamic model employed here does not account for atmospheric drag as the atmosphere model used in the orig409 Multiphase vehicle launch speed 10000 9000 8000 speed (m/s) 7000 6000 5000 4000 3000 2000 1000 0 100 200 300 400 500 600 700 800 900 1000 time (s) Figure 3.113: Speed for the vehicle launch problem Multiphase vehicle launch 1 u1 u2 u3 0.8 u (dimensionless) 0.6 0.4 0.2 0 -0.2 -0.4 -0.6 0 100 200 300 400 500 600 700 800 900 time (s) Figure 3.114: Controls for the vehicle launch problem 410 1000 inal study is not available. Otherwise, the equations and parameters are the same as those reported by Bhatt in his thesis. The effects of atmospheric drag are, however, small, and the results obtained are comparable with those given in Bhatt’s thesis. The implemented case corresponds with a maneovure lasting 7200 seconds and using 3 CMG’s. The problem is formulated as follows. Find qc (t) = [qc,1 (t) qc,2 (t) qc,3 (t) qc,4 ]T , t ∈ [t0 , tf ] and the scalar parameter γ to minimise, Z tf ||u(t)||2 dt (3.186) J = 0.1γ + t0 subject to the dynamical equations: 1 q̇(t) = T(q)(ω(t) − ωo (q)) 2 ω̇(t) = J−1 (τd (q) − ω(t) × (Jω(t)) − u(t)) (3.187) ḣ(t) = u(t) − ω(t) × h(t) the path constraints: ||q(t)||22 = 1 ||qc (t)||22 = 1 ||h(t)||22 ≤ γ (3.188) ||ḣ(t)||22 = ḣ2max the parameter bounds 0 ≤ γ ≤ h2max (3.189) q(t0 ) = q̄0 ω(t0 ) = ωo (q̄0 ) h(t0 ) = h̄0 q(tf ) = q¯f ω(tf ) = ωo (q̄f ) h(tf ) = h̄f (3.190) and the boundary conditions: where J is a 3×3 inertia matrix, q = [q1 , q2 , q3 , q4 ]T is the quarternion vector, ω is the spacecraft angular rate relative to an inertial reference frame and expressed in the body frame, h is the momentum, T(q) is given by: −q2 −q3 −q4 q1 −q4 q3 (3.191) T(q) = q4 q1 −q2 −q3 q2 q1 u is the control force, which is given by: u(t) = J (KP ε̃(q, qc ) + KD ω̃(ω, qc )) where ε̃(q, qc ) = 2T(qc )T q ω̃(ω, ωc ) = ω − ωc 411 (3.192) (3.193) ωo is given by: ωo (q) = nC2 (q) (3.194) where n is the orbital rotation rate, Cj is the j column of the rotation matrix: 1 − 2(q32 + q42 ) 2(q2 q3 + q1 q4 ) 2(q2 q4 − q1 q3 ) C(q) = 2(q2 q3 − q1 q4 ) 1 − 2(q22 + q42 ) 2(q3 q4 + q1 q2 ) (3.195) 2 2 2(q2 q4 + q1 q3 ) 2(q3 q4 − q1 q2 ) 1 − 2(q2 + q3 ) τd is the disturbance torque, which in this case only incorporates the gravity gradient torque τgg (the disturbance torque also incorporates the atmospheric drag torque in the original study): τd = τgg = 3n2 C3 (q) × (JC3 (q)) (3.196) The constant parameter values used were: n = 1.1461 × 10−3 rad/s, hmax = 3 × 3600.0 ft-lbf-sec, ḣmax = 200.0 ft-lbf, t0 = 0 s, tf = 7200 s, and 18836544.0 3666370.0 2965301.0 J = 3666370.0 27984088.0 −1129004.0 slug − ft2 (3.197) 2965301.0 −1129004.0 39442649.0 The PSOPT code that solves this problem is shown below. ////////////////////////////////////////////////////////////////////////// //////////////// zpm.cxx ///////////////////// ////////////////////////////////////////////////////////////////////////// //////////////// PSOPT Example ///////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //////// Title: Zero propellant maneouvre problem /////////////// //////// Last modified: 09 November 2009 //////////////// //////// Reference: S.A. Bhatt (2007), Masters Thesis,//////////////// //////// Rice University, Houston TX //////////////// ////////////////////////////////////////////////////////////////////////// //////// Copyright (c) Victor M. Becerra, 2009 //////////////// ////////////////////////////////////////////////////////////////////////// //////// This is part of the PSOPT software library, which /////////////// //////// is distributed under the terms of the GNU Lesser //////////////// //////// General Public License (LGPL) //////////////// ////////////////////////////////////////////////////////////////////////// #include "psopt.h" // Set CASE below to 1: 6000 s maneouvre, or to 2: 7200 maneouvre #define CASE 2 struct Constants { DMatrix J; double n; double Kp; double Kd; double hmax; }; typedef struct Constants Constants_; static Constants_ CONSTANTS; 412 void Tfun( adouble T[][3], adouble *q ) { adouble adouble adouble adouble q1 q2 q3 q4 = = = = q[CINDEX(1)]; q[CINDEX(2)]; q[CINDEX(3)]; q[CINDEX(4)]; T[CINDEX(1)][CINDEX(1)] = -q2 ; T[CINDEX(1)][CINDEX(2)] = -q3; T[CINDEX(1)][CINDEX(3)] = -q4; T[CINDEX(2)][CINDEX(1)] = q1 ; T[CINDEX(2)][CINDEX(2)] = -q4; T[CINDEX(2)][CINDEX(3)] = T[CINDEX(3)][CINDEX(1)] = q4 ; T[CINDEX(3)][CINDEX(2)] = T[CINDEX(4)][CINDEX(1)] = -q3; T[CINDEX(4)][CINDEX(2)] = q1; T[CINDEX(3)][CINDEX(3)] = -q2; q2; T[CINDEX(4)][CINDEX(3)] = } void compute_omega0(adouble* omega0, adouble* q) { // This function computes the angular speed in the rotating LVLH reference frame int i; double n = CONSTANTS.n; adouble C2[3]; adouble adouble adouble adouble q1 q2 q3 q4 = = = = q[CINDEX(1)]; q[CINDEX(2)]; q[CINDEX(3)]; q[CINDEX(4)]; C2[ CINDEX(1) ] = 2*(q2*q3 + q1*q4); C2[ CINDEX(2) ] = 1.0-2.0*(q2*q2+q4*q4); C2[ CINDEX(3) ] = 2*(q3*q4-q1*q2); for (i=0;i<3;i++) omega0[i] = -n*C2[i]; } void compute_control_torque(adouble* u, adouble* q, adouble* qc, adouble* omega ) { // This function computes the control torque // double Kp = CONSTANTS.Kp; // Proportional gain double Kd = CONSTANTS.Kd; // Derivative gain double n = CONSTANTS.n; // Orbital rotation rate [rad/s] int i, j; DMatrix& J = CONSTANTS.J; adouble T[4][3]; Tfun( T, q ); adouble Tc[4][3]; Tfun( Tc, qc ); adouble epsilon_tilde[3]; for(i=0;i<3;i++) epsilon_tilde[i] for(j=0;j<4;j++) epsilon_tilde[i] } } q3; { = 0.0; { += 2*Tc[j][i]*q[j]; adouble omega_c[3]; compute_omega0( omega_c, qc ); adouble omega_tilde[3]; for(i=0;i<3;i++) { omega_tilde[i] = omega[i]-omega_c[i]; } adouble uaux[3]; 413 q1; for(i=0;i<3;i++) { uaux[i]= Kp*epsilon_tilde[i]+Kd*omega_tilde[i]; } product_ad( J, uaux, 3, u ); } void quarternion2Euler( DMatrix& phi, DMatrix& theta, DMatrix& psi, DMatrix& q) { // This function finds the Euler angles given the quarternion vector // long N = q.GetNoCols(); DMatrix q0 = q(1,colon()); DMatrix q1 = q(2,colon()); DMatrix q2 = q(3,colon()); DMatrix q3 = q(4,colon()); phi.Resize(1,N); theta.Resize(1,N); psi.Resize(1,N); for(int i=1;i<=N;i++) { phi(i)=atan2( 2*(q0(i)*q1(i) + q2(i)*q3(i)), 1.0-2.0*(q1(i)*q1(i)+q2(i)*q2(i)) ); theta(i)=asin( 2*(q0(i)*q2(i)-q3(i)*q1(i)) ); psi(i) = atan2( 2*(q0(i)*q3(i)+q1(i)*q2(i)), 1.0-2.0*(q2(i)*q2(i)+q3(i)*q3(i)) ); } } void compute_aerodynamic_torque(adouble* tau_aero, adouble& time ) { // This function approximates the aerodynamic torque by using the model and // parameters given in the following reference: // A. Chun Lee (2003) "Robust Momemtum Manager Controller for Space Station Applications". // Master of Arts Thesis, Rice University. // double alpha1[3] = {1.0, 4.0, 1.0}; double alpha2[3] = {1.0, 2.0, 1.0}; double alpha3[3] = {0.5, 0.5, 0.5}; adouble t = time; double phi1 = 0.0; double phi2 = 0.0; double n = CONSTANTS.n; for(int i=0;i<3;i++) { // Aerodynamic torque in [lb-ft] tau_aero[i] = alpha1[i] + alpha2[i]*sin( n*t + phi1 ) + alpha3[i]*sin( 2*n*t + phi2); } } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the end point (Mayer) cost function ////////// ////////////////////////////////////////////////////////////////////////// adouble endpoint_cost(adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { double end_point_weight = 0.1; adouble gamma = parameters[ CINDEX(1) ]; return (end_point_weight*gamma); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the integrand (Lagrange) cost function ////// ////////////////////////////////////////////////////////////////////////// adouble integrand_cost(adouble* states, adouble* controls, adouble* parameters, 414 adouble& time, adouble* xad, int iphase, Workspace* workspace) { double running_cost_weight = 1.0; adouble q[4]; // quarternion vector adouble u[3]; // control torque q[CINDEX(1)] q[CINDEX(2)] q[CINDEX(3)] q[CINDEX(4)] = = = = states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; adouble omega[3]; // angular rate vector omega[CINDEX(1)] = states[ CINDEX(5) ]; omega[CINDEX(2)] = states[ CINDEX(6) ]; omega[CINDEX(3)] = states[ CINDEX(7) ]; adouble qc[4]; // control vector qc[CINDEX(1)] qc[CINDEX(2)] qc[CINDEX(3)] qc[CINDEX(4)] = = = = controls[ controls[ controls[ controls[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; compute_control_torque(u,q,qc,omega); return running_cost_weight*dot(u,u,3); } ////////////////////////////////////////////////////////////////////////// /////////////////// Define the DAE’s //////////////////////////////////// ////////////////////////////////////////////////////////////////////////// void dae(adouble* derivatives, adouble* path, adouble* states, adouble* controls, adouble* parameters, adouble& time, adouble* xad, int iphase, Workspace* workspace) { int i,j; double n = CONSTANTS.n; // Orbital rotation rate [rad/s] adouble q[4]; // quarternion vector q[CINDEX(1)] q[CINDEX(2)] q[CINDEX(3)] q[CINDEX(4)] = = = = states[ states[ states[ states[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; adouble omega[3]; // angular rate vector omega[CINDEX(1)] = states[ CINDEX(5) ]; omega[CINDEX(2)] = states[ CINDEX(6) ]; omega[CINDEX(3)] = states[ CINDEX(7) ]; adouble h[3]; // momentum vector h[CINDEX(1)] = states[ CINDEX(8) ]; h[CINDEX(2)] = states[ CINDEX(9) ]; h[CINDEX(3)] = states[ CINDEX(10) ]; adouble qc[4]; // control vector qc[CINDEX(1)] qc[CINDEX(2)] qc[CINDEX(3)] qc[CINDEX(4)] = = = = controls[ controls[ controls[ controls[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ]; ]; ]; ]; adouble C2[3], C3[3]; adouble u[3]; adouble gamma; gamma = parameters[ CINDEX(1) ]; // Inertia matrix in slug-ft^2 415 DMatrix& J = CONSTANTS.J; DMatrix Jinv; Jinv = inv(J); adouble adouble adouble adouble q1 q2 q3 q4 = = = = q[CINDEX(1)]; q[CINDEX(2)]; q[CINDEX(3)]; q[CINDEX(4)]; C3[ CINDEX(1) ] = 2*(q2*q4 - q1*q3); C3[ CINDEX(2) ] = 2*(q3*q4 + q1*q2); C3[ CINDEX(3) ] = 1.0-2.0*(q2*q2 + q3*q3); adouble T[4][3]; Tfun( T, q ); adouble qdot[4]; adouble omega0[3]; compute_omega0( omega0, q); // Quarternion attitude kinematics for(j=0;j<4;j++) { qdot[j]=0; for(i=0;i<3;i++) { qdot[j] += 0.5*T[j][i]*(omega[i]-omega0[i]); } } adouble Jomega[3]; product_ad( J, omega, 3, Jomega ); adouble omegaCrossJomega[3]; cross(omega,Jomega, omegaCrossJomega); adouble F[3]; // Compute the torque disturbances: adouble tau_grav[3], tau_aero[3]; adouble v1[3]; for(i=0;i<3;i++) { v1[i] = 3*pow(n,2)*C3[i]; } adouble JC3[3]; product_ad( J, C3, 3, JC3 ); //gravity gradient torque cross( v1, JC3, tau_grav ); //Aerodynamic torque compute_aerodynamic_torque(tau_aero, time ); for(i=0;i<3;i++) { // Uncomment this section to ignore the aerodynamic disturbance torque tau_aero[i] = 0.0; } adouble tau_d[3]; for (i=0;i<3;i++) { tau_d[i] = tau_grav[i] + tau_aero[i]; } 416 compute_control_torque(u, q, qc, omega ); for (i=0;i<3;i++) { F[i] = tau_d[i] - omegaCrossJomega[i] - u[i]; } adouble omega_dot[3]; // Rotational dynamics product_ad( Jinv, F, 3, omega_dot ); adouble OmegaCrossH[3]; cross( omega, h , OmegaCrossH ); adouble hdot[3]; //Momemtum derivative for(i=0; i<3; i++) { hdot[i] = u[i] - OmegaCrossH[i]; } derivatives[CINDEX(1)] = derivatives[CINDEX(2)] = derivatives[CINDEX(3)] = derivatives[CINDEX(4)] = derivatives[CINDEX(5)] = derivatives[CINDEX(6)] = derivatives[CINDEX(7)] = derivatives[CINDEX(8)] = derivatives[CINDEX(9)] = derivatives[CINDEX(10)]= path[ path[ path[ path[ CINDEX(1) CINDEX(2) CINDEX(3) CINDEX(4) ] ] ] ] = = = = dot( dot( dot( dot( qdot[ CINDEX(1) ]; qdot[ CINDEX(2) ]; qdot[ CINDEX(3) ]; qdot[ CINDEX(4) ]; omega_dot[ CINDEX(1) ]; omega_dot[ CINDEX(2) ]; omega_dot[ CINDEX(3) ]; hdot[ CINDEX(1) ]; hdot[ CINDEX(2) ]; hdot[ CINDEX(3) ]; q, q, 4); qc, qc, 4); h, h, 3 ) - gamma; // <= 0 hdot, hdot,3); // <= hdotmax^2, } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the events function //////////////////////////// //////////////////////////////////////////////////////////////////////////// void events(adouble* e, adouble* initial_states, adouble* final_states, adouble* parameters,adouble& t0, adouble& tf, adouble* xad, int iphase, Workspace* workspace) { adouble adouble adouble adouble adouble adouble adouble adouble adouble adouble q1_i q2_i q3_i q4_i omega1_i omega2_i omega3_i h1_i h2_i h3_i = = = = = = = = = = initial_states[CINDEX(1)]; initial_states[CINDEX(2)]; initial_states[CINDEX(3)]; initial_states[CINDEX(4)]; initial_states[CINDEX(5)]; initial_states[CINDEX(6)]; initial_states[CINDEX(7)]; initial_states[CINDEX(8)]; initial_states[CINDEX(9)]; initial_states[CINDEX(10)]; adouble adouble adouble adouble adouble adouble adouble adouble adouble q1_f q2_f q3_f q4_f omega1_f omega2_f omega3_f h1_f h2_f = = = = = = = = = final_states[CINDEX(1)]; final_states[CINDEX(2)]; final_states[CINDEX(3)]; final_states[CINDEX(4)]; final_states[CINDEX(5)]; final_states[CINDEX(6)]; final_states[CINDEX(7)]; final_states[CINDEX(8)]; final_states[CINDEX(9)]; 417 adouble h3_f = final_states[CINDEX(10)]; // Initial conditions e[ e[ e[ e[ e[ e[ e[ e[ e[ e[ CINDEX(1) ] CINDEX(2) ] CINDEX(3) ] CINDEX(4) ] CINDEX(5) ] CINDEX(6) ] CINDEX(7) ] CINDEX(8) ] CINDEX(9) ] CINDEX(10)] = = = = = = = = = = q1_i; q2_i; q3_i; q4_i; omega1_i; omega2_i; omega3_i; h1_i; h2_i; h3_i; // Final conditions e[ e[ e[ e[ e[ e[ e[ e[ e[ e[ CINDEX(11) CINDEX(12) CINDEX(13) CINDEX(14) CINDEX(15) CINDEX(16) CINDEX(17) CINDEX(18) CINDEX(19) CINDEX(20) ] ] ] ] ] ] ] ] ] ] = = = = = = = = = = q1_f; q2_f; q3_f; q4_f; omega1_f; omega2_f; omega3_f; h1_f; h2_f; h3_f; } /////////////////////////////////////////////////////////////////////////// /////////////////// Define the phase linkages function /////////////////// /////////////////////////////////////////////////////////////////////////// void linkages( adouble* linkages, adouble* xad, Workspace* workspace) { // Single phase } //////////////////////////////////////////////////////////////////////////// /////////////////// Define the main routine /////////////////////////////// //////////////////////////////////////////////////////////////////////////// int main(void) { //////////////////////////////////////////////////////////////////////////// /////////////////// Declare key structures //////////////////////////////// //////////////////////////////////////////////////////////////////////////// Alg algorithm; Sol solution; Prob problem; CONSTANTS.Kp = 0.000128; // Proportional gain CONSTANTS.Kd = 0.015846; // Derivative gain double hmax; // maximum momentum magnitude in [ft-lbf-sec] if (CASE==1) { CONSTANTS.n = 1.1461E-3; // Orbital rotation rate [rad/s] hmax = 4*3600.0; // 4 CMG’s } else if (CASE==2) { CONSTANTS.n = 1.1475E-3; hmax = 3*3600.0; // 3 CMG’s } CONSTANTS.hmax = hmax; DMatrix& J = CONSTANTS.J; J.Resize(3,3); // Inertia matrix in slug-ft^2 418 if (CASE==1) { J(1,1) = 17834580.0 ; J(1,2)= 2787992.0; J(1,3)= 2873636.0; J(2,1) = 2787992.0 ; J(2,2)= 2773815.0; J(2,3)= -863810.0; J(3,1) = 28736361.0 ; J(3,2)= -863810.0; J(3,3)= 38030467.0; } else if (CASE==2) { J(1,1) = 18836544.0 ; J(1,2)= 3666370.0; J(1,3)= 2965301.0; J(2,1) = 3666370.0 ; J(2,2)= 27984088.0; J(2,3)= -1129004.0; J(3,1) = 2965301.0 ; J(3,2)= -1129004.0; J(3,3)= 39442649.0; } //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem name //////////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.name problem.outfilename = "Zero Propellant Maneouvre of the ISS"; = "zpm.txt"; //////////////////////////////////////////////////////////////////////////// //////////// Define problem level constants & do level 1 setup //////////// //////////////////////////////////////////////////////////////////////////// problem.nphases problem.nlinkages = 1; = 0; psopt_level1_setup(problem); ///////////////////////////////////////////////////////////////////////////// ///////// Define phase related information & do level 2 setup //////////// ///////////////////////////////////////////////////////////////////////////// problem.phases(1).nstates problem.phases(1).ncontrols problem.phases(1).nevents problem.phases(1).npath problem.phases(1).nodes = 10; = 4; = 20; = 4; = problem.phases(1).nparameters "[20, 30, 40, 50, 60]"; = 1; psopt_level2_setup(problem, algorithm); //////////////////////////////////////////////////////////////////////////// /////////////////// Enter problem bounds information ////////////////////// //////////////////////////////////////////////////////////////////////////// // Control bounds problem.phases(1).bounds.lower.controls(1) problem.phases(1).bounds.lower.controls(2) problem.phases(1).bounds.lower.controls(3) problem.phases(1).bounds.lower.controls(4) = = = = -1.0; -1.0; -1.0; -1.0; problem.phases(1).bounds.upper.controls(1) problem.phases(1).bounds.upper.controls(2) problem.phases(1).bounds.upper.controls(3) problem.phases(1).bounds.upper.controls(4) = = = = 1.0; 1.0; 1.0; 1.0; // state bounds problem.phases(1).bounds.lower.states(1) = problem.phases(1).bounds.lower.states(2) = problem.phases(1).bounds.lower.states(3) = problem.phases(1).bounds.lower.states(4) = problem.phases(1).bounds.lower.states(5) = problem.phases(1).bounds.lower.states(6) = problem.phases(1).bounds.lower.states(7) = problem.phases(1).bounds.lower.states(8) = problem.phases(1).bounds.lower.states(9) = problem.phases(1).bounds.lower.states(10)= -1.0; -0.2; -0.2; -1.0; -1.E-2; -1.E-2; -1.E-2; -8000.0; -8000.0; -8000.0; problem.phases(1).bounds.upper.states(1) problem.phases(1).bounds.upper.states(2) problem.phases(1).bounds.upper.states(3) problem.phases(1).bounds.upper.states(4) problem.phases(1).bounds.upper.states(5) 1.0; 0.2; 0.2; 1.0; 1.E-2; = = = = = 419 problem.phases(1).bounds.upper.states(6) = problem.phases(1).bounds.upper.states(7) = problem.phases(1).bounds.upper.states(8) = problem.phases(1).bounds.upper.states(9) = problem.phases(1).bounds.upper.states(10)= 1.E-2; 1.E-2; 8000.0; 8000.0; 8000.0; // Parameter bound problem.phases(1).bounds.lower.parameters(1) = problem.phases(1).bounds.upper.parameters(1) = 0.0; hmax*hmax; // Event bounds // Initial / Final condition values: DMatrix q_i(4,1), q_f(4,1), omega_i(3,1), omega_f(3,1), h_i(3,1), h_f(3,1); adouble q_ad[4], omega_ad[3]; // Initial conditions if (CASE==1) { q_i(1) = 0.98966; q_i(2) = 0.02690; q_i(3) = -0.08246; q_i(4) = 0.11425; q_ad[ CINDEX(1) ]=q_i(1); q_ad[ CINDEX(2) ]=q_i(2); q_ad[ CINDEX(3) ]=q_i(3); q_ad[ CINDEX(4) ]=q_i(4); compute_omega0( omega_ad, q_ad); omega_i(1) = omega_ad[ CINDEX(1) ].value(); omega_i(2) = omega_ad[ CINDEX(2) ].value(); omega_i(3) = omega_ad[ CINDEX(3) ].value(); // // // omega_i(1) = -2.5410E-4; omega_i(2) = -1.1145E-3; omega_i(3) = 8.2609E-5; h_i(1) = -496.0; h_i(2) = -175.0; h_i(3) = -3892.0; } else if (CASE==2) { q_i(1) = 0.98996; q_i(2) = 0.02650; q_i(3) = -0.07891; q_i(4) = 0.11422; q_ad[ CINDEX(1) ]=q_i(1); q_ad[ CINDEX(2) ]=q_i(2); q_ad[ CINDEX(3) ]=q_i(3); q_ad[ CINDEX(4) ]=q_i(4); compute_omega0( omega_ad, q_ad); omega_i(1) = omega_ad[ CINDEX(1) ].value(); omega_i(2) = omega_ad[ CINDEX(2) ].value(); omega_i(3) = omega_ad[ CINDEX(3) ].value(); // // // omega_i(1) = -2.5470E-4; omega_i(2) = -1.1159E-3; omega_i(3) = 8.0882E-5; h_i(1) = 1000.0; h_i(2) = -500.0; h_i(3) = -4200.0; } // Final q_f(1) = q_f(2) = q_f(3) = q_f(4) = conditions 0.70531; -0.06201; -0.03518; -0.70531; q_ad[ CINDEX(1) ]=q_f(1); q_ad[ CINDEX(2) ]=q_f(2); q_ad[ CINDEX(3) ]=q_f(3); q_ad[ CINDEX(4) ]=q_f(4); compute_omega0( omega_ad, q_ad); omega_f(1) = omega_ad[ CINDEX(1) ].value(); omega_f(2) = omega_ad[ CINDEX(2) ].value(); omega_f(3) = omega_ad[ CINDEX(3) ].value(); 420 // // // omega_f(1) = 1.1353E-3; omega_f(2) = 3.0062E-6; omega_f(3) = -1.5713E-4; h_f(1) = -9.0; h_f(2) = -3557.0; h_f(3) = -135.0; double DQ = 0.0001; double DWF = 0.0; double DHF = 0.0; problem.phases(1).bounds.lower.events(1) = q_i(1)-DQ; problem.phases(1).bounds.lower.events(2) = q_i(2)-DQ; problem.phases(1).bounds.lower.events(3) = q_i(3)-DQ; problem.phases(1).bounds.lower.events(4) = q_i(4)-DQ; problem.phases(1).bounds.lower.events(5) = omega_i(1); problem.phases(1).bounds.lower.events(6) = omega_i(2); problem.phases(1).bounds.lower.events(7) = omega_i(3); problem.phases(1).bounds.lower.events(8) = h_i(1); problem.phases(1).bounds.lower.events(9) = h_i(2); problem.phases(1).bounds.lower.events(10)= h_i(3); problem.phases(1).bounds.lower.events(11) = q_f(1)-DQ; problem.phases(1).bounds.lower.events(12) = q_f(2)-DQ; problem.phases(1).bounds.lower.events(13) = q_f(3)-DQ; problem.phases(1).bounds.lower.events(14) = q_f(4)-DQ; problem.phases(1).bounds.lower.events(15) = omega_f(1)-DWF; problem.phases(1).bounds.lower.events(16) = omega_f(2)-DWF; problem.phases(1).bounds.lower.events(17) = omega_f(3)-DWF; problem.phases(1).bounds.lower.events(18) = h_f(1)-DHF; problem.phases(1).bounds.lower.events(19) = h_f(2)-DHF; problem.phases(1).bounds.lower.events(20)= h_f(3)-DHF; problem.phases(1).bounds.upper.events(1) = q_i(1)+DQ; problem.phases(1).bounds.upper.events(2) = q_i(2)+DQ; problem.phases(1).bounds.upper.events(3) = q_i(3)+DQ; problem.phases(1).bounds.upper.events(4) = q_i(4)+DQ; problem.phases(1).bounds.upper.events(5) = omega_i(1); problem.phases(1).bounds.upper.events(6) = omega_i(2); problem.phases(1).bounds.upper.events(7) = omega_i(3); problem.phases(1).bounds.upper.events(8) = h_i(1); problem.phases(1).bounds.upper.events(9) = h_i(2); problem.phases(1).bounds.upper.events(10)= h_i(3); problem.phases(1).bounds.upper.events(11) = q_f(1)+DQ; problem.phases(1).bounds.upper.events(12) = q_f(2)+DQ; problem.phases(1).bounds.upper.events(13) = q_f(3)+DQ; problem.phases(1).bounds.upper.events(14) = q_f(4)+DQ; problem.phases(1).bounds.upper.events(15) = omega_f(1)+DWF; problem.phases(1).bounds.upper.events(16) = omega_f(2)+DWF; problem.phases(1).bounds.upper.events(17) = omega_f(3)+DWF; problem.phases(1).bounds.upper.events(18) = h_f(1)+DHF; problem.phases(1).bounds.upper.events(19) = h_f(2)+DHF; problem.phases(1).bounds.upper.events(20)= h_f(3)+DHF; // Path bounds double hdotmax = 200.0; // [ ft-lbf ] double EQ_TOL = 0.0002; problem.phases(1).bounds.lower.path(1) = 1.0-EQ_TOL; problem.phases(1).bounds.upper.path(1) = 1.0+EQ_TOL; problem.phases(1).bounds.lower.path(2) = 1.0-EQ_TOL; problem.phases(1).bounds.upper.path(2) = 1.0+EQ_TOL; problem.phases(1).bounds.lower.path(3) = -hmax*hmax; problem.phases(1).bounds.upper.path(3) = 0.0; problem.phases(1).bounds.lower.path(4) = 0.0; problem.phases(1).bounds.upper.path(4) = hdotmax*hdotmax; // Time bounds double TFINAL; if (CASE==1) { TFINAL = 6000.0; 421 } else { TFINAL = 7200.0; } problem.phases(1).bounds.lower.StartTime problem.phases(1).bounds.upper.StartTime = 0.0; = 0.0; problem.phases(1).bounds.lower.EndTime problem.phases(1).bounds.upper.EndTime = TFINAL; = TFINAL; //////////////////////////////////////////////////////////////////////////// /////////////////// Register problem functions /////////////////////////// //////////////////////////////////////////////////////////////////////////// problem.integrand_cost = &integrand_cost; problem.endpoint_cost = &endpoint_cost; problem.dae = &dae; problem.events = &events; problem.linkages = &linkages; //////////////////////////////////////////////////////////////////////////// /////////////////// Define & register initial guess /////////////////////// //////////////////////////////////////////////////////////////////////////// DMatrix DMatrix DMatrix DMatrix time_guess = linspace(0.0, TFINAL, 50 ); state_guess = zeros(10,50); control_guess = zeros(4,50); parameter_guess = hmax*hmax*ones(1,1); control_guess(1, control_guess(2, control_guess(3, control_guess(4, state_guess(1, state_guess(2, state_guess(3, state_guess(4, colon() colon() colon() colon() colon() colon() colon() colon() ) ) ) ) ) ) ) ) = = = = linspace( linspace( linspace( linspace( = = = = linspace( linspace( linspace( linspace( q_i(1), q_i(2), q_i(3), q_i(4), q_i(1), q_i(2), q_i(3), q_i(4), q_i(1), q_i(2), q_i(3), q_i(4), q_i(1), q_i(2), q_i(3), q_i(4), 50 50 50 50 ); ); ); ); 50); 50); 50); 50); state_guess(5, colon() ) = linspace( omega_i(1), omega_f(1), 50); state_guess(6, colon() ) = linspace( omega_i(2), omega_f(2), 50); state_guess(7, colon() ) = linspace( omega_i(3), omega_f(3), 50); state_guess(8, colon() ) = linspace( h_i(1), h_f(1), 50); state_guess(9, colon() ) = linspace( h_i(2), h_f(2), 50); state_guess(10, colon()) = linspace( h_i(3), h_f(3), 50); problem.phases(1).guess.controls = control_guess; problem.phases(1).guess.states = state_guess; problem.phases(1).guess.time = time_guess; problem.phases(1).guess.parameters=parameter_guess; //////////////////////////////////////////////////////////////////////////// /////////////////// Enter algorithm options ////////////////////////////// //////////////////////////////////////////////////////////////////////////// algorithm.nlp_iter_max algorithm.nlp_tolerance algorithm.nlp_method algorithm.scaling algorithm.derivatives algorithm.defect_scaling algorithm.jac_sparsity_ratio = = = = = = = 1000; 1.e-5; "IPOPT"; "automatic"; "automatic"; "jacobian-based"; 0.104; //////////////////////////////////////////////////////////////////////////// /////////////////// Now call PSOPT to solve the problem ////////////////// //////////////////////////////////////////////////////////////////////////// psopt(solution, problem, algorithm); 422 //////////////////////////////////////////////////////////////////////////// /////////// Extract relevant variables from solution structure ////////// //////////////////////////////////////////////////////////////////////////// DMatrix states, controls, t; states controls t = solution.get_states_in_phase(1); = solution.get_controls_in_phase(1); = solution.get_time_in_phase(1); //////////////////////////////////////////////////////////////////////////// /////////// Save solution data to files if desired //////////////////////// //////////////////////////////////////////////////////////////////////////// states.Save("states.dat"); controls.Save("controls.dat"); t.Save("t.dat"); DMatrix omega, h, q, phi, theta, psi, qc, euler_angles; q omega h qc = = = = states( colon(1,4), colon() ); states( colon(5,7), colon() ); states( colon(8,10),colon() ); controls; quarternion2Euler(phi, theta, psi, q); euler_angles = phi && theta && psi; adouble qc_ad[4], u_ad[3]; DMatrix DMatrix DMatrix DMatrix u(3,length(t)); hnorm(1,length(t)); hi; hm = hmax*ones(1,length(t)); int i,j; for (i=1; i<= length(t); i++ ) { for(j=1;j<=3;j++) { omega_ad[j-1] = omega(j,i); } for(j=1;j<=4;j++) { q_ad[j-1] = q(j,i); qc_ad[j-1]= qc(j,i); } compute_control_torque(u_ad, q_ad, qc_ad, omega_ad ); for(j=1;j<=3;j++) { u(j,i) = u_ad[j-1].value(); } hi = h(colon(),i); hnorm(1,i) = enorm(hi); } omega = omega*(180.0/pi)*1000; // convert to mdeg/s phi = phi*180.0/pi; theta=theta*180.0/pi; psi=psi*180.0/pi; u.Save("u.dat"); euler_angles.Save("euler_angles.dat"); /////////////////////////////////////////////////////////////////////////// /////////// Plot some results if desired (requires gnuplot) /////////////// //////////////////////////////////////////////////////////////////////////// plot(t,q,problem.name+" quarternion elements: q", "time (s)", "q", "q"); plot(t,qc,problem.name+" Control variables: qc", "time (s)", "qc", "qc"); plot(t,phi,problem.name+" Euler angles: phi", "time (s)", "angles (deg)", "phi"); 423 plot(t,theta,problem.name+" Euler angles: theta", "time (s)", "angles (deg)", "theta"); plot(t,psi,problem.name+" Euler angle: psi", "time (s)", "psi (deg)", "psi"); plot(t,omega(1,colon()),problem.name+": omega 1","time (s)", "omega1", "omega1"); plot(t,omega(2,colon()),problem.name+": omega 2","time (s)", "omega2", "omega2"); plot(t,omega(3,colon()),problem.name+": omega 3","time (s)", "omega3", "omega3"); plot(t,h(1,colon()),problem.name+": momentum 1","time (s)", "h1", "h1"); plot(t,h(2,colon()),problem.name+": momentum 2","time (s)", "h2", "h2"); plot(t,h(3,colon()),problem.name+": momentum 3","time (s)", "h3", "h3"); plot(t,u(1,colon()),problem.name+": control torque 1","time (s)", "u1", "u1"); plot(t,u(2,colon()),problem.name+": control torque 2","time (s)", "u2", "u2"); plot(t,u(3,colon()),problem.name+": control torque 3","time (s)", "u3", "u3"); plot(t,hnorm,t,hm,problem.name+": momentum norm", "time (s)", "h", "h hmax"); plot(t,phi,problem.name+" Euler angles: phi", "pdf", "zpm_phi.pdf" ); "time (s)", "angles (deg)", "phi", plot(t,theta,problem.name+" Euler angles: theta", "time (s)", "angles (deg)", "theta", "pdf", "zpm_theta.pdf"); plot(t,psi,problem.name+" Euler angle: psi", "pdf", "zpm_psi.pdf"); "time (s)", "psi (deg)", "psi", plot(t,omega(1,colon()),problem.name+": omega 1","time (s)", "omega1", "omega1", "pdf", "zpm_omega1.pdf"); plot(t,omega(2,colon()),problem.name+": omega 2","time (s)", "omega2", "omega2", "pdf", "zpm_omega2.pdf"); plot(t,omega(3,colon()),problem.name+": omega 3","time (s)", "omega3", "omega3", "pdf", "zpm_omega3.pdf"); plot(t,h(1,colon()),problem.name+": momentum 1","time (s)", "h1", "h1", "pdf", "zpm_h1.pdf"); plot(t,h(2,colon()),problem.name+": momentum 2","time (s)", "h2", "h2", "pdf", "zpm_h2.pdf"); plot(t,h(3,colon()),problem.name+": momentum 3","time (s)", "h3", "h3", "pdf", "zpm_h3.pdf"); plot(t,u(1,colon()),problem.name+": control torque 1","time (s)", "u1", "u1", "pdf", "zpm_u1.pdf"); plot(t,u(2,colon()),problem.name+": control torque 2","time (s)", "u2", "u2", "pdf", "zpm_u2.pdf"); plot(t,u(3,colon()),problem.name+": control torque 3","time (s)", "u3", "u3", "pdf", "zpm_u3.pdf"); plot(t,hnorm,t,hm,problem.name+": momentum norm", "time (s)", "h (ft-lbf-sec)", "h hmax", "pdf", "zpm_hnorm.pdf"); plot(t,u,problem.name+": control torques","time (s)", "u (ft-lbf)", "u1 u2 u3", "pdf", "zpm_controls.pdf"); } //////////////////////////////////////////////////////////////////////////// /////////////////////// END OF FILE /////////////////////////////// //////////////////////////////////////////////////////////////////////////// The output from PSOPT is summarised in the box below and shown in Figures 3.115 to 3.127.. 424 Zero Propellant Maneouvre of the ISS Euler angles: phi phi 8 6 angles (deg) 4 2 0 -2 -4 0 1000 2000 3000 4000 5000 6000 7000 time (s) Figure 3.115: Euler angle φ (roll) PSOPT results summary ===================== Problem: Zero Propellant Maneouvre of the ISS CPU time (seconds): 5.996709e+01 NLP solver used: IPOPT PSOPT release number: 4.0 Date and time of this run: Thu Feb 21 16:03:58 2019 Optimal (unscaled) cost function value: 6.680109e+06 Phase 1 endpoint cost function value: 5.238287e+06 Phase 1 integrated part of the cost: 1.441822e+06 Phase 1 initial time: 0.000000e+00 Phase 1 final time: 7.200000e+03 Phase 1 maximum relative local error: 1.008458e-03 NLP solver reports: The problem has been solved! 425 8000 Zero Propellant Maneouvre of the ISS Euler angles: theta theta -3 -4 angles (deg) -5 -6 -7 -8 -9 -10 0 1000 2000 3000 4000 5000 6000 7000 8000 time (s) Figure 3.116: Euler angle θ (pitch) Zero Propellant Maneouvre of the ISS Euler angle: psi psi 0 psi (deg) -20 -40 -60 -80 0 1000 2000 3000 4000 5000 6000 time (s) Figure 3.117: Euler angle ψ (yaw) 426 7000 8000 Zero Propellant Maneouvre of the ISS: omega 1 omega1 60 50 omega1 40 30 20 10 0 -10 0 1000 2000 3000 4000 5000 6000 7000 8000 time (s) Figure 3.118: Angular speed ω1 (roll) Zero Propellant Maneouvre of the ISS: omega 2 omega2 0 -10 omega2 -20 -30 -40 -50 -60 0 1000 2000 3000 4000 5000 6000 7000 time (s) Figure 3.119: Angular speed ω2 (pitch) 427 8000 Zero Propellant Maneouvre of the ISS: omega 3 10 omega3 5 0 omega3 -5 -10 -15 -20 -25 0 1000 2000 3000 4000 5000 6000 7000 8000 time (s) Figure 3.120: Angular speed ω3 (yaw) Zero Propellant Maneouvre of the ISS: momentum 1 h1 6000 h1 4000 2000 0 -2000 0 1000 2000 3000 4000 5000 6000 time (s) Figure 3.121: Momentum h1 (roll) 428 7000 8000 Zero Propellant Maneouvre of the ISS: momentum 2 h2 3000 2000 1000 h2 0 -1000 -2000 -3000 -4000 -5000 0 1000 2000 3000 4000 5000 6000 7000 8000 time (s) Figure 3.122: Momentum h2 (pitch) Zero Propellant Maneouvre of the ISS: momentum 3 h3 6000 4000 h3 2000 0 -2000 -4000 0 1000 2000 3000 4000 5000 6000 time (s) Figure 3.123: Momentum h3 (yaw) 429 7000 8000 Zero Propellant Maneouvre of the ISS: control torque 1 u1 20 10 u1 0 -10 -20 -30 0 1000 2000 3000 4000 5000 6000 7000 8000 time (s) Figure 3.124: Control torque u1 (roll) Zero Propellant Maneouvre of the ISS: control torque 2 u2 0 -5 -10 u2 -15 -20 -25 -30 -35 -40 0 1000 2000 3000 4000 5000 6000 time (s) Figure 3.125: Control torque u2 (pitch) 430 7000 8000 Zero Propellant Maneouvre of the ISS: control torque 3 u3 10 5 0 u3 -5 -10 -15 -20 -25 -30 0 1000 2000 3000 4000 5000 6000 7000 8000 time (s) Figure 3.126: Control torque u3 (yaw) Zero Propellant Maneouvre of the ISS: momentum norm h hmax 11000 10000 9000 h (ft-lbf-sec) 8000 7000 6000 5000 4000 3000 0 1000 2000 3000 4000 5000 6000 time (s) Figure 3.127: Momentum norm ||h(t)|| 431 7000 8000 Acknowledgements The author is grateful to Dr. Martin Otter from the Institute for Robotics and System Dynamics, DLR, Germany, for kindly allowing the publication of a translated version of the DLR model 2 of the Manutec R3 robot (original Fortran subroutine R3M2SI) with the distribution of PSOPT . The author is also grateful to Naz Bedrossian from the Draper Laboratory (USA) for facilitating the thesis of S. Bhatt, where the dynamic model of the International Space Station is described. 432 References [1] N.S. Bedrossian, S. Bhatt, W. Kang, and I.M. Ross. Zero Propellant Maneuver Guidance. IEEE Control Systems Magazine, 29:53–73, 2009. [2] D. A. Benson. A Gauss Pseudospectral Transcription for Optimal Control. PhD thesis, MIT, Department of Aeronautics and Astronautics, Cambridge, Mass., 2004. [3] J. T. Betts. Practical Methods for Optimal Control Using Nonlinear Programming. SIAM, 2001. [4] J. T. Betts. Practical Methods for Optimal Control and Estimation Using Nonlinear Programming. SIAM, 2010. [5] S.A. Bhatt. Optimal reorientation of spacecraft using only control moment gyroscopes. Master’s thesis, Rice University, Houston, Texas, 2007. [6] A.E. Bryson. Dynamic Optimization. Addison-Wesley, 1999. [7] A.E. Bryson, M.N. Desai, and W.C. Hoffman. Energy-State Approximation in Performance Optimization of Supersonic Aircraft. Journal of Aircraft, 6:481–488, 1969. [8] A.E. Bryson and Yu-Chi Ho. Applied Optimal Control. Halsted Press, 1975. [9] R.L. Burden and J.D. Faires. Brooks/Cole, 2005. Numerical Analysis. Thomson [10] C. Canuto, M.Y. Hussaini, A. Quarteroni, and T.A. Zang. Spectral Methods: Fundamentals in Single Domains. Springer-Verlag, Berlin, 2006. [11] C. Canuto, M.Y. Hussaini, A. Quarteroni, and T.A. Zang. Spectral Methods: Evolution to Complex Geometries and Applications to Fluid Dynamics. Springer, Heidelberg, Germany, 2007. 433 [12] C.G. Canuto, M.Y. Hussaini, A. Quarteroni A., and T.A. Zang. Spectral Methods in Fluid Dynamics. Springer-Verlag, 1988. [13] A. R. Curtis, M. J. D. Powell, and J. K. Reid. On the Estimation of Sparse Jacobian Matrices. Journal of the Institute of Mathematics and Applications, 13:117–120, 1974. [14] T. Davis. Direct Methods for Sparse Linear Systems. SIAM, 2006. [15] E. D. Dolan and J. J. More. Benchmarking optimization software with COPS 3.0. Argonne National Laboratory, 9700 South Cass Avenue, Argonne, Illinois 60439, 2004. [16] G. Elnagar, M. A. Kazemi, and M. Razzaghi. The Pseudospectral Legendre Method for Discretizing Optimal Control Problems. IEEE Transactions on Automatic Control, 40:1793–1796, 1995. [17] F. Fahroo and I.M. Ross. Costate Estimation by a Legendre Pseudospectral Method. Journal of Guidance, Control, and Dynamics, 24:270–277, 2001. [18] F. Fahroo and I.M. Ross. Costate Estimation by a Legendre Pseudospectral Method. Journal of Guidance, Control, and Dynamics, 24:270–277, 2002. [19] F. Fahroo and I.M. Ross. Direct Trajectory Optimization by a Chebyshev Pseudospectral Method. Journal of Guidance Control and Dynamics, 25, 2002. [20] J. Franke and M. Otter. The manutec r3 benchmark models for the dynamic simulation of robots. Technical report, Institute for Robotics and System Dynamics, DLR Oberpfaffenhofen, 1993. [21] P.E. Gill, W. Murray, M.A. Saunders, and M.H. Wright. Maintaining LU factors of a general sparse matrix. Linear Algebra and its Applications, 88/89:239–270, 1987. [22] Q. Gong, F. Fahroo, and I.M. Ross. Spectral algorithms for pseudospectral methods in optimal control. Journal of Guidance Control and Dynamics, 31:460–471, 2008. [23] J. Hesthaven, S. Gottlieb, and D. Gottlieb. Spectral Methods for TimeDependent Problems. Cambridge University Press, Cambridge, 2007. [24] L.S. Jennings, M.E. Fisher, K.L. Teo, and C.J. Goh. MISER3 Optimal Control Software Version 2.0 Theory and User Manual. Department of Mathematics, The University of Western Australia, 2002. 434 [25] W. Kang and N. Bedrossian. Pseudospectral Optimal Control Theory Makes Debut Flight, Saves nasa $1m in Under Three Hours. SIAM News, 40, 2007. [26] W. Kang, Q. Gong, I. M. Ross, and F. Fahroo. On the Convergence of Nonlinear Optimal Control Using Pseudospectral Methods for Feedback Linearizable Systems. International Journal of Robust and Nonlinear Control, 17:1251–1277, 2007. [27] E. Kostina, M.A. Saunders, and I. Schierle. Computation of covariance matrices for constrained parameter estimation problems using lsqr. Technical Report SOL-2009-1, Standford University, System Optimization Laboratory, 2009. [28] Z. Li, M. R. Osborne, and T. Prvan. Parameter estimation of ordinary differential equations. IMA Journal of Numerical Analysis, 25:264–285, 2005. [29] R. Luus. Iterative Dynamic Programming. Chapman and Hall / CRC, 2002. [30] S. Marsili-Libelli, S. Guerrizio, and N. Checchi. Confidence regions of estimated parameters for ecological systems. Ecological Modelling, 165:127–146, 2003. [31] M. Otter and S. Turk. The dfvrl models 1 and 2 of the manutec r3 robot. Technical report, Institute for Robotics and System Dynamics, DLR Oberpfaffenhofen, Germany, 1987. [32] J.A. Pietz. Pseudospectral Collocation Methods for the Direct transcription of Optimal Control Problems. Master’s thesis, Rice University, Houston, Texas, 2003. [33] A.V. Rao, D. Benson, G. Huntington, and C. Francolin. User’s manual for GPOPS version 1.3: A Matlab package for dynamic optimization using the Gauss pseudospectral method. 2008. [34] A.V. Rao and K.D. Mease. Eigenvector Approximate Dichotomic Basis Method for Solving Hyper- sensitive Optimal Control Problems. Optimal Control Applications and Methods, 21:1–19, 2000. [35] I.M. Ross and F. Fahroo. Pseudospectral Knotting Methods for Solving Nonsmooth Optimal Control Problems. Journal of Guidance Control and Dynamics, 27:397–405, 2004. [36] P. E. Rutquist and M. M. Edvall. PROPT Matlab Optimal Control Software. TOMLAB Optimization, 2009. 435 [37] K. Schittkowski. Numerical Data Fitting in Dynamical Systems. Kluwer Academic Publishers, Dordrecht, The Netherlands, 2002. [38] O. Von Stryk. User’s guide for DIRCOL (Version 2.1): A direct collocation method for the numerical solution of optimal control problems. Technical Report, Technische Universitat Munchen, 1999. [39] S. Subchan and R. Zbikowski. Computational optimal control: tools and practice. Wiley, 2009. [40] K.L. Teo, C.J. Goh, and K.H. Wong. A Unified Computational Approach to Optimal Control Problems. Wiley, New York, 1991. [41] Lloyd N. Trefethen. Spectral Methods in MATLAB. SIAM, Philadelphia, 2000. [42] D.A. Vallado. Fundamentals of Astrodynamics and Applications. Kluwer Academic Publishers, Dordrecht, The Netherlands, 2001. [43] J. Vlassenbroeck and R. Van Doreen. A Chebyshev Technique for Solving Nonlinear Optimal Control Problems. IEEE Transactions on Automatic Control, 33:333–340, 1988. [44] A. Wächter and L. T. Biegler. On the Implementation of a Primal-Dual Interior Point Filter Line Search Algorithm for Large-Scale Nonlinear Programming. Mathematical Programming, 106:25–57, 2006. 436
Source Exif Data:
File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.5 Linearized : No Page Count : 437 Page Mode : UseOutlines Author : Title : Subject : Creator : LaTeX with hyperref package Producer : pdfTeX-1.40.18 Create Date : 2019:02:25 06:05:15Z Modify Date : 2019:02:25 06:05:15Z Trapped : False PTEX Fullbanner : This is pdfTeX, Version 3.14159265-2.6-1.40.18 (TeX Live 2017/Debian) kpathsea version 6.2.3EXIF Metadata provided by EXIF.tools