├── .gitignore ├── LICENSE ├── README.md ├── example_racecar.py ├── example_racecar.yaml ├── experiments └── ilqr_jax │ └── rollout.gif ├── iLQR ├── __init__.py ├── constraints.py ├── cost.py ├── dynamics.py ├── ellipsoid_obj.py ├── ilqr.py ├── shielding.py ├── track.py └── utils.py ├── misc ├── alter.png └── ego.png └── outerloop_center_smooth.csv /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python ### 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | *.png 7 | !misc/*.png 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | .pybuilder/ 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | # For a library or package, you might want to ignore these files since the code is 90 | # intended to run in multiple environments; otherwise, check them in: 91 | # .python-version 92 | 93 | # pipenv 94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 97 | # install all needed dependencies. 98 | #Pipfile.lock 99 | 100 | # poetry 101 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 105 | #poetry.lock 106 | 107 | # pdm 108 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 109 | #pdm.lock 110 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 111 | # in version control. 112 | # https://pdm.fming.dev/#use-with-ide 113 | .pdm.toml 114 | 115 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 116 | __pypackages__/ 117 | 118 | # Celery stuff 119 | celerybeat-schedule 120 | celerybeat.pid 121 | 122 | # SageMath parsed files 123 | *.sage.py 124 | 125 | # Environments 126 | .env 127 | .venv 128 | env/ 129 | venv/ 130 | ENV/ 131 | env.bak/ 132 | venv.bak/ 133 | 134 | # Spyder project settings 135 | .spyderproject 136 | .spyproject 137 | 138 | # Rope project settings 139 | .ropeproject 140 | 141 | # mkdocs documentation 142 | /site 143 | 144 | # mypy 145 | .mypy_cache/ 146 | .dmypy.json 147 | dmypy.json 148 | 149 | # Pyre type checker 150 | .pyre/ 151 | 152 | # pytype static type analyzer 153 | .pytype/ 154 | 155 | # Cython debug symbols 156 | cython_debug/ 157 | 158 | # PyCharm 159 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 160 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 161 | # and can be added to the global gitignore or merged into this file. For a more nuclear 162 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 163 | #.idea/ 164 | 165 | ### Python Patch ### 166 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 167 | poetry.toml 168 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Safe Robotics Lab 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Safe Autonomous Racing using ILQR and Rollout-based Shielding: A JAX Implementation 2 | 3 | [![License][license-shield]][license-url] 4 | [![Python 3.10](https://img.shields.io/badge/python-3.10-blue)](https://www.python.org/downloads/) 5 | [![Colab Notebook][homepage-shield]][homepage-url] 6 | 7 | 8 | 9 |
10 |

11 | 12 | Logo 13 | 14 | 15 |

16 | 17 |

18 |

19 | 20 | 21 | 22 |
23 |

Table of Contents

24 |
    25 |
  1. About The Project
  2. 26 |
  3. Dependencies
  4. 27 |
  5. Example
  6. 28 |
  7. License
  8. 29 |
  9. Contact
  10. 30 |
  11. Paper
  12. 31 |
  13. Acknowledgements
  14. 32 |
33 |
34 | 35 | 36 | 37 | ## About The Project 38 | 39 | This repository implements a safe autonomous racing example using ILQR and rollout-based shielding, which relies on [JAX](https://github.com/google/jax) for real-time computation performance based on automatic differentiation and just-in-time (JIT) compilation. 40 | The repo is primarily developed and maintained by [Haimin Hu](https://haiminhu.org/), a PhD student in the [Safe Robotics Lab](https://saferobotics.princeton.edu). 41 | [Zixu Zhang](https://zzx9636.github.io/), [Kai-Chieh Hsu](https://kaichiehhsu.github.io/) and [Duy Nguyen](https://ece.princeton.edu/people/duy-phuong-nguyen) also contributed much to the repo (their original repo is [here](https://github.com/SafeRoboticsLab/PrincetonRaceCar_planning)). 42 | 43 | 44 | ## Dependencies 45 | 46 | This repo depends on the following packages: 47 | 1. jax=0.4.19 48 | 2. jaxlib=0.4.16 49 | 3. matplotlib=3.5.1 50 | 4. numpy=1.21.5 51 | 5. pyspline=1.5.1 52 | 6. python=3.10 53 | 7. yaml=0.2.5 54 | 55 | 56 | ## Example 57 | Please refer to the [Colab Notebook](https://colab.research.google.com/drive/1_3HgZx7LTBw69xH61Us70xI8HISUeFA7?usp=sharing) for a demo usage of this repo. 58 | 59 | Alternatively, you can directly run the main script: 60 | ```shell 61 | python3 example_racecar.py 62 | ``` 63 | 64 | 65 | 66 | ## License 67 | 68 | Distributed under the BSD 3-Clause License. See `LICENSE` for more information. 69 | 70 | 71 | 72 | ## Contact 73 | 74 | Haimin Hu - [@HaiminHu](https://twitter.com/HaiminHu) - haiminh@princeton.edu 75 | 76 | 77 | 78 | ## Paper 79 | 80 | If you found this repository helpful, please consider citing one of the following papers that turned this repository into research contributions. 81 | 82 | * ILQR-based safety filter: 83 | ```tex 84 | @article{hu2023active, 85 | title={Active uncertainty reduction for safe and efficient interaction planning: A shielding-aware dual control approach}, 86 | author={Hu, Haimin and Isele, David and Bae, Sangjae and Fisac, Jaime F}, 87 | journal={The International Journal of Robotics Research}, 88 | pages={02783649231215371}, 89 | year={2023}, 90 | publisher={SAGE Publications Sage UK: London, England} 91 | } 92 | ``` 93 | 94 | * ILQR-based Stackelberg game solver: 95 | ```tex 96 | @inproceedings{hu2024plays, 97 | title={Who Plays First? Optimizing the Order of Play in Stackelberg Games with Many Robots}, 98 | author={Hu, Haimin and Dragotto, Gabriele and Zhang, Zixu and Liang, Kaiqu and Stellato, Bartolomeo and Fisac, Jaime F}, 99 | booktitle={Proceedings of Robotics: Science and Systems}, 100 | year={2024} 101 | } 102 | ``` 103 | 104 | * ILQ-based game solver for data-aware planning: 105 | ```tex 106 | @inproceedings{lidard2024blending, 107 | title={Blending Data-Driven Priors in Dynamic Games}, 108 | author={Lidard, Justin and Hu, Haimin and Hancock, Asher and Zhang, Zixu and Contreras, Albert Gim{\'o} and Modi, Vikash and DeCastro, Jonathan and Gopinath, Deepak and Rosman, Guy and Leonard, Naomi and others}, 109 | booktitle={Proceedings of Robotics: Science and Systems}, 110 | year={2024} 111 | } 112 | ``` 113 | 114 | 115 | 116 | ## Acknowledgements 117 | 118 | This repo is inspired by the following projects: 119 | * [Princeton Race Car](https://github.com/SafeRoboticsLab/PrincetonRaceCar_planning) 120 | * [Ellipsoidal Toolbox (ET)](https://www.mathworks.com/matlabcentral/fileexchange/21936-ellipsoidal-toolbox-et) 121 | * ellReach: Ellipsoidal Reachable Set Computation for Linear Time-Varying Systems (under development) 122 | 123 | 124 | 125 | 126 | [contributors-shield]: https://img.shields.io/github/contributors/SafeRoboticsLab/repo.svg?style=for-the-badge 127 | [contributors-url]: https://github.com/SafeRoboticsLab/SHARP/contributors 128 | [forks-shield]: https://img.shields.io/github/forks/SafeRoboticsLab/repo.svg?style=for-the-badge 129 | [forks-url]: https://github.com/SafeRoboticsLab/SHARP/network/members 130 | [stars-shield]: https://img.shields.io/github/stars/SafeRoboticsLab/repo.svg?style=for-the-badge 131 | [stars-url]: https://github.com/SafeRoboticsLab/SHARP/stargazers 132 | [issues-shield]: https://img.shields.io/github/issues/SafeRoboticsLab/repo.svg?style=for-the-badge 133 | [issues-url]: https://github.com/SafeRoboticsLab/SHARP/issues 134 | [license-shield]: https://img.shields.io/badge/License-BSD%203--Clause-blue.svg 135 | [license-url]: https://opensource.org/licenses/BSD-3-Clause 136 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 137 | [linkedin-url]: https://linkedin.com/in/SafeRoboticsLab 138 | [homepage-shield]: https://img.shields.io/badge/-Colab%20Notebook-orange 139 | [homepage-url]: https://colab.research.google.com/drive/1_3HgZx7LTBw69xH61Us70xI8HISUeFA7?usp=sharing 140 | -------------------------------------------------------------------------------- /example_racecar.py: -------------------------------------------------------------------------------- 1 | """ 2 | Please contact the author(s) of this library if you have any questions. 3 | Author: Haimin Hu (haiminh@princeton.edu) 4 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 5 | """ 6 | 7 | import os, jax, argparse 8 | import numpy as np 9 | from matplotlib import cm 10 | from matplotlib import pyplot as plt 11 | from matplotlib.transforms import Affine2D 12 | from IPython.display import Image 13 | import imageio.v2 as imageio 14 | 15 | jax.config.update('jax_platform_name', 'cpu') 16 | jax.config.update('jax_enable_x64', True) 17 | plt.rcParams['font.family'] = 'serif' 18 | plt.rcParams['font.serif'] = ['Times New Roman'] + plt.rcParams['font.serif'] 19 | 20 | from iLQR.utils import * 21 | from iLQR.ilqr import iLQR 22 | from iLQR.shielding import ILQshielding, NaiveSwerving 23 | from iLQR.ellipsoid_obj import EllipsoidObj 24 | 25 | 26 | def main(config_file): 27 | # Loads the config and track file. 28 | config = load_config(config_file) 29 | track = load_track(config) 30 | 31 | # Constructs static obstacles. 32 | static_obs_list = [] 33 | obs_a = config.LENGTH / 2.0 34 | obs_b = config.WIDTH / 2.0 35 | 36 | obs_q1 = np.array([0, 5.6])[:, np.newaxis] 37 | obs_Q1 = np.diag([obs_a**2, obs_b**2]) 38 | static_obs1 = EllipsoidObj(q=obs_q1, Q=obs_Q1) 39 | static_obs_list.append([static_obs1 for _ in range(config.N)]) 40 | 41 | obs_q2 = np.array([-2.15, 4.0])[:, np.newaxis] 42 | obs_Q2 = np.diag([obs_b**2, obs_a**2]) 43 | static_obs2 = EllipsoidObj(q=obs_q2, Q=obs_Q2) 44 | static_obs_list.append([static_obs2 for _ in range(config.N)]) 45 | 46 | obs_q3 = np.array([-2.4, 2.0])[:, np.newaxis] 47 | obs_Q3 = np.diag([obs_b**2, obs_a**2]) 48 | static_obs3 = EllipsoidObj(q=obs_q3, Q=obs_Q3) 49 | static_obs_list.append([static_obs3 for _ in range(config.N)]) 50 | 51 | static_obs_heading_list = [-np.pi, -np.pi / 2, -np.pi / 2] 52 | 53 | # Initialization. 54 | solver = iLQR(track, config, safety=False) # Nominal racing iLQR 55 | solver_sh = iLQR(track, config, safety=True) # Shielding safety backup iLQR 56 | 57 | # max_deacc = config.A_MIN / 3 # config.A_MIN, config.A_MIN / 2, config.A_MIN / 3 58 | # shielding = NaiveSwerving( 59 | # config, [static_obs1, static_obs2, static_obs3], solver.dynamics, 60 | # max_deacc=max_deacc, N_sh=15 61 | # ) 62 | 63 | shielding = ILQshielding( 64 | config, solver_sh, [static_obs1, static_obs2, static_obs3], static_obs_list, N_sh=15 65 | ) 66 | 67 | pos0, psi0 = track.interp([2]) # Position and yaw on the track. 68 | x_cur = np.array([3.3, 4.0, 0, np.pi / 2]) # Initial state. 69 | # x_cur = np.array([pos0[0], pos0[1], 0, psi0[-1]]) 70 | init_control = np.zeros((2, config.N)) 71 | t_total = 0. 72 | plot_cover = False 73 | 74 | itr_receding = config.MAX_ITER_RECEDING # The number of receding iterations. 75 | state_hist = np.zeros((4, itr_receding)) 76 | control_hist = np.zeros((2, itr_receding)) 77 | plan_hist = np.zeros((6, config.N, itr_receding)) 78 | K_hist = np.zeros((2, 4, config.N - 1, itr_receding)) 79 | fx_hist = np.zeros((4, 4, config.N, itr_receding)) 80 | fu_hist = np.zeros((4, 2, config.N, itr_receding)) 81 | 82 | # Specifies the folder to save figures. 83 | fig_prog_folder = os.path.join(config.OUT_FOLDER, "progress") 84 | os.makedirs(fig_prog_folder, exist_ok=True) 85 | ego = plt.imread('misc/ego.png', format="png") 86 | alter = plt.imread('misc/alter.png', format="png") 87 | 88 | # Define disturbances. 89 | sigma = np.array([config.SIGMA_X, config.SIGMA_Y, config.SIGMA_V, config.SIGMA_THETA]) 90 | 91 | # iLQR Planning. 92 | for i in range(itr_receding): 93 | # Plans the trajectory using iLQR. 94 | states, controls, t_process, status, _, K_closed_loop, fx, fu = ( 95 | solver.solve(x_cur, controls=init_control, obs_list=static_obs_list) 96 | ) 97 | 98 | # Shielding. 99 | control_sh = shielding.run(x=x_cur, u_nominal=controls[:, 0]) 100 | 101 | # Executes the control. 102 | x_cur = solver.dynamics.forward_step(x_cur, control_sh, step=1, noise=sigma)[0] 103 | print("[{}]: solver returns status {} and uses {:.3f}.".format(i, status, t_process), end='\r') 104 | if i > 0: # Excludes JAX compilation time at the first time step. 105 | t_total += t_process 106 | 107 | # Records planning history, states and controls. 108 | plan_hist[:4, :, i] = states 109 | plan_hist[4:, :, i] = controls 110 | state_hist[:, i] = states[:, 0] 111 | control_hist[:, i] = controls[:, 0] 112 | 113 | K_hist[:, :, :, i] = K_closed_loop 114 | fx_hist[:, :, :, i] = fx 115 | fu_hist[:, :, :, i] = fu 116 | 117 | # Updates the nominal control signal for warmstart of next receding horizon. 118 | init_control[:, :-1] = controls[:, 1:] 119 | 120 | # Plots the current progress. 121 | plt.clf() 122 | track.plot_track() 123 | for static_obs in static_obs_list: 124 | plot_ellipsoids( 125 | plt.gca(), static_obs[0:1], arg_list=[dict(c='k', linewidth=1.)], dims=[0, 1], N=50, 126 | plot_center=False, use_alpha=False 127 | ) 128 | if plot_cover: # plot circles that cover the footprint. 129 | static_obs[0].plot_circ(plt.gca()) 130 | solver.cost.soft_constraints.ego_ell[0].plot_circ(plt.gca()) 131 | if shielding.sh_flag: 132 | plot_ellipsoids( 133 | plt.gca(), [solver.cost.soft_constraints.ego_ell[0]], arg_list=[dict(c='r')], dims=[0, 1], 134 | N=50, plot_center=False 135 | ) 136 | else: 137 | plot_ellipsoids( 138 | plt.gca(), [solver.cost.soft_constraints.ego_ell[0]], arg_list=[dict(c='b')], dims=[0, 1], 139 | N=50, plot_center=False 140 | ) 141 | plt.plot(states[0, :], states[1, :], linewidth=2, c='b') 142 | if shielding.sh_flag: 143 | plt.plot(shielding.states[0, :], shielding.states[1, :], linewidth=2, c='r') 144 | sc = plt.scatter( 145 | state_hist[0, :i + 1], state_hist[1, :i + 1], s=24, c=state_hist[2, :i + 1], cmap=cm.jet, 146 | vmin=0, vmax=config.V_MAX, edgecolor='none', marker='o' 147 | ) 148 | # plot ego car figure 149 | transform_data = Affine2D().rotate_deg_around(*(x_cur[0], x_cur[1]), 150 | x_cur[3] / np.pi * 180) + plt.gca().transData 151 | plt.imshow( 152 | ego, transform=transform_data, interpolation='none', origin='lower', 153 | extent=[x_cur[0] - 0.25, x_cur[0] + 0.25, x_cur[1] - 0.1, 154 | x_cur[1] + 0.1], alpha=1.0, zorder=10.0, clip_on=True 155 | ) 156 | # plot alter cars figure 157 | for j in range(len(static_obs_list)): 158 | _static_obs = static_obs_list[j][0] 159 | transform_data = Affine2D().rotate_deg_around( 160 | *(_static_obs.q[0, 0], _static_obs.q[1, 0]), static_obs_heading_list[j] / np.pi * 180 161 | ) + plt.gca().transData 162 | plt.imshow( 163 | alter, transform=transform_data, interpolation='none', origin='lower', extent=[ 164 | _static_obs.q[0, 0] - 0.25, _static_obs.q[0, 0] + 0.25, _static_obs.q[1, 0] - 0.1, 165 | _static_obs.q[1, 0] + 0.1 166 | ], alpha=1.0, zorder=10.0, clip_on=True 167 | ) 168 | cbar = plt.colorbar(sc) 169 | cbar.set_label(r"velocity [$m/s$]", size=20) 170 | plt.axis('equal') 171 | plt.savefig(os.path.join(fig_prog_folder, str(i) + ".png"), dpi=200) 172 | 173 | plt.close('All') 174 | print("Planning uses {:.3f}.".format(t_total)) 175 | 176 | # Makes an animation. 177 | gif_path = os.path.join(config.OUT_FOLDER, 'rollout.gif') 178 | with imageio.get_writer(gif_path, mode='I', loop=0) as writer: 179 | for i in range(itr_receding): 180 | filename = os.path.join(fig_prog_folder, str(i) + ".png") 181 | image = imageio.imread(filename) 182 | writer.append_data(image) 183 | Image(open(gif_path, 'rb').read(), width=400) 184 | 185 | 186 | if __name__ == "__main__": 187 | parser = argparse.ArgumentParser() 188 | parser.add_argument( 189 | "-cf", "--config_file", help="config file path", type=str, 190 | default=os.path.join("", "example_racecar.yaml") 191 | ) 192 | args = parser.parse_args() 193 | main(args.config_file) 194 | -------------------------------------------------------------------------------- /example_racecar.yaml: -------------------------------------------------------------------------------- 1 | # VEHICLE: 2 | WIDTH: 0.2 3 | LENGTH: 0.5 4 | MASS : 2.99 # kg 5 | WHEELBASE: 0.257 6 | DIM_X: 4 7 | DIM_U: 2 8 | 9 | # TRACK 10 | TRACK_FILE: outerloop_center_smooth.csv 11 | TRACK_WIDTH_R: 0.3 12 | TRACK_WIDTH_L: 0.3 13 | TRACK_OFFSET: 0.15 14 | LOOP: False 15 | 16 | # CONSTRAINT PARAM 17 | Q1_V: 0. 18 | Q2_V: 0. 19 | Q1_ROAD: 2. 20 | Q2_ROAD: 5. 21 | Q1_LAT: 1. 22 | Q2_LAT: 5. 23 | Q1_OBS: 2. 24 | Q2_OBS: 5. 25 | BARRIER_THR: 20. 26 | EXP_UB: 50. 27 | 28 | # COST PARAM 29 | W_VEL: 4. 30 | W_CONTOUR: 30. 31 | W_THETA: 0.1 32 | W_ACCEL: 0.1 33 | W_DELTA: 0.1 34 | V_REF: 1. 35 | 36 | # CONSTRAINT 37 | V_MIN: 0. 38 | V_MAX: 1. 39 | A_MIN: -3.5 40 | A_MAX: 3.5 41 | DELTA_MIN: -0.4 42 | DELTA_MAX: 0.4 43 | ALAT_MIN: -5. 44 | ALAT_MAX: 5. 45 | 46 | # DISTURBANCE. 47 | SIGMA_X: 0. 48 | SIGMA_Y: 0. 49 | SIGMA_V: 0. 50 | SIGMA_THETA: 0. 51 | 52 | # SHIELDING 53 | COLL_CHECK_SLACK: 3.0 54 | 55 | # SOLVER: 56 | N: 31 57 | T: 4. 58 | MAX_ITER: 50 59 | MAX_ITER_RECEDING: 100 60 | OUT_FOLDER: experiments/ilqr_jax 61 | -------------------------------------------------------------------------------- /experiments/ilqr_jax/rollout.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/iLQR_jax_racing/bdc97019c1a3c79c46bd7166bb1be5662bb7268b/experiments/ilqr_jax/rollout.gif -------------------------------------------------------------------------------- /iLQR/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/iLQR_jax_racing/bdc97019c1a3c79c46bd7166bb1be5662bb7268b/iLQR/__init__.py -------------------------------------------------------------------------------- /iLQR/constraints.py: -------------------------------------------------------------------------------- 1 | """ 2 | Constraints. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 7 | """ 8 | 9 | import numpy as np 10 | from typing import List, Tuple 11 | 12 | from .ellipsoid_obj import EllipsoidObj 13 | 14 | 15 | class Constraints: 16 | 17 | def __init__(self, config): 18 | self.T = config.T # Planning Time Horizon 19 | self.N = config.N # number of planning steps 20 | self.dt = self.T / (self.N - 1) # time step for each planning step 21 | self.wheelbase = config.WHEELBASE # vehicle chassis length 22 | self.delta_min = config.DELTA_MIN # min steering angle rad 23 | self.delta_max = config.DELTA_MAX # max steering angle rad 24 | self.a_min = config.A_MIN # min longitudial accel 25 | self.a_max = config.A_MAX # max longitudial accel 26 | self.v_min = config.V_MIN # min velocity 27 | self.v_max = config.V_MAX # max velocity 28 | self.alat_min = config.ALAT_MIN # min lateral accel 29 | self.alat_max = config.ALAT_MAX # max lateral accel 30 | self.track_width_L = config.TRACK_WIDTH_L 31 | self.track_width_R = config.TRACK_WIDTH_R 32 | 33 | # System parameters. 34 | self.dim_x = config.DIM_X 35 | self.dim_u = config.DIM_U 36 | 37 | # Parameter for barrier functions. 38 | self.q1_v = config.Q1_V 39 | self.q2_v = config.Q2_V 40 | 41 | self.q1_road = config.Q1_ROAD 42 | self.q2_road = config.Q2_ROAD 43 | 44 | self.q1_lat = config.Q1_LAT 45 | self.q2_lat = config.Q2_LAT 46 | 47 | self.q1_obs = config.Q1_OBS 48 | self.q2_obs = config.Q2_OBS 49 | 50 | self.exp_ub = config.EXP_UB 51 | 52 | # Useful constants. 53 | self.zeros = np.zeros((self.N)) 54 | self.ones = np.ones((self.N)) 55 | 56 | self.gamma = 0.9 57 | ego_a = config.LENGTH / 2.0 58 | self.r = ego_b = config.WIDTH / 2.0 59 | wheelbase = config.WHEELBASE 60 | ego_q = np.array([wheelbase / 2, 0])[:, np.newaxis] 61 | ego_Q = np.diag([ego_a * ego_a, ego_b * ego_b]) 62 | self.ego = EllipsoidObj(q=ego_q, Q=ego_Q) 63 | self.ego_ell = None 64 | self.obs_list = None 65 | 66 | def update_obs(self, frs_list: List): 67 | """ 68 | Updates the obstacle list. 69 | 70 | Args: 71 | frs_list (List): obstacle list. 72 | """ 73 | self.obs_list = frs_list 74 | 75 | def state2ell(self, states: np.ndarray) -> List[EllipsoidObj]: 76 | """ 77 | Converts a state variable to an ellipsoid object. 78 | 79 | Args: 80 | states (np.ndarray): 4xN array of state. 81 | 82 | Returns: 83 | List[EllipsoidObj]: ellipsoid object. 84 | """ 85 | ego_ell = [] 86 | for i in range(self.N): 87 | theta = states[3, i] 88 | d = states[:2, i][:, np.newaxis] 89 | R = np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]) 90 | temp = self.ego @ R 91 | temp.add(d) 92 | ego_ell.append(temp) 93 | return ego_ell 94 | 95 | def get_cost( 96 | self, states: np.ndarray, controls: np.ndarray, closest_pt: np.ndarray, slope: np.ndarray 97 | ) -> np.ndarray: 98 | """ 99 | Computes the total soft constraint cost. 100 | 101 | Args: 102 | states (np.ndarray): 4xN array of state. 103 | controls (np.ndarray): 2xN array of control input. 104 | closest_pts (np.ndarray): 2xN array of each state's closest point [x,y] 105 | on the track. 106 | slope (np.ndarray): 1xN array of track's slopes (rad) at closest 107 | points. 108 | 109 | Returns: 110 | np.ndarray: total soft constraint cost. 111 | """ 112 | 113 | # Road Boundary constarint. 114 | c_boundary = self._road_boundary_cost(states, closest_pt, slope) 115 | 116 | # Obstacle constraints. 117 | c_obs = np.zeros(self.N) 118 | if len(self.obs_list) > 0: 119 | # Patch footprint around state trajectory. 120 | self.ego_ell = self.state2ell(states) 121 | for i in range(self.N): 122 | ego_i = self.ego_ell[i] 123 | for obs_j in self.obs_list: # obs_j is a list of obstacles. 124 | obs_j_i = obs_j[i] # Get the ith obstacle in list obs_j. 125 | c_obs[i] += self.gamma**i * ego_i.obstacle_cost(obs_j_i, self.q1_obs, self.q2_obs) 126 | 127 | # Minimum velocity constarint. 128 | c_vel = self.q1_v * (np.exp(-states[2, :] * self.q2_v)) 129 | 130 | # Lateral Acceleration constraint. 131 | accel = states[2, :]**2 * np.tan(controls[1, :]) / self.wheelbase 132 | error_ub = accel - self.alat_max 133 | error_lb = self.alat_min - accel 134 | 135 | b_ub = self.q1_lat * (np.exp(np.clip(self.q2_lat * error_ub, None, self.exp_ub)) - 1) 136 | b_lb = self.q1_lat * (np.exp(np.clip(self.q2_lat * error_lb, None, self.exp_ub)) - 1) 137 | c_lat = b_lb + b_ub 138 | 139 | return c_vel + c_boundary + c_lat + c_obs 140 | 141 | def _road_boundary_cost( 142 | self, states: np.ndarray, closest_pt: np.ndarray, slope: np.ndarray 143 | ) -> np.ndarray: 144 | """ 145 | Computes the road boundary cost. 146 | 147 | Args: 148 | states (np.ndarray): 4xN array of state. 149 | closest_pt (np.ndarray): 2xN array of each state's closest point [x,y] 150 | on the track. 151 | slope (np.ndarray): 1xN array of track's slopes (rad) at closest 152 | points. 153 | 154 | Returns: 155 | np.ndarray: road boundary cost. 156 | """ 157 | dx = states[0, :] - closest_pt[0, :] 158 | dy = states[1, :] - closest_pt[1, :] 159 | 160 | sr = np.sin(slope) 161 | cr = np.cos(slope) 162 | dis = sr*dx - cr*dy 163 | # Right bound. 164 | b_r = dis - (self.track_width_R - self.r) 165 | 166 | c_r = self.q1_road * np.exp(np.clip(self.q2_road * b_r, -0.025 * self.q2_road, 20)) 167 | # Left Bound. 168 | b_l = -dis - (self.track_width_L - self.r) 169 | 170 | c_l = self.q1_road * np.exp(np.clip(self.q2_road * b_l, -0.025 * self.q2_road, 20)) 171 | 172 | return c_l + c_r 173 | 174 | def get_obs_derivatives(self, states: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: 175 | """ 176 | Calculates the Jacobian and Hessian of the obstacle avoidance soft 177 | constraint cost. 178 | 179 | Args: 180 | states (np.ndarray): 4xN array of state. 181 | 182 | Returns: 183 | np.ndarray: Jacobian vector. 184 | np.ndarray: Hessian matrix. 185 | """ 186 | c_x_obs = np.zeros((self.dim_x, self.N)) 187 | c_xx_obs = np.zeros((self.dim_x, self.dim_x, self.N)) 188 | 189 | if len(self.obs_list) > 0: 190 | for i in range(self.N): 191 | ego_i = self.ego_ell[i] 192 | for obs_j in self.obs_list: 193 | obs_j_i = obs_j[i] 194 | c_x_obs_temp, c_xx_obs_temp = ego_i.obstacle_derivative( 195 | states[:, i], self.wheelbase / 2, obs_j_i, self.q1_obs, self.q2_obs 196 | ) 197 | c_x_obs[:, i] += self.gamma**i * c_x_obs_temp 198 | c_xx_obs[:, :, i] += self.gamma**i * c_xx_obs_temp 199 | 200 | return c_x_obs, c_xx_obs 201 | -------------------------------------------------------------------------------- /iLQR/cost.py: -------------------------------------------------------------------------------- 1 | """ 2 | Costs, gradients, and Hessians. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 7 | """ 8 | 9 | import numpy as np 10 | from typing import Tuple, List 11 | 12 | from .constraints import Constraints 13 | 14 | from functools import partial 15 | from jax import jit, jacfwd, hessian 16 | from jaxlib.xla_extension import ArrayImpl 17 | import jax.numpy as jnp 18 | import jax 19 | 20 | 21 | class Cost: 22 | 23 | def __init__(self, config, safety=True): 24 | self.config = config 25 | self.soft_constraints = Constraints(config) 26 | self.safety = safety 27 | 28 | # Planning parameters. 29 | self.T = config.T # planning horizon. 30 | self.N = config.N # number of planning steps. 31 | self.dt = self.T / (self.N - 1) # time step for each planning step. 32 | self.v_ref = config.V_REF # reference velocity. 33 | 34 | # System parameters. 35 | self.dim_x = config.DIM_X 36 | self.dim_u = config.DIM_U 37 | 38 | # Track parameters. 39 | self.track_width_R = config.TRACK_WIDTH_R 40 | self.track_width_L = config.TRACK_WIDTH_L 41 | 42 | # Racing cost parameters. 43 | self.w_vel = config.W_VEL 44 | self.w_contour = config.W_CONTOUR 45 | self.w_theta = config.W_THETA 46 | self.w_accel = config.W_ACCEL 47 | self.w_delta = config.W_DELTA 48 | self.wheelbase = config.WHEELBASE 49 | self.track_offset = config.TRACK_OFFSET 50 | self.W_state = np.array([[self.w_contour, 0], [0, self.w_vel]]) 51 | self.W_control = np.array([[self.w_accel, 0], [0, self.w_delta]]) 52 | 53 | # Soft constraint parameters. 54 | self.q1_v = config.Q1_V 55 | self.q2_v = config.Q2_V 56 | self.q1_road = config.Q1_ROAD 57 | self.q2_road = config.Q2_ROAD 58 | self.q1_lat = config.Q1_LAT 59 | self.q2_lat = config.Q2_LAT 60 | self.q1_obs = config.Q1_OBS 61 | self.q2_obs = config.Q2_OBS 62 | self.barrier_thr = config.BARRIER_THR 63 | self.v_min = config.V_MIN 64 | self.v_max = config.V_MAX 65 | self.a_min = config.A_MIN 66 | self.a_max = config.A_MAX 67 | self.delta_min = config.DELTA_MIN 68 | self.delta_max = config.DELTA_MAX 69 | self.alat_min = config.ALAT_MIN 70 | self.alat_max = config.ALAT_MAX 71 | self.r = config.WIDTH / 2. 72 | self.road_thr = 0.025 73 | 74 | # Useful constant vectors. 75 | self.zeros = np.zeros((self.N)) 76 | self.ones = np.ones((self.N)) 77 | 78 | # Computes cost gradients using Jax. 79 | self.cx_x = jit(jacfwd(self.state_cost_stage_jitted, argnums=0)) 80 | self.cp_x = self.progress_deriv_jitted 81 | self.cu_u = jit(jacfwd(self.control_cost_stage_jitted, argnums=0)) 82 | self.rb_x = jit(jacfwd(self.road_boundary_cost_stage_jitted, argnums=0)) 83 | self.la_x = jit(jacfwd(self.lat_acc_cost_stage_jitted, argnums=0)) 84 | self.la_u_tmp = jit(jacfwd(self.lat_acc_cost_stage_jitted, argnums=1)) 85 | self.vb_x = jit(jacfwd(self.vel_bound_cost_stage_jitted, argnums=0)) 86 | 87 | # Vectorizes gradients using Jax. 88 | self.cx_x = jit(jax.vmap(self.cx_x, in_axes=(1, 1, 1), out_axes=(1))) 89 | self.cp_x = jit(jax.vmap(self.cp_x, in_axes=(1), out_axes=(1))) 90 | self.cu_u = jit(jax.vmap(self.cu_u, in_axes=(1), out_axes=(1))) 91 | self.rb_x = jit(jax.vmap(self.rb_x, in_axes=(1, 1, 1), out_axes=(1))) 92 | self.la_x = jit(jax.vmap(self.la_x, in_axes=(1, 1), out_axes=(1))) 93 | self.la_u = jit(jax.vmap(self.la_u_tmp, in_axes=(1, 1), out_axes=(1))) 94 | self.vb_x = jit(jax.vmap(self.vb_x, in_axes=(1), out_axes=(1))) 95 | 96 | # Computes cost Hessians and second-order derivatives using Jax. 97 | self.cx_xx = jit(hessian(self.state_cost_stage_jitted, argnums=0)) 98 | self.cu_uu = jit(hessian(self.control_cost_stage_jitted, argnums=0)) 99 | self.c_ux = jnp.zeros((self.dim_u, self.dim_x)) 100 | self.rb_xx = jit(hessian(self.road_boundary_cost_stage_jitted, argnums=0)) 101 | self.la_xx = jit(hessian(self.lat_acc_cost_stage_jitted, argnums=0)) 102 | self.la_uu = jit(hessian(self.lat_acc_cost_stage_jitted, argnums=1)) 103 | self.la_ux = jit(jacfwd(self.la_u_tmp, argnums=0)) 104 | self.vb_xx = jit(hessian(self.vel_bound_cost_stage_jitted, argnums=0)) 105 | 106 | # Vectorizes Hessians and second-order derivatives using Jax. 107 | self.cx_xx = jit(jax.vmap(self.cx_xx, in_axes=(1, 1, 1), out_axes=(2))) 108 | self.cu_uu = jit(jax.vmap(self.cu_uu, in_axes=(1), out_axes=(2))) 109 | self.rb_xx = jit(jax.vmap(self.rb_xx, in_axes=(1, 1, 1), out_axes=(2))) 110 | self.la_xx = jit(jax.vmap(self.la_xx, in_axes=(1, 1), out_axes=(2))) 111 | self.la_uu = jit(jax.vmap(self.la_uu, in_axes=(1, 1), out_axes=(2))) 112 | self.la_ux = jit(jax.vmap(self.la_ux, in_axes=(1, 1), out_axes=(2))) 113 | self.vb_xx = jit(jax.vmap(self.vb_xx, in_axes=(1), out_axes=(2))) 114 | 115 | # # Vectorizes soft constraint cost gradient and Hessians. 116 | # self.lat_acc_deriv_vmap = jit( 117 | # jax.vmap( 118 | # self.lat_acc_deriv_jitted, in_axes=(1, 1), 119 | # out_axes=(1, 2, 1, 2, 2) 120 | # ) 121 | # ) 122 | # self.road_boundary_deriv_vmap = jit( 123 | # jax.vmap( 124 | # self.road_boundary_deriv_jitted, in_axes=(1, 1, 1), 125 | # out_axes=(1, 2) 126 | # ) 127 | # ) 128 | # self.vel_bound_deriv_vmap = jit( 129 | # jax.vmap(self.vel_bound_deriv_jitted, in_axes=(1), out_axes=(1, 2)) 130 | # ) 131 | 132 | def update_obs(self, frs_list: List): 133 | """ 134 | Updates the obstacle list. 135 | 136 | Args: 137 | frs_list (List): obstacle list. 138 | """ 139 | self.soft_constraints.update_obs(frs_list) 140 | 141 | def get_cost( 142 | self, states: np.ndarray, controls: np.ndarray, closest_pts: np.ndarray, slope: np.ndarray, 143 | theta: np.ndarray 144 | ) -> np.ndarray: 145 | """ 146 | Calculates the cost given planned states and controls. 147 | 148 | Args: 149 | states (np.ndarray): 4xN array of planned trajectory. 150 | controls (np.ndarray): 2xN array of planned control. 151 | closest_pts (np.ndarray): 2xN array of each state's closest point [x,y] 152 | on the track. 153 | slope (np.ndarray): 1xN array of track's slopes (rad) at closest 154 | points. 155 | theta (np.ndarray): 1xN array of the progress at each state. 156 | 157 | Returns: 158 | np.ndarray: total cost. 159 | """ 160 | transform = np.array([[np.sin(slope), -np.cos(slope), self.zeros, self.zeros], 161 | [self.zeros, self.zeros, self.ones, self.zeros]]) 162 | 163 | ref_states = np.zeros_like(states) 164 | ref_states[0, :] = closest_pts[0, :] + np.sin(slope) * self.track_offset 165 | ref_states[1, :] = closest_pts[1, :] - np.cos(slope) * self.track_offset 166 | ref_states[2, :] = self.v_ref 167 | 168 | error = states - ref_states 169 | Q_trans = np.einsum( 170 | 'abn, bcn->acn', np.einsum('dan, ab -> dbn', transform.transpose(1, 0, 2), self.W_state), 171 | transform 172 | ) 173 | 174 | c_state = np.einsum('an, an->n', error, np.einsum('abn, bn->an', Q_trans, error)) 175 | c_progress = -self.w_theta * np.sum(theta) 176 | 177 | c_control = np.einsum('an, an->n', controls, np.einsum('ab, bn->an', self.W_control, controls)) 178 | 179 | c_control[-1] = 0 180 | 181 | c_constraint = self.soft_constraints.get_cost(states, controls, closest_pts, slope) 182 | 183 | if not self.safety: 184 | c_constraint = 0 185 | 186 | J = np.sum(c_state + c_constraint + c_control) + c_progress 187 | 188 | return J 189 | 190 | def get_derivatives_jax( 191 | self, states: ArrayImpl, controls: ArrayImpl, closest_pts: ArrayImpl, slopes: ArrayImpl 192 | ) -> Tuple[ArrayImpl, ArrayImpl, ArrayImpl, ArrayImpl, ArrayImpl]: 193 | """ 194 | Calculates Jacobian and Hessian of the overall cost using Jax. 195 | 196 | Args: 197 | states (ArrayImpl): current states of the shape (dim_x, N). 198 | controls (ArrayImpl): current controls of the shape (dim_u, N). 199 | closest_pts (ArrayImpl): each state's closest point [x,y] 200 | on the track (2, N). 201 | slopes (ArrayImpl): track's slopes (rad) at closest points (1, N). 202 | 203 | Returns: 204 | ArrayImpl: c_x of the shape (dim_x, N). 205 | ArrayImpl: c_xx of the shape (dim_x, dim_x, N). 206 | ArrayImpl: c_u of the shape (dim_u, N). 207 | ArrayImpl: c_uu of the shape (dim_u, dim_u, N). 208 | ArrayImpl: c_ux of the shape (dim_u, dim_x, N). 209 | """ 210 | # Obtains racing cost gradients and Hessians 211 | c_x_cost = self.cx_x(states, closest_pts, slopes) 212 | c_xx_cost = self.cx_xx(states, closest_pts, slopes) 213 | 214 | c_p_cost = self.cp_x(slopes) 215 | 216 | c_u_cost = self.cu_u(controls) 217 | c_uu_cost = self.cu_uu(controls) 218 | 219 | q = c_x_cost + c_p_cost 220 | Q = c_xx_cost 221 | r = c_u_cost 222 | R = c_uu_cost 223 | S = np.zeros((self.dim_u, self.dim_x, states.shape[1])) 224 | 225 | # Obtains soft constraint cost gradients and Hessians. 226 | # Analytical. 227 | if self.safety: 228 | c_x_obs, c_xx_obs = self.soft_constraints.get_obs_derivatives(np.asarray(states)) 229 | 230 | c_x_rb = self.rb_x(states, closest_pts, slopes) 231 | c_xx_rb = self.rb_xx(states, closest_pts, slopes) 232 | 233 | # Jax-autodiff'd. 234 | c_x_lat = self.la_x(states, controls) 235 | c_xx_lat = self.la_xx(states, controls) 236 | c_u_lat = self.la_u(states, controls) 237 | c_uu_lat = self.la_uu(states, controls) 238 | c_ux_lat = self.la_ux(states, controls) 239 | 240 | c_x_v = self.vb_x(states) 241 | c_xx_v = self.vb_xx(states) 242 | 243 | if self.safety: 244 | q += c_x_lat + c_x_v + c_x_rb + c_x_obs 245 | Q += c_xx_lat + c_xx_v + c_xx_rb + c_xx_obs 246 | else: 247 | q += c_x_lat + c_x_v + c_x_rb 248 | Q += c_xx_lat + c_xx_v + c_xx_rb 249 | r += c_u_lat 250 | R += c_uu_lat 251 | S += c_ux_lat 252 | 253 | return q, Q, r, R, S 254 | 255 | # --------------------------- Jitted racing costs ---------------------------- 256 | @partial(jit, static_argnums=(0,)) 257 | def state_cost_stage_jitted( 258 | self, state: ArrayImpl, closest_pt: ArrayImpl, slope: ArrayImpl 259 | ) -> ArrayImpl: 260 | """ 261 | Computes the stage state cost. 262 | 263 | Args: 264 | state (ArrayImpl): (dim_x,) 265 | closest_pt (ArrayImpl): (2,) 266 | slope (ArrayImpl): scalar 267 | 268 | Returns: 269 | ArrayImpl: cost (scalar) 270 | """ 271 | sr = jnp.sin(slope)[0] 272 | cr = jnp.cos(slope)[0] 273 | transform = jnp.array([[sr, -cr, 0., 0.], [0., 0., 1., 0.]]) 274 | Q = transform.T @ self.W_state @ transform 275 | ref_state = jnp.zeros((self.dim_x,)) 276 | ref_state = ref_state.at[0].set(closest_pt[0] + sr * self.track_offset) 277 | ref_state = ref_state.at[1].set(closest_pt[1] - cr * self.track_offset) 278 | ref_state = ref_state.at[2].set(self.v_ref) 279 | return (state - ref_state).T @ Q @ (state-ref_state) 280 | 281 | @partial(jit, static_argnums=(0,)) 282 | def control_cost_stage_jitted(self, control: ArrayImpl) -> ArrayImpl: 283 | """ 284 | Computes the stage control cost c(u) = u.T @ R @ u, where u is the control. 285 | 286 | Args: 287 | control (ArrayImpl): (dim_u,) 288 | 289 | Returns: 290 | ArrayImpl: cost (scalar) 291 | """ 292 | return control.T @ self.W_control @ control 293 | 294 | @partial(jit, static_argnums=(0,)) 295 | def progress_deriv_jitted(self, slope: ArrayImpl) -> ArrayImpl: 296 | """ 297 | Computes the derivative of the progress cost w.r.t. the slope. 298 | 299 | Args: 300 | slope (ArrayImpl): scalar 301 | 302 | Returns: 303 | ArrayImpl: (dim_x,) 304 | """ 305 | sr = jnp.sin(slope)[0] 306 | cr = jnp.cos(slope)[0] 307 | return -self.w_theta * jnp.hstack((cr, sr, 0, 0)) 308 | 309 | # ----------------------- Jitted soft constraint costs ----------------------- 310 | @partial(jit, static_argnums=(0,)) 311 | def lat_acc_cost_stage_jitted(self, state: ArrayImpl, control: ArrayImpl) -> ArrayImpl: 312 | ''' 313 | Calculates the lateral acceleration soft constraint cost. 314 | 315 | Args: 316 | state (ArrayImpl): (dim_x,) 317 | control (ArrayImpl): (dim_u,) 318 | 319 | Returns: 320 | ArrayImpl: dtype=float32 321 | ''' 322 | accel = state[2]**2 * jnp.tan(control[1]) / self.wheelbase 323 | error_ub = accel - self.alat_max 324 | error_lb = self.alat_min - accel 325 | 326 | c_lat_ub = self.q1_lat * (jnp.exp(self.q2_lat * error_ub) - 1.) 327 | c_lat_lb = self.q1_lat * (jnp.exp(self.q2_lat * error_lb) - 1.) 328 | return c_lat_lb + c_lat_ub 329 | 330 | @partial(jit, static_argnums=(0,)) 331 | def road_boundary_cost_stage_jitted( 332 | self, state: ArrayImpl, closest_pt: ArrayImpl, slope: ArrayImpl 333 | ) -> ArrayImpl: 334 | """ 335 | Calculates the road boundary soft constraint cost. 336 | 337 | Args: 338 | state (ArrayImpl): (dim_x,) 339 | closest_pt (ArrayImpl): (2,) 340 | slope (ArrayImpl): (1,) 341 | 342 | Returns: 343 | ArrayImpl: dtype=float32 344 | """ 345 | sr = jnp.sin(slope) 346 | cr = jnp.cos(slope) 347 | dx = state[0] - closest_pt[0] 348 | dy = state[1] - closest_pt[1] 349 | dis = (sr*dx - cr*dy)[0] 350 | 351 | _min_val = -self.road_thr * self.q2_road 352 | _max_val = self.barrier_thr 353 | 354 | # Right bound. 355 | b_r = dis - (self.track_width_R - self.r) 356 | c_r = (self.q1_road * jnp.exp(jnp.clip(self.q2_road * b_r, _min_val, _max_val))) 357 | 358 | # Left bound. 359 | b_l = -dis - (self.track_width_L - self.r) 360 | c_l = (self.q1_road * jnp.exp(jnp.clip(self.q2_road * b_l, _min_val, _max_val))) 361 | return c_l + c_r 362 | 363 | @partial(jit, static_argnums=(0,)) 364 | def vel_bound_cost_stage_jitted(self, state: ArrayImpl) -> ArrayImpl: 365 | """ 366 | Calculates the velocity bound soft constraint cost. 367 | 368 | Args: 369 | state (ArrayImpl): (dim_x,) 370 | 371 | Returns: 372 | ArrayImpl: dtype=float32 373 | """ 374 | cons_v_min = self.v_min - state[2] 375 | cons_v_max = state[2] - self.v_max 376 | barrier_v_min = self.q1_v * jnp.exp(jnp.clip(self.q2_v * cons_v_min, None, self.barrier_thr)) 377 | barrier_v_max = self.q1_v * jnp.exp(jnp.clip(self.q2_v * cons_v_max, None, self.barrier_thr)) 378 | return barrier_v_min + barrier_v_max 379 | 380 | # ----------- Jitted analytical derivatives (for comparison only) ------------ 381 | @partial(jit, static_argnums=(0,)) 382 | def lat_acc_deriv_jitted( 383 | self, state: ArrayImpl, control: ArrayImpl 384 | ) -> Tuple[ArrayImpl, ArrayImpl, ArrayImpl, ArrayImpl, ArrayImpl]: 385 | """ 386 | Calculates the Jacobian and Hessian of lateral acc. soft constraint cost. 387 | 388 | Args: 389 | state (ArrayImpl): of the shape (dim_x,). 390 | control (ArrayImpl): of the shape (dim_u,). 391 | 392 | Returns: 393 | ArrayImpl: c_x of the shape (dim_x,). 394 | ArrayImpl: c_xx of the shape (dim_x, dim_x). 395 | ArrayImpl: c_u of the shape (dim_u,). 396 | ArrayImpl: c_uu of the shape (dim_u, dim_u). 397 | ArrayImpl: c_ux of the shape (dim_u, dim_x). 398 | """ 399 | dim_x = self.dim_x 400 | wheelbase = self.wheelbase 401 | q1_lat = self.q1_lat 402 | q2_lat = self.q2_lat 403 | barrier_thr = self.barrier_thr 404 | 405 | c_x = jnp.zeros((dim_x,)) 406 | c_xx = jnp.zeros((dim_x, dim_x)) 407 | c_u = jnp.zeros((2,)) 408 | c_uu = jnp.zeros((2, 2)) 409 | c_ux = jnp.zeros((2, dim_x)) 410 | 411 | accel = state[2]**2 * jnp.tan(control[1]) / wheelbase 412 | error_ub = accel - self.alat_max 413 | error_lb = self.alat_min - accel 414 | 415 | cost_a_lat_min = q1_lat * jnp.exp(jnp.clip(q2_lat * error_lb, None, barrier_thr)) 416 | cost_a_lat_max = q1_lat * jnp.exp(jnp.clip(q2_lat * error_ub, None, barrier_thr)) 417 | 418 | da_dx = 2 * state[2] * jnp.tan(control[1]) / wheelbase 419 | da_dxx = 2 * jnp.tan(control[1]) / wheelbase 420 | 421 | da_du = state[2]**2 / (jnp.cos(control[1])**2 * wheelbase) 422 | da_duu = (state[2]**2 * jnp.sin(control[1]) / (jnp.cos(control[1])**3 * wheelbase)) 423 | 424 | da_dux = 2 * state[2] / (jnp.cos(control[1])**2 * wheelbase) 425 | 426 | c_x = c_x.at[2].set(q2_lat * (cost_a_lat_max-cost_a_lat_min) * da_dx) 427 | c_u = c_u.at[1].set(q2_lat * (cost_a_lat_max-cost_a_lat_min) * da_du) 428 | 429 | c_xx = c_xx.at[2, 2].set( 430 | q2_lat**2 * (cost_a_lat_max+cost_a_lat_min) * da_dx**2 431 | + q2_lat * (cost_a_lat_max-cost_a_lat_min) * da_dxx 432 | ) 433 | c_uu = c_uu.at[1, 1].set( 434 | q2_lat**2 * (cost_a_lat_max+cost_a_lat_min) * da_du**2 435 | + q2_lat * (cost_a_lat_max-cost_a_lat_min) * da_duu 436 | ) 437 | 438 | c_ux = c_ux.at[1, 2].set( 439 | q2_lat**2 * (cost_a_lat_max+cost_a_lat_min) * da_dx * da_du 440 | + q2_lat * (cost_a_lat_max-cost_a_lat_min) * da_dux 441 | ) 442 | return c_x, c_xx, c_u, c_uu, c_ux 443 | 444 | @partial(jit, static_argnums=(0,)) 445 | def road_boundary_deriv_jitted(self, state: ArrayImpl, closest_pt: ArrayImpl, 446 | slope: ArrayImpl) -> Tuple[ArrayImpl, ArrayImpl]: 447 | """ 448 | Calculates the Jacobian and Hessian of road boundary soft constraint cost. 449 | 450 | Args: 451 | state (ArrayImpl): (dim_x,) 452 | closest_pt (ArrayImpl): (2,) 453 | slope (ArrayImpl): (1,) 454 | 455 | Returns: 456 | ArrayImpl: (dim_x,) 457 | ArrayImpl: (dim_x, dim_x) 458 | """ 459 | dim_x = self.dim_x 460 | sr = jnp.sin(slope) 461 | cr = jnp.cos(slope) 462 | dx = state[0] - closest_pt[0] 463 | dy = state[1] - closest_pt[1] 464 | dis = sr*dx - cr*dy 465 | transform = jnp.array([sr, -cr]) 466 | 467 | cons_road_r = dis - (self.track_width_R - self.r) 468 | c_x_r = jnp.zeros((dim_x,)) 469 | c_xx_r = jnp.zeros((dim_x, dim_x)) 470 | _c_x_r, _c_xx_r = barrier_function_jitted( 471 | q1=self.q1_road, q2=self.q2_road, cons=cons_road_r, cons_dot=transform, cons_min=None, 472 | cons_max=self.barrier_thr 473 | ) 474 | c_x_r = c_x_r.at[:2].set(jnp.ravel(_c_x_r)) 475 | c_xx_r = c_xx_r.at[:2, :2].set(_c_xx_r) 476 | 477 | cons_road_l = -dis - (self.track_width_L - self.r) 478 | c_x_l = jnp.zeros((dim_x,)) 479 | c_xx_l = jnp.zeros((dim_x, dim_x)) 480 | _c_x_l, _c_xx_l = barrier_function_jitted( 481 | q1=self.q1_road, q2=self.q2_road, cons=cons_road_l, cons_dot=-transform, cons_min=None, 482 | cons_max=self.barrier_thr 483 | ) 484 | c_x_l = c_x_l.at[:2].set(jnp.ravel(_c_x_l)) 485 | c_xx_l = c_xx_l.at[:2, :2].set(_c_xx_l) 486 | 487 | return c_x_r + c_x_l, c_xx_r + c_xx_l 488 | 489 | @partial(jit, static_argnums=(0,)) 490 | def vel_bound_deriv_jitted(self, state: ArrayImpl) -> Tuple[ArrayImpl, ArrayImpl]: 491 | """ 492 | Calculates the Jacobian and Hessian of velocity soft constraint cost. 493 | 494 | Args: 495 | state (ArrayImpl): (dim_x,) 496 | 497 | Returns: 498 | ArrayImpl: (dim_x,) 499 | ArrayImpl: (dim_x, dim_x) 500 | """ 501 | dim_x = self.dim_x 502 | transform = 1. 503 | c_x = jnp.zeros((dim_x,)) 504 | c_xx = jnp.zeros((dim_x, dim_x)) 505 | 506 | cons_v_min = self.v_min - state[2] 507 | cons_v_max = state[2] - self.v_max 508 | 509 | _c_x_min, _c_xx_min = barrier_function_jitted( 510 | self.q1_v, self.q2_v, cons_v_min, -transform, cons_max=self.barrier_thr 511 | ) 512 | _c_x_max, _c_xx_max = barrier_function_jitted( 513 | self.q1_v, self.q2_v, cons_v_max, transform, cons_max=self.barrier_thr 514 | ) 515 | c_x = c_x.at[2].set(_c_x_min + _c_x_max) 516 | c_xx = c_xx.at[2, 2].set((_c_xx_min + _c_xx_max)[0, 0]) 517 | 518 | return c_x, c_xx 519 | 520 | 521 | @jit 522 | def barrier_function_jitted( 523 | q1: ArrayImpl, q2: ArrayImpl, cons: ArrayImpl, cons_dot: ArrayImpl, cons_min: ArrayImpl = None, 524 | cons_max: ArrayImpl = None 525 | ) -> Tuple[ArrayImpl, ArrayImpl]: 526 | """ 527 | Computes the barrier function with clipping. 528 | 529 | Args: 530 | q1 (ArrayImpl): scalar 531 | q2 (ArrayImpl): scalar 532 | cons (ArrayImpl): scalar 533 | cons_dot (ArrayImpl): N-by-1 534 | cons_min (ArrayImpl): scalar 535 | cons_max (ArrayImpl): scalar 536 | 537 | Returns: 538 | ArrayImpl: N-by-1 539 | ArrayImpl: N-by-N 540 | """ 541 | tmp = jnp.clip(q2 * cons, cons_min, cons_max) 542 | b = q1 * (jnp.exp(tmp)) 543 | 544 | b_dot = q2 * b * cons_dot 545 | b_ddot = (q2**2) * b * jnp.outer(cons_dot, cons_dot) 546 | 547 | return b_dot, b_ddot 548 | -------------------------------------------------------------------------------- /iLQR/dynamics.py: -------------------------------------------------------------------------------- 1 | """ 2 | Dynamics. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 7 | TODOs: 8 | - Make state & ctrl indices global, static variables, e.g. self._px_idx = 0 9 | """ 10 | 11 | from typing import Optional, Tuple 12 | import numpy as np 13 | 14 | from functools import partial 15 | from jax import jit, jacfwd 16 | from jaxlib.xla_extension import ArrayImpl 17 | import jax.numpy as jnp 18 | import jax 19 | 20 | 21 | class Dynamics: 22 | 23 | def __init__(self, config): 24 | self.dim_x = 4 25 | self.dim_u = 2 26 | 27 | self.T = config.T # Planning Time Horizon 28 | self.N = config.N # number of planning steps 29 | self.dt = self.T / (self.N - 1) # time step for each planning step 30 | 31 | self.wheelbase = config.WHEELBASE # vehicle chassis length 32 | self.delta_min = config.DELTA_MIN # min steering angle rad 33 | self.delta_max = config.DELTA_MAX # max steering angle rad 34 | self.a_min = config.A_MIN # min longitudial accel 35 | self.a_max = config.A_MAX # max longitudial accel 36 | self.v_min = config.V_MIN # min velocity 37 | self.v_max = config.V_MAX # max velocity 38 | 39 | # Useful constants. 40 | self.zeros = np.zeros((self.N)) 41 | self.ones = np.ones((self.N)) 42 | 43 | # Computes Jacobian matrices using Jax. 44 | self.jac_f = jit(jacfwd(self.dct_time_dyn, argnums=[0, 1])) 45 | 46 | # Vectorizes Jacobians using Jax. 47 | self.jac_f = jit(jax.vmap(self.jac_f, in_axes=(1, 1), out_axes=(2, 2))) 48 | 49 | def forward_step( 50 | self, state: np.ndarray, control: np.ndarray, step: Optional[int] = 1, 51 | noise: Optional[np.ndarray] = None, noise_type: Optional[str] = 'unif' 52 | ) -> Tuple[np.ndarray, np.ndarray]: 53 | """ 54 | Finds the next state of the vehicle given the current state and 55 | control input state. 56 | 57 | Args: 58 | state (np.ndarray): (4, ) array [X, Y, V, psi]. 59 | control (np.ndarray): (2, ) array [a, delta]. 60 | step (int, optional): The number of segements to forward the 61 | dynamics. Defaults to 1. 62 | noise (np.ndarray, optional): The ball radius or standard 63 | deviation of the Gaussian noise. The magnitude should be in the 64 | sense of self.dt. Defaults to None. 65 | noise_type(str, optional): Uniform or Gaussian. Defaults to 'unif'. 66 | 67 | Returns: 68 | np.ndarray: next state. 69 | np.ndarray: clipped control. 70 | """ 71 | # Clips the controller values between min and max accel and steer values. 72 | accel = np.clip(control[0], self.a_min, self.a_max) 73 | delta = np.clip(control[1], self.delta_min, self.delta_max) 74 | control_clip = np.array([accel, delta]) 75 | next_state = state 76 | dt_step = self.dt / step 77 | 78 | for _ in range(step): 79 | # State: [x, y, v, psi] 80 | d_x = ((next_state[2] * dt_step + 0.5 * accel * dt_step**2) * np.cos(next_state[3])) 81 | d_y = ((next_state[2] * dt_step + 0.5 * accel * dt_step**2) * np.sin(next_state[3])) 82 | d_v = accel * dt_step 83 | d_psi = ((next_state[2] * dt_step + 0.5 * accel * dt_step**2) * np.tan(delta) 84 | / self.wheelbase) 85 | next_state = next_state + np.array([d_x, d_y, d_v, d_psi]) 86 | if noise is not None: 87 | T = np.array([[np.cos(next_state[-1]), np.sin(next_state[-1]), 0, 0], 88 | [-np.sin(next_state[-1]), 89 | np.cos(next_state[-1]), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]) 90 | if noise_type == 'unif': 91 | rv = np.random.rand(4) - 0.5 92 | else: 93 | rv = np.random.normal(size=(4)) 94 | next_state = next_state + (T@noise) * rv / step 95 | 96 | # Clip the velocity. 97 | next_state[2] = np.clip(next_state[2], 0, None) 98 | 99 | return next_state, control_clip 100 | 101 | # ---------------------------- Jitted functions ------------------------------ 102 | @partial(jit, static_argnums=(0,)) 103 | def dct_time_dyn(self, state: ArrayImpl, control: ArrayImpl) -> ArrayImpl: 104 | """ 105 | Computes the one-step time evolution of the system. 106 | 107 | Args: 108 | state (ArrayImpl): (4,) jnp array [x, y, v, psi]. 109 | control (ArrayImpl): (2,) jnp array [a, delta]. 110 | 111 | Returns: 112 | ArrayImpl: next state. 113 | """ 114 | d_x = ((state[2] * self.dt + 0.5 * control[0] * self.dt**2) * jnp.cos(state[3])) 115 | d_y = ((state[2] * self.dt + 0.5 * control[0] * self.dt**2) * jnp.sin(state[3])) 116 | d_v = control[0] * self.dt 117 | d_psi = ((state[2] * self.dt + 0.5 * control[0] * self.dt**2) * jnp.tan(control[1]) 118 | / self.wheelbase) 119 | return state + jnp.hstack((d_x, d_y, d_v, d_psi)) 120 | 121 | @partial(jit, static_argnums=(0,)) 122 | def integrate_forward_jax(self, state: ArrayImpl, 123 | control: ArrayImpl) -> Tuple[ArrayImpl, ArrayImpl]: 124 | """ 125 | Computes the next state. 126 | 127 | Args: 128 | state (ArrayImpl): (4,) jnp array [x, y, v, psi]. 129 | control (ArrayImpl): (2,) jnp array [a, delta]. 130 | 131 | Returns: 132 | state_next: ArrayImpl 133 | control_next: ArrayImpl 134 | """ 135 | # Clips the control values with their limits. 136 | accel = jnp.clip(control[0], self.a_min, self.a_max) 137 | delta = jnp.clip(control[1], self.delta_min, self.delta_max) 138 | 139 | # Integrates the system one-step forward in time using the Euler method. 140 | control_clip = jnp.hstack((accel, delta)) 141 | state_next = self.dct_time_dyn(state, control_clip) 142 | 143 | return state_next, control_clip 144 | 145 | @partial(jit, static_argnums=(0,)) 146 | def get_AB_matrix_jax(self, nominal_states: ArrayImpl, 147 | nominal_controls: ArrayImpl) -> Tuple[ArrayImpl, ArrayImpl]: 148 | """ 149 | Returns the linearized 'A' and 'B' matrix of the ego vehicle around 150 | nominal states and controls. 151 | 152 | Args: 153 | nominal_states (ArrayImpl): (nx, N) states along the nominal traj. 154 | nominal_controls (ArrayImpl): (nu, N) controls along the traj. 155 | 156 | Returns: 157 | ArrayImpl: the Jacobian of next state w.r.t. the current state. 158 | ArrayImpl: the Jacobian of next state w.r.t. the current control. 159 | """ 160 | A, B = self.jac_f(nominal_states, nominal_controls) 161 | return A, B 162 | -------------------------------------------------------------------------------- /iLQR/ellipsoid_obj.py: -------------------------------------------------------------------------------- 1 | """ 2 | Ellipsoid object for collision avoidance. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: 7 | - ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 8 | - Ellipsoidal Toolbox (Alex Kurzhanskiy) 9 | - ellReach (Python) Toolbox (Haimin Hu) 10 | """ 11 | 12 | from __future__ import annotations 13 | from typing import Tuple, List 14 | import numpy as np 15 | import matplotlib 16 | 17 | 18 | class EllipsoidObj(): 19 | 20 | def __init__( 21 | self, q: np.ndarray = np.array([]), Q: np.ndarray = np.array([[], []]), 22 | r=None, n_circ=None, center_L=None, major_axis=None 23 | ): 24 | 25 | def symmetricize(Q): 26 | if Q.shape[1] == 0: 27 | return Q 28 | else: 29 | return (Q + Q.T) / 2 30 | 31 | self.q = q 32 | self.Q = symmetricize(Q) 33 | self.Qinv = np.linalg.inv(self.Q) 34 | 35 | if q.shape[0] != 2: 36 | raise ValueError( 37 | "The dimension should be 2 but get {}!".format(q.shape[0]) 38 | ) 39 | 40 | # Assume 2d ellipsoid。 41 | if r is None: 42 | # print("use eigval") 43 | eigVal, eigVec = np.linalg.eig(Q) 44 | eigVal = np.sqrt(eigVal) 45 | if eigVal[0] > eigVal[1]: 46 | a = eigVal[0] 47 | b = eigVal[1] 48 | self.major_axis = eigVec[:, 0] 49 | else: 50 | a = eigVal[1] 51 | b = eigVal[0] 52 | self.major_axis = eigVec[:, 1] 53 | self.r = b # semi-minor axis 54 | self.n_circ = int(np.ceil(a / b)) #circles to cover the ellipsoid. 55 | # Assume local coordinate's origin is at the center of ellipsoid, 56 | # and x is align with major axis. Locations of circle centers are on 57 | # the local coordinate. 58 | self.center_L = np.linspace(-a + self.r, a - self.r, self.n_circ) 59 | else: 60 | self.r = r 61 | self.n_circ = n_circ 62 | self.center_L = center_L 63 | self.major_axis = major_axis 64 | 65 | # position of circle center on global coordinates 66 | self.center = np.einsum('n,a->an', self.center_L, self.major_axis) + self.q 67 | 68 | self.closest_pt = None 69 | self.slope = None 70 | self.width_L = None 71 | self.width_R = None 72 | 73 | def copy(self) -> EllipsoidObj: 74 | """ 75 | Creats an identical ellipsoid object. 76 | 77 | Returns: 78 | EllipsoidObj: an ellipsoid object. 79 | """ 80 | return EllipsoidObj(q=self.q, Q=self.Q) 81 | 82 | def is_degenerate(self) -> bool: 83 | """ 84 | Checks if the ellipsoid is degenerate. 85 | Returns True if the ellipsoid is degenerate, False - otherwise. 86 | 87 | Returns: 88 | bool: flag indicating if the ellipsoid is degenerate. 89 | """ 90 | return self.rank() < self.dim() 91 | 92 | def is_empty(self) -> bool: 93 | """ 94 | Checks if the ellipsoid object is empty. 95 | Returns True if the ellipsoid is empty, False - otherwise. 96 | 97 | Returns: 98 | bool: flag indicating if the ellipsoid is empty. 99 | """ 100 | return self.dim() == 0 101 | 102 | def dim(self) -> int: 103 | """ 104 | Returns the dimension of the ellipsoid. 105 | 106 | Returns: 107 | int: dimension of the ellipsoid. 108 | """ 109 | return self.q.size 110 | 111 | def rank(self) -> int: 112 | """ 113 | Returns the rank of the shape matrix. 114 | 115 | Returns: 116 | int: rank of the shape matrix. 117 | """ 118 | if self.dim() == 0: 119 | return 0 120 | else: 121 | return np.linalg.matrix_rank(self.Q) 122 | 123 | def parameters(self) -> Tuple[np.ndarray, np.ndarray]: 124 | """ 125 | Returns parameters of the ellipsoid. 126 | 127 | Returns: 128 | np.ndarray: center of the ellipsoid. 129 | np.ndarray: shape matrix of the ellipsoid. 130 | """ 131 | return self.q, self.Q 132 | 133 | def rho(self, L: np.ndarray, 134 | eps: float = 1e-10) -> Tuple[np.ndarray, np.ndarray]: 135 | """ 136 | Computes the support function of the ellipsoid in directions specified by 137 | the columns of matrix L, and boundary points X of this ellipsoid that 138 | correspond to directions in L. 139 | 140 | Args: 141 | L (np.ndarray): a direction matrix. 142 | eps (float): tolerance number. 143 | 144 | Returns: 145 | res (np.ndarray): the values of the support function for the specified 146 | ellipsoid and directions L. 147 | x (np.ndarray): boundary points of the ellipsoid that correspond to 148 | directions in L. 149 | """ 150 | d = L.shape[1] 151 | res = np.array([]).reshape((1, 0)) 152 | x = np.array([]).reshape((self.dim(), 0)) 153 | for i in range(d): 154 | l = L[:, i].reshape(self.dim(), 1) 155 | sr = np.maximum(np.sqrt(l.T @ self.Q @ l)[0, 0], eps) 156 | res = np.hstack((res, self.q.T @ l + sr)) 157 | x = np.hstack((x, self.Q @ l / sr + self.q)) 158 | return res, x 159 | 160 | def __matmul__(self, A: np.ndarray) -> EllipsoidObj: 161 | """ 162 | Multiplies the ellipsoid with a matrix or a scalar. 163 | If E(q,Q) is an ellipsoid, and A - matrix of suitable dimensions, then 164 | E(q, Q) @ A = E(Aq, AQA'). 165 | 166 | Args: 167 | A (np.ndarray): a matrix. 168 | 169 | Returns: 170 | EllipsoidObj: an ellipsoid object. 171 | """ 172 | q = A @ self.q 173 | Q = A @ self.Q @ A.T 174 | new_major_axis = A @ self.major_axis 175 | return EllipsoidObj( 176 | q=q, Q=Q, r=self.r, n_circ=self.n_circ, center_L=self.center_L, 177 | major_axis=new_major_axis 178 | ) 179 | 180 | def __add__(self, b: np.ndarray) -> EllipsoidObj: 181 | """ 182 | Computes vector addition with an ellipsoid: 183 | E + b = E(q + b, Q). 184 | 185 | Args: 186 | b (np.ndarray): a vector. 187 | """ 188 | if b.shape != self.q.shape: 189 | raise ValueError("Dimensions of b and q of the ellipsoid do not match.") 190 | return EllipsoidObj( 191 | q=self.q + b, Q=self.Q, r=self.r, n_circ=self.n_circ, 192 | center_L=self.center_L, major_axis=self.major_axis 193 | ) 194 | 195 | def add(self, b: np.ndarray): 196 | """ 197 | Computes vector addition with an ellipsoid: 198 | E + b = E(q + b, Q). 199 | 200 | Args: 201 | b (np.ndarray): a vector. 202 | """ 203 | self.q = self.q + b 204 | self.center = self.center + b 205 | 206 | def __sub__(self, b: np.ndarray) -> EllipsoidObj: 207 | """ 208 | Computes vector subtraction from an ellipsoid: 209 | E - b = E(q - b, Q). 210 | 211 | Args: 212 | b (np.ndarray): a vector. 213 | """ 214 | if b.shape != self.q.shape: 215 | raise ValueError("Dimensions of b and q of the ellipsoid do not match.") 216 | return EllipsoidObj( 217 | q=self.q - b, Q=self.Q, r=self.r, n_circ=self.n_circ, 218 | center_L=self.center_L, major_axis=self.major_axis 219 | ) 220 | 221 | def plot_circ(self, ax: matplotlib.axes.Axes): 222 | """ 223 | Plots a circle. 224 | 225 | Args: 226 | ax (matplotlib.axes.Axes): matplotlib axe object. 227 | """ 228 | theta = np.linspace(0, 2 * np.pi, 100) 229 | x0 = self.r * np.cos(theta) 230 | y0 = self.r * np.sin(theta) 231 | for i in range(self.n_circ): 232 | x = self.center[0, i] + x0 233 | y = self.center[1, i] + y0 234 | ax.plot(x, y) 235 | 236 | def projection(self, dims: List) -> EllipsoidObj: 237 | """ 238 | Computes projection of the ellipsoid onto the given dimensions. 239 | 240 | Args: 241 | dims (List): dimension to be preserved. 242 | 243 | Returns: 244 | EllipsoidObj: projected ellipsoid (of lower dimension). 245 | """ 246 | if not isinstance(dims, list): 247 | raise ValueError("Dims must be a list.") 248 | n = self.dim() 249 | m = len(dims) 250 | B = np.zeros((n, m)) 251 | for i in range(m): 252 | B[dims[i], i] = 1 253 | return self @ B.T 254 | 255 | def is_internal( 256 | self, x: np.ndarray, abs_tol: float = 1e-7, slack: float = 0. 257 | ) -> bool: 258 | """ 259 | Checks if the given point belongs to the ellipsoid, i.e. if inequality 260 | <(x - q), Q^(-1)(x - q)> <= 1 261 | holds. 262 | 263 | Args: 264 | x (np.ndarray): a column vector with the same dimension as the 265 | ellipsoid object. 266 | abs_tol (float, optional): tolerance number. Defaults to 1e-7. 267 | slack (float, optional): slack variable. Defaults to 0.. 268 | 269 | Returns: 270 | bool: flag indicating if the given point belongs to the ellipsoid. 271 | """ 272 | q = x - self.q 273 | r = (q.T @ self.Qinv @ q)[0, 0] 274 | if r < 1 + slack or np.abs(r - 1 + slack) < abs_tol: 275 | return True 276 | else: 277 | return False 278 | 279 | def plot( 280 | self, ax: matplotlib.axes.Axes, color: str = 'r', dims: List = None, 281 | N: int = 200, plot_center: bool = True 282 | ): 283 | """ 284 | Plots the ellipsoid object in 1D, 2D or 3D. 285 | 286 | Args: 287 | ax (matplotlib.axes.Axes): matplotlib axe object. 288 | color (str): color scheme. 289 | dims (List): dimension to be preserved. 290 | N (int): number of boundary points. 291 | plot_center (bool): flag indicating if to plot the ellipsoid center. 292 | """ 293 | if dims: 294 | E = self.projection(dims) 295 | else: 296 | E = self.copy() 297 | if E.dim() > 3: 298 | raise ValueError("Ellipsoid dimension can be 1, 2 or 3.") 299 | if E.dim() == 1: 300 | x = np.array([(E.q - np.sqrt(E.Q))[0, 0], (E.q + np.sqrt(E.Q))[0, 0]]) 301 | ax.plot(x, np.zeros_like(x), color) 302 | ax.plot(E.q, 0, color + '*') 303 | if E.dim() == 2: 304 | phi = np.array(np.linspace(0., 2. * np.pi, N)) 305 | L = np.concatenate( 306 | (np.cos(phi).reshape(1, N), np.sin(phi).reshape(1, N)), axis=0 307 | ) 308 | _, x = E.rho(L) 309 | ax.plot(x[0, :], x[1, :], color) 310 | if plot_center: 311 | ax.plot(E.q[0, 0], E.q[1, 0], color + '.') 312 | ax.set_aspect('equal') 313 | if E.dim() == 3: 314 | raise NotImplementedError 315 | 316 | def obstacle_cost(self, obs: EllipsoidObj, q1: float, q2: float) -> float: 317 | """ 318 | Computes the barrier cost given an obstacle and hyperparameters. 319 | 320 | Args: 321 | obs (EllipsoidObj): obstacle. 322 | q1 (float): shape parameter. 323 | q2 (float): scale parameter. 324 | 325 | Returns: 326 | float: barrier cost. 327 | """ 328 | # Broadcasts to (2, self.n_circ, obs.n_circ). 329 | d = (self.center.T[:, np.newaxis, :] - obs.center.T).T 330 | # Reshapes to (2, self.n_circ*obs.n_circ). 331 | d = np.reshape(d, (2, self.n_circ * obs.n_circ), "F") 332 | # Ignores and sets to -0.2 when self is far away from the obstacle. 333 | g = np.clip(self.r + obs.r - np.linalg.norm(d, axis=0), -0.2, None) 334 | b = np.sum(q1 * np.exp(q2 * g)) # barrier 335 | return b 336 | 337 | def obstacle_derivative( 338 | self, state: np.ndarray, center_L: float, obs: EllipsoidObj, q1: float, 339 | q2: float 340 | ) -> Tuple(np.ndarray, np.ndarray): 341 | """ 342 | Computes ellipsoidal obstacle soft constraint cost Jacobian and Hessian. 343 | 344 | Args: 345 | state (np.ndarray): nominal state trajectory, of the shape 346 | (x_dim, N). 347 | center_L (float): displacement of vehicle origin along the major 348 | axis of ellipsoid. 349 | obs (EllipsoidObj): obstacle (or its FRS) represented by ellipsoid. 350 | q1 (float): barrier function parameters. 351 | q2 (float): barrier function parameters. 352 | 353 | Returns: 354 | np.ndarray: Jacobian of softconstraint of shape (x_dim, N). 355 | np.ndarray: Hessian of softconstraint of shape (x_dim, x_dim, N). 356 | """ 357 | x = state[0] 358 | y = state[1] 359 | theta = state[3] 360 | ct = np.cos(theta) 361 | st = np.sin(theta) 362 | center_L = self.center_L + center_L 363 | 364 | c_x = np.zeros(4) 365 | c_xx = np.zeros((4, 4)) 366 | 367 | for i in range(self.n_circ): 368 | c_x_temp = np.zeros((4, obs.n_circ)) 369 | c_xx_temp = np.zeros((4, 4, obs.n_circ)) 370 | center_i = self.center[:, i][:, np.newaxis] 371 | dxy = center_i - obs.center 372 | dx = dxy[0, :] 373 | dy = dxy[1, :] 374 | d = np.linalg.norm(dxy, axis=0) 375 | 376 | b = self.r + obs.r - d 377 | 378 | idx_ignore = b < -0.2 379 | c = q1 * np.exp(q2 * b) 380 | 381 | # Jacobian 382 | c_x_temp[:2, :] = (-(q2 * c * dxy) / d) 383 | 384 | c_x_temp[3, :] = -( 385 | center_L[i] * q2 * c * 386 | (y*ct - obs.center[1, :] * ct - x*st + obs.center[0, :] * st) 387 | ) / d 388 | 389 | # Hessian 390 | c_xx_temp[0, 0, :] = ((q2 * c * dx**2) / (d**3) - (q2*c) / d + 391 | (q2**2 * c * dx**2) / (d**2)) 392 | c_xx_temp[1, 1, :] = ((q2 * c * dy**2) / (d**3) - (q2*c) / d + 393 | (q2**2 * c * dy**2) / (d**2)) 394 | c_xx_temp[0, 1, :] = ((q2**2 * c * dx * dy) / (d**2) + (q2*c*dx*dy) / 395 | (d**3)) 396 | c_xx_temp[1, 0, :] = ((q2**2 * c * dx * dy) / (d**2) + (q2*c*dx*dy) / 397 | (d**3)) 398 | 399 | c_x_temp[:, idx_ignore] = 0 400 | c_xx_temp[:, :, idx_ignore] = 0 401 | 402 | c_x += c_x_temp.sum(axis=-1) 403 | c_xx += c_xx_temp.sum(axis=-1) 404 | 405 | return c_x, c_xx 406 | -------------------------------------------------------------------------------- /iLQR/ilqr.py: -------------------------------------------------------------------------------- 1 | """ 2 | Jaxified iterative Linear Quadrative Regulator (iLQR). 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 7 | """ 8 | 9 | import time 10 | import numpy as np 11 | from typing import Tuple 12 | 13 | from .cost import Cost 14 | from .dynamics import Dynamics 15 | 16 | from functools import partial 17 | from jax import jit, lax 18 | from jaxlib.xla_extension import ArrayImpl 19 | import jax.numpy as jnp 20 | 21 | 22 | class iLQR(): 23 | 24 | def __init__(self, ref_path, config, safety=True): 25 | 26 | self.T = config.T 27 | self.N = config.N 28 | 29 | self.ref_path = ref_path 30 | 31 | self.steps = config.MAX_ITER 32 | 33 | self.tol = 1e-2 34 | self.lambad = 10 35 | self.lambad_max = 100 36 | self.lambad_min = 1e-3 37 | 38 | self.dynamics = Dynamics(config) 39 | self.alphas = 1.1**(-np.arange(10)**2) 40 | 41 | self.dim_x = self.dynamics.dim_x 42 | self.dim_u = self.dynamics.dim_u 43 | 44 | self.cost = Cost(config, safety) 45 | 46 | def forward_pass( 47 | self, nominal_states: ArrayImpl, nominal_controls: ArrayImpl, Ks: ArrayImpl, ks: ArrayImpl, 48 | alpha: float 49 | ) -> Tuple[np.ndarray, np.ndarray, float, np.ndarray, np.ndarray, np.ndarray]: 50 | """ 51 | Forward pass wrapper. 52 | Calls forward_pass_jax to speed up computation. 53 | 54 | Args: 55 | nominal_states (ArrayImpl): (dim_x, N) 56 | nominal_controls (ArrayImpl): (dim_u, N) 57 | Ks (ArrayImpl): gain matrices (dim_u, dim_x, N - 1) 58 | ks (ArrayImpl): gain vectors (dim_u, N - 1) 59 | alpha (float): scalar parameter 60 | 61 | Returns: 62 | X (np.ndarray): (dim_x, N) 63 | U (np.ndarray): (dim_u, N) 64 | J (float): total cost. 65 | closest_pts (np.ndarray): 2xN array of each state's closest point [x,y] 66 | on the track. 67 | slope (np.ndarray): 1xN array of track's slopes (rad) at closest 68 | points. 69 | theta (np.ndarray): 1xN array of the progress at each state. 70 | """ 71 | Xs, Us = self.forward_pass_jax(nominal_states, nominal_controls, Ks, ks, alpha) 72 | Xs = np.asarray(Xs) 73 | Us = np.asarray(Us) 74 | 75 | closest_pt, slope, theta = self.ref_path.get_closest_pts(Xs[:2, :]) 76 | J = self.cost.get_cost(Xs, Us, closest_pt, slope, theta) 77 | return Xs, Us, J, closest_pt, slope, theta 78 | 79 | def backward_pass( 80 | self, nominal_states: ArrayImpl, nominal_controls: ArrayImpl, closest_pts: ArrayImpl, 81 | slopes: ArrayImpl 82 | ) -> Tuple[np.ndarray, np.ndarray]: 83 | """ 84 | Backward pass wrapper. 85 | Calls get_AB_matrix_jax and backward_pass_jax to speed up computation. 86 | 87 | Args: 88 | nominal_states (ArrayImpl): (dim_x, N) 89 | nominal_controls (ArrayImpl): (dim_u, N) 90 | closest_pts (ArrayImpl): (2, N) 91 | slopes (ArrayImpl): (1, N) 92 | 93 | Returns: 94 | Ks (np.ndarray): gain matrices (dim_u, dim_x, N - 1) 95 | ks (np.ndarray): gain vectors (dim_u, N - 1) 96 | """ 97 | # t0 = time.time() 98 | L_x, L_xx, L_u, L_uu, L_ux = self.cost.get_derivatives_jax( 99 | nominal_states, nominal_controls, closest_pts, slopes 100 | ) 101 | fx, fu = self.dynamics.get_AB_matrix_jax(nominal_states, nominal_controls) 102 | Ks, ks = self.backward_pass_jax(L_x, L_xx, L_u, L_uu, L_ux, fx, fu) 103 | # print("backward pass use: ", time.time()-t0) 104 | return np.asarray(Ks), np.asarray(ks) 105 | 106 | def solve( 107 | self, cur_state: np.ndarray, controls: np.ndarray = None, obs_list: list = [], 108 | record: bool = False 109 | ) -> Tuple[np.ndarray, np.ndarray, float, int, np.ndarray, np.ndarray, np.ndarray, np.ndarray]: 110 | """ 111 | Solves the iLQR problem. 112 | 113 | Args: 114 | cur_state (np.ndarray): (dim_x,) 115 | controls (np.ndarray, optional): (self.dim_u,). Defaults to None. 116 | obs_list (list, optional): obstacle list. Defaults to []. 117 | record (bool, optional): Defaults to False. 118 | 119 | Returns: 120 | states: np.ndarray 121 | controls: np.ndarray 122 | t_process: float 123 | status: int 124 | theta: np.ndarray 125 | Ks: np.ndarray 126 | fx: np.ndarray 127 | fu: np.ndarray 128 | """ 129 | status = 0 130 | 131 | time0 = time.time() 132 | 133 | if controls is None: 134 | controls = np.zeros((self.dim_u, self.N)) 135 | states = np.zeros((self.dim_x, self.N)) 136 | states[:, 0] = cur_state 137 | 138 | for i in range(1, self.N): 139 | states[:, i], _ = self.dynamics.forward_step(states[:, i - 1], controls[:, i - 1]) 140 | closest_pt, slope, theta = self.ref_path.get_closest_pts(states[:2, :]) 141 | 142 | self.cost.update_obs(obs_list) 143 | 144 | J = self.cost.get_cost(states, controls, closest_pt, slope, theta) 145 | 146 | converged = False 147 | 148 | for i in range(self.steps): 149 | slope = slope[np.newaxis, :] 150 | Ks, ks = self.backward_pass(states, controls, closest_pt, slope) 151 | 152 | updated = False 153 | for alpha in self.alphas: 154 | X_new, U_new, J_new, closest_pt_new, slope_new, theta_new = ( 155 | self.forward_pass(states, controls, Ks, ks, alpha) 156 | ) 157 | if J_new <= J: 158 | if np.abs((J-J_new) / J) < self.tol: 159 | converged = True 160 | J = J_new 161 | states = X_new 162 | controls = U_new 163 | closest_pt = closest_pt_new 164 | slope = slope_new 165 | theta = theta_new 166 | updated = True 167 | break 168 | if updated: 169 | self.lambad *= 0.7 170 | else: 171 | status = 2 172 | break 173 | self.lambad = max(self.lambad_min, self.lambad) 174 | 175 | if converged: 176 | status = 1 177 | break 178 | t_process = time.time() - time0 179 | 180 | if record: 181 | Ks, _ = self.backward_pass_jax(states, controls, closest_pt, slope) 182 | fx, fu = self.dynamics.get_AB_matrix_jax(states, controls) 183 | else: 184 | Ks = None 185 | fx = None 186 | fu = None 187 | return states, controls, t_process, status, theta, Ks, fx, fu 188 | 189 | # ----------------------------- Jitted functions ----------------------------- 190 | @partial(jit, static_argnums=(0,)) 191 | def forward_pass_jax( 192 | self, nominal_states: ArrayImpl, nominal_controls: ArrayImpl, Ks: ArrayImpl, ks: ArrayImpl, 193 | alpha: float 194 | ) -> Tuple[ArrayImpl, ArrayImpl]: 195 | """ 196 | Jitted forward pass looped computation. 197 | 198 | Args: 199 | nominal_states (ArrayImpl): (dim_x, N) 200 | nominal_controls (ArrayImpl): (dim_u, N) 201 | Ks (ArrayImpl): gain matrices (dim_u, dim_x, N - 1) 202 | ks (ArrayImpl): gain vectors (dim_u, N - 1) 203 | alpha (float): scalar parameter 204 | 205 | Returns: 206 | Xs (ArrayImpl): (dim_x, N) 207 | Us (ArrayImpl): (dim_u, N) 208 | """ 209 | 210 | @jit 211 | def forward_pass_looper(i, _carry): 212 | Xs, Us = _carry 213 | u = ( 214 | nominal_controls[:, i] + alpha * ks[:, i] 215 | + Ks[:, :, i] @ (Xs[:, i] - nominal_states[:, i]) 216 | ) 217 | X_next, U_next = self.dynamics.integrate_forward_jax(Xs[:, i], u) 218 | Xs = Xs.at[:, i + 1].set(X_next) 219 | Us = Us.at[:, i].set(U_next) 220 | return Xs, Us 221 | 222 | Xs = jnp.zeros((self.dim_x, self.N)) 223 | Us = jnp.zeros((self.dim_u, self.N)) 224 | Xs = Xs.at[:, 0].set(nominal_states[:, 0]) 225 | Xs, Us = lax.fori_loop(0, self.N - 1, forward_pass_looper, (Xs, Us)) 226 | return Xs, Us 227 | 228 | @partial(jit, static_argnums=(0,)) 229 | def backward_pass_jax( 230 | self, L_x: ArrayImpl, L_xx: ArrayImpl, L_u: ArrayImpl, L_uu: ArrayImpl, L_ux: ArrayImpl, 231 | fx: ArrayImpl, fu: ArrayImpl 232 | ) -> Tuple[ArrayImpl, ArrayImpl]: 233 | """ 234 | Jitted backward pass looped computation. 235 | 236 | Args: 237 | L_x (ArrayImpl): (dim_x, N) 238 | L_xx (ArrayImpl): (dim_x, dim_x, N) 239 | L_u (ArrayImpl): (dim_u, N) 240 | L_uu (ArrayImpl): (dim_u, dim_u, N) 241 | L_ux (ArrayImpl): (dim_u, dim_x, N) 242 | fx (ArrayImpl): (dim_x, dim_x, N) 243 | fu (ArrayImpl): (dim_x, dim_u, N) 244 | 245 | Returns: 246 | Ks (ArrayImpl): gain matrices (dim_u, dim_x, N - 1) 247 | ks (ArrayImpl): gain vectors (dim_u, N - 1) 248 | """ 249 | 250 | @jit 251 | def backward_pass_looper(i, _carry): 252 | V_x, V_xx, ks, Ks = _carry 253 | n = self.N - 2 - i 254 | 255 | Q_x = L_x[:, n] + fx[:, :, n].T @ V_x 256 | Q_u = L_u[:, n] + fu[:, :, n].T @ V_x 257 | Q_xx = L_xx[:, :, n] + fx[:, :, n].T @ V_xx @ fx[:, :, n] 258 | Q_ux = L_ux[:, :, n] + fu[:, :, n].T @ V_xx @ fx[:, :, n] 259 | Q_uu = L_uu[:, :, n] + fu[:, :, n].T @ V_xx @ fu[:, :, n] 260 | 261 | Q_uu_inv = jnp.linalg.inv(Q_uu + reg_mat) 262 | 263 | Ks = Ks.at[:, :, n].set(-Q_uu_inv @ Q_ux) 264 | ks = ks.at[:, n].set(-Q_uu_inv @ Q_u) 265 | 266 | V_x = Q_x - Ks[:, :, n].T @ Q_uu @ ks[:, n] 267 | V_xx = Q_xx - Ks[:, :, n].T @ Q_uu @ Ks[:, :, n] 268 | 269 | return V_x, V_xx, ks, Ks 270 | 271 | Ks = jnp.zeros((self.dim_u, self.dim_x, self.N - 1)) 272 | ks = jnp.zeros((self.dim_u, self.N - 1)) 273 | 274 | V_x = L_x[:, -1] 275 | V_xx = L_xx[:, :, -1] 276 | 277 | reg_mat = self.lambad * jnp.eye(self.dim_u) 278 | 279 | V_x, V_xx, ks, Ks = lax.fori_loop(0, self.N - 1, backward_pass_looper, (V_x, V_xx, ks, Ks)) 280 | return Ks, ks 281 | -------------------------------------------------------------------------------- /iLQR/shielding.py: -------------------------------------------------------------------------------- 1 | """ 2 | iLQR-based shielding. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | """ 7 | 8 | import numpy as np 9 | 10 | from jaxlib.xla_extension import ArrayImpl 11 | 12 | 13 | class Shielding(object): 14 | 15 | def __init__(self, obs_list, config, N_sh=None): 16 | """ 17 | Base class for all shielding mechanisms. 18 | 19 | Args: 20 | obs_list (list): list of ellipsoidal obstacles. 21 | config (Struct): parameter data. 22 | N_sh (int): shielding rollout horizon. 23 | """ 24 | 25 | self.obs_list = obs_list 26 | self.config = config 27 | self.coll_check_slack = config.COLL_CHECK_SLACK 28 | self.N_sh = N_sh 29 | self.sh_flag = False 30 | 31 | def is_collision(self, states: ArrayImpl) -> bool: 32 | """ 33 | Checks collision with the static obstacles. 34 | 35 | Args: 36 | states (ArrayImpl): state trajectory of the ego agent. 37 | 38 | Returns: 39 | bool: True if there is a collision. 40 | """ 41 | states = np.asarray(states) 42 | 43 | if len(states.shape) < 2: 44 | states = states[:, np.newaxis] 45 | 46 | if self.N_sh is None: 47 | N_sh = states.shape[1] 48 | else: 49 | N_sh = min(states.shape[1], self.N_sh) 50 | 51 | res = False 52 | for obs in self.obs_list: 53 | for k in range(N_sh): 54 | pos_k = states[:2, k] 55 | pos_k = pos_k[:, np.newaxis] 56 | if obs.is_internal(pos_k, slack=self.coll_check_slack): 57 | return True 58 | return res 59 | 60 | 61 | class NaiveSwerving(Shielding): 62 | 63 | def __init__(self, config, obs_list, dynamics, max_deacc, N_sh=None): 64 | """ 65 | Naive swerving shielding strategy: maximum steering and deacceleration. 66 | 67 | Args: 68 | obs_list (list): list of ellipsoidal obstacles. 69 | config (Struct): parameter data. 70 | dynamics (Dynamics): dynamical system. 71 | max_deacc (float): maximum deacceleration. 72 | N_sh (int): shielding rollout horizon. 73 | """ 74 | 75 | self.states = None 76 | self.controls_init = np.zeros((2, config.N)) 77 | self.max_deacc = max_deacc 78 | self.delta_min = config.DELTA_MIN 79 | self.delta_max = config.DELTA_MAX 80 | self.dynamics = dynamics 81 | super(NaiveSwerving, self).__init__(obs_list, config, N_sh) 82 | 83 | def run(self, x: ArrayImpl, u_nominal: ArrayImpl) -> np.ndarray: 84 | """ 85 | Runs the naive swerving shielding. 86 | 87 | Args: 88 | x (ArrayImpl): current state. 89 | u_nominal (ArrayImpl): nominal control. 90 | 91 | Returns: 92 | np.ndarray: shielded control. 93 | """ 94 | 95 | x = np.asarray(x) 96 | u_nominal = np.asarray(u_nominal) 97 | 98 | # Resets the shielding flag. 99 | self.sh_flag = False 100 | 101 | # One-step simulation with robot's nominal policy. 102 | x_next_nom = self.dynamics.forward_step(x, u_nominal)[0] 103 | 104 | success_delta_min = True 105 | 106 | if self.is_collision(x_next_nom): 107 | self.sh_flag = True 108 | else: 109 | # Computes iLQR shielding policies starting with x_next_nom. 110 | # 1. First try delta_min. 111 | u_sh = np.array([self.max_deacc, self.delta_min]) 112 | x_sh = x_next_nom 113 | for _ in range(self.N_sh - 1): 114 | x_sh = self.dynamics.forward_step(x_sh, u_sh)[0] 115 | if self.is_collision(x_sh): 116 | success_delta_min = False 117 | break 118 | 119 | # 2. If delta_min fails, then try delta_max. 120 | if not success_delta_min: 121 | u_sh = np.array([self.max_deacc, self.delta_max]) 122 | x_sh = x_next_nom 123 | for _ in range(self.N_sh - 1): 124 | x_sh = self.dynamics.forward_step(x_sh, u_sh)[0] 125 | if self.is_collision(x_sh): 126 | self.sh_flag = True 127 | break 128 | 129 | # Shielding is needed. 130 | if self.sh_flag: 131 | # print("Shielding override.") 132 | if success_delta_min: 133 | u_sh = np.array([self.max_deacc, self.delta_min]) 134 | 135 | else: 136 | u_sh = np.array([self.max_deacc, self.delta_max]) 137 | 138 | states_sh = x[:, np.newaxis] 139 | for _ in range(self.N_sh - 1): 140 | x = self.dynamics.forward_step(x, u_sh)[0] 141 | states_sh = np.hstack((states_sh, x[:, np.newaxis])) 142 | 143 | self.states = states_sh 144 | 145 | return u_sh 146 | 147 | # Shielding is not needed. 148 | else: 149 | return u_nominal 150 | 151 | 152 | class ILQshielding(Shielding): 153 | 154 | def __init__(self, config, solver, obs_list, obs_list_timed, N_sh=None): 155 | """ 156 | ILQR-based shielding policy. 157 | 158 | Args: 159 | config (Struct): parameter data. 160 | solver (iLQR): iLQR solver. 161 | obs_list (list): list of ellipsoidal obstacles. 162 | obs_list_timed (list): list of time-varying obstacles. 163 | N_sh (int): shielding rollout horizon. 164 | """ 165 | 166 | self.solver = solver 167 | self.obs_list_timed = obs_list_timed 168 | self.states = None 169 | self.controls_init = np.zeros((2, config.N)) 170 | super(ILQshielding, self).__init__(obs_list, config, N_sh) 171 | 172 | def run(self, x: ArrayImpl, u_nominal: ArrayImpl) -> np.ndarray: 173 | """ 174 | Runs the iLQR-based shielding. 175 | 176 | Args: 177 | x (ArrayImpl): current state. 178 | u_nominal (ArrayImpl): nominal control. 179 | 180 | Returns: 181 | np.ndarray: shielded control. 182 | """ 183 | 184 | # Resets the shielding flag. 185 | self.sh_flag = False 186 | 187 | # Computes iLQR shielding policies starting with x. 188 | states_sh, controls_sh, _, _, _, _, _, _ = ( 189 | self.solver.solve(x, controls=self.controls_init, obs_list=self.obs_list_timed) 190 | ) 191 | self.states = states_sh 192 | 193 | # One-step simulation with robot's nominal policy. 194 | x_next_nom = self.solver.dynamics.forward_step(x, u_nominal)[0] 195 | if self.is_collision(x_next_nom): 196 | self.sh_flag = True 197 | else: 198 | # Computes iLQR shielding policies starting with x_next_nom. 199 | states, _, _, _, _, _, _, _ = ( 200 | self.solver.solve(x_next_nom, controls=self.controls_init, obs_list=self.obs_list_timed) 201 | ) 202 | if self.is_collision(states): 203 | self.sh_flag = True 204 | 205 | # Updates the init. control signal for warmstart of next receding horizon. 206 | self.controls_init[:, :-1] = controls_sh[:, 1:] 207 | 208 | # Shielding is needed. 209 | if self.sh_flag: 210 | # print("Shielding override.") 211 | controls_sh = np.asarray(controls_sh[:, 0]) 212 | if controls_sh[1] <= 0: 213 | u_sh = np.array([controls_sh[0], self.config.DELTA_MIN]) 214 | else: 215 | u_sh = np.array([controls_sh[0], self.config.DELTA_MAX]) 216 | return u_sh 217 | 218 | # Shielding is not needed. 219 | else: 220 | return np.asarray(u_nominal) 221 | -------------------------------------------------------------------------------- /iLQR/track.py: -------------------------------------------------------------------------------- 1 | """ 2 | Racing track. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 7 | """ 8 | 9 | import numpy as np 10 | from typing import Tuple 11 | from matplotlib import pyplot as plt 12 | from pyspline.pyCurve import Curve 13 | 14 | 15 | class Track: 16 | 17 | def __init__( 18 | self, center_line: np.ndarray = None, width_left: float = None, 19 | width_right: float = None, loop: bool = True 20 | ): 21 | """ 22 | Constructs a track with a fixed width. 23 | 24 | Args: 25 | center_line (np.ndarray, optional): 2D numpy array containing samples 26 | of track center line [[x1,x2,...], [y1,y2,...]]. Defaults to None. 27 | width_left (float, optional): width of the track on the left side. 28 | Defaults to None. 29 | width_right (float, optional): width of the track on the right side. 30 | Defaults to None. 31 | loop (bool, optional): flag indicating if the track has loop. 32 | Defaults to True. 33 | """ 34 | self.width_left = width_left 35 | self.width_right = width_right 36 | self.loop = loop 37 | 38 | if center_line is not None: 39 | self.center_line = Curve(x=center_line[0, :], y=center_line[1, :], k=3) 40 | self.length = self.center_line.getLength() 41 | else: 42 | self.length = None 43 | self.center_line = None 44 | 45 | self.track_bound = None 46 | self.track_center = None 47 | 48 | def _interp_s(self, s: list) -> Tuple[np.ndarray, np.ndarray]: 49 | """ 50 | Given a list of s (progress since start), returns corresponing (x,y) points 51 | on the track. In addition, return slope of trangent line on those points. 52 | 53 | Args: 54 | s (list): progress since start. 55 | 56 | Returns: 57 | np.ndarray: (x,y) points on the track. 58 | np.ndarray: slopes of trangent line. 59 | """ 60 | try: 61 | n = len(s) 62 | except: 63 | s = np.array([s]) 64 | n = len(s) 65 | 66 | interp_pt = self.center_line.getValue(s) 67 | slope = np.zeros(n) 68 | 69 | for i in range(n): 70 | deri = self.center_line.getDerivative(s[i]) 71 | slope[i] = np.arctan2(deri[1], deri[0]) 72 | return interp_pt.T, slope 73 | 74 | def interp(self, theta_list: list) -> Tuple[np.ndarray, np.ndarray]: 75 | """ 76 | Given a list of theta (progress since start), return corresponing (x,y) 77 | points on the track. In addition, return slope of trangent line on those 78 | points. 79 | 80 | Args: 81 | theta_list (list): progress since start 82 | 83 | Returns: 84 | np.ndarray: (x,y) points on the track. 85 | np.ndarray: slopes of trangent line. 86 | """ 87 | if self.loop: 88 | s = np.remainder(theta_list, self.length) / self.length 89 | else: 90 | s = np.array(theta_list) / self.length 91 | s[s > 1] = 1 92 | return self._interp_s(s) 93 | 94 | def get_closest_pts( 95 | self, points: np.ndarray 96 | ) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: 97 | """ 98 | Gets the closest points. 99 | 100 | Args: 101 | points (np.ndarray): points on the track of [2xn] shape. 102 | 103 | Returns: 104 | np.ndarray: (x,y) points on the track. 105 | np.ndarray: slopes of trangent line. 106 | np.ndarray: projected progress 107 | """ 108 | s, _ = self.center_line.projectPoint(points.T, eps=1e-3) 109 | closest_pt, slope = self._interp_s(s) 110 | return closest_pt, slope, s * self.length 111 | 112 | def project_point(self, point: np.ndarray) -> np.ndarray: 113 | """ 114 | Projects a point. 115 | 116 | Args: 117 | point (np.ndarray): points on the track of [2xn] shape. 118 | 119 | Returns: 120 | np.ndarray: projected points. 121 | """ 122 | s, _ = self.center_line.projectPoint(point, eps=1e-3) 123 | return s * self.length 124 | 125 | def get_track_width(self, 126 | theta: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: 127 | """ 128 | Gets the width of the track. 129 | 130 | Args: 131 | theta (np.ndarray): progress since start. 132 | 133 | Returns: 134 | np.ndarray: left width array. 135 | np.ndarray: right width array. 136 | """ 137 | temp = np.ones_like(theta) 138 | return self.width_left * temp, self.width_right * temp 139 | 140 | def plot_track(self): 141 | """ 142 | Plots the track. 143 | """ 144 | N = 500 145 | if self.track_bound is None: 146 | theta_sample = np.linspace(0, 1, N, endpoint=False) * self.length 147 | interp_pt, slope = self.interp(theta_sample) 148 | 149 | if self.loop: 150 | self.track_bound = np.zeros((4, N + 1)) 151 | else: 152 | self.track_bound = np.zeros((4, N)) 153 | 154 | self.track_bound[ 155 | 0, :N] = interp_pt[0, :] - np.sin(slope) * self.width_left 156 | self.track_bound[ 157 | 1, :N] = interp_pt[1, :] + np.cos(slope) * self.width_left 158 | 159 | self.track_bound[ 160 | 2, :N] = interp_pt[0, :] + np.sin(slope) * self.width_right 161 | self.track_bound[ 162 | 3, :N] = interp_pt[1, :] - np.cos(slope) * self.width_right 163 | 164 | if self.loop: 165 | self.track_bound[:, -1] = self.track_bound[:, 0] 166 | 167 | plt.plot(self.track_bound[0, :], self.track_bound[1, :], 'k-') 168 | plt.plot(self.track_bound[2, :], self.track_bound[3, :], 'k-') 169 | 170 | def plot_track_center(self): 171 | """ 172 | Plots the center of the track. 173 | """ 174 | N = 500 175 | if self.track_center is None: 176 | theta_sample = np.linspace(0, 1, N, endpoint=False) * self.length 177 | interp_pt, slope = self.interp(theta_sample) 178 | self.track_center = interp_pt 179 | print(len(slope)) 180 | 181 | plt.plot(self.track_center[0, :], self.track_center[1, :], 'r--') 182 | 183 | 184 | if __name__ == '__main__': 185 | import csv 186 | track_file = 'outerloop_center_smooth.csv' 187 | x = [] 188 | y = [] 189 | with open(track_file, newline='') as f: 190 | spamreader = csv.reader(f, delimiter=',') 191 | for i, row in enumerate(spamreader): 192 | if i > 0: 193 | x.append(float(row[0])) 194 | y.append(float(row[1])) 195 | 196 | center_line = np.array([x, y]) 197 | track = Track( 198 | center_line=center_line, width_left=0.3, width_right=0.3, loop=True 199 | ) 200 | 201 | track.plot_track() 202 | track.plot_track_center() 203 | plt.show() 204 | -------------------------------------------------------------------------------- /iLQR/utils.py: -------------------------------------------------------------------------------- 1 | """ 2 | Util functions. 3 | 4 | Please contact the author(s) of this library if you have any questions. 5 | Author: Haimin Hu (haiminh@princeton.edu) 6 | Reference: ECE346@Princeton (Zixu Zhang, Kai-Chieh Hsu, Duy P. Nguyen) 7 | """ 8 | 9 | import yaml 10 | import csv 11 | import numpy as np 12 | 13 | from .track import Track 14 | 15 | 16 | class Struct: 17 | """ 18 | Struct for managing parameters. 19 | """ 20 | 21 | def __init__(self, data): 22 | for key, value in data.items(): 23 | setattr(self, key, value) 24 | 25 | 26 | def load_config(file_path): 27 | """ 28 | Loads the config file. 29 | 30 | Args: 31 | file_path (string): path to the parameter file. 32 | 33 | Returns: 34 | Struct: parameters. 35 | """ 36 | with open(file_path) as f: 37 | data = yaml.safe_load(f) 38 | config = Struct(data) 39 | return config 40 | 41 | 42 | def load_track(config): 43 | """ 44 | Loads the track. 45 | 46 | Args: 47 | config (Struct): problem parameters. 48 | 49 | Returns: 50 | Track: the track object. 51 | """ 52 | x_center_line = [] 53 | y_center_line = [] 54 | with open(config.TRACK_FILE) as f: 55 | spamreader = csv.reader(f, delimiter=',') 56 | for i, row in enumerate(spamreader): 57 | if i > 0: 58 | x_center_line.append(float(row[0])) 59 | y_center_line.append(float(row[1])) 60 | 61 | center_line = np.array([x_center_line, y_center_line]) 62 | track = Track( 63 | center_line=center_line, width_left=config.TRACK_WIDTH_L, 64 | width_right=config.TRACK_WIDTH_R, loop=config.LOOP 65 | ) 66 | return track 67 | 68 | 69 | def plot_ellipsoids( 70 | ax, E_list, arg_list=[], dims=None, N=200, plot_center=True, 71 | use_alpha=False 72 | ): 73 | """ 74 | Plot a list of ellipsoids 75 | Args: 76 | E_list (list): list of ellipsoids 77 | arg_list (list): list of args dictionary. 78 | dims (list): dimension to be preserved. 79 | N (int): number of boundary points. 80 | plot_center (bool): plot the center of the ellipsoid if True. Defaults to 81 | True. 82 | use_alpha (bool): make later ellipsoids more transparent if True. 83 | Defaults to False. 84 | """ 85 | if len(arg_list) == 0: 86 | arg_list = [dict(c='r')] * len(E_list) 87 | elif len(arg_list) == 1: 88 | arg_list = arg_list * len(E_list) 89 | else: 90 | assert len(arg_list) == len(E_list), "The length does not match." 91 | if use_alpha: 92 | alpha_list = np.linspace(1., 0.1, len(E_list)) 93 | else: 94 | alpha_list = np.ones(len(E_list)) 95 | 96 | for E0, arg, alpha in zip(E_list, arg_list, alpha_list): 97 | if dims: 98 | E = E0.projection(dims) 99 | else: 100 | E = E0.copy() 101 | if E.dim() > 3: 102 | raise ValueError("[ellReach-plot] ellipsoid dimension can be 1, 2 or 3.") 103 | if E.dim() == 1: 104 | x = np.array([(E.q - np.sqrt(E.Q))[0, 0], (E.q + np.sqrt(E.Q))[0, 0]]) 105 | ax.plot(x, np.zeros_like(x), color=arg) 106 | ax.plot(E.q, 0, color=arg, marker='*') 107 | if E.dim() == 2: 108 | if E.is_degenerate(): 109 | print("degenerate!") 110 | phi = np.array(np.linspace(0., 2. * np.pi, N)) 111 | L = np.concatenate( 112 | (np.cos(phi).reshape(1, N), np.sin(phi).reshape(1, N)), axis=0 113 | ) 114 | _, x = E.rho(L) 115 | # x = np.concatenate((x, x[:, 0].reshape(2, 1)), axis=1) 116 | ax.plot(x[0, :], x[1, :], alpha=alpha, **arg) 117 | if plot_center: 118 | ax.plot(E.q[0, 0], E.q[1, 0], marker='.', alpha=alpha, **arg) 119 | ax.set_aspect('equal') 120 | if E.dim() == 3: 121 | raise NotImplementedError 122 | -------------------------------------------------------------------------------- /misc/alter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/iLQR_jax_racing/bdc97019c1a3c79c46bd7166bb1be5662bb7268b/misc/alter.png -------------------------------------------------------------------------------- /misc/ego.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SafeRoboticsLab/iLQR_jax_racing/bdc97019c1a3c79c46bd7166bb1be5662bb7268b/misc/ego.png -------------------------------------------------------------------------------- /outerloop_center_smooth.csv: -------------------------------------------------------------------------------- 1 | -0.618804608073334,-0.0356295937401568 2 | -0.615545566529325,-0.0354481224108462 3 | -0.612284641734617,-0.0352667471001334 4 | -0.609019950438511,-0.0350855638266163 5 | -0.605749609390307,-0.0349046686088927 6 | -0.602471735339307,-0.0347241574655604 7 | -0.59918444503481,-0.0345441264152173 8 | -0.595885855226119,-0.0343646714764611 9 | -0.592574082662533,-0.0341858886678898 10 | -0.589247244093355,-0.034007874008101 11 | -0.585903456267883,-0.0338307235156928 12 | -0.58254083593542,-0.0336545332092628 13 | -0.579157499845266,-0.033479399107409 14 | -0.575751564746722,-0.0333054172287291 15 | -0.572321147389089,-0.033132683591821 16 | -0.568864364521668,-0.0329612942152825 17 | -0.565379332893759,-0.0327913451177114 18 | -0.561864169254663,-0.0326229323177056 19 | -0.558316990353682,-0.032456151833863 20 | -0.554735912940115,-0.0322910996847812 21 | -0.551119053763264,-0.0321278718890582 22 | -0.54746452957243,-0.0319665644652918 23 | -0.543770457116914,-0.0318072734320798 24 | -0.540034953146015,-0.0316500948080201 25 | -0.536256134409036,-0.0314951246117104 26 | -0.532432117655277,-0.0313424588617486 27 | -0.528561019634038,-0.0311921935767326 28 | -0.524640957094621,-0.0310444247752601 29 | -0.520670046786327,-0.0308992484759291 30 | -0.516646405458456,-0.0307567606973372 31 | -0.512568149860309,-0.0306170574580824 32 | -0.508433396741187,-0.0304802347767625 33 | -0.504240262850391,-0.0303463886719753 34 | -0.499986864937221,-0.0302156151623186 35 | -0.495671319750979,-0.0300880102663903 36 | -0.491291744040965,-0.0299636700027883 37 | -0.486846254556481,-0.0298426903901102 38 | -0.482332968046826,-0.029725167446954 39 | -0.477750001261302,-0.0296111971919175 40 | -0.473095470949209,-0.0295008756435985 41 | -0.468367493859849,-0.0293942988205948 42 | -0.463564186742523,-0.0292915627415043 43 | -0.45868366634653,-0.0291927634249249 44 | -0.453724049421173,-0.0290979968894542 45 | -0.448683452715751,-0.0290073591536903 46 | -0.443559992979565,-0.0289209462362308 47 | -0.438351786961918,-0.0288388541556736 48 | -0.433056951412108,-0.0287611789306166 49 | -0.427673603079438,-0.0286880165796576 50 | -0.422199858713207,-0.0286194631213944 51 | -0.416633835062717,-0.0285556145744248 52 | -0.41097425706643,-0.0284965344613146 53 | -0.405222282419454,-0.028442156320501 54 | -0.399379677006056,-0.0283923811943892 55 | -0.393448206710507,-0.0283471101253842 56 | -0.387429637417073,-0.0283062441558912 57 | -0.381325735010025,-0.0282696843283154 58 | -0.375138265373631,-0.0282373316850619 59 | -0.36886899439216,-0.0282090872685358 60 | -0.36251968794988,-0.0281848521211423 61 | -0.35609211193106,-0.0281645272852864 62 | -0.349588032219968,-0.0281480138033734 63 | -0.343009214700875,-0.0281352127178084 64 | -0.336357425258048,-0.0281260250709965 65 | -0.329634429775755,-0.0281203519053429 66 | -0.322841994138267,-0.0281180942632526 67 | -0.31598188422985,-0.0281191531871309 68 | -0.309055865934775,-0.0281234297193828 69 | -0.30206570513731,-0.0281308249024135 70 | -0.295013167721724,-0.0281412397786282 71 | -0.287900019572285,-0.0281545753904319 72 | -0.280728026573262,-0.0281707327802298 73 | -0.273498954608923,-0.0281896129904271 74 | -0.266214569563538,-0.0282111170634288 75 | -0.258876637321376,-0.0282351460416402 76 | -0.251486923766704,-0.0282616009674663 77 | -0.244047194783792,-0.0282903828833123 78 | -0.236559216256909,-0.0283213928315834 79 | -0.229024754070322,-0.0283545318546846 80 | -0.221445574108302,-0.0283897009950211 81 | -0.213823442255116,-0.028426801294998 82 | -0.206160124395033,-0.0284657337970206 83 | -0.198457386412322,-0.0285063995434938 84 | -0.190716994191252,-0.0285486995768229 85 | -0.182940713616092,-0.0285925349394129 86 | -0.175130310571109,-0.0286378066736691 87 | -0.167287550940574,-0.0286844158219966 88 | -0.159414200608754,-0.0287322634268004 89 | -0.151512025459919,-0.0287812505304858 90 | -0.143582791378337,-0.0288312781754579 91 | -0.135628264248276,-0.0288822474041217 92 | -0.127650209954006,-0.0289340592588825 93 | -0.119650394379795,-0.0289866147821454 94 | -0.111630583409912,-0.0290398150163155 95 | -0.103592542928626,-0.029093561003798 96 | -0.0955380388202057,-0.0291477537869979 97 | -0.0874688369689192,-0.0292022944083205 98 | -0.0793867032590355,-0.0292570839101709 99 | -0.0712934035748234,-0.0293120233349541 100 | -0.0631907038005517,-0.0293670137250754 101 | -0.0550803698204889,-0.0294219561229398 102 | -0.046963823022465,-0.0294767578970855 103 | -0.0388411068085542,-0.0295313517205818 104 | -0.0307119200843916,-0.0295856765926312 105 | -0.0225759617556125,-0.0296396715124359 106 | -0.0144329307278521,-0.0296932754791984 107 | -0.0062825259067458,-0.0297464274921211 108 | 0.00187555380207126,-0.0297990665504063 109 | 0.0100416094929638,-0.0298511316532563 110 | 0.0182159422602966,-0.0299025617998735 111 | 0.0263988531984344,-0.0299532959894603 112 | 0.0345906434017419,-0.0300032732212191 113 | 0.0427916139645839,-0.0300524324943522 114 | 0.0510020659813251,-0.0301007128080619 115 | 0.0592223005463303,-0.0301480531615507 116 | 0.0674526187539643,-0.0301943925540209 117 | 0.0756933216985917,-0.0302396699846749 118 | 0.0839447104745773,-0.030283824452715 119 | 0.092207086176286,-0.0303267949573436 120 | 0.100480749898082,-0.030368520497763 121 | 0.108766002734331,-0.0304089400731757 122 | 0.117063145779397,-0.0304479926827839 123 | 0.125372480127645,-0.0304856173257901 124 | 0.13369430687344,-0.0305217530013966 125 | 0.142028927111147,-0.0305563387088058 126 | 0.150376641935129,-0.03058931344722 127 | 0.158737752439753,-0.0306206162158416 128 | 0.167112559719382,-0.030650186013873 129 | 0.175501364868381,-0.0306779618405166 130 | 0.183904468981116,-0.0307038826949746 131 | 0.192322173151951,-0.0307278875764494 132 | 0.200754778475251,-0.0307499154841435 133 | 0.20920258604538,-0.0307699054172592 134 | 0.217665896956703,-0.0307877963749988 135 | 0.226145012303586,-0.0308035273565648 136 | 0.234640233180392,-0.0308170373611594 137 | 0.243151860681486,-0.030828265387985 138 | 0.251680195901234,-0.0308371504362441 139 | 0.260225539934,-0.0308436315051389 140 | 0.268788193874148,-0.0308476475938718 141 | 0.277368458816044,-0.0308491377016453 142 | 0.285966635854052,-0.0308480408276616 143 | 0.294583026082537,-0.0308442959711231 144 | 0.303217930595864,-0.0308378421312322 145 | 0.311871650488397,-0.0308286183071912 146 | 0.320544486854502,-0.0308165634982026 147 | 0.329236740788543,-0.0308016167034686 148 | 0.337948713384884,-0.0307837169221917 149 | 0.34668070573789,-0.0307628031535741 150 | 0.355433018941927,-0.0307388143968184 151 | 0.364205954091359,-0.0307116896511267 152 | 0.372999835272426,-0.030681387462656 153 | 0.381815078538866,-0.0306479445653808 154 | 0.390652122936292,-0.03061141724023 155 | 0.39951140751032,-0.0305718617681327 156 | 0.408393371306562,-0.0305293344300178 157 | 0.417298453370633,-0.0304838915068144 158 | 0.426227092748145,-0.0304355892794513 159 | 0.435179728484713,-0.0303844840288576 160 | 0.44415679962595,-0.0303306320359623 161 | 0.45315874521747,-0.0302740895816944 162 | 0.462186004304887,-0.0302149129469828 163 | 0.471239015933814,-0.0301531584127565 164 | 0.480318219149865,-0.0300888822599446 165 | 0.489424052998653,-0.0300221407694759 166 | 0.498556956525794,-0.0299529902222796 167 | 0.507717368776899,-0.0298814868992845 168 | 0.516905728797583,-0.0298076870814196 169 | 0.526122475633459,-0.029731647049614 170 | 0.535368048330142,-0.0296534230847967 171 | 0.544642885933245,-0.0295730714678965 172 | 0.553947427488381,-0.0294906484798426 173 | 0.563282112041164,-0.0294062104015638 174 | 0.572647378637209,-0.0293198135139892 175 | 0.582043666322128,-0.0292315140980477 176 | 0.591471414141535,-0.0291413684346684 177 | 0.600931061141044,-0.0290494328047802 178 | 0.610423046366269,-0.0289557634893122 179 | 0.619947808862824,-0.0288604167691932 180 | 0.629505787676321,-0.0287634489253523 181 | 0.639097421852376,-0.0286649162387184 182 | 0.6487231504366,-0.0285648749902206 183 | 0.658383412474609,-0.0284633814607879 184 | 0.668078647012016,-0.0283604919313491 185 | 0.677809293094434,-0.0282562626828334 186 | 0.687575789767478,-0.0281507499961696 187 | 0.697378576076761,-0.0280440101522869 188 | 0.707218091067896,-0.0279360994321141 189 | 0.717094773786497,-0.0278270741165802 190 | 0.727009063278179,-0.0277169904866143 191 | 0.736961398588554,-0.0276059048231453 192 | 0.746952218763237,-0.0274938734071022 193 | 0.756981962847841,-0.027380952519414 194 | 0.767051069887979,-0.0272671984410096 195 | 0.777159978929266,-0.0271526674528181 196 | 0.787309129017316,-0.0270374158357685 197 | 0.797498959197741,-0.0269214998707897 198 | 0.807729908516155,-0.0268049758388107 199 | 0.818002416018173,-0.0266879000207605 200 | 0.828316920749408,-0.0265703286975681 201 | 0.838673861755473,-0.0264523181501624 202 | 0.849073496887066,-0.0263339136073332 203 | 0.859515359215216,-0.026215116089313 204 | 0.869998800616036,-0.0260959155641948 205 | 0.880523172965639,-0.025976302000072 206 | 0.891087828140138,-0.0258562653650377 207 | 0.901692118015645,-0.025735795627185 208 | 0.912335394468273,-0.0256148827546071 209 | 0.923017009374135,-0.0254935167153973 210 | 0.933736314609345,-0.0253716874776487 211 | 0.944492662050014,-0.0252493850094546 212 | 0.955285403572256,-0.0251265992789079 213 | 0.966113891052183,-0.0250033202541021 214 | 0.976977476365908,-0.0248795379031302 215 | 0.987875511389545,-0.0247552421940854 216 | 0.998807347999205,-0.0246304230950609 217 | 1.009772338071,-0.0245050705741499 218 | 1.02076983348105,-0.0243791745994455 219 | 1.03179918610546,-0.0242527251390411 220 | 1.04285974782034,-0.0241257121610296 221 | 1.05395087050181,-0.0239981256335044 222 | 1.06507190602599,-0.0238699555245586 223 | 1.07622220626897,-0.0237411918022853 224 | 1.08740112310689,-0.0236118244347779 225 | 1.09860800841584,-0.0234818433901293 226 | 1.10984221407195,-0.0233512386364329 227 | 1.12110309195132,-0.0232200001417818 228 | 1.13238999393006,-0.0230881178742692 229 | 1.1437022718843,-0.0229555818019883 230 | 1.15503927769015,-0.0228223818930322 231 | 1.1664003632237,-0.0226885081154942 232 | 1.17778488036109,-0.0225539504374674 233 | 1.18919218097842,-0.022418698827045 234 | 1.20062161695181,-0.0222827432523202 235 | 1.21207254015736,-0.0221460736813861 236 | 1.22354430247119,-0.022008680082336 237 | 1.23503625576942,-0.0218705524232631 238 | 1.24654775192815,-0.0217316806722604 239 | 1.2580781428235,-0.0215920547974213 240 | 1.26962678033158,-0.0214516647668388 241 | 1.28119301632851,-0.0213105005486062 242 | 1.29277620269039,-0.0211685521108166 243 | 1.30437569129335,-0.0210258094215633 244 | 1.31599083401349,-0.0208822624489393 245 | 1.32762098272692,-0.020737901161038 246 | 1.33926548930976,-0.0205927155259524 247 | 1.35092370563813,-0.0204466955117758 248 | 1.36259498358813,-0.0202998310866013 249 | 1.37427867503587,-0.0201521122185222 250 | 1.38597413185748,-0.0200035288756315 251 | 1.39768070592906,-0.0198540710260225 252 | 1.40939762383543,-0.019703732037419 253 | 1.42112361099622,-0.0195525188760672 254 | 1.43285726753976,-0.0194004419078438 255 | 1.4445971935944,-0.0192475114986257 256 | 1.45634198928847,-0.0190937380142897 257 | 1.4680902547503,-0.0189391318207125 258 | 1.47984059010823,-0.0187837032837711 259 | 1.49159159549059,-0.018627462769342 260 | 1.50334187102573,-0.0184704206433023 261 | 1.51509001684197,-0.0183125872715286 262 | 1.52683463306765,-0.0181539730198978 263 | 1.53857431983111,-0.0179945882542867 264 | 1.55030767726068,-0.0178344433405721 265 | 1.5620333054847,-0.0176735486446307 266 | 1.57374980463151,-0.0175119145323395 267 | 1.58545577482943,-0.0173495513695751 268 | 1.59714981620681,-0.0171864695222143 269 | 1.60883052889198,-0.0170226793561341 270 | 1.62049651301328,-0.0168581912372112 271 | 1.63214636869903,-0.0166930155313223 272 | 1.64377869607759,-0.0165271626043443 273 | 1.65539209527727,-0.016360642822154 274 | 1.66698516642643,-0.0161934665506282 275 | 1.67855650965339,-0.0160256441556437 276 | 1.69010472508649,-0.0158571860030772 277 | 1.70162841285406,-0.0156881024588057 278 | 1.71312617308445,-0.0155184038887058 279 | 1.72459660590598,-0.0153481006586545 280 | 1.73603831144699,-0.0151772031345284 281 | 1.74744988983582,-0.0150057216822044 282 | 1.75882994120081,-0.0148336666675593 283 | 1.77017706567028,-0.0146610484564699 284 | 1.78148986337257,-0.014487877414813 285 | 1.79276693443603,-0.0143141639084654 286 | 1.80400687898898,-0.0141399183033039 287 | 1.81520829715976,-0.0139651509652053 288 | 1.82636978907671,-0.0137898722600464 289 | 1.83748995486816,-0.013614092553704 290 | 1.84856739466245,-0.0134378222120549 291 | 1.85960070858792,-0.0132610716009759 292 | 1.87058849677289,-0.0130838510863438 293 | 1.8815293593457,-0.0129061710340354 294 | 1.8924218964347,-0.0127280418099276 295 | 1.90326470816821,-0.012549473779897 296 | 1.91405639467457,-0.0123704773098205 297 | 1.92479555608212,-0.012191062765575 298 | 1.93548079251919,-0.0120112405130371 299 | 1.94611070411412,-0.0118310209180838 300 | 1.95668389099524,-0.0116504143465917 301 | 1.96719895329089,-0.0114694311644378 302 | 1.97765503657423,-0.0112879838978037 303 | 1.98805346819767,-0.0111055937140909 304 | 1.99839612095847,-0.0109216839410056 305 | 2.00868486765388,-0.0107356779062542 306 | 2.01892158108114,-0.010546998937543 307 | 2.02910813403749,-0.0103550703625784 308 | 2.03924639932019,-0.0101593155090666 309 | 2.04933824972648,-0.00995915770471409 310 | 2.0593855580536,-0.00975402027722706 311 | 2.06939019709881,-0.00954332655431188 312 | 2.07935403965935,-0.00932649986367488 313 | 2.08927895853246,-0.00910296353302238 314 | 2.0991668265154,-0.00887214089006072 315 | 2.1090195164054,-0.00863345526249621 316 | 2.11883890099972,-0.00838632997803519 317 | 2.1286268530956,-0.00813018836438397 318 | 2.1383852454903,-0.0078644537492489 319 | 2.14811595098104,-0.00758854946033628 320 | 2.15782084236509,-0.00730189882535246 321 | 2.16750179243968,-0.00700392517200376 322 | 2.17716067400207,-0.0066940518279965 323 | 2.1867993598495,-0.006371702121037 324 | 2.19641972277922,-0.00603629937883161 325 | 2.20602363558847,-0.00568726692908664 326 | 2.2156129710745,-0.00532402809950841 327 | 2.22518960203456,-0.00494600621780327 328 | 2.23475540126589,-0.00455262461167752 329 | 2.24431224156575,-0.0041433066088375 330 | 2.25386199573136,-0.00371747553698954 331 | 2.26340653655999,-0.00327455472383996 332 | 2.27294773684889,-0.00281396749709509 333 | 2.28248746939528,-0.00233513718446126 334 | 2.29202760699643,-0.00183748711364479 335 | 2.30157002244958,-0.001320440612352 336 | 2.31111658855197,-0.000783421008289232 337 | 2.32066917810085,-0.000225851629162803 338 | 2.33022966389348,0.000352844197320958 339 | 2.33979991872708,0.000953243143455724 340 | 2.34938181539892,0.00157592188153517 341 | 2.35897722670624,0.00222145708385297 342 | 2.36858802544628,0.00289042542270279 343 | 2.37821608441629,0.00358340357037832 344 | 2.38786327641352,0.00430096819917322 345 | 2.39753147423521,0.00504369598138116 346 | 2.40722255067861,0.00581216358929582 347 | 2.41693837854097,0.00660694769521089 348 | 2.42668083061954,0.00742862497142001 349 | 2.43645177971155,0.00827777209021687 350 | 2.44625309861425,0.00915496572389515 351 | 2.45608666012491,0.0100607825447485 352 | 2.46595342646491,0.0109957710863912 353 | 2.47585071755235,0.0119603673277193 354 | 2.48577494272946,0.0129549791089498 355 | 2.49572251133849,0.0139800142702993 356 | 2.50568983272169,0.0150358806519847 357 | 2.51567331622129,0.0161229860942226 358 | 2.52566937117953,0.0172417384372299 359 | 2.53567440693867,0.0183925455212233 360 | 2.54568483284094,0.0195758151864196 361 | 2.55569705822859,0.0207919552730355 362 | 2.56570749244386,0.0220413736212879 363 | 2.57571254482899,0.0233244780713935 364 | 2.58570862472622,0.024641676463569 365 | 2.59569214147781,0.0259933766380312 366 | 2.60565950442599,0.0273799864349969 367 | 2.61560712291301,0.0288019136946829 368 | 2.6255314062811,0.0302595662573059 369 | 2.63542876387252,0.0317533519630826 370 | 2.6452956050295,0.0332836786522299 371 | 2.65512833909429,0.0348509541649645 372 | 2.66492337540913,0.0364555863415032 373 | 2.67467712331626,0.0380979830220627 374 | 2.68438599215793,0.0397785520468599 375 | 2.69404639127639,0.0414977012561114 376 | 2.70365473001386,0.043255838490034 377 | 2.71320741771261,0.0450533715888446 378 | 2.72270086371486,0.0468907083927598 379 | 2.73213147736287,0.0487682567419965 380 | 2.74149566799887,0.0506864244767714 381 | 2.75078984496511,0.0526456194373012 382 | 2.76001041760383,0.0546462494638028 383 | 2.76915379525728,0.0566887223964929 384 | 2.7782163872677,0.0587734460755883 385 | 2.78719460297733,0.0609008283413057 386 | 2.79608485172841,0.0630712770338618 387 | 2.80488354286319,0.0652851999934736 388 | 2.81358708572392,0.0675430050603577 389 | 2.82219188965282,0.0698451000747309 390 | 2.83069436399216,0.0721918928768099 391 | 2.83909091808416,0.0745837913068116 392 | 2.84737796127108,0.0770212032049526 393 | 2.85555190289515,0.0795045364114499 394 | 2.86360915229862,0.08203419876652 395 | 2.87154611882374,0.0846105981103799 396 | 2.87935921181274,0.0872341422832462 397 | 2.88704484060787,0.0899052391253357 398 | 2.89459941455137,0.0926242964768652 399 | 2.90201934298549,0.0953917221780515 400 | 2.90930103525247,0.0982079240691113 401 | 2.91644090069454,0.101073309990261 402 | 2.92343641946037,0.103988213502409 403 | 2.93028935492426,0.106952671049223 404 | 2.93700254126692,0.109966644795061 405 | 2.94357881266905,0.113030096904285 406 | 2.95002100331137,0.116142989541251 407 | 2.9563319473746,0.11930528487032 408 | 2.96251447903943,0.122516945055851 409 | 2.9685714324866,0.125777932262202 410 | 2.97450564189679,0.129088208653733 411 | 2.98031994145073,0.132447736394803 412 | 2.98601716532913,0.135856477649771 413 | 2.9916001477127,0.139314394582996 414 | 2.99707172278215,0.142821449358836 415 | 3.00243472471819,0.146377604141652 416 | 3.00769198770154,0.149982821095803 417 | 3.01284634591289,0.153637062385646 418 | 3.01790063353298,0.157340290175542 419 | 3.0228576847425,0.16109246662985 420 | 3.02772033372217,0.164893553912928 421 | 3.0324914146527,0.168743514189136 422 | 3.0371737617148,0.172642309622832 423 | 3.04177020908918,0.176589902378376 424 | 3.04628359095656,0.180586254620128 425 | 3.05071674149764,0.184631328512445 426 | 3.05507249489314,0.188725086219687 427 | 3.05935368532377,0.192867489906214 428 | 3.06356314697024,0.197058501736384 429 | 3.06770371401325,0.201298083874556 430 | 3.07177822063353,0.20558619848509 431 | 3.07578950101178,0.209922807732344 432 | 3.07974038932872,0.214307873780678 433 | 3.08363371976505,0.218741358794451 434 | 3.08747232650149,0.223223224938021 435 | 3.09125904371875,0.227753434375748 436 | 3.09499670559753,0.232331949271991 437 | 3.09868814631856,0.236958731791109 438 | 3.10233620006254,0.241633744097462 439 | 3.10594370101019,0.246356948355407 440 | 3.10951348334221,0.251128306729305 441 | 3.11304838123932,0.255947781383514 442 | 3.11655122888222,0.260815334482394 443 | 3.12002486045164,0.265730928190303 444 | 3.12347211012827,0.270694524671601 445 | 3.12689581209284,0.275706086090647 446 | 3.13029880052605,0.280765574611799 447 | 3.13368390960861,0.285872952399418 448 | 3.13705397352124,0.291028181617861 449 | 3.14041182644465,0.296231224431488 450 | 3.14376030255955,0.301482043004659 451 | 3.14710223604665,0.306780599501732 452 | 3.15043989771599,0.31212708433995 453 | 3.15377330489494,0.317522600948096 454 | 3.15710191154022,0.322968481007834 455 | 3.16042517160853,0.328466056200832 456 | 3.16374253905656,0.334016658208753 457 | 3.16705346784104,0.339621618713265 458 | 3.17035741191865,0.345282269396033 459 | 3.17365382524611,0.350999941938723 460 | 3.17694216178013,0.356775968023 461 | 3.1802218754774,0.36261167933053 462 | 3.18349242029464,0.36850840754298 463 | 3.18675325018854,0.374467484342014 464 | 3.19000381911581,0.3804902414093 465 | 3.19324358103317,0.386578010426501 466 | 3.1964719898973,0.392732123075285 467 | 3.19968849966493,0.398953911037317 468 | 3.20289256429274,0.405244705994263 469 | 3.20608363773746,0.411605839627788 470 | 3.20926117395578,0.418038643619559 471 | 3.21242462690441,0.424544449651241 472 | 3.21557345054005,0.431124589404499 473 | 3.21870709881941,0.437780394561001 474 | 3.22182502569919,0.444513196802411 475 | 3.2249266851361,0.451324327810395 476 | 3.22801153108685,0.458215119266619 477 | 3.23107901750813,0.465186902852749 478 | 3.23412859835666,0.472241010250451 479 | 3.23715972758914,0.479378773141389 480 | 3.24017185916228,0.486601523207231 481 | 3.24316444703277,0.493910592129643 482 | 3.24613694515733,0.501307311590288 483 | 3.24908880749266,0.508793013270834 484 | 3.25201948799546,0.516369028852947 485 | 3.25492844062244,0.524036690018291 486 | 3.25781511933031,0.531797328448534 487 | 3.26067897807577,0.539652275825339 488 | 3.26351947081553,0.547602863830375 489 | 3.26633605150628,0.555650424145305 490 | 3.26912817410474,0.563796288451797 491 | 3.27189529256761,0.572041788431515 492 | 3.2746368608516,0.580388255766125 493 | 3.27735233291341,0.588837022137294 494 | 3.28004116270974,0.597389419226687 495 | 3.28270280419731,0.60604677871597 496 | 3.28533671133281,0.614810432286808 497 | 3.28794233807296,0.623681711620868 498 | 3.29051913837445,0.632661948399815 499 | 3.29306656619399,0.641752474305315 500 | 3.29558407548829,0.650954621019034 501 | 3.29807112021405,0.660269720222637 502 | 3.30052716949759,0.669698803932208 503 | 3.30295175314369,0.679241705501502 504 | 3.30534441612674,0.688897958618691 505 | 3.30770470342113,0.698667096971947 506 | 3.31003216000126,0.708548654249443 507 | 3.31232633084151,0.718542164139351 508 | 3.31458676091627,0.728647160329844 509 | 3.31681299519994,0.738863176509093 510 | 3.3190045786669,0.749189746365272 511 | 3.32116105629156,0.759626403586552 512 | 3.32328197304829,0.770172681861106 513 | 3.3253668739115,0.780828114877106 514 | 3.32741530385557,0.791592236322725 515 | 3.32942680785489,0.802464579886135 516 | 3.33140093088386,0.813444679255508 517 | 3.33333721791687,0.824532068119017 518 | 3.3352352139283,0.835726280164834 519 | 3.33709446389256,0.847026849081131 520 | 3.33891451278402,0.858433308556082 521 | 3.34069490557709,0.869945192277857 522 | 3.34243518724615,0.88156203393463 523 | 3.34413490276559,0.893283367214572 524 | 3.34579359710981,0.905108725805857 525 | 3.3474108152532,0.917037643396657 526 | 3.34898610217014,0.929069653675143 527 | 3.35051900283504,0.941204290329489 528 | 3.35200906222228,0.953441087047866 529 | 3.35345582530624,0.965779577518448 530 | 3.35485883706134,0.978219295429406 531 | 3.35621764246195,0.990759774468912 532 | 3.35753178648246,1.00340054832514 533 | 3.35880081409727,1.01614115068626 534 | 3.36002427028077,1.02898111524045 535 | 3.36120170000735,1.04191997567587 536 | 3.36233264825141,1.05495726568071 537 | 3.36341665998732,1.06809251894313 538 | 3.36445328018949,1.0813252691513 539 | 3.36544205383231,1.0946550499934 540 | 3.36638252589016,1.1080813951576 541 | 3.36727424133744,1.12160383833208 542 | 3.36811674514855,1.135221913205 543 | 3.36890958229786,1.14893515346453 544 | 3.36965229775977,1.16274309279886 545 | 3.37034443650868,1.17664526489615 546 | 3.37098554351897,1.19064120344457 547 | 3.37157516376504,1.20473044213229 548 | 3.37211284222127,1.2189125146475 549 | 3.37259812386207,1.23318695467836 550 | 3.37303055366181,1.24755329591304 551 | 3.3734096765949,1.26201107203972 552 | 3.37373537049325,1.27655959071791 553 | 3.37400884461897,1.29119725549252 554 | 3.37423164109169,1.30592224387979 555 | 3.37440530203103,1.32073273339598 556 | 3.37453136955662,1.33562690155734 557 | 3.37461138578809,1.3506029258801 558 | 3.37464689284509,1.36565898388053 559 | 3.37463943284722,1.38079325307487 560 | 3.37459054791413,1.39600391097936 561 | 3.37450178016545,1.41128913511027 562 | 3.3743746717208,1.42664710298383 563 | 3.37421076469982,1.44207599211629 564 | 3.37401160122214,1.45757398002392 565 | 3.37377872340738,1.47313924422294 566 | 3.37351367337518,1.48876996222962 567 | 3.37321799324517,1.50446431156019 568 | 3.37289322513698,1.52022046973092 569 | 3.37254091117023,1.53603661425805 570 | 3.37216259346457,1.55191092265782 571 | 3.37175981413961,1.56784157244649 572 | 3.37133411531499,1.5838267411403 573 | 3.37088703911034,1.59986460625551 574 | 3.3704201276453,1.61595334530836 575 | 3.36993492303948,1.63209113581511 576 | 3.36943296741252,1.64827615529199 577 | 3.36891580288406,1.66450658125526 578 | 3.36838497157372,1.68078059122118 579 | 3.36784201560112,1.69709636270597 580 | 3.36728847708592,1.71345207322591 581 | 3.36672589814772,1.72984590029723 582 | 3.36615582090617,1.74627602143618 583 | 3.36557978748089,1.76274061415901 584 | 3.36499933999151,1.77923785598198 585 | 3.36441602055767,1.79576592442133 586 | 3.36383137129899,1.8123229969933 587 | 3.3632469343351,1.82890725121415 588 | 3.36266425178565,1.84551686460013 589 | 3.36208486577024,1.86215001466749 590 | 3.36151031840852,1.87880487893247 591 | 3.36094215182012,1.89547963491132 592 | 3.36038190812467,1.91217246012029 593 | 3.35983112944179,1.92888153207564 594 | 3.35929135789112,1.9456050282936 595 | 3.35876413559228,1.96234112629044 596 | 3.35825100466492,1.97908800358239 597 | 3.35775350722865,1.9958438376857 598 | 3.35727318540311,2.01260680611663 599 | 3.35681158130793,2.02937508639143 600 | 3.35637023706274,2.04614685602634 601 | 3.35595069478716,2.06292029253761 602 | 3.35555408724519,2.07969379909861 603 | 3.35517990977821,2.09646668151122 604 | 3.35482724837195,2.11323847123443 605 | 3.35449518901214,2.13000869972724 606 | 3.35418281768454,2.14677689844863 607 | 3.35388922037487,2.16354259885762 608 | 3.35361348306887,2.18030533241318 609 | 3.35335469175227,2.19706463057433 610 | 3.35311193241083,2.21382002480004 611 | 3.35288429103026,2.23057104654932 612 | 3.35267085359631,2.24731722728116 613 | 3.35247070609471,2.26405809845456 614 | 3.35228293451121,2.28079319152852 615 | 3.35210662483153,2.29752203796202 616 | 3.35194086304142,2.31424416921406 617 | 3.35178473512661,2.33095911674364 618 | 3.35163732707283,2.34766641200976 619 | 3.35149772486584,2.3643655864714 620 | 3.35136501449135,2.38105617158757 621 | 3.35123828193511,2.39773769881725 622 | 3.35111661318286,2.41440969961946 623 | 3.35099909422033,2.43107170545316 624 | 3.35088481103326,2.44772324777738 625 | 3.35077284960739,2.46436385805109 626 | 3.35066229592845,2.4809930677333 627 | 3.35055223598218,2.497610408283 628 | 3.35044175575431,2.51421541115918 629 | 3.35032994123059,2.53080760782085 630 | 3.35021587839674,2.54738652972699 631 | 3.35009865323852,2.5639517083366 632 | 3.34997735174164,2.58050267510867 633 | 3.34985105989186,2.59703896150221 634 | 3.3497188636749,2.6135600989762 635 | 3.34957984907651,2.63006561898965 636 | 3.34943310208242,2.64655505300154 637 | 3.34927770867836,2.66302793247087 638 | 3.34911275485008,2.67948378885664 639 | 3.34893732658331,2.69592215361785 640 | 3.34875050986379,2.71234255821348 641 | 3.34855139067725,2.72874453410253 642 | 3.34833905500943,2.745127612744 643 | 3.34811258884607,2.76149132559688 644 | 3.3478710781729,2.77783520412018 645 | 3.34761360897567,2.79415877977287 646 | 3.3473392672401,2.81046158401396 647 | 3.34704713895194,2.82674314830245 648 | 3.34673631009693,2.84300300409733 649 | 3.34640586666079,2.85924068285759 650 | 3.34605489462926,2.87545571604223 651 | 3.34568247998809,2.89164763511024 652 | 3.34528794318179,2.90781609590446 653 | 3.34487154249002,2.92396125180303 654 | 3.34443377065123,2.94008338056795 655 | 3.34397512040386,2.9561827599612 656 | 3.34349608448634,2.97225966774477 657 | 3.34299715563714,2.98831438168066 658 | 3.34247882659468,3.00434717953084 659 | 3.34194159009741,3.02035833905731 660 | 3.34138593888377,3.03634813802205 661 | 3.34081236569221,3.05231685418706 662 | 3.34022136326118,3.06826476531431 663 | 3.3396134243291,3.0841921491658 664 | 3.33898904163444,3.10009928350352 665 | 3.33834870791562,3.11598644608945 666 | 3.33769291591109,3.13185391468559 667 | 3.33702215835931,3.14770196705391 668 | 3.3363369279987,3.16353088095641 669 | 3.33563771756771,3.17934093415508 670 | 3.33492501980479,3.19513240441191 671 | 3.33419932744838,3.21090556948887 672 | 3.33346113323692,3.22666070714797 673 | 3.33271092990885,3.24239809515118 674 | 3.33194921020262,3.2581180112605 675 | 3.33117646685668,3.27382073323792 676 | 3.33039319260945,3.28950653884542 677 | 3.3295998801994,3.30517570584499 678 | 3.32879702236495,3.32082851199861 679 | 3.32798511184456,3.33646523506829 680 | 3.32716464137667,3.352086152816 681 | 3.32633610369972,3.36769154300373 682 | 3.32549999155215,3.38328168339347 683 | 3.3246567976724,3.39885685174721 684 | 3.32380701479893,3.41441732582694 685 | 3.32295113567017,3.42996338339465 686 | 3.32208965302456,3.44549530221231 687 | 3.32122305960056,3.46101336004193 688 | 3.32035184813659,3.47651783464549 689 | 3.31947651137111,3.49200900378497 690 | 3.31859754204256,3.50748714522237 691 | 3.31771543288938,3.52295253671967 692 | 3.31683067665002,3.53840545603886 693 | 3.31594376606291,3.55384618094194 694 | 3.3150551938665,3.56927498919087 695 | 3.31416545279924,3.58469215854767 696 | 3.31327503559957,3.60009796677431 697 | 3.31238443500592,3.61549269163278 698 | 3.31149414375675,3.63087661088507 699 | 3.3106046545905,3.64625000229316 700 | 3.3097164602456,3.66161314361906 701 | 3.30883005346051,3.67696631262473 702 | 3.30794528422591,3.69230961972738 703 | 3.30705943154147,3.70764250596499 704 | 3.30616913165912,3.72296424503075 705 | 3.30527102083076,3.73827411061784 706 | 3.30436173530831,3.75357137641947 707 | 3.3034379113437,3.76885531612882 708 | 3.30249618518884,3.78412520343907 709 | 3.30153319309564,3.79938031204342 710 | 3.30054557131603,3.81461991563505 711 | 3.29952995610192,3.82984328790716 712 | 3.29848298370523,3.84504970255294 713 | 3.29740129037787,3.86023843326556 714 | 3.29628151237176,3.87540875373823 715 | 3.29512028593882,3.89055993766414 716 | 3.29391424733097,3.90569125873646 717 | 3.29266003280013,3.9208019906484 718 | 3.2913542785982,3.93589140709314 719 | 3.28999362097712,3.95095878176386 720 | 3.28857469618878,3.96600338835376 721 | 3.28709414048513,3.98102450055604 722 | 3.28554859011806,3.99602139206387 723 | 3.28393468133949,4.01099333657044 724 | 3.28224905040136,4.02593960776895 725 | 3.28048833355556,4.04085947935259 726 | 3.27864916705402,4.05575222501454 727 | 3.27672818714866,4.070617118448 728 | 3.27472203009139,4.08545343334615 729 | 3.27262733213413,4.10026044340218 730 | 3.2704407295288,4.11503742230928 731 | 3.26815885852731,4.12978364376064 732 | 3.26577835538159,4.14449838144945 733 | 3.26329585634354,4.15918090906891 734 | 3.26070799766509,4.17383050031219 735 | 3.25801141559815,4.18844642887249 736 | 3.25520274639464,4.20302796844299 737 | 3.25227862630648,4.2175743927169 738 | 3.24923569158559,4.23208497538738 739 | 3.24607057848388,4.24655899014765 740 | 3.24277992325326,4.26099571069088 741 | 3.23936036214567,4.27539441071026 742 | 3.235808531413,4.28975436389898 743 | 3.23212106730719,4.30407484395024 744 | 3.22829460608014,4.31835512455722 745 | 3.22432578398378,4.33259447941311 746 | 3.22021123727003,4.3467921822111 747 | 3.21594760219079,4.36094750664438 748 | 3.21153151499799,4.37505972640613 749 | 3.20695961194354,4.38912811518956 750 | 3.20222852927936,4.40315194668784 751 | 3.19733490325737,4.41713049459417 752 | 3.1922764071782,4.43106278106866 753 | 3.18705486253734,4.44494682213909 754 | 3.181673127879,4.4587803823002 755 | 3.17613406174738,4.47256122604668 756 | 3.17044052268668,4.48628711787327 757 | 3.16459536924111,4.49995582227466 758 | 3.15860145995487,4.51356510374559 759 | 3.15246165337217,4.52711272678076 760 | 3.14617880803722,4.54059645587489 761 | 3.13975578249421,4.55401405552269 762 | 3.13319543528736,4.56736329021888 763 | 3.12650062496086,4.58064192445818 764 | 3.11967421005893,4.5938477227353 765 | 3.11271904912576,4.60697844954496 766 | 3.10563800070556,4.62003186938186 767 | 3.09843392334255,4.63300574674074 768 | 3.09110967558091,4.6458978461163 769 | 3.08366811596486,4.65870593200325 770 | 3.07611210303859,4.67142776889632 771 | 3.06844449534633,4.68406112129022 772 | 3.06066815143226,4.69660375367967 773 | 3.0527859298406,4.70905343055937 774 | 3.04480068911555,4.72140791642405 775 | 3.03671528780132,4.73366497576842 776 | 3.0285325844421,4.7458223730872 777 | 3.02025543758211,4.75787787287509 778 | 3.01188670576554,4.76982923962683 779 | 3.00342924753661,4.78167423783712 780 | 2.99488592143952,4.79341063200067 781 | 2.98625958601846,4.80503618661221 782 | 2.97755309981766,4.81654866616645 783 | 2.96876932138131,4.8279458351581 784 | 2.95991110925362,4.83922545808188 785 | 2.95098132197879,4.85038529943251 786 | 2.94198281810102,4.8614231237047 787 | 2.93291845616453,4.87233669539316 788 | 2.92379109471351,4.88312377899262 789 | 2.91460359229217,4.89378213899778 790 | 2.90535880744472,4.90430953990337 791 | 2.89605959871536,4.91470374620409 792 | 2.88670882464829,4.92496252239467 793 | 2.87730934378772,4.93508363296981 794 | 2.86786401467786,4.94506484242424 795 | 2.85837569586291,4.95490391525267 796 | 2.84884724588707,4.96459861594981 797 | 2.83928152329455,4.97414670901038 798 | 2.82968138662955,4.9835459589291 799 | 2.82004969443628,4.99279413020069 800 | 2.81038930525895,5.00188898731984 801 | 2.80070307764175,5.01082829478129 802 | 2.79099327881149,5.01961049186245 803 | 2.7812598107254,5.02823671697152 804 | 2.77150198402327,5.0367087832994 805 | 2.76171910934494,5.04502850403699 806 | 2.7519104973302,5.05319769237521 807 | 2.74207545861889,5.06121816150495 808 | 2.7322133038508,5.06909172461711 809 | 2.72232334366577,5.0768201949026 810 | 2.7124048887036,5.08440538555232 811 | 2.70245724960411,5.09184910975718 812 | 2.69247973700711,5.09915318070808 813 | 2.68247166155242,5.10631941159591 814 | 2.67243233387985,5.11334961561159 815 | 2.66236106462923,5.12024560594601 816 | 2.65225716444036,5.12700919579008 817 | 2.64211994395305,5.13364219833471 818 | 2.63194871380714,5.14014642677079 819 | 2.62174278464242,5.14652369428923 820 | 2.61150146709872,5.15277581408093 821 | 2.60122407181584,5.15890459933679 822 | 2.59090990943362,5.16491186324772 823 | 2.58055829059185,5.17079941900461 824 | 2.57016852593036,5.17656907979839 825 | 2.55973992608896,5.18222265881993 826 | 2.54927180170746,5.18776196926016 827 | 2.53876346342569,5.19318882430997 828 | 2.52821422188345,5.19850503716026 829 | 2.51762338772056,5.20371242100194 830 | 2.50699027157684,5.20881278902591 831 | 2.4963141840921,5.21380795442307 832 | 2.48559443590616,5.21869973038433 833 | 2.47483033765883,5.22348993010058 834 | 2.46402119998993,5.22818036676274 835 | 2.45316633353926,5.23277285356171 836 | 2.44226504894666,5.23726920368838 837 | 2.43131665685193,5.24167123033367 838 | 2.42032046789488,5.24598074668847 839 | 2.40927579271534,5.25019956594368 840 | 2.39818194195312,5.25432950129022 841 | 2.38703822624803,5.25837236591898 842 | 2.37584395623988,5.26232997302086 843 | 2.3645984425685,5.26620413578677 844 | 2.3533009958737,5.26999666740762 845 | 2.34195092679529,5.2737093810743 846 | 2.33054754597309,5.27734408997772 847 | 2.31909016404692,5.28090260730877 848 | 2.30757809165658,5.28438674625837 849 | 2.2960106394419,5.28779832001742 850 | 2.28438711804268,5.29113914177682 851 | 2.27270683809876,5.29441102472747 852 | 2.26096926064213,5.29761556233972 853 | 2.24917444827363,5.30075346920172 854 | 2.2373226139863,5.30382524018104 855 | 2.22541397077317,5.30683137014528 856 | 2.21344873162727,5.30977235396202 857 | 2.20142710954163,5.31264868649884 858 | 2.18934931750929,5.31546086262334 859 | 2.17721556852328,5.31820937720311 860 | 2.16502607557663,5.32089472510571 861 | 2.15278105166237,5.32351740119876 862 | 2.14048070977354,5.32607790034982 863 | 2.12812526290316,5.32857671742649 864 | 2.11571492404428,5.33101434729635 865 | 2.10324990618993,5.33339128482699 866 | 2.09073042233313,5.335708024886 867 | 2.07815668546693,5.33796506234096 868 | 2.06552890858434,5.34016289205946 869 | 2.05284730467842,5.34230200890909 870 | 2.04011208674218,5.34438290775743 871 | 2.02732346776866,5.34640608347206 872 | 2.0144816607509,5.34837203092058 873 | 2.00158687868192,5.35028124497058 874 | 1.98863933455476,5.35213422048963 875 | 1.97563924136246,5.35393145234532 876 | 1.96258681209804,5.35567343540525 877 | 1.94948225975454,5.35736066453699 878 | 1.93632579732499,5.35899363460814 879 | 1.92311763780242,5.36057284048627 880 | 1.90985799417987,5.36209877703899 881 | 1.89654707945037,5.36357193913387 882 | 1.88318510660695,5.36499282163849 883 | 1.86977228864264,5.36636191942046 884 | 1.85630883855048,5.36767972734734 885 | 1.8427949693235,5.36894674028674 886 | 1.82923089395473,5.37016345310623 887 | 1.81561682543721,5.3713303606734 888 | 1.80195297676396,5.37244795785585 889 | 1.78823956092803,5.37351673952114 890 | 1.77447679092243,5.37453720053688 891 | 1.76066487974022,5.37550983577065 892 | 1.74680404037441,5.37643514009003 893 | 1.73289448581805,5.37731360836261 894 | 1.71893642906416,5.37814573545598 895 | 1.70493008310577,5.37893201623773 896 | 1.69087566093593,5.37967294557543 897 | 1.67677337554766,5.38036901833668 898 | 1.66262343993399,5.38102072938907 899 | 1.64842606708796,5.38162857360017 900 | 1.6341814700026,5.38219304583758 901 | 1.61988986167095,5.38271464096889 902 | 1.60555142338741,5.38319396768934 903 | 1.5911662096519,5.38363209000484 904 | 1.57673424326571,5.38403018574898 905 | 1.56225554703015,5.38438943275532 906 | 1.54773014374651,5.38471100885746 907 | 1.53315805621608,5.38499609188895 908 | 1.51853930724017,5.38524585968339 909 | 1.50387391962005,5.38546149007434 910 | 1.48916191615704,5.38564416089539 911 | 1.47440331965242,5.3857950499801 912 | 1.4595981529075,5.38591533516206 913 | 1.44474643872356,5.38600619427485 914 | 1.42984819990191,5.38606880515203 915 | 1.41490345924383,5.38610434562719 916 | 1.39991223955063,5.38611399353391 917 | 1.3848745636236,5.38609892670575 918 | 1.36979045426403,5.38606032297631 919 | 1.35465993427322,5.38599936017914 920 | 1.33948302645247,5.38591721614784 921 | 1.32425975360307,5.38581506871597 922 | 1.30899013852632,5.38569409571712 923 | 1.29367420402351,5.38555547498485 924 | 1.27831197289594,5.38540038435275 925 | 1.26290346794491,5.3852300016544 926 | 1.2474487119717,5.38504550472337 927 | 1.23194772777762,5.38484807139324 928 | 1.21640053816396,5.38463887949758 929 | 1.20080716593202,5.38441910686996 930 | 1.18516763388309,5.38418993134398 931 | 1.16948196481846,5.3839525307532 932 | 1.15375018153944,5.38370808293121 933 | 1.13797230684732,5.38345776571157 934 | 1.12214836354339,5.38320275692786 935 | 1.10627837442895,5.38294423441366 936 | 1.09036236230529,5.38268337600256 937 | 1.07440034997372,5.38242135952811 938 | 1.05839236023552,5.38215936282391 939 | 1.042338415892,5.38189856372353 940 | 1.02623853974444,5.38164014006054 941 | 1.01009275459415,5.38138526966852 942 | 0.993901083242412,5.38113513038105 943 | 0.97766354849053,5.38089090003171 944 | 0.961380173139799,5.38065375645406 945 | 0.945050979991512,5.3804248774817 946 | 0.928675991846967,5.38020544094819 947 | 0.912255231507458,5.37999662468711 948 | 0.895788721774281,5.37979960653204 949 | 0.879276485448732,5.37961556431655 950 | 0.862718545332106,5.37944567587423 951 | 0.846114924225698,5.37929111903864 952 | 0.829465896284255,5.37915286449969 953 | 0.812772741076322,5.37903105437251 954 | 0.796036989523893,5.3789256236286 955 | 0.779260172548964,5.3788365072394 956 | 0.762443821073531,5.37876364017639 957 | 0.745589466019588,5.37870695741105 958 | 0.728698638309131,5.37866639391483 959 | 0.711772868864156,5.37864188465921 960 | 0.694813688606657,5.37863336461566 961 | 0.677822628458629,5.37864076875565 962 | 0.660801219342069,5.37866403205063 963 | 0.643750992178971,5.3787030894721 964 | 0.62667347789133,5.3787578759915 965 | 0.609570207401142,5.37882832658032 966 | 0.592442711630403,5.37891437621002 967 | 0.575292521501107,5.37901595985206 968 | 0.558121167935249,5.37913301247793 969 | 0.540930181854825,5.37926546905908 970 | 0.523721094181831,5.37941326456699 971 | 0.506495435838261,5.37957633397312 972 | 0.48925473774611,5.37975461224895 973 | 0.472000530827375,5.37994803436594 974 | 0.45473434600405,5.38015653529556 975 | 0.43745771419813,5.38038005000928 976 | 0.420172166331612,5.38061851347858 977 | 0.402879233326489,5.38087186067491 978 | 0.385580446104758,5.38114002656975 979 | 0.368277335588413,5.38142294613457 980 | 0.350971432699451,5.38172055434083 981 | 0.333664268359865,5.38203278616001 982 | 0.316357373491652,5.38235957656357 983 | 0.299052279016807,5.38270086052299 984 | 0.281750515857324,5.38305657300972 985 | 0.2644536149352,5.38342664899525 986 | 0.24716310717243,5.38381102345103 987 | 0.229880523491008,5.38420963134855 988 | 0.212607394812931,5.38462240765926 989 | 0.195345252060192,5.38504928735464 990 | 0.178095626154789,5.38549020540615 991 | 0.160860048018715,5.38594509678527 992 | 0.143640048573967,5.38641389646346 993 | 0.126437158742539,5.3868965394122 994 | 0.109252909446426,5.38739296060294 995 | 0.0920888316076249,5.38790309500716 996 | 0.0749464561481298,5.38842687759634 997 | 0.0578273139899362,5.38896424334193 998 | 0.0407329360550396,5.38951512721541 999 | 0.0236648532654349,5.39007946418824 1000 | 0.00662459654311764,5.3906571892319 1001 | -0.010386303189917,5.39124823731785 1002 | -0.0273667088018218,5.39185221351505 1003 | -0.0443170583213421,5.39246740328237 1004 | -0.061238183567371,5.39309176217616 1005 | -0.0781309163588019,5.39372324575279 1006 | -0.0949960885145282,5.39435980956862 1007 | -0.111834531853443,5.39499940917999 1008 | -0.12864707819444,5.39564000014328 1009 | -0.145434559356412,5.39627953801483 1010 | -0.162197807158252,5.396915978351 1011 | -0.178937653418855,5.39754727670816 1012 | -0.195654929957112,5.39817138864265 1013 | -0.212350468591918,5.39878626971084 1014 | -0.229025101142165,5.39938987546909 1015 | -0.245679659426748,5.39998016147375 1016 | -0.262314975264559,5.40055508328118 1017 | -0.278931880474491,5.40111259644773 1018 | -0.295531206875438,5.40165065652977 1019 | -0.312113786286294,5.40216721908366 1020 | -0.328680450525951,5.40266023966574 1021 | -0.345232031413303,5.40312767383238 1022 | -0.361769360767243,5.40356747713994 1023 | -0.378293270406665,5.40397760514477 1024 | -0.394804592150461,5.40435601340323 1025 | -0.411304157817525,5.40470065747168 1026 | -0.427792799226751,5.40500949290648 1027 | -0.444271348197031,5.40528047526398 1028 | -0.460740636547259,5.40551156010054 1029 | -0.477201496096328,5.40570070297252 1030 | -0.493654758663132,5.40584585943628 1031 | -0.510101256066564,5.40594498504817 1032 | -0.526541820125517,5.40599603536455 1033 | -0.542977282658884,5.40599696594179 1034 | -0.559408475485559,5.40594573233623 1035 | -0.575836230424435,5.40584029010423 1036 | -0.592261379294405,5.40567859480216 1037 | -0.608684753914363,5.40545860198637 1038 | -0.625107186103202,5.40517826721322 1039 | -0.641529507679816,5.40483554603906 1040 | -0.657952550463096,5.40442839402026 1041 | -0.674377146271938,5.40395476671316 1042 | -0.690804126925234,5.40341261967414 1043 | -0.707234324241877,5.40279990845954 1044 | -0.723668570040761,5.40211458862573 1045 | -0.740107696140779,5.40135461572905 1046 | -0.756552534360824,5.40051794532588 1047 | -0.77300391651979,5.39960253297256 1048 | -0.78946267443657,5.39860633422546 1049 | -0.805929639930058,5.39752730464093 1050 | -0.822405644819145,5.39636339977533 1051 | -0.838891520922727,5.39511257518502 1052 | -0.855387204695287,5.39377314947443 1053 | -0.87188905113367,5.39234489344031 1054 | -0.888392519870315,5.3908279409275 1055 | -0.904893070537659,5.38922242578082 1056 | -0.921386162768139,5.3875284818451 1057 | -0.937867256194191,5.38574624296516 1058 | -0.954331810448254,5.38387584298585 1059 | -0.970775285162764,5.38191741575199 1060 | -0.987193139970158,5.3798710951084 1061 | -1.00358083450287,5.37773701489991 1062 | -1.01993382839335,5.37551530897136 1063 | -1.03624758127402,5.37320611116757 1064 | -1.05251755277732,5.37080955533338 1065 | -1.06873920253569,5.3683257753136 1066 | -1.08490799018157,5.36575490495308 1067 | -1.1010193753474,5.36309707809663 1068 | -1.1170688176656,5.36035242858909 1069 | -1.13305177676863,5.35752109027529 1070 | -1.14896371228891,5.35460319700005 1071 | -1.16480008385888,5.35159888260821 1072 | -1.18055635111099,5.34850828094458 1073 | -1.19622797367766,5.34533152585402 1074 | -1.21181041119134,5.34206875118133 1075 | -1.22729912328445,5.33872009077135 1076 | -1.24268956958945,5.33528567846891 1077 | -1.25797720973876,5.33176564811884 1078 | -1.27315750336482,5.32816013356596 1079 | -1.28822591010008,5.32446926865511 1080 | -1.30317788957696,5.32069318723111 1081 | -1.31800890142791,5.3168320231388 1082 | -1.33271440528536,5.312885910223 1083 | -1.34728986078174,5.30885498232854 1084 | -1.36173072754951,5.30473937330024 1085 | -1.37603246522108,5.30053921698295 1086 | -1.39019053342891,5.29625464722149 1087 | -1.40420039180542,5.29188579786068 1088 | -1.41805749998306,5.28743280274536 1089 | -1.43175731759426,5.28289579572035 1090 | -1.44529530427146,5.27827491063048 1091 | -1.4586669196471,5.27357028132059 1092 | -1.4718676233536,5.26878204163549 1093 | -1.48489287502342,5.26391032542003 1094 | -1.49773813428899,5.25895526651903 1095 | -1.51039886078274,5.25391699877731 1096 | -1.52287051413711,5.24879565603971 1097 | -1.53514855398454,5.24359137215106 1098 | -1.54722843995747,5.23830428095618 1099 | -1.55910563168833,5.2329345162999 1100 | -1.57077558880956,5.22748221202705 1101 | -1.58223377095359,5.22194750198247 1102 | -1.59347696155705,5.21633076357887 1103 | -1.60450723927327,5.21063334850052 1104 | -1.61532800655975,5.20485685199962 1105 | -1.62594266587399,5.19900286932831 1106 | -1.63635461967352,5.19307299573878 1107 | -1.64656727041584,5.1870688264832 1108 | -1.65658402055845,5.18099195681374 1109 | -1.66640827255887,5.17484398198257 1110 | -1.6760434288746,5.16862649724186 1111 | -1.68549289196316,5.16234109784378 1112 | -1.69476006428205,5.15598937904051 1113 | -1.70384834828878,5.14957293608422 1114 | -1.71276114644086,5.14309336422707 1115 | -1.72150186119581,5.13655225872124 1116 | -1.73007389501111,5.12995121481891 1117 | -1.7384806503443,5.12329182777223 1118 | -1.74672552965287,5.11657569283339 1119 | -1.75481193539433,5.10980440525456 1120 | -1.7627432700262,5.1029795602879 1121 | -1.77052293600598,5.0961027531856 1122 | -1.77815433579118,5.08917557919981 1123 | -1.78564087183931,5.08219963358271 1124 | -1.79298594660788,5.07517651158648 1125 | -1.80019296255439,5.06810780846328 1126 | -1.80726532213636,5.06099511946529 1127 | -1.81420642781129,5.05384003984467 1128 | -1.8210196820367,5.04664416485361 1129 | -1.82770848727009,5.03940908974426 1130 | -1.83427624596897,5.03213640976881 1131 | -1.84072636059085,5.02482772017942 1132 | -1.84706223359324,5.01748461622827 1133 | -1.85328726743364,5.01010869316752 1134 | -1.85940486456958,5.00270154624935 1135 | -1.86541842745854,4.99526477072593 1136 | -1.87133135855805,4.98779996184944 1137 | -1.87714706032561,4.98030871487203 1138 | -1.88286893521874,4.97279262504589 1139 | -1.88850038569493,4.96525328762318 1140 | -1.8940448142117,4.95769229785609 1141 | -1.89950562322656,4.95011125099677 1142 | -1.90488621519702,4.9425117422974 1143 | -1.91018999258058,4.93489536701015 1144 | -1.91542035783476,4.9272637203872 1145 | -1.92058071341706,4.91961839768071 1146 | -1.92567446178499,4.91196099414286 1147 | -1.93070500539606,4.90429310502581 1148 | -1.93567574670778,4.89661632558175 1149 | -1.94059008817765,4.88893225106284 1150 | -1.9454514322632,4.88124247672124 1151 | -1.95026318142192,4.87354859780914 1152 | -1.95502819492936,4.8658516237501 1153 | -1.9597471593332,4.85815022065318 1154 | -1.96442021799918,4.85044246879887 1155 | -1.96904751429301,4.84272644846762 1156 | -1.97362919158041,4.83500023993992 1157 | -1.97816539322711,4.82726192349622 1158 | -1.98265626259884,4.819509579417 1159 | -1.9871019430613,4.81174128798273 1160 | -1.99150257798024,4.80395512947386 1161 | -1.99585831072136,4.79614918417088 1162 | -2.00016928465039,4.78832153235425 1163 | -2.00443564313306,4.78047025430444 1164 | -2.00865752953508,4.77259343030191 1165 | -2.01283508722219,4.76468914062714 1166 | -2.01696845956009,4.75675546556059 1167 | -2.02105778991453,4.74879048538273 1168 | -2.02510322165121,4.74079228037404 1169 | -2.02910489813586,4.73275893081497 1170 | -2.0330629627342,4.724688516986 1171 | -2.03697755881197,4.7165791191676 1172 | -2.04084882973487,4.70842881764023 1173 | -2.04467691886863,4.70023569268436 1174 | -2.04846196957897,4.69199782458046 1175 | -2.05220412523163,4.68371329360901 1176 | -2.05590352919231,4.67538018005046 1177 | -2.05956032482675,4.66699656418529 1178 | -2.06317465550066,4.65856052629396 1179 | -2.06674666457977,4.65007014665695 1180 | -2.0702764954298,4.64152350555471 1181 | -2.07376429141647,4.63291868326773 1182 | -2.07721019590551,4.62425376007647 1183 | -2.08061435226264,4.61552681626139 1184 | -2.08397690385359,4.60673593210297 1185 | -2.08729799404406,4.59787918788167 1186 | -2.0905777661998,4.58895466387797 1187 | -2.09381636368651,4.57996044037232 1188 | -2.09701392986993,4.57089459764521 1189 | -2.10017060811577,4.56175521597709 1190 | -2.10328654178976,4.55254037564844 1191 | -2.10636187425762,4.54324815693972 1192 | -2.10939674888507,4.53387664013141 1193 | -2.11239130903784,4.52442390550396 1194 | -2.11534569808165,4.51488803333786 1195 | -2.11826005938222,4.50526710391356 1196 | -2.12113453630528,4.49555919751155 1197 | -2.12396927221654,4.48576239441227 1198 | -2.12676441048174,4.47587477489621 1199 | -2.12952009446659,4.46589441924384 1200 | -2.13223646753681,4.45581940773561 1201 | -2.13491367305813,4.445647820652 1202 | -2.13755176558402,4.43537809125174 1203 | -2.14015044441897,4.42501006470656 1204 | -2.14270932005521,4.41454393916646 1205 | -2.14522800298498,4.40397991278144 1206 | -2.14770610370051,4.3933181837015 1207 | -2.15014323269404,4.38255895007664 1208 | -2.15253900045781,4.37170241005686 1209 | -2.15489301748404,4.36074876179214 1210 | -2.15720489426497,4.3496982034325 1211 | -2.15947424129285,4.33855093312793 1212 | -2.1617006690599,4.32730714902842 1213 | -2.16388378805836,4.31596704928398 1214 | -2.16602320878047,4.3045308320446 1215 | -2.16811854171845,4.29299869546028 1216 | -2.17016939736456,4.28137083768101 1217 | -2.17217538621101,4.26964745685681 1218 | -2.17413611875005,4.25782875113765 1219 | -2.17605120547392,4.24591491867355 1220 | -2.17792025687484,4.2339061576145 1221 | -2.17974288344506,4.2218026661105 1222 | -2.1815186956768,4.20960464231154 1223 | -2.18324730406231,4.19731228436762 1224 | -2.18492831909382,4.18492579042875 1225 | -2.18656135126356,4.17244535864491 1226 | -2.18814601106378,4.15987118716612 1227 | -2.1896819089867,4.14720347414235 1228 | -2.19116865552456,4.13444241772362 1229 | -2.1926058611696,4.12158821605992 1230 | -2.19399313641404,4.10864106730125 1231 | -2.19533009175014,4.09560116959761 1232 | -2.19661633767012,4.08246872109899 1233 | -2.19785148466621,4.0692439199554 1234 | -2.19903514323066,4.05592696431682 1235 | -2.2001669238557,4.04251805233326 1236 | -2.20124643703357,4.02901738215472 1237 | -2.20227329325649,4.01542515193119 1238 | -2.2032471030167,4.00174155981268 1239 | -2.20416747680645,3.98796680394917 1240 | -2.20503402511796,3.97410108249067 1241 | -2.20584635844348,3.96014459358718 1242 | -2.20660408727523,3.94609753538869 1243 | -2.20730682210545,3.93196010604521 1244 | -2.20795417342638,3.91773250370672 1245 | -2.20854575173025,3.90341492652323 1246 | -2.2090811675093,3.88900757264473 1247 | -2.20956003125577,3.87451064022123 1248 | -2.20998195346188,3.85992432740272 1249 | -2.21034654461988,3.8452488323392 1250 | -2.21065341522199,3.83048435318066 1251 | -2.21090217576046,3.81563108807711 1252 | -2.2110928130739,3.80068938326507 1253 | -2.21122681938647,3.78566017732716 1254 | -2.21130606326867,3.77054455693252 1255 | -2.21133241329105,3.75534360875031 1256 | -2.21130773802413,3.74005841944967 1257 | -2.21123390603844,3.72469007569975 1258 | -2.21111278590451,3.70923966416969 1259 | -2.21094624619286,3.69370827152864 1260 | -2.21073615547402,3.67809698444576 1261 | -2.21048438231852,3.66240688959018 1262 | -2.21019279529689,3.64663907363106 1263 | -2.20986326297965,3.63079462323753 1264 | -2.20949765393734,3.61487462507876 1265 | -2.20909783674047,3.59888016582387 1266 | -2.20866567995959,3.58281233214204 1267 | -2.20820305216521,3.56667221070239 1268 | -2.20771182192786,3.55046088817407 1269 | -2.20719385781807,3.53417945122625 1270 | -2.20665102840637,3.51782898652805 1271 | -2.20608520226329,3.50141058074863 1272 | -2.20549824795935,3.48492532055713 1273 | -2.20489203406508,3.46837429262271 1274 | -2.20426842915102,3.4517585836145 1275 | -2.20362930178767,3.43507928020166 1276 | -2.20297652054559,3.41833746905334 1277 | -2.20231195399528,3.40153423683867 1278 | -2.20163747070729,3.38467067022682 1279 | -2.20095493925213,3.36774785588692 1280 | -2.20026622820034,3.35076688048811 1281 | -2.19957320612244,3.33372883069956 1282 | -2.19887774158897,3.31663479319041 1283 | -2.19818170317044,3.29948585462979 1284 | -2.19748695943738,3.28228310168687 1285 | -2.19679537896033,3.26502762103079 1286 | -2.19610883030981,3.24772049933069 1287 | -2.19542918205635,3.23036282325572 1288 | -2.19475830277048,3.21295567947503 1289 | -2.19409806102272,3.19550015465776 1290 | -2.19345032538361,3.17799733547307 1291 | -2.19281696442366,3.1604483085901 1292 | -2.19219984671341,3.14285416067799 1293 | -2.19160084082339,3.1252159784059 1294 | -2.19102181532412,3.10753484844297 1295 | -2.19046463878613,3.08981185745835 1296 | -2.18993117977994,3.07204809212119 1297 | -2.1894233068761,3.05424463910063 1298 | -2.18894288864511,3.03640258506581 1299 | -2.18849179365752,3.0185230166859 1300 | -2.18807189048385,3.00060702063003 1301 | -2.18768504769462,2.98265568356735 1302 | -2.18733262403744,2.96467005592319 1303 | -2.18701393896818,2.94665104314766 1304 | -2.1867278021198,2.92859951444703 1305 | -2.18647302312524,2.9105163390276 1306 | -2.18624841161746,2.89240238609563 1307 | -2.18605277722941,2.87425852485742 1308 | -2.18588492959403,2.85608562451925 1309 | -2.18574367834428,2.8378845542874 1310 | -2.1856278331131,2.81965618336816 1311 | -2.18553620353346,2.80140138096781 1312 | -2.18546759923829,2.78312101629263 1313 | -2.18542082986055,2.7648159585489 1314 | -2.18539470503319,2.74648707694292 1315 | -2.18538803438916,2.72813524068096 1316 | -2.18539962756141,2.70976131896931 1317 | -2.18542829418289,2.69136618101424 1318 | -2.18547284388655,2.67295069602206 1319 | -2.18553208630534,2.65451573319902 1320 | -2.18560483107221,2.63606216175143 1321 | -2.18568988782011,2.61759085088557 1322 | -2.185786066182,2.59910266980771 1323 | -2.18589217579082,2.58059848772414 1324 | -2.18600702627952,2.56207917384115 1325 | -2.18612942728105,2.54354559736501 1326 | -2.18625818842837,2.52499862750202 1327 | -2.18639211935442,2.50643913345845 1328 | -2.18653002969216,2.48786798444059 1329 | -2.18667072907453,2.46928604965473 1330 | -2.18681302713449,2.45069419830713 1331 | -2.18695573350498,2.4320932996041 1332 | -2.18709765781896,2.41348422275191 1333 | -2.18723760970938,2.39486783695685 1334 | -2.18737439880918,2.37624501142519 1335 | -2.18750683475132,2.35761661536323 1336 | -2.18763372716875,2.33898351797725 1337 | -2.18775388569442,2.32034658847352 1338 | -2.18786611996127,2.30170669605834 1339 | -2.18796923960227,2.28306470993798 1340 | -2.18806205425035,2.26442149931874 1341 | -2.18814337353847,2.24577793340689 1342 | -2.18821200709958,2.22713488140872 1343 | -2.18826676456664,2.2084932125305 1344 | -2.18830645557258,2.18985379597853 1345 | -2.18832988975037,2.17121750095909 1346 | -2.18833587673295,2.15258519667846 1347 | -2.18832322615327,2.13395775234293 1348 | -2.18829074764428,2.11533603715877 1349 | -2.18823725083893,2.09672092033228 1350 | -2.18816154537018,2.07811327106973 1351 | -2.18806244087097,2.05951395857741 1352 | -2.18793889360071,2.04092390448276 1353 | -2.18779044632464,2.0223442400979 1354 | -2.18761678843443,2.00377614915609 1355 | -2.18741760932177,1.98522081539061 1356 | -2.18719259837835,1.96667942253471 1357 | -2.18694144499586,1.94815315432168 1358 | -2.18666383856598,1.92964319448479 1359 | -2.18635946848039,1.9111507267573 1360 | -2.1860280241308,1.89267693487249 1361 | -2.18566919490887,1.87422300256362 1362 | -2.1852826702063,1.85579011356397 1363 | -2.18486813941478,1.8373794516068 1364 | -2.18442529192598,1.8189922004254 1365 | -2.1839538171316,1.80062954375301 1366 | -2.18345340442332,1.78229266532293 1367 | -2.18292374319284,1.76398274886842 1368 | -2.18236452283182,1.74570097812274 1369 | -2.18177543273197,1.72744853681917 1370 | -2.18115616228497,1.70922660869098 1371 | -2.1805064008825,1.69103637747144 1372 | -2.17982583791625,1.67287902689383 1373 | -2.17911416277791,1.6547557406914 1374 | -2.17837106485916,1.63666770259743 1375 | -2.17759623355169,1.61861609634519 1376 | -2.17678935824719,1.60060210566796 1377 | -2.17595012833734,1.582626914299 1378 | -2.17507823321383,1.56469170597158 1379 | -2.17417336226834,1.54679766441897 1380 | -2.17323520489256,1.52894597337445 1381 | -2.17226345047818,1.51113781657128 1382 | -2.17125778841689,1.49337437774274 1383 | -2.17021790810036,1.47565684062209 1384 | -2.16914349892029,1.4579863889426 1385 | -2.16803425026837,1.44036420643755 1386 | -2.16688985153627,1.4227914768402 1387 | -2.16570999211569,1.40526938388384 1388 | -2.16449436139831,1.38779911130171 1389 | -2.16324264877582,1.37038184282711 1390 | -2.1619545436399,1.35301876219329 1391 | -2.16062973538224,1.33571105313353 1392 | -2.15926791339453,1.3184598993811 1393 | -2.15786876706845,1.30126648466927 1394 | -2.1564319857957,1.2841319927313 1395 | -2.15495725896794,1.26705760730048 1396 | -2.15344427597688,1.25004451211007 1397 | -2.1518927262142,1.23309389089333 1398 | -2.15030229907159,1.21620692738355 1399 | -2.14867268394072,1.19938480531398 1400 | -2.1470035702133,1.18262870841791 1401 | -2.14529464728099,1.1659398204286 1402 | -2.1435456018318,1.14931956426808 1403 | -2.14175610973888,1.13277031961336 1404 | -2.13992584417171,1.11629470533022 1405 | -2.13805447829977,1.09989534028443 1406 | -2.1361416852925,1.08357484334178 1407 | -2.1341871383194,1.06733583336803 1408 | -2.13219051054992,1.05118092922895 1409 | -2.13015147515354,1.03511274979033 1410 | -2.12806970529973,1.01913391391794 1411 | -2.12594487415795,1.00324704047754 1412 | -2.12377665489767,0.987454748334925 1413 | -2.12156472068837,0.971759656355855 1414 | -2.11930874469951,0.95616438340611 1415 | -2.11700840010056,0.940671548351463 1416 | -2.114663360061,0.925283770057689 1417 | -2.11227329775028,0.91000366739056 1418 | -2.10983788633789,0.894833859215853 1419 | -2.10735679899329,0.879776964399342 1420 | -2.10482970888595,0.864835601806799 1421 | -2.10225628918534,0.850012390304001 1422 | -2.09963621306092,0.835309948756721 1423 | -2.09696915368217,0.820730896030733 1424 | -2.09425478421857,0.806277850991812 1425 | -2.09149277783956,0.791953432505732 1426 | -2.08868280771464,0.777760259438267 1427 | -2.08582454701326,0.763700950655192 1428 | -2.08291766890489,0.749778125022281 1429 | -2.07996184655901,0.735994401405308 1430 | -2.07695675314508,0.722352398670048 1431 | -2.07390206183258,0.708854735682275 1432 | -2.07079744579097,0.695504031307762 1433 | -2.06764257818972,0.682302904412285 1434 | -2.0644371321983,0.669253973861618 1435 | -2.06118078098618,0.656359858521535 1436 | -2.05787319772284,0.64362317725781 1437 | -2.05451405557773,0.631046548936218 1438 | -2.05110302772034,0.618632592422533 1439 | -2.04763978732012,0.606383926582529 1440 | -2.04412400754655,0.59430317028198 1441 | -2.04055536156909,0.582392942386662 1442 | -2.03693352255723,0.570655861762347 1443 | -2.03325816368042,0.559094547274811 1444 | -2.02952895810814,0.547711617789828 1445 | -2.02574557900985,0.536509692173171 1446 | -2.02190769955502,0.525491389290616 1447 | -2.01801499291313,0.514659328007936 1448 | -2.01406713225365,0.504016127190907 1449 | -2.01006379074603,0.493564405705301 1450 | -2.00600464155976,0.483306782416894 1451 | -2.0018893578643,0.47324587619146 1452 | -1.9977178154328,0.46338337931648 1453 | -1.99349070045315,0.453717277766267 1454 | -1.9892089017169,0.444244630936839 1455 | -1.9848733080156,0.434962498224216 1456 | -1.98048480814083,0.425867939024416 1457 | -1.97604429088414,0.41695801273346 1458 | -1.9715526450371,0.408229778747366 1459 | -1.96701075939126,0.399680296462153 1460 | -1.96241952273818,0.391306625273841 1461 | -1.95777982386942,0.383105824578449 1462 | -1.95309255157654,0.375074953771996 1463 | -1.94835859465111,0.367211072250501 1464 | -1.94357884188469,0.359511239409983 1465 | -1.93875418206883,0.351972514646462 1466 | -1.93388550399509,0.344591957355957 1467 | -1.92897369645503,0.337366626934487 1468 | -1.92401964824022,0.330293582778071 1469 | -1.91902424814222,0.323369884282728 1470 | -1.91398838495258,0.316592590844478 1471 | -1.90891294746286,0.309958761859339 1472 | -1.90379882446463,0.303465456723332 1473 | -1.89864690474945,0.297109734832474 1474 | -1.89345807710887,0.290888655582786 1475 | -1.88823323033446,0.284799278370287 1476 | -1.88297325321778,0.278838662590995 1477 | -1.87767903455038,0.27300386764093 1478 | -1.87235146312382,0.267291952916111 1479 | -1.86699142772968,0.261699977812557 1480 | -1.8615998171595,0.256225001726288 1481 | -1.85617752020485,0.250864084053323 1482 | -1.85072542565729,0.24561428418968 1483 | -1.84524442230837,0.24047266153138 1484 | -1.83973539894967,0.23543627547444 1485 | -1.83419924437273,0.230502185414881 1486 | -1.82863684736912,0.225667450748722 1487 | -1.82304909673039,0.220929130871982 1488 | -1.81743688124812,0.216284285180679 1489 | -1.81180108971386,0.211729973070834 1490 | -1.80614261091916,0.207263253938465 1491 | -1.8004623336556,0.202881187179592 1492 | -1.79476114671472,0.198580832190233 1493 | -1.7890399388881,0.194359248366409 1494 | -1.78329959896728,0.190213495104138 1495 | -1.77754101574384,0.186140631799439 1496 | -1.77176507800933,0.182137717848331 1497 | -1.7659726745553,0.178201812646835 1498 | -1.76016469417333,0.174329975590968 1499 | -1.75434202565497,0.17051926607675 1500 | -1.74850555779178,0.166766743500201 1501 | -1.74265617937532,0.163069467257339 1502 | -1.73679414051082,0.159424903158788 1503 | -1.73091713655816,0.155832142673587 1504 | -1.7250222241909,0.152290683685379 1505 | -1.71910646008257,0.148800024077809 1506 | -1.71316690090674,0.145359661734518 1507 | -1.70720060333696,0.14196909453915 1508 | -1.70120462404676,0.138627820375349 1509 | -1.69517601970971,0.135335337126759 1510 | -1.68911184699935,0.132091142677021 1511 | -1.68300916258924,0.128894734909781 1512 | -1.67686502315291,0.12574561170868 1513 | -1.67067648536394,0.122643270957363 1514 | -1.66444060589585,0.119587210539472 1515 | -1.65815444142222,0.116576928338652 1516 | -1.65181504861657,0.113611922238545 1517 | -1.64541948415247,0.110691690122795 1518 | -1.63896480470347,0.107815729875044 1519 | -1.63244806694311,0.104983539378938 1520 | -1.62586632754494,0.102194616518118 1521 | -1.61921664318252,0.0994484591762276 1522 | -1.6124960705294,0.096744565236911 1523 | -1.60570166625912,0.0940824325838111 1524 | -1.59883048704524,0.0914615591005714 1525 | -1.5918795895613,0.088881442670835 1526 | -1.58484603048086,0.0863415811782454 1527 | -1.57772686647747,0.0838414725064459 1528 | -1.57051915422467,0.0813806145390798 1529 | -1.56321995039602,0.0789585051597905 1530 | -1.55582631166507,0.0765746422522213 1531 | -1.54833529470537,0.0742285237000154 1532 | -1.54074395619046,0.0719196473868164 1533 | -1.5330493527939,0.0696475111962674 1534 | -1.52524854118924,0.0674116130120119 1535 | -1.51733857805002,0.0652114507176931 1536 | -1.5093165200498,0.0630465221969544 1537 | -1.50117942386214,0.0609163253334392 1538 | -1.49292434616057,0.0588203580107908 1539 | -1.48454834361864,0.0567581181126524 1540 | -1.47604847290992,0.0547291035226675 1541 | -1.46742179070795,0.0527328121244794 1542 | -1.45866535368627,0.0507687418017314 1543 | -1.44977621851845,0.0488363904380668 1544 | -1.44075144187802,0.046935255917129 1545 | -1.43158808043855,0.0450648361225614 1546 | -1.42228319087357,0.0432246289380072 1547 | -1.41283382985665,0.0414141322471098 1548 | -1.40323705406132,0.0396328439335125 1549 | -1.39348992016115,0.0378802618808586 1550 | -1.38358948482967,0.0361558839727916 1551 | -1.37353280474045,0.0344592080929547 1552 | -1.36331795774838,0.0327897251446778 1553 | -1.3529471064338,0.0311468981100369 1554 | -1.34242343455839,0.0295301829907945 1555 | -1.33175012588383,0.0279390357887132 1556 | -1.32093036417181,0.0263729125055555 1557 | -1.309967333184,0.0248312691430838 1558 | -1.29886421668211,0.0233135617030609 1559 | -1.2876241984278,0.0218192461872491 1560 | -1.27625046218276,0.020347778597411 1561 | -1.26474619170868,0.0188986149353092 1562 | -1.25311457076725,0.0174712112027061 1563 | -1.24135878312013,0.0160650234013644 1564 | -1.22948201252903,0.0146795075330465 1565 | -1.21748744275562,0.0133141195995149 1566 | -1.20537825756158,0.0119683156025322 1567 | -1.19315764070861,0.010641551543861 1568 | -1.18082877595838,0.00933328342526373 1569 | -1.16839484707258,0.00804296724850294 1570 | -1.15585903781289,0.00677005901534117 1571 | -1.143224531941,0.00551401472754094 1572 | -1.13049451321859,0.0042742903868648 1573 | -1.11767216540734,0.00305034199507526 1574 | -1.10476067226895,0.00184162555393487 1575 | -1.09176321756508,0.000647597065206153 1576 | -1.07868298505743,-0.000532287469348372 1577 | -1.06552315850768,-0.00169857204796615 1578 | -1.05228692167752,-0.00285180066888468 1579 | -1.03897745832863,-0.00399251733034141 1580 | -1.02559795222268,-0.00512126603057381 1581 | -1.01215158712138,-0.00623859076781936 1582 | -0.99864154678639,-0.00734503554031552 1583 | -0.985071014979409,-0.00844114434629976 1584 | -0.971443175462116,-0.00952746118400956 1585 | -0.957761211996195,-0.0106045300516824 1586 | -0.944028308343331,-0.0116728949475557 1587 | -0.930247648265207,-0.012733099869867 1588 | -0.916422415523507,-0.0137856888168537 1589 | -0.902555793879916,-0.0148312057867533 1590 | -0.888650967096116,-0.0158701947778032 1591 | -0.874711118933793,-0.016903199788241 1592 | -0.86073943315463,-0.0179307648163041 1593 | -0.846739093520312,-0.01895343386023 1594 | -0.832713283792521,-0.0199717509182561 1595 | -0.818665187732943,-0.02098625998862 1596 | -0.80459798910326,-0.021997505069559 1597 | -0.790514871665158,-0.0230060301593107 1598 | -0.77641901918032,-0.0240123792561125 1599 | -0.76231361541043,-0.0250170963582019 1600 | -0.748201844117172,-0.0260207254638164 1601 | -0.73408688906223,-0.0270238105711934 1602 | -0.719971934007288,-0.0280268956785704 1603 | -0.70586016271403,-0.0290305247841848 1604 | -0.69175475894414,-0.0300352418862742 1605 | -0.677658906459302,-0.031041590983076 1606 | -0.6635757890212,-0.0320501160728277 1607 | -0.649508590391518,-0.0330613611537667 1608 | -0.635460494331939,-0.0340758702241306 1609 | -0.621434684604149,-0.0350941872821567 1610 | --------------------------------------------------------------------------------