├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ ├── Linux_CI.yml
│ └── MacOS_CI.yml
├── .gitignore
├── .lgtm.yml
├── .travis.yml
├── AerialNavigation
├── drone_3d_trajectory_following
│ ├── Quadrotor.py
│ ├── TrajectoryGenerator.py
│ └── drone_3d_trajectory_following.py
└── rocket_powered_landing
│ ├── figure.png
│ ├── rocket_powered_landing.ipynb
│ └── rocket_powered_landing.py
├── ArmNavigation
├── __init__.py
├── arm_obstacle_navigation
│ ├── arm_obstacle_navigation.py
│ └── arm_obstacle_navigation_2.py
├── n_joint_arm_3d
│ ├── NLinkArm3d.py
│ ├── __init__py.py
│ ├── random_forward_kinematics.py
│ └── random_inverse_kinematics.py
├── n_joint_arm_to_point_control
│ ├── NLinkArm.py
│ ├── __init__.py
│ └── n_joint_arm_to_point_control.py
└── two_joint_arm_to_point_control
│ ├── Planar_Two_Link_IK.ipynb
│ └── two_joint_arm_to_point_control.py
├── Bipedal
├── __init__.py
└── bipedal_planner
│ └── bipedal_planner.py
├── CONTRIBUTING.md
├── Introduction
└── introduction.ipynb
├── InvertedPendulumCart
└── inverted_pendulum_mpc_control.py
├── LICENSE
├── Localization
├── Kalmanfilter_basics.ipynb
├── Kalmanfilter_basics_2.ipynb
├── bayes_filter.png
├── ensemble_kalman_filter
│ └── ensemble_kalman_filter.py
├── extended_kalman_filter
│ ├── ekf.png
│ ├── extended_kalman_filter.py
│ └── extended_kalman_filter_localization.ipynb
├── histogram_filter
│ └── histogram_filter.py
├── particle_filter
│ ├── particle_filter.ipynb
│ └── particle_filter.py
└── unscented_kalman_filter
│ └── unscented_kalman_filter.py
├── Mapping
├── circle_fitting
│ └── circle_fitting.py
├── gaussian_grid_map
│ └── gaussian_grid_map.py
├── grid_map_lib
│ └── grid_map_lib.py
├── kmeans_clustering
│ └── kmeans_clustering.py
├── lidar_to_grid_map
│ ├── animation.gif
│ ├── grid_map_example.png
│ ├── lidar01.csv
│ ├── lidar_to_grid_map.py
│ └── lidar_to_grid_map_tutorial.ipynb
├── raycasting_grid_map
│ └── raycasting_grid_map.py
└── rectangle_fitting
│ ├── rectangle_fitting.py
│ └── simulator.py
├── PathPlanning
├── AStar
│ └── a_star.py
├── BSplinePath
│ ├── Figure_1.png
│ └── bspline_path.py
├── BatchInformedRRTStar
│ └── batch_informed_rrtstar.py
├── BezierPath
│ ├── Figure_1.png
│ ├── Figure_2.png
│ └── bezier_path.py
├── BidirectionalAStar
│ └── bidirectional_a_star.py
├── BidirectionalBreadthFirstSearch
│ └── bidirectional_breadth_first_search.py
├── BreadthFirstSearch
│ └── breadth_first_search.py
├── ClosedLoopRRTStar
│ ├── Figure_1.png
│ ├── Figure_3.png
│ ├── Figure_4.png
│ ├── Figure_5.png
│ ├── __init__.py
│ ├── closed_loop_rrt_star_car.py
│ ├── pure_pursuit.py
│ └── unicycle_model.py
├── CubicSpline
│ ├── Figure_1.png
│ ├── Figure_2.png
│ ├── Figure_3.png
│ ├── __init__.py
│ └── cubic_spline_planner.py
├── DepthFirstSearch
│ └── depth_first_search.py
├── Dijkstra
│ └── dijkstra.py
├── DubinsPath
│ ├── __init__.py
│ └── dubins_path_planning.py
├── DynamicWindowApproach
│ └── dynamic_window_approach.py
├── Eta3SplinePath
│ └── eta3_spline_path.py
├── Eta3SplineTrajectory
│ └── eta3_spline_trajectory.py
├── FrenetOptimalTrajectory
│ └── frenet_optimal_trajectory.py
├── GreedyBestFirstSearch
│ └── greedy_best_first_search.py
├── GridBasedSweepCPP
│ └── grid_based_sweep_coverage_path_planner.py
├── HybridAStar
│ ├── __init__.py
│ ├── a_star_heuristic.py
│ ├── car.py
│ └── hybrid_a_star.py
├── InformedRRTStar
│ └── informed_rrt_star.py
├── LQRPlanner
│ └── LQRplanner.py
├── LQRRRTStar
│ └── lqr_rrt_star.py
├── ModelPredictiveTrajectoryGenerator
│ ├── __init__.py
│ ├── lookuptable.png
│ ├── lookuptable_generator.py
│ ├── model_predictive_trajectory_generator.py
│ └── motion_model.py
├── PotentialFieldPlanning
│ └── potential_field_planning.py
├── ProbabilisticRoadMap
│ └── probabilistic_road_map.py
├── QuinticPolynomialsPlanner
│ ├── quintic_polynomials_planner.ipynb
│ └── quintic_polynomials_planner.py
├── RRT
│ ├── __init__.py
│ ├── figure_1.png
│ ├── rrt.py
│ └── rrt_with_pathsmoothing.py
├── RRTDubins
│ ├── __init__.py
│ └── rrt_dubins.py
├── RRTStar
│ ├── Figure_1.png
│ ├── __init__.py
│ ├── rrt_star.ipynb
│ └── rrt_star.py
├── RRTStarDubins
│ └── rrt_star_dubins.py
├── RRTStarReedsShepp
│ ├── figure_1.png
│ └── rrt_star_reeds_shepp.py
├── ReedsSheppPath
│ └── reeds_shepp_path_planning.py
├── StateLatticePlanner
│ ├── Figure_1.png
│ ├── Figure_2.png
│ ├── Figure_3.png
│ ├── Figure_4.png
│ ├── Figure_5.png
│ ├── Figure_6.png
│ ├── __init__.py
│ ├── lookuptable.csv
│ └── state_lattice_planner.py
├── VisibilityRoadMap
│ ├── __init__.py
│ ├── geometry.py
│ └── visibility_road_map.py
├── VoronoiRoadMap
│ ├── dijkstra_search.py
│ ├── kdtree.py
│ └── voronoi_road_map.py
└── __init__.py
├── PathTracking
├── cgmres_nmpc
│ ├── Figure_1.png
│ ├── Figure_2.png
│ ├── Figure_3.png
│ ├── Figure_4.png
│ ├── cgmres_nmpc.ipynb
│ └── cgmres_nmpc.py
├── lqr_speed_steer_control
│ └── lqr_speed_steer_control.py
├── lqr_steer_control
│ ├── __init__.py
│ └── lqr_steer_control.py
├── model_predictive_speed_and_steer_control
│ ├── Model_predictive_speed_and_steering_control.ipynb
│ └── model_predictive_speed_and_steer_control.py
├── move_to_pose
│ └── move_to_pose.py
├── pure_pursuit
│ └── pure_pursuit.py
├── rear_wheel_feedback
│ ├── Figure_2.png
│ └── rear_wheel_feedback.py
└── stanley_controller
│ └── stanley_controller.py
├── README.md
├── SLAM
├── EKFSLAM
│ ├── animation.png
│ ├── ekf_slam.ipynb
│ └── ekf_slam.py
├── FastSLAM1
│ ├── FastSLAM1.ipynb
│ ├── animation.png
│ └── fast_slam1.py
├── FastSLAM2
│ └── fast_slam2.py
├── GraphBasedSLAM
│ ├── LaTeX
│ │ ├── .gitignore
│ │ ├── graphSLAM.bib
│ │ └── graphSLAM_formulation.tex
│ ├── data
│ │ ├── README.rst
│ │ └── input_INTEL.g2o
│ ├── graphSLAM_SE2_example.ipynb
│ ├── graphSLAM_doc.ipynb
│ ├── graphSLAM_formulation.pdf
│ ├── graph_based_slam.py
│ ├── graphslam
│ │ ├── __init__.py
│ │ ├── edge
│ │ │ ├── __init__.py
│ │ │ └── edge_odometry.py
│ │ ├── graph.py
│ │ ├── load.py
│ │ ├── pose
│ │ │ ├── __init__.py
│ │ │ └── se2.py
│ │ ├── util.py
│ │ └── vertex.py
│ └── images
│ │ └── Graph_SLAM_optimization.gif
├── __init__.py
└── iterative_closest_point
│ └── iterative_closest_point.py
├── _config.yml
├── appveyor.yml
├── docs
├── Makefile
├── README.md
├── _static
│ └── .gitkeep
├── conf.py
├── getting_started.rst
├── index.rst
├── jupyternotebook2rst.py
├── make.bat
├── modules
│ ├── FastSLAM1.rst
│ ├── FastSLAM1_files
│ │ ├── FastSLAM1_12_0.png
│ │ ├── FastSLAM1_12_1.png
│ │ └── FastSLAM1_1_0.png
│ ├── Kalmanfilter_basics.rst
│ ├── Kalmanfilter_basics_2.rst
│ ├── Kalmanfilter_basics_2_files
│ │ └── Kalmanfilter_basics_2_5_0.png
│ ├── Kalmanfilter_basics_files
│ │ ├── Kalmanfilter_basics_14_1.png
│ │ ├── Kalmanfilter_basics_16_0.png
│ │ ├── Kalmanfilter_basics_19_1.png
│ │ ├── Kalmanfilter_basics_21_1.png
│ │ ├── Kalmanfilter_basics_22_0.png
│ │ └── Kalmanfilter_basics_28_1.png
│ ├── Model_predictive_speed_and_steering_control.rst
│ ├── Planar_Two_Link_IK.rst
│ ├── Planar_Two_Link_IK_files
│ │ ├── Planar_Two_Link_IK_12_0.png
│ │ ├── Planar_Two_Link_IK_5_0.png
│ │ ├── Planar_Two_Link_IK_7_0.png
│ │ └── Planar_Two_Link_IK_9_0.png
│ ├── aerial_navigation.rst
│ ├── appendix.rst
│ ├── arm_navigation.rst
│ ├── cgmres_nmpc.rst
│ ├── cgmres_nmpc_files
│ │ ├── cgmres_nmpc_1_0.png
│ │ ├── cgmres_nmpc_2_0.png
│ │ ├── cgmres_nmpc_3_0.png
│ │ └── cgmres_nmpc_4_0.png
│ ├── extended_kalman_filter_localization.rst
│ ├── extended_kalman_filter_localization_files
│ │ └── extended_kalman_filter_localization_1_0.png
│ ├── introduction.rst
│ ├── localization.rst
│ ├── mapping.rst
│ ├── path_planning.rst
│ ├── path_tracking.rst
│ ├── rocket_powered_landing.rst
│ ├── rrt_star.rst
│ ├── rrt_star_files
│ │ └── rrt_star_1_0.png
│ └── slam.rst
└── notebook_template.ipynb
├── environment.yml
├── icon.png
├── mypy.ini
├── requirements.txt
├── rundiffstylecheck.sh
├── runtests.sh
├── tests
├── .gitkeep
├── test_LQR_planner.py
├── test_a_star.py
├── test_batch_informed_rrt_star.py
├── test_bezier_path.py
├── test_cgmres_nmpc.py
├── test_circle_fitting.py
├── test_closed_loop_rrt_star_car.py
├── test_dijkstra.py
├── test_drone_3d_trajectory_following.py
├── test_dubins_path_planning.py
├── test_dynamic_window_approach.py
├── test_ekf_slam.py
├── test_eta3_spline_path.py
├── test_extended_kalman_filter.py
├── test_fast_slam1.py
├── test_fast_slam2.py
├── test_frenet_optimal_trajectory.py
├── test_gaussian_grid_map.py
├── test_graph_based_slam.py
├── test_grid_based_sweep_coverage_path_planner.py
├── test_grid_map_lib.py
├── test_histogram_filter.py
├── test_hybrid_a_star.py
├── test_informed_rrt_star.py
├── test_inverted_pendulum_mpc_control.py
├── test_iterative_closest_point.py
├── test_kmeans_clustering.py
├── test_lqr_rrt_star.py
├── test_lqr_speed_steer_control.py
├── test_lqr_steer_control.py
├── test_model_predictive_speed_and_steer_control.py
├── test_move_to_pose.py
├── test_n_joint_arm_to_point_control.py
├── test_particle_filter.py
├── test_potential_field_planning.py
├── test_probabilistic_road_map.py
├── test_pure_pursuit.py
├── test_quintic_polynomials_planner.py
├── test_raycasting_grid_map.py
├── test_rear_wheel_feedback.py
├── test_reeds_shepp_path_planning.py
├── test_rocket_powered_landing.py
├── test_rrt.py
├── test_rrt_dubins.py
├── test_rrt_star.py
├── test_rrt_star_dubins.py
├── test_rrt_star_reeds_shepp.py
├── test_stanley_controller.py
├── test_state_lattice_planner.py
├── test_two_joint_arm_to_point_control.py
├── test_unscented_kalman_filter.py
├── test_visibility_road_map_planner.py
└── test_voronoi_road_map_planner.py
└── users_comments.md
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | github: AtsushiSakai
3 | patreon: myenigma
4 | custom: https://www.paypal.me/myenigmapay/
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **Expected behavior**
14 | A clear and concise description of what you expected to happen.
15 |
16 | **Screenshots**
17 | If applicable, add screenshots to help explain your problem.
18 |
19 | **Desktop (please complete the following information):**
20 | - Python version (This repo only supports Python 3.7.x or higher).
21 | - Each library version
22 |
--------------------------------------------------------------------------------
/.github/workflows/Linux_CI.yml:
--------------------------------------------------------------------------------
1 | name: Linux_CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 | strategy:
10 | matrix:
11 | python-version: ['3.8']
12 |
13 | name: Python ${{ matrix.python-version }} CI
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 | - run: git fetch --prune --unshallow
18 |
19 | - name: Setup python
20 | uses: actions/setup-python@v2
21 | with:
22 | python-version: ${{ matrix.python-version }}
23 | - name: Install dependencies
24 | run: |
25 | python -m pip install --upgrade pip
26 | pip install -r requirements.txt
27 | - name: install coverage
28 | run: pip install coverage
29 | - name: install mypy
30 | run: pip install mypy
31 | - name: install pycodestyle
32 | run: pip install pycodestyle
33 | - name: mypy check
34 | run: |
35 | find AerialNavigation -name "*.py" | xargs mypy
36 | find ArmNavigation -name "*.py" | xargs mypy
37 | find Bipedal -name "*.py" | xargs mypy
38 | find InvertedPendulumCart -name "*.py" | xargs mypy
39 | find Localization -name "*.py" | xargs mypy
40 | find Mapping -name "*.py" | xargs mypy
41 | find PathPlanning -name "*.py" | xargs mypy
42 | find PathTracking -name "*.py" | xargs mypy
43 | find SLAM -name "*.py" | xargs mypy
44 | - name: do diff style check
45 | run: bash rundiffstylecheck.sh
46 | - name: do all unit tests
47 | run: bash runtests.sh
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/.github/workflows/MacOS_CI.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: MacOS_CI
4 |
5 | # Controls when the action will run. Triggers the workflow on push or pull request
6 | # events but only for the master branch
7 | on: [push, pull_request]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | strategy:
13 | matrix:
14 | python-version: [ '3.8' ]
15 | name: Python ${{ matrix.python-version }} CI
16 | steps:
17 | - uses: actions/checkout@v2
18 | - run: git fetch --prune --unshallow
19 |
20 | - name: Setup python
21 | uses: actions/setup-python@v2
22 | with:
23 | python-version: ${{ matrix.python-version }}
24 |
25 | - name: Install dependencies
26 | run: |
27 | python -m pip install --upgrade pip
28 | pip install -r requirements.txt
29 | - name: install coverage
30 | run: pip install coverage
31 |
32 | - name: install mypy
33 | run: pip install mypy
34 |
35 | - name: install pycodestyle
36 | run: pip install pycodestyle
37 |
38 | - name: mypy check
39 | run: |
40 | find AerialNavigation -name "*.py" | xargs mypy
41 | find ArmNavigation -name "*.py" | xargs mypy
42 | find Bipedal -name "*.py" | xargs mypy
43 | find InvertedPendulumCart -name "*.py" | xargs mypy
44 | find Localization -name "*.py" | xargs mypy
45 | find Mapping -name "*.py" | xargs mypy
46 | find PathPlanning -name "*.py" | xargs mypy
47 | find PathTracking -name "*.py" | xargs mypy
48 | find SLAM -name "*.py" | xargs mypy
49 |
50 | - name: do diff style check
51 | run: bash rundiffstylecheck.sh
52 |
53 | - name: do all unit tests
54 | run: bash runtests.sh
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.csv
2 | *.gif
3 | *.g2o
4 |
5 | # Byte-compiled / optimized / DLL files
6 | __pycache__/
7 | *.py[cod]
8 | *$py.class
9 | _build/
10 | .idea/
11 |
12 | # C extensions
13 | *.so
14 |
15 | # Distribution / packaging
16 | .Python
17 | env/
18 | build/
19 | develop-eggs/
20 | dist/
21 | downloads/
22 | eggs/
23 | .eggs/
24 | lib/
25 | lib64/
26 | parts/
27 | sdist/
28 | var/
29 | *.egg-info/
30 | .installed.cfg
31 | *.egg
32 |
33 | .DS_Store
34 |
35 | # PyInstaller
36 | # Usually these files are written by a python script from a template
37 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
38 | *.manifest
39 | *.spec
40 |
41 | # Installer logs
42 | pip-log.txt
43 | pip-delete-this-directory.txt
44 |
45 | # Unit test / coverage reports
46 | htmlcov/
47 | .tox/
48 | .coverage
49 | .coverage.*
50 | .cache
51 | nosetests.xml
52 | coverage.xml
53 | *,cover
54 | .hypothesis/
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | #Ipython Notebook
70 | .ipynb_checkpoints
71 |
72 | matplotrecorder/*
73 | .vscode/settings.json
74 |
--------------------------------------------------------------------------------
/.lgtm.yml:
--------------------------------------------------------------------------------
1 | extraction:
2 | python:
3 | python_setup:
4 | version: 3
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 |
3 | matrix:
4 | include:
5 | - os: linux
6 |
7 | python:
8 | - 3.8
9 |
10 | before_install:
11 | - sudo apt-get update
12 | - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh
13 | - chmod +x miniconda.sh
14 | - bash miniconda.sh -b -p $HOME/miniconda
15 | - export PATH="$HOME/miniconda/bin:$PATH"
16 | - hash -r
17 | - conda config --set always_yes yes --set changeps1 no
18 | - conda update -q conda
19 | # Useful for debugging any issues with conda
20 | - conda info -a
21 | # - conda install python==3.6.8
22 |
23 | install:
24 | - conda install numpy==1.15
25 | - conda install scipy
26 | - conda install matplotlib
27 | - conda install pandas
28 | - pip install cvxpy
29 | - conda install coveralls
30 |
31 | script:
32 | - python --version
33 | - ./runtests.sh
34 |
35 | after_success:
36 | - coveralls
37 |
--------------------------------------------------------------------------------
/AerialNavigation/drone_3d_trajectory_following/Quadrotor.py:
--------------------------------------------------------------------------------
1 | """
2 | Class for plotting a quadrotor
3 |
4 | Author: Daniel Ingram (daniel-s-ingram)
5 | """
6 |
7 | from math import cos, sin
8 | import numpy as np
9 | import matplotlib.pyplot as plt
10 |
11 | class Quadrotor():
12 | def __init__(self, x=0, y=0, z=0, roll=0, pitch=0, yaw=0, size=0.25, show_animation=True):
13 | self.p1 = np.array([size / 2, 0, 0, 1]).T
14 | self.p2 = np.array([-size / 2, 0, 0, 1]).T
15 | self.p3 = np.array([0, size / 2, 0, 1]).T
16 | self.p4 = np.array([0, -size / 2, 0, 1]).T
17 |
18 | self.x_data = []
19 | self.y_data = []
20 | self.z_data = []
21 | self.show_animation = show_animation
22 |
23 | if self.show_animation:
24 | plt.ion()
25 | fig = plt.figure()
26 | # for stopping simulation with the esc key.
27 | fig.canvas.mpl_connect('key_release_event',
28 | lambda event: [exit(0) if event.key == 'escape' else None])
29 |
30 | self.ax = fig.add_subplot(111, projection='3d')
31 |
32 | self.update_pose(x, y, z, roll, pitch, yaw)
33 |
34 | def update_pose(self, x, y, z, roll, pitch, yaw):
35 | self.x = x
36 | self.y = y
37 | self.z = z
38 | self.roll = roll
39 | self.pitch = pitch
40 | self.yaw = yaw
41 | self.x_data.append(x)
42 | self.y_data.append(y)
43 | self.z_data.append(z)
44 |
45 | if self.show_animation:
46 | self.plot()
47 |
48 | def transformation_matrix(self):
49 | x = self.x
50 | y = self.y
51 | z = self.z
52 | roll = self.roll
53 | pitch = self.pitch
54 | yaw = self.yaw
55 | return np.array(
56 | [[cos(yaw) * cos(pitch), -sin(yaw) * cos(roll) + cos(yaw) * sin(pitch) * sin(roll), sin(yaw) * sin(roll) + cos(yaw) * sin(pitch) * cos(roll), x],
57 | [sin(yaw) * cos(pitch), cos(yaw) * cos(roll) + sin(yaw) * sin(pitch)
58 | * sin(roll), -cos(yaw) * sin(roll) + sin(yaw) * sin(pitch) * cos(roll), y],
59 | [-sin(pitch), cos(pitch) * sin(roll), cos(pitch) * cos(yaw), z]
60 | ])
61 |
62 | def plot(self): # pragma: no cover
63 | T = self.transformation_matrix()
64 |
65 | p1_t = np.matmul(T, self.p1)
66 | p2_t = np.matmul(T, self.p2)
67 | p3_t = np.matmul(T, self.p3)
68 | p4_t = np.matmul(T, self.p4)
69 |
70 | plt.cla()
71 |
72 | self.ax.plot([p1_t[0], p2_t[0], p3_t[0], p4_t[0]],
73 | [p1_t[1], p2_t[1], p3_t[1], p4_t[1]],
74 | [p1_t[2], p2_t[2], p3_t[2], p4_t[2]], 'k.')
75 |
76 | self.ax.plot([p1_t[0], p2_t[0]], [p1_t[1], p2_t[1]],
77 | [p1_t[2], p2_t[2]], 'r-')
78 | self.ax.plot([p3_t[0], p4_t[0]], [p3_t[1], p4_t[1]],
79 | [p3_t[2], p4_t[2]], 'r-')
80 |
81 | self.ax.plot(self.x_data, self.y_data, self.z_data, 'b:')
82 |
83 | plt.xlim(-5, 5)
84 | plt.ylim(-5, 5)
85 | self.ax.set_zlim(0, 10)
86 |
87 | plt.pause(0.001)
88 |
--------------------------------------------------------------------------------
/AerialNavigation/drone_3d_trajectory_following/TrajectoryGenerator.py:
--------------------------------------------------------------------------------
1 | """
2 | Generates a quintic polynomial trajectory.
3 |
4 | Author: Daniel Ingram (daniel-s-ingram)
5 | """
6 |
7 | import numpy as np
8 |
9 | class TrajectoryGenerator():
10 | def __init__(self, start_pos, des_pos, T, start_vel=[0,0,0], des_vel=[0,0,0], start_acc=[0,0,0], des_acc=[0,0,0]):
11 | self.start_x = start_pos[0]
12 | self.start_y = start_pos[1]
13 | self.start_z = start_pos[2]
14 |
15 | self.des_x = des_pos[0]
16 | self.des_y = des_pos[1]
17 | self.des_z = des_pos[2]
18 |
19 | self.start_x_vel = start_vel[0]
20 | self.start_y_vel = start_vel[1]
21 | self.start_z_vel = start_vel[2]
22 |
23 | self.des_x_vel = des_vel[0]
24 | self.des_y_vel = des_vel[1]
25 | self.des_z_vel = des_vel[2]
26 |
27 | self.start_x_acc = start_acc[0]
28 | self.start_y_acc = start_acc[1]
29 | self.start_z_acc = start_acc[2]
30 |
31 | self.des_x_acc = des_acc[0]
32 | self.des_y_acc = des_acc[1]
33 | self.des_z_acc = des_acc[2]
34 |
35 | self.T = T
36 |
37 | def solve(self):
38 | A = np.array(
39 | [[0, 0, 0, 0, 0, 1],
40 | [self.T**5, self.T**4, self.T**3, self.T**2, self.T, 1],
41 | [0, 0, 0, 0, 1, 0],
42 | [5*self.T**4, 4*self.T**3, 3*self.T**2, 2*self.T, 1, 0],
43 | [0, 0, 0, 2, 0, 0],
44 | [20*self.T**3, 12*self.T**2, 6*self.T, 2, 0, 0]
45 | ])
46 |
47 | b_x = np.array(
48 | [[self.start_x],
49 | [self.des_x],
50 | [self.start_x_vel],
51 | [self.des_x_vel],
52 | [self.start_x_acc],
53 | [self.des_x_acc]
54 | ])
55 |
56 | b_y = np.array(
57 | [[self.start_y],
58 | [self.des_y],
59 | [self.start_y_vel],
60 | [self.des_y_vel],
61 | [self.start_y_acc],
62 | [self.des_y_acc]
63 | ])
64 |
65 | b_z = np.array(
66 | [[self.start_z],
67 | [self.des_z],
68 | [self.start_z_vel],
69 | [self.des_z_vel],
70 | [self.start_z_acc],
71 | [self.des_z_acc]
72 | ])
73 |
74 | self.x_c = np.linalg.solve(A, b_x)
75 | self.y_c = np.linalg.solve(A, b_y)
76 | self.z_c = np.linalg.solve(A, b_z)
--------------------------------------------------------------------------------
/AerialNavigation/rocket_powered_landing/figure.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/AerialNavigation/rocket_powered_landing/figure.png
--------------------------------------------------------------------------------
/ArmNavigation/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/ArmNavigation/__init__.py
--------------------------------------------------------------------------------
/ArmNavigation/n_joint_arm_3d/__init__py.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/ArmNavigation/n_joint_arm_3d/__init__py.py
--------------------------------------------------------------------------------
/ArmNavigation/n_joint_arm_3d/random_forward_kinematics.py:
--------------------------------------------------------------------------------
1 | """
2 | Forward Kinematics for an n-link arm in 3D
3 | Author: Takayuki Murooka (takayuki5168)
4 | """
5 | import math
6 | from NLinkArm3d import NLinkArm
7 | import random
8 |
9 |
10 | def random_val(min_val, max_val):
11 | return min_val + random.random() * (max_val - min_val)
12 |
13 |
14 | if __name__ == "__main__":
15 | print("Start solving Forward Kinematics 10 times")
16 |
17 | # init NLinkArm with Denavit-Hartenberg parameters of PR2
18 | n_link_arm = NLinkArm([[0., -math.pi / 2, .1, 0.],
19 | [math.pi / 2, math.pi / 2, 0., 0.],
20 | [0., -math.pi / 2, 0., .4],
21 | [0., math.pi / 2, 0., 0.],
22 | [0., -math.pi / 2, 0., .321],
23 | [0., math.pi / 2, 0., 0.],
24 | [0., 0., 0., 0.]])
25 |
26 | # execute FK 10 times
27 | for i in range(10):
28 | n_link_arm.set_joint_angles(
29 | [random_val(-1, 1) for j in range(len(n_link_arm.link_list))])
30 |
31 | ee_pose = n_link_arm.forward_kinematics(plot=True)
32 |
--------------------------------------------------------------------------------
/ArmNavigation/n_joint_arm_3d/random_inverse_kinematics.py:
--------------------------------------------------------------------------------
1 | """
2 | Inverse Kinematics for an n-link arm in 3D
3 | Author: Takayuki Murooka (takayuki5168)
4 | """
5 | import math
6 | from NLinkArm3d import NLinkArm
7 | import random
8 |
9 |
10 | def random_val(min_val, max_val):
11 | return min_val + random.random() * (max_val - min_val)
12 |
13 |
14 | if __name__ == "__main__":
15 | print("Start solving Inverse Kinematics 10 times")
16 |
17 | # init NLinkArm with Denavit-Hartenberg parameters of PR2
18 | n_link_arm = NLinkArm([[0., -math.pi / 2, .1, 0.],
19 | [math.pi / 2, math.pi / 2, 0., 0.],
20 | [0., -math.pi / 2, 0., .4],
21 | [0., math.pi / 2, 0., 0.],
22 | [0., -math.pi / 2, 0., .321],
23 | [0., math.pi / 2, 0., 0.],
24 | [0., 0., 0., 0.]])
25 |
26 | # execute IK 10 times
27 | for i in range(10):
28 | n_link_arm.inverse_kinematics([random_val(-0.5, 0.5),
29 | random_val(-0.5, 0.5),
30 | random_val(-0.5, 0.5),
31 | random_val(-0.5, 0.5),
32 | random_val(-0.5, 0.5),
33 | random_val(-0.5, 0.5)], plot=True)
34 |
--------------------------------------------------------------------------------
/ArmNavigation/n_joint_arm_to_point_control/NLinkArm.py:
--------------------------------------------------------------------------------
1 | """
2 | Class for controlling and plotting an arm with an arbitrary number of links.
3 |
4 | Author: Daniel Ingram
5 | """
6 |
7 | import numpy as np
8 | import matplotlib.pyplot as plt
9 |
10 |
11 | class NLinkArm(object):
12 | def __init__(self, link_lengths, joint_angles, goal, show_animation):
13 | self.show_animation = show_animation
14 | self.n_links = len(link_lengths)
15 | if self.n_links != len(joint_angles):
16 | raise ValueError()
17 |
18 | self.link_lengths = np.array(link_lengths)
19 | self.joint_angles = np.array(joint_angles)
20 | self.points = [[0, 0] for _ in range(self.n_links + 1)]
21 |
22 | self.lim = sum(link_lengths)
23 | self.goal = np.array(goal).T
24 |
25 | if show_animation: # pragma: no cover
26 | self.fig = plt.figure()
27 | self.fig.canvas.mpl_connect('button_press_event', self.click)
28 |
29 | plt.ion()
30 | plt.show()
31 |
32 | self.update_points()
33 |
34 | def update_joints(self, joint_angles):
35 | self.joint_angles = joint_angles
36 |
37 | self.update_points()
38 |
39 | def update_points(self):
40 | for i in range(1, self.n_links + 1):
41 | self.points[i][0] = self.points[i - 1][0] + \
42 | self.link_lengths[i - 1] * \
43 | np.cos(np.sum(self.joint_angles[:i]))
44 | self.points[i][1] = self.points[i - 1][1] + \
45 | self.link_lengths[i - 1] * \
46 | np.sin(np.sum(self.joint_angles[:i]))
47 |
48 | self.end_effector = np.array(self.points[self.n_links]).T
49 | if self.show_animation: # pragma: no cover
50 | self.plot()
51 |
52 | def plot(self): # pragma: no cover
53 | plt.cla()
54 | # for stopping simulation with the esc key.
55 | plt.gcf().canvas.mpl_connect('key_release_event',
56 | lambda event: [exit(0) if event.key == 'escape' else None])
57 |
58 | for i in range(self.n_links + 1):
59 | if i is not self.n_links:
60 | plt.plot([self.points[i][0], self.points[i + 1][0]],
61 | [self.points[i][1], self.points[i + 1][1]], 'r-')
62 | plt.plot(self.points[i][0], self.points[i][1], 'ko')
63 |
64 | plt.plot(self.goal[0], self.goal[1], 'gx')
65 |
66 | plt.plot([self.end_effector[0], self.goal[0]], [
67 | self.end_effector[1], self.goal[1]], 'g--')
68 |
69 | plt.xlim([-self.lim, self.lim])
70 | plt.ylim([-self.lim, self.lim])
71 | plt.draw()
72 | plt.pause(0.0001)
73 |
74 | def click(self, event):
75 | self.goal = np.array([event.xdata, event.ydata]).T
76 | self.plot()
77 |
--------------------------------------------------------------------------------
/ArmNavigation/n_joint_arm_to_point_control/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/ArmNavigation/n_joint_arm_to_point_control/__init__.py
--------------------------------------------------------------------------------
/ArmNavigation/two_joint_arm_to_point_control/two_joint_arm_to_point_control.py:
--------------------------------------------------------------------------------
1 | """
2 | Inverse kinematics of a two-joint arm
3 | Left-click the plot to set the goal position of the end effector
4 |
5 | Author: Daniel Ingram (daniel-s-ingram)
6 | Atsushi Sakai (@Atsushi_twi)
7 |
8 | Ref: P. I. Corke, "Robotics, Vision & Control", Springer 2017, ISBN 978-3-319-54413-7 p102
9 | - [Robotics, Vision and Control \| SpringerLink](https://link.springer.com/book/10.1007/978-3-642-20144-8)
10 |
11 | """
12 |
13 | import matplotlib.pyplot as plt
14 | import numpy as np
15 |
16 |
17 | # Similation parameters
18 | Kp = 15
19 | dt = 0.01
20 |
21 | # Link lengths
22 | l1 = l2 = 1
23 |
24 | # Set initial goal position to the initial end-effector position
25 | x = 2
26 | y = 0
27 |
28 | show_animation = True
29 |
30 | if show_animation:
31 | plt.ion()
32 |
33 |
34 | def two_joint_arm(GOAL_TH=0.0, theta1=0.0, theta2=0.0):
35 | """
36 | Computes the inverse kinematics for a planar 2DOF arm
37 | """
38 | while True:
39 | try:
40 | theta2_goal = np.arccos(
41 | (x**2 + y**2 - l1**2 - l2**2) / (2 * l1 * l2))
42 | theta1_goal = np.math.atan2(y, x) - np.math.atan2(l2 *
43 | np.sin(theta2_goal), (l1 + l2 * np.cos(theta2_goal)))
44 |
45 | if theta1_goal < 0:
46 | theta2_goal = -theta2_goal
47 | theta1_goal = np.math.atan2(
48 | y, x) - np.math.atan2(l2 * np.sin(theta2_goal), (l1 + l2 * np.cos(theta2_goal)))
49 |
50 | theta1 = theta1 + Kp * ang_diff(theta1_goal, theta1) * dt
51 | theta2 = theta2 + Kp * ang_diff(theta2_goal, theta2) * dt
52 | except ValueError as e:
53 | print("Unreachable goal")
54 |
55 | wrist = plot_arm(theta1, theta2, x, y)
56 |
57 | # check goal
58 | d2goal = np.hypot(wrist[0] - x, wrist[1] - y)
59 |
60 | if abs(d2goal) < GOAL_TH and x is not None:
61 | return theta1, theta2
62 |
63 |
64 | def plot_arm(theta1, theta2, x, y): # pragma: no cover
65 | shoulder = np.array([0, 0])
66 | elbow = shoulder + np.array([l1 * np.cos(theta1), l1 * np.sin(theta1)])
67 | wrist = elbow + \
68 | np.array([l2 * np.cos(theta1 + theta2), l2 * np.sin(theta1 + theta2)])
69 |
70 | if show_animation:
71 | plt.cla()
72 |
73 | plt.plot([shoulder[0], elbow[0]], [shoulder[1], elbow[1]], 'k-')
74 | plt.plot([elbow[0], wrist[0]], [elbow[1], wrist[1]], 'k-')
75 |
76 | plt.plot(shoulder[0], shoulder[1], 'ro')
77 | plt.plot(elbow[0], elbow[1], 'ro')
78 | plt.plot(wrist[0], wrist[1], 'ro')
79 |
80 | plt.plot([wrist[0], x], [wrist[1], y], 'g--')
81 | plt.plot(x, y, 'g*')
82 |
83 | plt.xlim(-2, 2)
84 | plt.ylim(-2, 2)
85 |
86 | plt.show()
87 | plt.pause(dt)
88 |
89 | return wrist
90 |
91 |
92 | def ang_diff(theta1, theta2):
93 | # Returns the difference between two angles in the range -pi to +pi
94 | return (theta1 - theta2 + np.pi) % (2 * np.pi) - np.pi
95 |
96 |
97 | def click(event): # pragma: no cover
98 | global x, y
99 | x = event.xdata
100 | y = event.ydata
101 |
102 |
103 | def animation():
104 | from random import random
105 | global x, y
106 | theta1 = theta2 = 0.0
107 | for i in range(5):
108 | x = 2.0 * random() - 1.0
109 | y = 2.0 * random() - 1.0
110 | theta1, theta2 = two_joint_arm(
111 | GOAL_TH=0.01, theta1=theta1, theta2=theta2)
112 |
113 |
114 | def main(): # pragma: no cover
115 | fig = plt.figure()
116 | fig.canvas.mpl_connect("button_press_event", click)
117 | # for stopping simulation with the esc key.
118 | fig.canvas.mpl_connect('key_release_event',
119 | lambda event: [exit(0) if event.key == 'escape' else None])
120 | two_joint_arm()
121 |
122 |
123 | if __name__ == "__main__":
124 | # animation()
125 | main()
126 |
--------------------------------------------------------------------------------
/Bipedal/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/Bipedal/__init__.py
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Python
2 |
3 | :+1::tada: First of off, thanks very much for taking the time to contribute! :tada::+1:
4 |
5 | The following is a set of guidelines for contributing to PythonRobotics.
6 |
7 | These are mostly guidelines, not rules.
8 |
9 | Use your best judgment, and feel free to propose changes to this document in a pull request.
10 |
11 | # Before contributing
12 |
13 | ## Taking a look at the paper.
14 |
15 | Please check this paper to understand the philosophy of this project.
16 |
17 | - [\[1808\.10703\] PythonRobotics: a Python code collection of robotics algorithms](https://arxiv.org/abs/1808.10703) ([BibTeX](https://github.com/AtsushiSakai/PythonRoboticsPaper/blob/master/python_robotics.bib))
18 |
19 | ## Check your Python version.
20 |
21 | We only accept a PR for Python 3.6.x or higher.
22 |
23 | We will not accept a PR for Python 2.x.
24 |
--------------------------------------------------------------------------------
/Introduction/introduction.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Introduction"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": []
14 | },
15 | {
16 | "cell_type": "markdown",
17 | "metadata": {},
18 | "source": [
19 | "## Ref"
20 | ]
21 | }
22 | ],
23 | "metadata": {
24 | "kernelspec": {
25 | "display_name": "Python 3",
26 | "language": "python",
27 | "name": "python3"
28 | },
29 | "language_info": {
30 | "codemirror_mode": {
31 | "name": "ipython",
32 | "version": 3
33 | },
34 | "file_extension": ".py",
35 | "mimetype": "text/x-python",
36 | "name": "python",
37 | "nbconvert_exporter": "python",
38 | "pygments_lexer": "ipython3",
39 | "version": "3.6.7"
40 | }
41 | },
42 | "nbformat": 4,
43 | "nbformat_minor": 2
44 | }
45 |
--------------------------------------------------------------------------------
/InvertedPendulumCart/inverted_pendulum_mpc_control.py:
--------------------------------------------------------------------------------
1 | """
2 | Inverted Pendulum MPC control
3 | author: Atsushi Sakai
4 | """
5 |
6 | import matplotlib.pyplot as plt
7 | import numpy as np
8 | import math
9 | import time
10 | import cvxpy
11 |
12 | # Model parameters
13 |
14 | l_bar = 2.0 # length of bar
15 | M = 1.0 # [kg]
16 | m = 0.3 # [kg]
17 | g = 9.8 # [m/s^2]
18 |
19 | Q = np.diag([0.0, 1.0, 1.0, 0.0])
20 | R = np.diag([0.01])
21 | nx = 4 # number of state
22 | nu = 1 # number of input
23 | T = 30 # Horizon length
24 | delta_t = 0.1 # time tick
25 |
26 | animation = True
27 |
28 |
29 | def main():
30 | x0 = np.array([
31 | [0.0],
32 | [0.0],
33 | [0.3],
34 | [0.0]
35 | ])
36 |
37 | x = np.copy(x0)
38 |
39 | for i in range(50):
40 |
41 | # calc control input
42 | optimized_x, optimized_delta_x, optimized_theta, optimized_delta_theta, optimized_input = mpc_control(x)
43 |
44 | # get input
45 | u = optimized_input[0]
46 |
47 | # simulate inverted pendulum cart
48 | x = simulation(x, u)
49 |
50 | if animation:
51 | plt.clf()
52 | px = float(x[0])
53 | theta = float(x[2])
54 | plot_cart(px, theta)
55 | plt.xlim([-5.0, 2.0])
56 | plt.pause(0.001)
57 |
58 |
59 | def simulation(x, u):
60 | A, B = get_model_matrix()
61 |
62 | x = np.dot(A, x) + np.dot(B, u)
63 |
64 | return x
65 |
66 |
67 | def mpc_control(x0):
68 | x = cvxpy.Variable((nx, T + 1))
69 | u = cvxpy.Variable((nu, T))
70 |
71 | A, B = get_model_matrix()
72 |
73 | cost = 0.0
74 | constr = []
75 | for t in range(T):
76 | cost += cvxpy.quad_form(x[:, t + 1], Q)
77 | cost += cvxpy.quad_form(u[:, t], R)
78 | constr += [x[:, t + 1] == A * x[:, t] + B * u[:, t]]
79 |
80 | constr += [x[:, 0] == x0[:, 0]]
81 | prob = cvxpy.Problem(cvxpy.Minimize(cost), constr)
82 |
83 | start = time.time()
84 | prob.solve(verbose=False)
85 | elapsed_time = time.time() - start
86 | print("calc time:{0} [sec]".format(elapsed_time))
87 |
88 | if prob.status == cvxpy.OPTIMAL:
89 | ox = get_nparray_from_matrix(x.value[0, :])
90 | dx = get_nparray_from_matrix(x.value[1, :])
91 | theta = get_nparray_from_matrix(x.value[2, :])
92 | dtheta = get_nparray_from_matrix(x.value[3, :])
93 |
94 | ou = get_nparray_from_matrix(u.value[0, :])
95 |
96 | return ox, dx, theta, dtheta, ou
97 |
98 |
99 | def get_nparray_from_matrix(x):
100 | """
101 | get build-in list from matrix
102 | """
103 | return np.array(x).flatten()
104 |
105 |
106 | def get_model_matrix():
107 | A = np.array([
108 | [0.0, 1.0, 0.0, 0.0],
109 | [0.0, 0.0, m * g / M, 0.0],
110 | [0.0, 0.0, 0.0, 1.0],
111 | [0.0, 0.0, g * (M + m) / (l_bar * M), 0.0]
112 | ])
113 | A = np.eye(nx) + delta_t * A
114 |
115 | B = np.array([
116 | [0.0],
117 | [1.0 / M],
118 | [0.0],
119 | [1.0 / (l_bar * M)]
120 | ])
121 | B = delta_t * B
122 |
123 | return A, B
124 |
125 |
126 | def flatten(a):
127 | return np.array(a).flatten()
128 |
129 |
130 | def plot_cart(xt, theta):
131 | cart_w = 1.0
132 | cart_h = 0.5
133 | radius = 0.1
134 |
135 | cx = np.array([-cart_w / 2.0, cart_w / 2.0, cart_w /
136 | 2.0, -cart_w / 2.0, -cart_w / 2.0])
137 | cy = np.array([0.0, 0.0, cart_h, cart_h, 0.0])
138 | cy += radius * 2.0
139 |
140 | cx = cx + xt
141 |
142 | bx = np.array([0.0, l_bar * math.sin(-theta)])
143 | bx += xt
144 | by = np.array([cart_h, l_bar * math.cos(-theta) + cart_h])
145 | by += radius * 2.0
146 |
147 | angles = np.arange(0.0, math.pi * 2.0, math.radians(3.0))
148 | ox = np.array([radius * math.cos(a) for a in angles])
149 | oy = np.array([radius * math.sin(a) for a in angles])
150 |
151 | rwx = np.copy(ox) + cart_w / 4.0 + xt
152 | rwy = np.copy(oy) + radius
153 | lwx = np.copy(ox) - cart_w / 4.0 + xt
154 | lwy = np.copy(oy) + radius
155 |
156 | wx = np.copy(ox) + bx[-1]
157 | wy = np.copy(oy) + by[-1]
158 |
159 | plt.plot(flatten(cx), flatten(cy), "-b")
160 | plt.plot(flatten(bx), flatten(by), "-k")
161 | plt.plot(flatten(rwx), flatten(rwy), "-k")
162 | plt.plot(flatten(lwx), flatten(lwy), "-k")
163 | plt.plot(flatten(wx), flatten(wy), "-k")
164 | plt.title("x:" + str(round(xt, 2)) + ",theta:" +
165 | str(round(math.degrees(theta), 2)))
166 |
167 | plt.axis("equal")
168 |
169 |
170 | if __name__ == '__main__':
171 | main()
172 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Atsushi Sakai
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Localization/bayes_filter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/Localization/bayes_filter.png
--------------------------------------------------------------------------------
/Localization/extended_kalman_filter/ekf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/Localization/extended_kalman_filter/ekf.png
--------------------------------------------------------------------------------
/Localization/particle_filter/particle_filter.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {
6 | "collapsed": true,
7 | "pycharm": {
8 | "name": "#%% md\n"
9 | }
10 | },
11 | "source": [
12 | "# Particle Filter Localization\n",
13 | "\n"
14 | ]
15 | },
16 | {
17 | "cell_type": "markdown",
18 | "source": [
19 | "## How to calculate covariance matrix from particles\n",
20 | "\n",
21 | "The covariance matrix $\\Xi$ from particle information is calculated by the following equation: \n",
22 | "\n",
23 | "$\\Xi_{j,k}=\\frac{1}{1-\\sum^N_{i=1}(w^i)^2}\\sum^N_{i=1}w^i(x^i_j-\\mu_j)(x^i_k-\\mu_k)$\n",
24 | "\n",
25 | "- $\\Xi_{j,k}$ is covariance matrix element at row $i$ and column $k$.\n",
26 | "\n",
27 | "- $w^i$ is the weight of the $i$ th particle. \n",
28 | "\n",
29 | "- $x^i_j$ is the $j$ th state of the $i$ th particle. \n",
30 | "\n",
31 | "- $\\mu_j$ is the $j$ th mean state of particles.\n",
32 | "\n",
33 | "Ref:\n",
34 | "\n",
35 | "- [Improving the particle filter in high dimensions using conjugate artificial process noise](https://arxiv.org/pdf/1801.07000.pdf)\n"
36 | ],
37 | "metadata": {
38 | "collapsed": false
39 | }
40 | }
41 | ],
42 | "metadata": {
43 | "kernelspec": {
44 | "display_name": "Python 3",
45 | "language": "python",
46 | "name": "python3"
47 | },
48 | "language_info": {
49 | "codemirror_mode": {
50 | "name": "ipython",
51 | "version": 2
52 | },
53 | "file_extension": ".py",
54 | "mimetype": "text/x-python",
55 | "name": "python",
56 | "nbconvert_exporter": "python",
57 | "pygments_lexer": "ipython2",
58 | "version": "2.7.6"
59 | },
60 | "pycharm": {
61 | "stem_cell": {
62 | "cell_type": "raw",
63 | "source": [],
64 | "metadata": {
65 | "collapsed": false
66 | }
67 | }
68 | }
69 | },
70 | "nbformat": 4,
71 | "nbformat_minor": 0
72 | }
--------------------------------------------------------------------------------
/Mapping/circle_fitting/circle_fitting.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Object shape recognition with circle fitting
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import matplotlib.pyplot as plt
10 | import math
11 | import random
12 | import numpy as np
13 |
14 | show_animation = True
15 |
16 |
17 | def circle_fitting(x, y):
18 | """
19 | Circle Fitting with least squared
20 | input: point x-y positions
21 | output cxe x center position
22 | cye y center position
23 | re radius of circle
24 | error: prediction error
25 | """
26 |
27 | sumx = sum(x)
28 | sumy = sum(y)
29 | sumx2 = sum([ix ** 2 for ix in x])
30 | sumy2 = sum([iy ** 2 for iy in y])
31 | sumxy = sum([ix * iy for (ix, iy) in zip(x, y)])
32 |
33 | F = np.array([[sumx2, sumxy, sumx],
34 | [sumxy, sumy2, sumy],
35 | [sumx, sumy, len(x)]])
36 |
37 | G = np.array([[-sum([ix ** 3 + ix * iy ** 2 for (ix, iy) in zip(x, y)])],
38 | [-sum([ix ** 2 * iy + iy ** 3 for (ix, iy) in zip(x, y)])],
39 | [-sum([ix ** 2 + iy ** 2 for (ix, iy) in zip(x, y)])]])
40 |
41 | T = np.linalg.inv(F).dot(G)
42 |
43 | cxe = float(T[0] / -2)
44 | cye = float(T[1] / -2)
45 | re = math.sqrt(cxe**2 + cye**2 - T[2])
46 |
47 | error = sum([np.hypot(cxe - ix, cye - iy) - re for (ix, iy) in zip(x, y)])
48 |
49 | return (cxe, cye, re, error)
50 |
51 |
52 | def get_sample_points(cx, cy, cr, angle_reso):
53 | x, y, angle, r = [], [], [], []
54 |
55 | # points sampling
56 | for theta in np.arange(0.0, 2.0 * math.pi, angle_reso):
57 | nx = cx + cr * math.cos(theta)
58 | ny = cy + cr * math.sin(theta)
59 | nangle = math.atan2(ny, nx)
60 | nr = math.hypot(nx, ny) * random.uniform(0.95, 1.05)
61 |
62 | x.append(nx)
63 | y.append(ny)
64 | angle.append(nangle)
65 | r.append(nr)
66 |
67 | # ray casting filter
68 | rx, ry = ray_casting_filter(x, y, angle, r, angle_reso)
69 |
70 | return rx, ry
71 |
72 |
73 | def ray_casting_filter(xl, yl, thetal, rangel, angle_reso):
74 | rx, ry = [], []
75 | rangedb = [float("inf") for _ in range(
76 | int(math.floor((math.pi * 2.0) / angle_reso)) + 1)]
77 |
78 | for i, _ in enumerate(thetal):
79 | angleid = math.floor(thetal[i] / angle_reso)
80 |
81 | if rangedb[angleid] > rangel[i]:
82 | rangedb[angleid] = rangel[i]
83 |
84 | for i, _ in enumerate(rangedb):
85 | t = i * angle_reso
86 | if rangedb[i] != float("inf"):
87 | rx.append(rangedb[i] * math.cos(t))
88 | ry.append(rangedb[i] * math.sin(t))
89 |
90 | return rx, ry
91 |
92 |
93 | def plot_circle(x, y, size, color="-b"): # pragma: no cover
94 | deg = list(range(0, 360, 5))
95 | deg.append(0)
96 | xl = [x + size * math.cos(np.deg2rad(d)) for d in deg]
97 | yl = [y + size * math.sin(np.deg2rad(d)) for d in deg]
98 | plt.plot(xl, yl, color)
99 |
100 |
101 | def main():
102 |
103 | # simulation parameters
104 | simtime = 15.0 # simulation time
105 | dt = 1.0 # time tick
106 |
107 | cx = -2.0 # initial x position of obstacle
108 | cy = -8.0 # initial y position of obstacle
109 | cr = 1.0 # obstacle radious
110 | theta = np.deg2rad(30.0) # obstacle moving direction
111 | angle_reso = np.deg2rad(3.0) # sensor angle resolution
112 |
113 | time = 0.0
114 | while time <= simtime:
115 | time += dt
116 |
117 | cx += math.cos(theta)
118 | cy += math.cos(theta)
119 |
120 | x, y = get_sample_points(cx, cy, cr, angle_reso)
121 |
122 | ex, ey, er, error = circle_fitting(x, y)
123 | print("Error:", error)
124 |
125 | if show_animation: # pragma: no cover
126 | plt.cla()
127 | # for stopping simulation with the esc key.
128 | plt.gcf().canvas.mpl_connect('key_release_event',
129 | lambda event: [exit(0) if event.key == 'escape' else None])
130 | plt.axis("equal")
131 | plt.plot(0.0, 0.0, "*r")
132 | plot_circle(cx, cy, cr)
133 | plt.plot(x, y, "xr")
134 | plot_circle(ex, ey, er, "-r")
135 | plt.pause(dt)
136 |
137 | print("Done")
138 |
139 |
140 | if __name__ == '__main__':
141 | main()
142 |
--------------------------------------------------------------------------------
/Mapping/gaussian_grid_map/gaussian_grid_map.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | 2D gaussian grid map sample
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 | import numpy as np
11 | import matplotlib.pyplot as plt
12 | from scipy.stats import norm
13 |
14 | EXTEND_AREA = 10.0 # [m] grid map extention length
15 |
16 | show_animation = True
17 |
18 |
19 | def generate_gaussian_grid_map(ox, oy, xyreso, std):
20 |
21 | minx, miny, maxx, maxy, xw, yw = calc_grid_map_config(ox, oy, xyreso)
22 |
23 | gmap = [[0.0 for i in range(yw)] for i in range(xw)]
24 |
25 | for ix in range(xw):
26 | for iy in range(yw):
27 |
28 | x = ix * xyreso + minx
29 | y = iy * xyreso + miny
30 |
31 | # Search minimum distance
32 | mindis = float("inf")
33 | for (iox, ioy) in zip(ox, oy):
34 | d = math.hypot(iox - x, ioy - y)
35 | if mindis >= d:
36 | mindis = d
37 |
38 | pdf = (1.0 - norm.cdf(mindis, 0.0, std))
39 | gmap[ix][iy] = pdf
40 |
41 | return gmap, minx, maxx, miny, maxy
42 |
43 |
44 | def calc_grid_map_config(ox, oy, xyreso):
45 | minx = round(min(ox) - EXTEND_AREA / 2.0)
46 | miny = round(min(oy) - EXTEND_AREA / 2.0)
47 | maxx = round(max(ox) + EXTEND_AREA / 2.0)
48 | maxy = round(max(oy) + EXTEND_AREA / 2.0)
49 | xw = int(round((maxx - minx) / xyreso))
50 | yw = int(round((maxy - miny) / xyreso))
51 |
52 | return minx, miny, maxx, maxy, xw, yw
53 |
54 |
55 | def draw_heatmap(data, minx, maxx, miny, maxy, xyreso):
56 | x, y = np.mgrid[slice(minx - xyreso / 2.0, maxx + xyreso / 2.0, xyreso),
57 | slice(miny - xyreso / 2.0, maxy + xyreso / 2.0, xyreso)]
58 | plt.pcolor(x, y, data, vmax=1.0, cmap=plt.cm.Blues)
59 | plt.axis("equal")
60 |
61 |
62 | def main():
63 | print(__file__ + " start!!")
64 |
65 | xyreso = 0.5 # xy grid resolution
66 | STD = 5.0 # standard diviation for gaussian distribution
67 |
68 | for i in range(5):
69 | ox = (np.random.rand(4) - 0.5) * 10.0
70 | oy = (np.random.rand(4) - 0.5) * 10.0
71 | gmap, minx, maxx, miny, maxy = generate_gaussian_grid_map(
72 | ox, oy, xyreso, STD)
73 |
74 | if show_animation: # pragma: no cover
75 | plt.cla()
76 | # for stopping simulation with the esc key.
77 | plt.gcf().canvas.mpl_connect('key_release_event',
78 | lambda event: [exit(0) if event.key == 'escape' else None])
79 | draw_heatmap(gmap, minx, maxx, miny, maxy, xyreso)
80 | plt.plot(ox, oy, "xr")
81 | plt.plot(0.0, 0.0, "ob")
82 | plt.pause(1.0)
83 |
84 |
85 | if __name__ == '__main__':
86 | main()
87 |
--------------------------------------------------------------------------------
/Mapping/kmeans_clustering/kmeans_clustering.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Object clustering with k-means algorithm
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 | import matplotlib.pyplot as plt
11 | import random
12 |
13 | # k means parameters
14 | MAX_LOOP = 10
15 | DCOST_TH = 0.1
16 | show_animation = True
17 |
18 |
19 | def kmeans_clustering(rx, ry, nc):
20 | clusters = Clusters(rx, ry, nc)
21 | clusters.calc_centroid()
22 |
23 | pre_cost = float("inf")
24 | for loop in range(MAX_LOOP):
25 | print("loop:", loop)
26 | cost = clusters.update_clusters()
27 | clusters.calc_centroid()
28 |
29 | d_cost = abs(cost - pre_cost)
30 | if d_cost < DCOST_TH:
31 | break
32 | pre_cost = cost
33 |
34 | return clusters
35 |
36 |
37 | class Clusters:
38 |
39 | def __init__(self, x, y, n_label):
40 | self.x = x
41 | self.y = y
42 | self.n_data = len(self.x)
43 | self.n_label = n_label
44 | self.labels = [random.randint(0, n_label - 1)
45 | for _ in range(self.n_data)]
46 | self.center_x = [0.0 for _ in range(n_label)]
47 | self.center_y = [0.0 for _ in range(n_label)]
48 |
49 | def plot_cluster(self):
50 | for label in set(self.labels):
51 | x, y = self._get_labeled_x_y(label)
52 | plt.plot(x, y, ".")
53 |
54 | def calc_centroid(self):
55 | for label in set(self.labels):
56 | x, y = self._get_labeled_x_y(label)
57 | n_data = len(x)
58 | self.center_x[label] = sum(x) / n_data
59 | self.center_y[label] = sum(y) / n_data
60 |
61 | def update_clusters(self):
62 | cost = 0.0
63 |
64 | for ip in range(self.n_data):
65 | px = self.x[ip]
66 | py = self.y[ip]
67 |
68 | dx = [icx - px for icx in self.center_x]
69 | dy = [icy - py for icy in self.center_y]
70 |
71 | dist_list = [math.hypot(idx, idy) for (idx, idy) in zip(dx, dy)]
72 | min_dist = min(dist_list)
73 | min_id = dist_list.index(min_dist)
74 | self.labels[ip] = min_id
75 | cost += min_dist
76 |
77 | return cost
78 |
79 | def _get_labeled_x_y(self, target_label):
80 | x = [self.x[i] for i, label in enumerate(self.labels) if label == target_label]
81 | y = [self.y[i] for i, label in enumerate(self.labels) if label == target_label]
82 | return x, y
83 |
84 |
85 | def calc_raw_data(cx, cy, n_points, rand_d):
86 | rx, ry = [], []
87 |
88 | for (icx, icy) in zip(cx, cy):
89 | for _ in range(n_points):
90 | rx.append(icx + rand_d * (random.random() - 0.5))
91 | ry.append(icy + rand_d * (random.random() - 0.5))
92 |
93 | return rx, ry
94 |
95 |
96 | def update_positions(cx, cy):
97 | # object moving parameters
98 | DX1 = 0.4
99 | DY1 = 0.5
100 | DX2 = -0.3
101 | DY2 = -0.5
102 |
103 | cx[0] += DX1
104 | cy[0] += DY1
105 | cx[1] += DX2
106 | cy[1] += DY2
107 |
108 | return cx, cy
109 |
110 |
111 | def main():
112 | print(__file__ + " start!!")
113 |
114 | cx = [0.0, 8.0]
115 | cy = [0.0, 8.0]
116 | n_points = 10
117 | rand_d = 3.0
118 | n_cluster = 2
119 | sim_time = 15.0
120 | dt = 1.0
121 | time = 0.0
122 |
123 | while time <= sim_time:
124 | print("Time:", time)
125 | time += dt
126 |
127 | # objects moving simulation
128 | cx, cy = update_positions(cx, cy)
129 | raw_x, raw_y = calc_raw_data(cx, cy, n_points, rand_d)
130 |
131 | clusters = kmeans_clustering(raw_x, raw_y, n_cluster)
132 |
133 | # for animation
134 | if show_animation: # pragma: no cover
135 | plt.cla()
136 | # for stopping simulation with the esc key.
137 | plt.gcf().canvas.mpl_connect('key_release_event',
138 | lambda event: [exit(0) if event.key == 'escape' else None])
139 | clusters.plot_cluster()
140 | plt.plot(cx, cy, "or")
141 | plt.xlim(-2.0, 10.0)
142 | plt.ylim(-2.0, 10.0)
143 | plt.pause(dt)
144 |
145 | print("Done")
146 |
147 |
148 | if __name__ == '__main__':
149 | main()
150 |
--------------------------------------------------------------------------------
/Mapping/lidar_to_grid_map/animation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/Mapping/lidar_to_grid_map/animation.gif
--------------------------------------------------------------------------------
/Mapping/lidar_to_grid_map/grid_map_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/Mapping/lidar_to_grid_map/grid_map_example.png
--------------------------------------------------------------------------------
/Mapping/lidar_to_grid_map/lidar01.csv:
--------------------------------------------------------------------------------
1 | 0.008450416037156572,0.5335
2 | 0.046902201120156306,0.5345
3 | 0.08508127850753233,0.537
4 | 0.1979822644959155,0.2605
5 | 0.21189035697274505,0.2625
6 | 0.2587960806200922,0.26475
7 | 0.3043382657893199,0.2675
8 | 0.34660795861105775,0.27075
9 | 0.43632879047139106,0.59
10 | 0.4739624524675188,0.60025
11 | 0.5137777760286397,0.611
12 | 0.5492297764597742,0.6265
13 | 0.5895905154121426,0.64
14 | 0.6253152235389017,0.6565
15 | 0.6645851317087743,0.676
16 | 0.6997644244442851,0.6975
17 | 0.7785769484796541,0.3345
18 | 0.7772134100015329,0.74575
19 | 0.8652979956881222,0.3315
20 | 0.8996591653367609,0.31775
21 | 0.9397471965935056,0.31275
22 | 0.9847439663714841,0.31125
23 | 1.0283771976713423,0.31325
24 | 1.0641019057981014,0.31975
25 | 1.1009174447073562,0.3335
26 | 1.2012738766970301,0.92275
27 | 1.2397256617800307,0.95325
28 | 1.2779047391674068,0.9865
29 | 1.316629231946031,1.01775
30 | 1.3561718478115274,1.011
31 | 1.3948963405901518,1.0055
32 | 1.4330754179775278,1.00225
33 | 1.4731634492342724,0.99975
34 | 1.5113425266216485,0.9975
35 | 1.5517032655740168,1.001
36 | 1.5896096352657691,1.00275
37 | 1.6288795434356418,1.008
38 | 1.6684221593011381,1.0135
39 | 1.7066012366885142,1.022
40 | 1.7453257294671385,1.02875
41 | 1.7862318838107551,0.9935
42 | 1.8257744996762515,1.0025
43 | 1.8661352386286207,0.96075
44 | 1.9045870237116205,0.92125
45 | 1.9465840088377355,0.8855
46 | 1.986944747790103,0.85725
47 | 2.025669240568728,0.832
48 | 2.065757271825472,0.80675
49 | 2.1066634261690886,0.78875
50 | 2.1464787497302105,0.7705
51 | 2.1865667809869542,0.75625
52 | 2.2261093968524506,0.74475
53 | 2.2683790896741876,0.68275
54 | 2.3090125363221823,0.6375
55 | 2.3510095214482956,0.59925
56 | 2.3916429680962885,0.5665
57 | 2.4330945378311526,0.538
58 | 2.4783640153047557,0.50825
59 | 2.5203610004308707,0.4875
60 | 2.562903400948233,0.46825
61 | 2.599173524466238,0.45
62 | 2.642806755766097,0.4355
63 | 2.685076448587836,0.42275
64 | 2.722437402888339,0.4125
65 | 2.766888757275069,0.40125
66 | 2.8007045115324587,0.39525
67 | 2.841883373571701,0.385
68 | 2.8819714048284446,0.3805
69 | 2.922332143780814,0.38575
70 | 2.9637837135156797,0.38425
71 | 3.0005992524249336,0.36575
72 | 3.0401418682904318,0.3765
73 | 3.079957191851552,0.3915
74 | 3.115409192282687,0.408
75 | 3.154679100452558,0.4265
76 | 3.184949654666836,0.447
77 | 3.2242195628367085,0.4715
78 | 3.2574899017028507,0.49875
79 | 3.2959416867858504,0.52875
80 | 3.3292120256519926,0.564
81 | 3.3665729799524957,0.6055
82 | 3.4031158111661277,0.6515
83 | 3.438022396206014,0.70675
84 | 3.4756560582021407,0.771
85 | 3.513562427893893,0.77075
86 | 3.5522869206725183,0.7785
87 | 3.621827383056667,0.79575
88 | 3.65918833735717,0.8045
89 | 3.697367414744546,0.81725
90 | 3.7377281536969154,0.83325
91 | 3.775634523388667,0.8415
92 | 3.8135408930804187,0.85575
93 | 3.8522653858590425,0.87325
94 | 3.8898990478551703,0.88725
95 | 3.9299870791119154,0.906
96 | 3.9665299103255465,0.9265
97 | 4.006072526191043,0.94575
98 | 4.043978895882795,0.97175
99 | 4.081885265574547,1.02075
100 | 4.1206097583531704,1.046
101 | 4.1587888357405465,1.07025
102 | 4.196422497736674,1.097
103 | 4.234874282819675,1.12575
104 | 4.286688744988257,0.73475
105 | 4.324322406984384,0.72225
106 | 4.364410438241129,0.731
107 | 4.405862007975994,0.7405
108 | 4.44267754688525,0.749
109 | 4.484129116620115,0.76025
110 | 4.522853609398739,0.76825
111 | 4.560759979090491,0.77125
112 | 4.5989390564778665,0.77725
113 | 4.640117918517108,0.782
114 | 4.679115118991357,0.78425
115 | 4.717294196378733,0.789
116 | 4.757109519939853,0.78825
117 | 4.796652135805349,0.7855
118 | 4.8337403824102285,0.786
119 | 4.871646752101981,0.78275
120 | 4.9109166602718535,0.7785
121 | 4.950186568441726,0.7635
122 | 4.990274599698471,0.74725
123 | 5.028180969390222,0.737
124 | 5.0677235852557185,0.72575
125 | 5.109720570381833,0.71525
126 | 5.149263186247329,0.70625
127 | 5.1863514328522085,0.69975
128 | 5.230530079543315,0.693
129 | 5.269799987713188,0.68925
130 | 5.307979065100563,0.68425
131 | 5.347248973270435,0.68275
132 | 5.386518881440308,0.68075
133 | 5.426606912697053,0.68175
134 | 5.465604113171301,0.67825
135 | 5.502419652080556,0.6835
136 | 5.543871221815422,0.6885
137 | 5.580959468420302,0.67925
138 | 5.624319992024535,0.6555
139 | 5.660044700151294,0.639
140 | 5.700950854494911,0.623
141 | 5.740220762664784,0.6075
142 | 5.783581286269018,0.59475
143 | 5.820124117482649,0.58475
144 | 5.861848394913139,0.57325
145 | 5.899209349213642,0.565
146 | 5.938751965079138,0.55525
147 | 5.9782945809446355,0.55175
148 | 6.017564489114507,0.546
149 | 6.059288766544997,0.5405
150 | 6.097467843932373,0.53825
151 | 6.139464829058487,0.534
152 | 6.176825783358991,0.5325
153 | 6.215822983833238,0.53125
154 | 6.252911230438118,0.53075
--------------------------------------------------------------------------------
/Mapping/raycasting_grid_map/raycasting_grid_map.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Ray casting 2D grid map example
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 | import numpy as np
11 | import matplotlib.pyplot as plt
12 |
13 | EXTEND_AREA = 10.0
14 |
15 | show_animation = True
16 |
17 |
18 | def calc_grid_map_config(ox, oy, xyreso):
19 | minx = round(min(ox) - EXTEND_AREA / 2.0)
20 | miny = round(min(oy) - EXTEND_AREA / 2.0)
21 | maxx = round(max(ox) + EXTEND_AREA / 2.0)
22 | maxy = round(max(oy) + EXTEND_AREA / 2.0)
23 | xw = int(round((maxx - minx) / xyreso))
24 | yw = int(round((maxy - miny) / xyreso))
25 |
26 | return minx, miny, maxx, maxy, xw, yw
27 |
28 |
29 | class precastDB:
30 |
31 | def __init__(self):
32 | self.px = 0.0
33 | self.py = 0.0
34 | self.d = 0.0
35 | self.angle = 0.0
36 | self.ix = 0
37 | self.iy = 0
38 |
39 | def __str__(self):
40 | return str(self.px) + "," + str(self.py) + "," + str(self.d) + "," + str(self.angle)
41 |
42 |
43 | def atan_zero_to_twopi(y, x):
44 | angle = math.atan2(y, x)
45 | if angle < 0.0:
46 | angle += math.pi * 2.0
47 |
48 | return angle
49 |
50 |
51 | def precasting(minx, miny, xw, yw, xyreso, yawreso):
52 |
53 | precast = [[] for i in range(int(round((math.pi * 2.0) / yawreso)) + 1)]
54 |
55 | for ix in range(xw):
56 | for iy in range(yw):
57 | px = ix * xyreso + minx
58 | py = iy * xyreso + miny
59 |
60 | d = math.hypot(px, py)
61 | angle = atan_zero_to_twopi(py, px)
62 | angleid = int(math.floor(angle / yawreso))
63 |
64 | pc = precastDB()
65 |
66 | pc.px = px
67 | pc.py = py
68 | pc.d = d
69 | pc.ix = ix
70 | pc.iy = iy
71 | pc.angle = angle
72 |
73 | precast[angleid].append(pc)
74 |
75 | return precast
76 |
77 |
78 | def generate_ray_casting_grid_map(ox, oy, xyreso, yawreso):
79 |
80 | minx, miny, maxx, maxy, xw, yw = calc_grid_map_config(ox, oy, xyreso)
81 |
82 | pmap = [[0.0 for i in range(yw)] for i in range(xw)]
83 |
84 | precast = precasting(minx, miny, xw, yw, xyreso, yawreso)
85 |
86 | for (x, y) in zip(ox, oy):
87 |
88 | d = math.hypot(x, y)
89 | angle = atan_zero_to_twopi(y, x)
90 | angleid = int(math.floor(angle / yawreso))
91 |
92 | gridlist = precast[angleid]
93 |
94 | ix = int(round((x - minx) / xyreso))
95 | iy = int(round((y - miny) / xyreso))
96 |
97 | for grid in gridlist:
98 | if grid.d > d:
99 | pmap[grid.ix][grid.iy] = 0.5
100 |
101 | pmap[ix][iy] = 1.0
102 |
103 | return pmap, minx, maxx, miny, maxy, xyreso
104 |
105 |
106 | def draw_heatmap(data, minx, maxx, miny, maxy, xyreso):
107 | x, y = np.mgrid[slice(minx - xyreso / 2.0, maxx + xyreso / 2.0, xyreso),
108 | slice(miny - xyreso / 2.0, maxy + xyreso / 2.0, xyreso)]
109 | plt.pcolor(x, y, data, vmax=1.0, cmap=plt.cm.Blues)
110 | plt.axis("equal")
111 |
112 |
113 | def main():
114 | print(__file__ + " start!!")
115 |
116 | xyreso = 0.25 # x-y grid resolution [m]
117 | yawreso = np.deg2rad(10.0) # yaw angle resolution [rad]
118 |
119 | for i in range(5):
120 | ox = (np.random.rand(4) - 0.5) * 10.0
121 | oy = (np.random.rand(4) - 0.5) * 10.0
122 | pmap, minx, maxx, miny, maxy, xyreso = generate_ray_casting_grid_map(
123 | ox, oy, xyreso, yawreso)
124 |
125 | if show_animation: # pragma: no cover
126 | plt.cla()
127 | # for stopping simulation with the esc key.
128 | plt.gcf().canvas.mpl_connect('key_release_event',
129 | lambda event: [exit(0) if event.key == 'escape' else None])
130 | draw_heatmap(pmap, minx, maxx, miny, maxy, xyreso)
131 | plt.plot(ox, oy, "xr")
132 | plt.plot(0.0, 0.0, "ob")
133 | plt.pause(1.0)
134 |
135 |
136 | if __name__ == '__main__':
137 | main()
138 |
--------------------------------------------------------------------------------
/Mapping/rectangle_fitting/simulator.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Simulator
4 |
5 | author: Atsushi Sakai
6 |
7 | """
8 |
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | import math
12 | import random
13 |
14 |
15 | class VehicleSimulator():
16 |
17 | def __init__(self, ix, iy, iyaw, iv, max_v, w, L):
18 | self.x = ix
19 | self.y = iy
20 | self.yaw = iyaw
21 | self.v = iv
22 | self.max_v = max_v
23 | self.W = w
24 | self.L = L
25 | self._calc_vehicle_contour()
26 |
27 | def update(self, dt, a, omega):
28 | self.x += self.v * np.cos(self.yaw) * dt
29 | self.y += self.v * np.sin(self.yaw) * dt
30 | self.yaw += omega * dt
31 | self.v += a * dt
32 | if self.v >= self.max_v:
33 | self.v = self.max_v
34 |
35 | def plot(self):
36 | plt.plot(self.x, self.y, ".b")
37 |
38 | # convert global coordinate
39 | gx, gy = self.calc_global_contour()
40 | plt.plot(gx, gy, "--b")
41 |
42 | def calc_global_contour(self):
43 | gx = [(ix * np.cos(self.yaw) + iy * np.sin(self.yaw)) +
44 | self.x for (ix, iy) in zip(self.vc_x, self.vc_y)]
45 | gy = [(ix * np.sin(self.yaw) - iy * np.cos(self.yaw)) +
46 | self.y for (ix, iy) in zip(self.vc_x, self.vc_y)]
47 |
48 | return gx, gy
49 |
50 | def _calc_vehicle_contour(self):
51 |
52 | self.vc_x = []
53 | self.vc_y = []
54 |
55 | self.vc_x.append(self.L / 2.0)
56 | self.vc_y.append(self.W / 2.0)
57 |
58 | self.vc_x.append(self.L / 2.0)
59 | self.vc_y.append(-self.W / 2.0)
60 |
61 | self.vc_x.append(-self.L / 2.0)
62 | self.vc_y.append(-self.W / 2.0)
63 |
64 | self.vc_x.append(-self.L / 2.0)
65 | self.vc_y.append(self.W / 2.0)
66 |
67 | self.vc_x.append(self.L / 2.0)
68 | self.vc_y.append(self.W / 2.0)
69 |
70 | self.vc_x, self.vc_y = self._interporate(self.vc_x, self.vc_y)
71 |
72 | def _interporate(self, x, y):
73 | rx, ry = [], []
74 | dtheta = 0.05
75 | for i in range(len(x) - 1):
76 | rx.extend([(1.0 - θ) * x[i] + θ * x[i + 1]
77 | for θ in np.arange(0.0, 1.0, dtheta)])
78 | ry.extend([(1.0 - θ) * y[i] + θ * y[i + 1]
79 | for θ in np.arange(0.0, 1.0, dtheta)])
80 |
81 | rx.extend([(1.0 - θ) * x[len(x) - 1] + θ * x[1]
82 | for θ in np.arange(0.0, 1.0, dtheta)])
83 | ry.extend([(1.0 - θ) * y[len(y) - 1] + θ * y[1]
84 | for θ in np.arange(0.0, 1.0, dtheta)])
85 |
86 | return rx, ry
87 |
88 |
89 | class LidarSimulator():
90 |
91 | def __init__(self):
92 | self.range_noise = 0.01
93 |
94 | def get_observation_points(self, vlist, angle_reso):
95 | x, y, angle, r = [], [], [], []
96 |
97 | # store all points
98 | for v in vlist:
99 |
100 | gx, gy = v.calc_global_contour()
101 |
102 | for vx, vy in zip(gx, gy):
103 | vangle = math.atan2(vy, vx)
104 | vr = np.hypot(vx, vy) * random.uniform(1.0 - self.range_noise,
105 | 1.0 + self.range_noise)
106 |
107 | x.append(vx)
108 | y.append(vy)
109 | angle.append(vangle)
110 | r.append(vr)
111 |
112 | # ray casting filter
113 | rx, ry = self.ray_casting_filter(x, y, angle, r, angle_reso)
114 |
115 | return rx, ry
116 |
117 | def ray_casting_filter(self, xl, yl, thetal, rangel, angle_reso):
118 | rx, ry = [], []
119 | rangedb = [float("inf") for _ in range(
120 | int(np.floor((np.pi * 2.0) / angle_reso)) + 1)]
121 |
122 | for i in range(len(thetal)):
123 | angleid = int(round(thetal[i] / angle_reso))
124 |
125 | if rangedb[angleid] > rangel[i]:
126 | rangedb[angleid] = rangel[i]
127 |
128 | for i in range(len(rangedb)):
129 | t = i * angle_reso
130 | if rangedb[i] != float("inf"):
131 | rx.append(rangedb[i] * np.cos(t))
132 | ry.append(rangedb[i] * np.sin(t))
133 |
134 | return rx, ry
135 |
136 |
137 | def main():
138 | print("start!!")
139 |
140 | print("done!!")
141 |
142 |
143 | if __name__ == '__main__':
144 | main()
145 |
--------------------------------------------------------------------------------
/PathPlanning/BSplinePath/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/BSplinePath/Figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/BSplinePath/bspline_path.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Path Planner with B-Spline
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import numpy as np
10 | import matplotlib.pyplot as plt
11 | import scipy.interpolate as scipy_interpolate
12 |
13 |
14 | def approximate_b_spline_path(x: list, y: list, n_path_points: int,
15 | degree: int = 3) -> tuple:
16 | """
17 | approximate points with a B-Spline path
18 |
19 | :param x: x position list of approximated points
20 | :param y: y position list of approximated points
21 | :param n_path_points: number of path points
22 | :param degree: (Optional) B Spline curve degree
23 | :return: x and y position list of the result path
24 | """
25 | t = range(len(x))
26 | x_tup = scipy_interpolate.splrep(t, x, k=degree)
27 | y_tup = scipy_interpolate.splrep(t, y, k=degree)
28 |
29 | x_list = list(x_tup)
30 | x_list[1] = x + [0.0, 0.0, 0.0, 0.0]
31 |
32 | y_list = list(y_tup)
33 | y_list[1] = y + [0.0, 0.0, 0.0, 0.0]
34 |
35 | ipl_t = np.linspace(0.0, len(x) - 1, n_path_points)
36 | rx = scipy_interpolate.splev(ipl_t, x_list)
37 | ry = scipy_interpolate.splev(ipl_t, y_list)
38 |
39 | return rx, ry
40 |
41 |
42 | def interpolate_b_spline_path(x: list, y: list, n_path_points: int,
43 | degree: int = 3) -> tuple:
44 | """
45 | interpolate points with a B-Spline path
46 |
47 | :param x: x positions of interpolated points
48 | :param y: y positions of interpolated points
49 | :param n_path_points: number of path points
50 | :param degree: B-Spline degree
51 | :return: x and y position list of the result path
52 | """
53 | ipl_t = np.linspace(0.0, len(x) - 1, len(x))
54 | spl_i_x = scipy_interpolate.make_interp_spline(ipl_t, x, k=degree)
55 | spl_i_y = scipy_interpolate.make_interp_spline(ipl_t, y, k=degree)
56 |
57 | travel = np.linspace(0.0, len(x) - 1, n_path_points)
58 | return spl_i_x(travel), spl_i_y(travel)
59 |
60 |
61 | def main():
62 | print(__file__ + " start!!")
63 | # way points
64 | way_point_x = [-1.0, 3.0, 4.0, 2.0, 1.0]
65 | way_point_y = [0.0, -3.0, 1.0, 1.0, 3.0]
66 | n_course_point = 100 # sampling number
67 |
68 | rax, ray = approximate_b_spline_path(way_point_x, way_point_y,
69 | n_course_point)
70 | rix, riy = interpolate_b_spline_path(way_point_x, way_point_y,
71 | n_course_point)
72 |
73 | # show results
74 | plt.plot(way_point_x, way_point_y, '-og', label="way points")
75 | plt.plot(rax, ray, '-r', label="Approximated B-Spline path")
76 | plt.plot(rix, riy, '-b', label="Interpolated B-Spline path")
77 | plt.grid(True)
78 | plt.legend()
79 | plt.axis("equal")
80 | plt.show()
81 |
82 |
83 | if __name__ == '__main__':
84 | main()
85 |
--------------------------------------------------------------------------------
/PathPlanning/BezierPath/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/BezierPath/Figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/BezierPath/Figure_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/BezierPath/Figure_2.png
--------------------------------------------------------------------------------
/PathPlanning/ClosedLoopRRTStar/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ClosedLoopRRTStar/Figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/ClosedLoopRRTStar/Figure_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ClosedLoopRRTStar/Figure_3.png
--------------------------------------------------------------------------------
/PathPlanning/ClosedLoopRRTStar/Figure_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ClosedLoopRRTStar/Figure_4.png
--------------------------------------------------------------------------------
/PathPlanning/ClosedLoopRRTStar/Figure_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ClosedLoopRRTStar/Figure_5.png
--------------------------------------------------------------------------------
/PathPlanning/ClosedLoopRRTStar/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ClosedLoopRRTStar/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/ClosedLoopRRTStar/unicycle_model.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Unicycle model class
4 |
5 | author Atsushi Sakai
6 |
7 | """
8 |
9 | import math
10 | import numpy as np
11 |
12 | dt = 0.05 # [s]
13 | L = 0.9 # [m]
14 | steer_max = np.deg2rad(40.0)
15 | curvature_max = math.tan(steer_max) / L
16 | curvature_max = 1.0 / curvature_max + 1.0
17 |
18 | accel_max = 5.0
19 |
20 |
21 | class State:
22 |
23 | def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
24 | self.x = x
25 | self.y = y
26 | self.yaw = yaw
27 | self.v = v
28 |
29 |
30 | def update(state, a, delta):
31 |
32 | state.x = state.x + state.v * math.cos(state.yaw) * dt
33 | state.y = state.y + state.v * math.sin(state.yaw) * dt
34 | state.yaw = state.yaw + state.v / L * math.tan(delta) * dt
35 | state.yaw = pi_2_pi(state.yaw)
36 | state.v = state.v + a * dt
37 |
38 | return state
39 |
40 |
41 | def pi_2_pi(angle):
42 | return (angle + math.pi) % (2 * math.pi) - math.pi
43 |
44 |
45 | if __name__ == '__main__': # pragma: no cover
46 | print("start unicycle simulation")
47 | import matplotlib.pyplot as plt
48 |
49 | T = 100
50 | a = [1.0] * T
51 | delta = [np.deg2rad(1.0)] * T
52 | # print(delta)
53 | # print(a, delta)
54 |
55 | state = State()
56 |
57 | x = []
58 | y = []
59 | yaw = []
60 | v = []
61 |
62 | for (ai, di) in zip(a, delta):
63 | state = update(state, ai, di)
64 |
65 | x.append(state.x)
66 | y.append(state.y)
67 | yaw.append(state.yaw)
68 | v.append(state.v)
69 |
70 | plt.subplots(1)
71 | plt.plot(x, y)
72 | plt.axis("equal")
73 | plt.grid(True)
74 |
75 | plt.subplots(1)
76 | plt.plot(v)
77 | plt.grid(True)
78 |
79 | plt.show()
80 |
--------------------------------------------------------------------------------
/PathPlanning/CubicSpline/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/CubicSpline/Figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/CubicSpline/Figure_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/CubicSpline/Figure_2.png
--------------------------------------------------------------------------------
/PathPlanning/CubicSpline/Figure_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/CubicSpline/Figure_3.png
--------------------------------------------------------------------------------
/PathPlanning/CubicSpline/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/CubicSpline/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/DubinsPath/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/DubinsPath/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/HybridAStar/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/HybridAStar/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/HybridAStar/car.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Car model for Hybrid A* path planning
4 |
5 | author: Zheng Zh (@Zhengzh)
6 |
7 | """
8 |
9 |
10 | import matplotlib.pyplot as plt
11 | from math import sqrt, cos, sin, tan, pi
12 |
13 | WB = 3. # rear to front wheel
14 | W = 2. # width of car
15 | LF = 3.3 # distance from rear to vehicle front end
16 | LB = 1.0 # distance from rear to vehicle back end
17 | MAX_STEER = 0.6 # [rad] maximum steering angle
18 |
19 | WBUBBLE_DIST = (LF - LB) / 2.0
20 | WBUBBLE_R = sqrt(((LF + LB) / 2.0)**2 + 1)
21 |
22 | # vehicle rectangle verticles
23 | VRX = [LF, LF, -LB, -LB, LF]
24 | VRY = [W / 2, -W / 2, -W / 2, W / 2, W / 2]
25 |
26 |
27 | def check_car_collision(xlist, ylist, yawlist, ox, oy, kdtree):
28 | for x, y, yaw in zip(xlist, ylist, yawlist):
29 | cx = x + WBUBBLE_DIST * cos(yaw)
30 | cy = y + WBUBBLE_DIST * sin(yaw)
31 |
32 | ids = kdtree.search_in_distance([cx, cy], WBUBBLE_R)
33 |
34 | if not ids:
35 | continue
36 |
37 | if not rectangle_check(x, y, yaw,
38 | [ox[i] for i in ids], [oy[i] for i in ids]):
39 | return False # collision
40 |
41 | return True # no collision
42 |
43 |
44 | def rectangle_check(x, y, yaw, ox, oy):
45 | # transform obstacles to base link frame
46 | c, s = cos(-yaw), sin(-yaw)
47 | for iox, ioy in zip(ox, oy):
48 | tx = iox - x
49 | ty = ioy - y
50 | rx = c * tx - s * ty
51 | ry = s * tx + c * ty
52 |
53 | if not (rx > LF or rx < -LB or ry > W / 2.0 or ry < -W / 2.0):
54 | return False # no collision
55 |
56 | return True # collision
57 |
58 |
59 | def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"):
60 | """Plot arrow."""
61 | if not isinstance(x, float):
62 | for (ix, iy, iyaw) in zip(x, y, yaw):
63 | plot_arrow(ix, iy, iyaw)
64 | else:
65 | plt.arrow(x, y, length * cos(yaw), length * sin(yaw),
66 | fc=fc, ec=ec, head_width=width, head_length=width, alpha=0.4)
67 | # plt.plot(x, y)
68 |
69 |
70 | def plot_car(x, y, yaw):
71 | car_color = '-k'
72 | c, s = cos(yaw), sin(yaw)
73 |
74 | car_outline_x, car_outline_y = [], []
75 | for rx, ry in zip(VRX, VRY):
76 | tx = c * rx - s * ry + x
77 | ty = s * rx + c * ry + y
78 | car_outline_x.append(tx)
79 | car_outline_y.append(ty)
80 |
81 | arrow_x, arrow_y, arrow_yaw = c * 1.5 + x, s * 1.5 + y, yaw
82 | plot_arrow(arrow_x, arrow_y, arrow_yaw)
83 |
84 | plt.plot(car_outline_x, car_outline_y, car_color)
85 |
86 |
87 | def pi_2_pi(angle):
88 | return (angle + pi) % (2 * pi) - pi
89 |
90 |
91 | def move(x, y, yaw, distance, steer, L=WB):
92 | x += distance * cos(yaw)
93 | y += distance * sin(yaw)
94 | yaw += pi_2_pi(distance * tan(steer) / L) # distance/2
95 |
96 | return x, y, yaw
97 |
98 |
99 | if __name__ == '__main__':
100 | x, y, yaw = 0., 0., 1.
101 | plt.axis('equal')
102 | plot_car(x, y, yaw)
103 | plt.show()
--------------------------------------------------------------------------------
/PathPlanning/LQRPlanner/LQRplanner.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | LQR local path planning
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 | import random
11 |
12 | import matplotlib.pyplot as plt
13 | import numpy as np
14 | import scipy.linalg as la
15 |
16 | SHOW_ANIMATION = True
17 |
18 |
19 | class LQRPlanner:
20 |
21 | def __init__(self):
22 | self.MAX_TIME = 100.0 # Maximum simulation time
23 | self.DT = 0.1 # Time tick
24 | self.GOAL_DIST = 0.1
25 | self.MAX_ITER = 150
26 | self.EPS = 0.01
27 |
28 | def lqr_planning(self, sx, sy, gx, gy, show_animation=True):
29 |
30 | rx, ry = [sx], [sy]
31 |
32 | x = np.array([sx - gx, sy - gy]).reshape(2, 1) # State vector
33 |
34 | # Linear system model
35 | A, B = self.get_system_model()
36 |
37 | found_path = False
38 |
39 | time = 0.0
40 | while time <= self.MAX_TIME:
41 | time += self.DT
42 |
43 | u = self.lqr_control(A, B, x)
44 |
45 | x = A @ x + B @ u
46 |
47 | rx.append(x[0, 0] + gx)
48 | ry.append(x[1, 0] + gy)
49 |
50 | d = math.sqrt((gx - rx[-1]) ** 2 + (gy - ry[-1]) ** 2)
51 | if d <= self.GOAL_DIST:
52 | found_path = True
53 | break
54 |
55 | # animation
56 | if show_animation: # pragma: no cover
57 | # for stopping simulation with the esc key.
58 | plt.gcf().canvas.mpl_connect('key_release_event',
59 | lambda event: [exit(0) if event.key == 'escape' else None])
60 | plt.plot(sx, sy, "or")
61 | plt.plot(gx, gy, "ob")
62 | plt.plot(rx, ry, "-r")
63 | plt.axis("equal")
64 | plt.pause(1.0)
65 |
66 | if not found_path:
67 | print("Cannot found path")
68 | return [], []
69 |
70 | return rx, ry
71 |
72 | def solve_dare(self, A, B, Q, R):
73 | """
74 | solve a discrete time_Algebraic Riccati equation (DARE)
75 | """
76 | X, Xn = Q, Q
77 |
78 | for i in range(self.MAX_ITER):
79 | Xn = A.T * X * A - A.T * X * B * \
80 | la.inv(R + B.T * X * B) * B.T * X * A + Q
81 | if (abs(Xn - X)).max() < self.EPS:
82 | break
83 | X = Xn
84 |
85 | return Xn
86 |
87 | def dlqr(self, A, B, Q, R):
88 | """Solve the discrete time lqr controller.
89 | x[k+1] = A x[k] + B u[k]
90 | cost = sum x[k].T*Q*x[k] + u[k].T*R*u[k]
91 | # ref Bertsekas, p.151
92 | """
93 |
94 | # first, try to solve the ricatti equation
95 | X = self.solve_dare(A, B, Q, R)
96 |
97 | # compute the LQR gain
98 | K = la.inv(B.T @ X @ B + R) @ (B.T @ X @ A)
99 |
100 | eigValues = la.eigvals(A - B @ K)
101 |
102 | return K, X, eigValues
103 |
104 | def get_system_model(self):
105 |
106 | A = np.array([[self.DT, 1.0],
107 | [0.0, self.DT]])
108 | B = np.array([0.0, 1.0]).reshape(2, 1)
109 |
110 | return A, B
111 |
112 | def lqr_control(self, A, B, x):
113 |
114 | Kopt, X, ev = self.dlqr(A, B, np.eye(2), np.eye(1))
115 |
116 | u = -Kopt @ x
117 |
118 | return u
119 |
120 |
121 | def main():
122 | print(__file__ + " start!!")
123 |
124 | ntest = 10 # number of goal
125 | area = 100.0 # sampling area
126 |
127 | lqr_planner = LQRPlanner()
128 |
129 | for i in range(ntest):
130 | sx = 6.0
131 | sy = 6.0
132 | gx = random.uniform(-area, area)
133 | gy = random.uniform(-area, area)
134 |
135 | rx, ry = lqr_planner.lqr_planning(sx, sy, gx, gy, show_animation=SHOW_ANIMATION)
136 |
137 | if SHOW_ANIMATION: # pragma: no cover
138 | plt.plot(sx, sy, "or")
139 | plt.plot(gx, gy, "ob")
140 | plt.plot(rx, ry, "-r")
141 | plt.axis("equal")
142 | plt.pause(1.0)
143 |
144 |
145 | if __name__ == '__main__':
146 | main()
147 |
--------------------------------------------------------------------------------
/PathPlanning/ModelPredictiveTrajectoryGenerator/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ModelPredictiveTrajectoryGenerator/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/ModelPredictiveTrajectoryGenerator/lookuptable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/ModelPredictiveTrajectoryGenerator/lookuptable.png
--------------------------------------------------------------------------------
/PathPlanning/ModelPredictiveTrajectoryGenerator/lookuptable_generator.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Lookup Table generation for model predictive trajectory generator
4 |
5 | author: Atsushi Sakai
6 |
7 | """
8 | from matplotlib import pyplot as plt
9 | import numpy as np
10 | import math
11 | import model_predictive_trajectory_generator as planner
12 | import motion_model
13 | import pandas as pd
14 |
15 |
16 | def calc_states_list():
17 | maxyaw = np.deg2rad(-30.0)
18 |
19 | x = np.arange(10.0, 30.0, 5.0)
20 | y = np.arange(0.0, 20.0, 2.0)
21 | yaw = np.arange(-maxyaw, maxyaw, maxyaw)
22 |
23 | states = []
24 | for iyaw in yaw:
25 | for iy in y:
26 | for ix in x:
27 | states.append([ix, iy, iyaw])
28 | print("nstate:", len(states))
29 |
30 | return states
31 |
32 |
33 | def search_nearest_one_from_lookuptable(tx, ty, tyaw, lookuptable):
34 | mind = float("inf")
35 | minid = -1
36 |
37 | for (i, table) in enumerate(lookuptable):
38 |
39 | dx = tx - table[0]
40 | dy = ty - table[1]
41 | dyaw = tyaw - table[2]
42 | d = math.sqrt(dx ** 2 + dy ** 2 + dyaw ** 2)
43 | if d <= mind:
44 | minid = i
45 | mind = d
46 |
47 | # print(minid)
48 |
49 | return lookuptable[minid]
50 |
51 |
52 | def save_lookup_table(fname, table):
53 | mt = np.array(table)
54 | print(mt)
55 | # save csv
56 | df = pd.DataFrame()
57 | df["x"] = mt[:, 0]
58 | df["y"] = mt[:, 1]
59 | df["yaw"] = mt[:, 2]
60 | df["s"] = mt[:, 3]
61 | df["km"] = mt[:, 4]
62 | df["kf"] = mt[:, 5]
63 | df.to_csv(fname, index=None)
64 |
65 | print("lookup table file is saved as " + fname)
66 |
67 |
68 | def generate_lookup_table():
69 | states = calc_states_list()
70 | k0 = 0.0
71 |
72 | # x, y, yaw, s, km, kf
73 | lookuptable = [[1.0, 0.0, 0.0, 1.0, 0.0, 0.0]]
74 |
75 | for state in states:
76 | bestp = search_nearest_one_from_lookuptable(
77 | state[0], state[1], state[2], lookuptable)
78 |
79 | target = motion_model.State(x=state[0], y=state[1], yaw=state[2])
80 | init_p = np.array(
81 | [math.sqrt(state[0] ** 2 + state[1] ** 2), bestp[4], bestp[5]]).reshape(3, 1)
82 |
83 | x, y, yaw, p = planner.optimize_trajectory(target, k0, init_p)
84 |
85 | if x is not None:
86 | print("find good path")
87 | lookuptable.append(
88 | [x[-1], y[-1], yaw[-1], float(p[0]), float(p[1]), float(p[2])])
89 |
90 | print("finish lookup table generation")
91 |
92 | save_lookup_table("lookuptable.csv", lookuptable)
93 |
94 | for table in lookuptable:
95 | xc, yc, yawc = motion_model.generate_trajectory(
96 | table[3], table[4], table[5], k0)
97 | plt.plot(xc, yc, "-r")
98 | xc, yc, yawc = motion_model.generate_trajectory(
99 | table[3], -table[4], -table[5], k0)
100 | plt.plot(xc, yc, "-r")
101 |
102 | plt.grid(True)
103 | plt.axis("equal")
104 | plt.show()
105 |
106 | print("Done")
107 |
108 |
109 | def main():
110 | generate_lookup_table()
111 |
112 |
113 | if __name__ == '__main__':
114 | main()
115 |
--------------------------------------------------------------------------------
/PathPlanning/ModelPredictiveTrajectoryGenerator/model_predictive_trajectory_generator.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Model trajectory generator
4 |
5 | author: Atsushi Sakai(@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 |
11 | import matplotlib.pyplot as plt
12 | import numpy as np
13 |
14 | import motion_model
15 |
16 | # optimization parameter
17 | max_iter = 100
18 | h = np.array([0.5, 0.02, 0.02]).T # parameter sampling distance
19 | cost_th = 0.1
20 |
21 | show_animation = True
22 |
23 |
24 | def plot_arrow(x, y, yaw, length=1.0, width=0.5, fc="r", ec="k"): # pragma: no cover
25 | """
26 | Plot arrow
27 | """
28 | plt.arrow(x, y, length * math.cos(yaw), length * math.sin(yaw),
29 | fc=fc, ec=ec, head_width=width, head_length=width)
30 | plt.plot(x, y)
31 | plt.plot(0, 0)
32 |
33 |
34 | def calc_diff(target, x, y, yaw):
35 | d = np.array([target.x - x[-1],
36 | target.y - y[-1],
37 | motion_model.pi_2_pi(target.yaw - yaw[-1])])
38 |
39 | return d
40 |
41 |
42 | def calc_j(target, p, h, k0):
43 | xp, yp, yawp = motion_model.generate_last_state(
44 | p[0, 0] + h[0], p[1, 0], p[2, 0], k0)
45 | dp = calc_diff(target, [xp], [yp], [yawp])
46 | xn, yn, yawn = motion_model.generate_last_state(
47 | p[0, 0] - h[0], p[1, 0], p[2, 0], k0)
48 | dn = calc_diff(target, [xn], [yn], [yawn])
49 | d1 = np.array((dp - dn) / (2.0 * h[0])).reshape(3, 1)
50 |
51 | xp, yp, yawp = motion_model.generate_last_state(
52 | p[0, 0], p[1, 0] + h[1], p[2, 0], k0)
53 | dp = calc_diff(target, [xp], [yp], [yawp])
54 | xn, yn, yawn = motion_model.generate_last_state(
55 | p[0, 0], p[1, 0] - h[1], p[2, 0], k0)
56 | dn = calc_diff(target, [xn], [yn], [yawn])
57 | d2 = np.array((dp - dn) / (2.0 * h[1])).reshape(3, 1)
58 |
59 | xp, yp, yawp = motion_model.generate_last_state(
60 | p[0, 0], p[1, 0], p[2, 0] + h[2], k0)
61 | dp = calc_diff(target, [xp], [yp], [yawp])
62 | xn, yn, yawn = motion_model.generate_last_state(
63 | p[0, 0], p[1, 0], p[2, 0] - h[2], k0)
64 | dn = calc_diff(target, [xn], [yn], [yawn])
65 | d3 = np.array((dp - dn) / (2.0 * h[2])).reshape(3, 1)
66 |
67 | J = np.hstack((d1, d2, d3))
68 |
69 | return J
70 |
71 |
72 | def selection_learning_param(dp, p, k0, target):
73 | mincost = float("inf")
74 | mina = 1.0
75 | maxa = 2.0
76 | da = 0.5
77 |
78 | for a in np.arange(mina, maxa, da):
79 | tp = p + a * dp
80 | xc, yc, yawc = motion_model.generate_last_state(
81 | tp[0], tp[1], tp[2], k0)
82 | dc = calc_diff(target, [xc], [yc], [yawc])
83 | cost = np.linalg.norm(dc)
84 |
85 | if cost <= mincost and a != 0.0:
86 | mina = a
87 | mincost = cost
88 |
89 | # print(mincost, mina)
90 | # input()
91 |
92 | return mina
93 |
94 |
95 | def show_trajectory(target, xc, yc): # pragma: no cover
96 | plt.clf()
97 | plot_arrow(target.x, target.y, target.yaw)
98 | plt.plot(xc, yc, "-r")
99 | plt.axis("equal")
100 | plt.grid(True)
101 | plt.pause(0.1)
102 |
103 |
104 | def optimize_trajectory(target, k0, p):
105 | for i in range(max_iter):
106 | xc, yc, yawc = motion_model.generate_trajectory(p[0], p[1], p[2], k0)
107 | dc = np.array(calc_diff(target, xc, yc, yawc)).reshape(3, 1)
108 |
109 | cost = np.linalg.norm(dc)
110 | if cost <= cost_th:
111 | print("path is ok cost is:" + str(cost))
112 | break
113 |
114 | J = calc_j(target, p, h, k0)
115 | try:
116 | dp = - np.linalg.inv(J) @ dc
117 | except np.linalg.linalg.LinAlgError:
118 | print("cannot calc path LinAlgError")
119 | xc, yc, yawc, p = None, None, None, None
120 | break
121 | alpha = selection_learning_param(dp, p, k0, target)
122 |
123 | p += alpha * np.array(dp)
124 | # print(p.T)
125 |
126 | if show_animation: # pragma: no cover
127 | show_trajectory(target, xc, yc)
128 | else:
129 | xc, yc, yawc, p = None, None, None, None
130 | print("cannot calc path")
131 |
132 | return xc, yc, yawc, p
133 |
134 |
135 | def test_optimize_trajectory(): # pragma: no cover
136 |
137 | # target = motion_model.State(x=5.0, y=2.0, yaw=np.deg2rad(00.0))
138 | target = motion_model.State(x=5.0, y=2.0, yaw=np.deg2rad(90.0))
139 | k0 = 0.0
140 |
141 | init_p = np.array([6.0, 0.0, 0.0]).reshape(3, 1)
142 |
143 | x, y, yaw, p = optimize_trajectory(target, k0, init_p)
144 |
145 | if show_animation:
146 | show_trajectory(target, x, y)
147 | plot_arrow(target.x, target.y, target.yaw)
148 | plt.axis("equal")
149 | plt.grid(True)
150 | plt.show()
151 |
152 |
153 | def main(): # pragma: no cover
154 | print(__file__ + " start!!")
155 | test_optimize_trajectory()
156 |
157 |
158 | if __name__ == '__main__':
159 | main()
160 |
--------------------------------------------------------------------------------
/PathPlanning/ModelPredictiveTrajectoryGenerator/motion_model.py:
--------------------------------------------------------------------------------
1 | import math
2 | import numpy as np
3 | import scipy.interpolate
4 |
5 | # motion parameter
6 | L = 1.0 # wheel base
7 | ds = 0.1 # course distanse
8 | v = 10.0 / 3.6 # velocity [m/s]
9 |
10 |
11 | class State:
12 |
13 | def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0):
14 | self.x = x
15 | self.y = y
16 | self.yaw = yaw
17 | self.v = v
18 |
19 |
20 | def pi_2_pi(angle):
21 | return (angle + math.pi) % (2 * math.pi) - math.pi
22 |
23 |
24 | def update(state, v, delta, dt, L):
25 |
26 | state.v = v
27 | state.x = state.x + state.v * math.cos(state.yaw) * dt
28 | state.y = state.y + state.v * math.sin(state.yaw) * dt
29 | state.yaw = state.yaw + state.v / L * math.tan(delta) * dt
30 | state.yaw = pi_2_pi(state.yaw)
31 |
32 | return state
33 |
34 |
35 | def generate_trajectory(s, km, kf, k0):
36 |
37 | n = s / ds
38 | time = s / v # [s]
39 |
40 | if isinstance(time, type(np.array([]))): time = time[0]
41 | if isinstance(km, type(np.array([]))): km = km[0]
42 | if isinstance(kf, type(np.array([]))): kf = kf[0]
43 |
44 | tk = np.array([0.0, time / 2.0, time])
45 | kk = np.array([k0, km, kf])
46 | t = np.arange(0.0, time, time / n)
47 | fkp = scipy.interpolate.interp1d(tk, kk, kind="quadratic")
48 | kp = [fkp(ti) for ti in t]
49 | dt = float(time / n)
50 |
51 | # plt.plot(t, kp)
52 | # plt.show()
53 |
54 | state = State()
55 | x, y, yaw = [state.x], [state.y], [state.yaw]
56 |
57 | for ikp in kp:
58 | state = update(state, v, ikp, dt, L)
59 | x.append(state.x)
60 | y.append(state.y)
61 | yaw.append(state.yaw)
62 |
63 | return x, y, yaw
64 |
65 |
66 | def generate_last_state(s, km, kf, k0):
67 |
68 | n = s / ds
69 | time = s / v # [s]
70 |
71 | if isinstance(time, type(np.array([]))): time = time[0]
72 | if isinstance(km, type(np.array([]))): km = km[0]
73 | if isinstance(kf, type(np.array([]))): kf = kf[0]
74 |
75 | tk = np.array([0.0, time / 2.0, time])
76 | kk = np.array([k0, km, kf])
77 | t = np.arange(0.0, time, time / n)
78 | fkp = scipy.interpolate.interp1d(tk, kk, kind="quadratic")
79 | kp = [fkp(ti) for ti in t]
80 | dt = time / n
81 |
82 | # plt.plot(t, kp)
83 | # plt.show()
84 |
85 | state = State()
86 |
87 | _ = [update(state, v, ikp, dt, L) for ikp in kp]
88 |
89 | return state.x, state.y, state.yaw
90 |
--------------------------------------------------------------------------------
/PathPlanning/PotentialFieldPlanning/potential_field_planning.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Potential Field based path planner
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | Ref:
8 | https://www.cs.cmu.edu/~motionplanning/lecture/Chap4-Potential-Field_howie.pdf
9 |
10 | """
11 |
12 | import numpy as np
13 | import matplotlib.pyplot as plt
14 |
15 | # Parameters
16 | KP = 5.0 # attractive potential gain
17 | ETA = 100.0 # repulsive potential gain
18 | AREA_WIDTH = 30.0 # potential area width [m]
19 |
20 | show_animation = True
21 |
22 |
23 | def calc_potential_field(gx, gy, ox, oy, reso, rr):
24 | minx = min(ox) - AREA_WIDTH / 2.0
25 | miny = min(oy) - AREA_WIDTH / 2.0
26 | maxx = max(ox) + AREA_WIDTH / 2.0
27 | maxy = max(oy) + AREA_WIDTH / 2.0
28 | xw = int(round((maxx - minx) / reso))
29 | yw = int(round((maxy - miny) / reso))
30 |
31 | # calc each potential
32 | pmap = [[0.0 for i in range(yw)] for i in range(xw)]
33 |
34 | for ix in range(xw):
35 | x = ix * reso + minx
36 |
37 | for iy in range(yw):
38 | y = iy * reso + miny
39 | ug = calc_attractive_potential(x, y, gx, gy)
40 | uo = calc_repulsive_potential(x, y, ox, oy, rr)
41 | uf = ug + uo
42 | pmap[ix][iy] = uf
43 |
44 | return pmap, minx, miny
45 |
46 |
47 | def calc_attractive_potential(x, y, gx, gy):
48 | return 0.5 * KP * np.hypot(x - gx, y - gy)
49 |
50 |
51 | def calc_repulsive_potential(x, y, ox, oy, rr):
52 | # search nearest obstacle
53 | minid = -1
54 | dmin = float("inf")
55 | for i, _ in enumerate(ox):
56 | d = np.hypot(x - ox[i], y - oy[i])
57 | if dmin >= d:
58 | dmin = d
59 | minid = i
60 |
61 | # calc repulsive potential
62 | dq = np.hypot(x - ox[minid], y - oy[minid])
63 |
64 | if dq <= rr:
65 | if dq <= 0.1:
66 | dq = 0.1
67 |
68 | return 0.5 * ETA * (1.0 / dq - 1.0 / rr) ** 2
69 | else:
70 | return 0.0
71 |
72 |
73 | def get_motion_model():
74 | # dx, dy
75 | motion = [[1, 0],
76 | [0, 1],
77 | [-1, 0],
78 | [0, -1],
79 | [-1, -1],
80 | [-1, 1],
81 | [1, -1],
82 | [1, 1]]
83 |
84 | return motion
85 |
86 |
87 | def potential_field_planning(sx, sy, gx, gy, ox, oy, reso, rr):
88 |
89 | # calc potential field
90 | pmap, minx, miny = calc_potential_field(gx, gy, ox, oy, reso, rr)
91 |
92 | # search path
93 | d = np.hypot(sx - gx, sy - gy)
94 | ix = round((sx - minx) / reso)
95 | iy = round((sy - miny) / reso)
96 | gix = round((gx - minx) / reso)
97 | giy = round((gy - miny) / reso)
98 |
99 | if show_animation:
100 | draw_heatmap(pmap)
101 | # for stopping simulation with the esc key.
102 | plt.gcf().canvas.mpl_connect('key_release_event',
103 | lambda event: [exit(0) if event.key == 'escape' else None])
104 | plt.plot(ix, iy, "*k")
105 | plt.plot(gix, giy, "*m")
106 |
107 | rx, ry = [sx], [sy]
108 | motion = get_motion_model()
109 | while d >= reso:
110 | minp = float("inf")
111 | minix, miniy = -1, -1
112 | for i, _ in enumerate(motion):
113 | inx = int(ix + motion[i][0])
114 | iny = int(iy + motion[i][1])
115 | if inx >= len(pmap) or iny >= len(pmap[0]):
116 | p = float("inf") # outside area
117 | else:
118 | p = pmap[inx][iny]
119 | if minp > p:
120 | minp = p
121 | minix = inx
122 | miniy = iny
123 | ix = minix
124 | iy = miniy
125 | xp = ix * reso + minx
126 | yp = iy * reso + miny
127 | d = np.hypot(gx - xp, gy - yp)
128 | rx.append(xp)
129 | ry.append(yp)
130 |
131 | if show_animation:
132 | plt.plot(ix, iy, ".r")
133 | plt.pause(0.01)
134 |
135 | print("Goal!!")
136 |
137 | return rx, ry
138 |
139 |
140 | def draw_heatmap(data):
141 | data = np.array(data).T
142 | plt.pcolor(data, vmax=100.0, cmap=plt.cm.Blues)
143 |
144 |
145 | def main():
146 | print("potential_field_planning start")
147 |
148 | sx = 0.0 # start x position [m]
149 | sy = 10.0 # start y positon [m]
150 | gx = 30.0 # goal x position [m]
151 | gy = 30.0 # goal y position [m]
152 | grid_size = 0.5 # potential grid size [m]
153 | robot_radius = 5.0 # robot radius [m]
154 |
155 | ox = [15.0, 5.0, 20.0, 25.0] # obstacle x position list [m]
156 | oy = [25.0, 15.0, 26.0, 25.0] # obstacle y position list [m]
157 |
158 | if show_animation:
159 | plt.grid(True)
160 | plt.axis("equal")
161 |
162 | # path generation
163 | _, _ = potential_field_planning(
164 | sx, sy, gx, gy, ox, oy, grid_size, robot_radius)
165 |
166 | if show_animation:
167 | plt.show()
168 |
169 |
170 | if __name__ == '__main__':
171 | print(__file__ + " start!!")
172 | main()
173 | print(__file__ + " Done!!")
174 |
--------------------------------------------------------------------------------
/PathPlanning/QuinticPolynomialsPlanner/quintic_polynomials_planner.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Quintic polynomials planner"
8 | ]
9 | },
10 | {
11 | "cell_type": "markdown",
12 | "metadata": {},
13 | "source": [
14 | "## Quintic polynomials for one dimensional robot motion\n",
15 | "\n",
16 | "We assume a one-dimensional robot motion $x(t)$ at time $t$ is formulated as a quintic polynomials based on time as follows:\n",
17 | "\n",
18 | "$x(t) = a_0+a_1t+a_2t^2+a_3t^3+a_4t^4+a_5t^5$ --(1)\n",
19 | "\n",
20 | "$a_0, a_1. a_2, a_3, a_4, a_5$ are parameters of the quintic polynomial.\n",
21 | "\n",
22 | "It is assumed that terminal states (start and end) are known as boundary conditions.\n",
23 | "\n",
24 | "Start position, velocity, and acceleration are $x_s, v_s, a_s$ respectively.\n",
25 | "\n",
26 | "End position, velocity, and acceleration are $x_e, v_e, a_e$ respectively.\n",
27 | "\n",
28 | "So, when time is 0.\n",
29 | "\n",
30 | "$x(0) = a_0 = x_s$ -- (2)\n",
31 | "\n",
32 | "Then, differentiating the equation (1) with t, \n",
33 | "\n",
34 | "$x'(t) = a_1+2a_2t+3a_3t^2+4a_4t^3+5a_5t^4$ -- (3)\n",
35 | "\n",
36 | "So, when time is 0,\n",
37 | "\n",
38 | "$x'(0) = a_1 = v_s$ -- (4)\n",
39 | "\n",
40 | "Then, differentiating the equation (3) with t again, \n",
41 | "\n",
42 | "$x''(t) = 2a_2+6a_3t+12a_4t^2$ -- (5)\n",
43 | "\n",
44 | "So, when time is 0,\n",
45 | "\n",
46 | "$x''(0) = 2a_2 = a_s$ -- (6)\n",
47 | "\n",
48 | "so, we can calculate $a_0$, $a_1$, $a_2$ with eq. (2), (4), (6) and boundary conditions.\n",
49 | "\n",
50 | "$a_3, a_4, a_5$ are still unknown in eq(1).\n"
51 | ]
52 | },
53 | {
54 | "cell_type": "markdown",
55 | "metadata": {},
56 | "source": [
57 | "We assume that the end time for a maneuver is $T$, we can get these equations from eq (1), (3), (5):\n",
58 | "\n",
59 | "$x(T)=a_0+a_1T+a_2T^2+a_3T^3+a_4T^4+a_5T^5=x_e$ -- (7)\n",
60 | "\n",
61 | "$x'(T)=a_1+2a_2T+3a_3T^2+4a_4T^3+5a_5T^4=v_e$ -- (8)\n",
62 | "\n",
63 | "$x''(T)=2a_2+6a_3T+12a_4T^2+20a_5T^3=a_e$ -- (9)\n"
64 | ]
65 | },
66 | {
67 | "cell_type": "markdown",
68 | "metadata": {},
69 | "source": [
70 | "From eq (7), (8), (9), we can calculate $a_3, a_4, a_5$ to solve the linear equations.\n",
71 | "\n",
72 | "$Ax=b$\n",
73 | "\n",
74 | "$\\begin{bmatrix} T^3 & T^4 & T^5 \\\\ 3T^2 & 4T^3 & 5T^4 \\\\ 6T & 12T^2 & 20T^3 \\end{bmatrix}\n",
75 | "\\begin{bmatrix} a_3\\\\ a_4\\\\ a_5\\end{bmatrix}=\\begin{bmatrix} x_e-x_s-v_sT-0.5a_sT^2\\\\ v_e-v_s-a_sT\\\\ a_e-a_s\\end{bmatrix}$\n",
76 | "\n",
77 | "We can get all unknown parameters now"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "## Quintic polynomials for two dimensional robot motion (x-y)\n",
85 | "\n",
86 | "If you use two quintic polynomials along x axis and y axis, you can plan for two dimensional robot motion in x-y plane.\n",
87 | "\n",
88 | "$x(t) = a_0+a_1t+a_2t^2+a_3t^3+a_4t^4+a_5t^5$ --(10)\n",
89 | "\n",
90 | "$y(t) = b_0+b_1t+b_2t^2+b_3t^3+b_4t^4+b_5t^5$ --(11)\n",
91 | "\n",
92 | "It is assumed that terminal states (start and end) are known as boundary conditions.\n",
93 | "\n",
94 | "Start position, orientation, velocity, and acceleration are $x_s, y_s, \\theta_s, v_s, a_s$ respectively.\n",
95 | "\n",
96 | "End position, orientation, velocity, and acceleration are $x_e, y_e. \\theta_e, v_e, a_e$ respectively.\n",
97 | "\n",
98 | "Each velocity and acceleration boundary condition can be calculated with each orientation.\n",
99 | "\n",
100 | "$v_{xs}=v_scos(\\theta_s), v_{ys}=v_ssin(\\theta_s)$\n",
101 | "\n",
102 | "$v_{xe}=v_ecos(\\theta_e), v_{ye}=v_esin(\\theta_e)$\n"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": null,
108 | "metadata": {},
109 | "outputs": [],
110 | "source": []
111 | }
112 | ],
113 | "metadata": {
114 | "kernelspec": {
115 | "display_name": "Python 3",
116 | "language": "python",
117 | "name": "python3"
118 | },
119 | "language_info": {
120 | "codemirror_mode": {
121 | "name": "ipython",
122 | "version": 3
123 | },
124 | "file_extension": ".py",
125 | "mimetype": "text/x-python",
126 | "name": "python",
127 | "nbconvert_exporter": "python",
128 | "pygments_lexer": "ipython3",
129 | "version": "3.7.5"
130 | },
131 | "pycharm": {
132 | "stem_cell": {
133 | "cell_type": "raw",
134 | "metadata": {
135 | "collapsed": false
136 | },
137 | "source": []
138 | }
139 | }
140 | },
141 | "nbformat": 4,
142 | "nbformat_minor": 1
143 | }
144 |
--------------------------------------------------------------------------------
/PathPlanning/RRT/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/RRT/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/RRT/figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/RRT/figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/RRT/rrt_with_pathsmoothing.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Path planning Sample Code with RRT with path smoothing
4 |
5 | @author: AtsushiSakai(@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 | import os
11 | import random
12 | import sys
13 |
14 | import matplotlib.pyplot as plt
15 |
16 | sys.path.append(os.path.dirname(os.path.abspath(__file__)))
17 |
18 | try:
19 | from rrt import RRT
20 | except ImportError:
21 | raise
22 |
23 | show_animation = True
24 |
25 |
26 | def get_path_length(path):
27 | le = 0
28 | for i in range(len(path) - 1):
29 | dx = path[i + 1][0] - path[i][0]
30 | dy = path[i + 1][1] - path[i][1]
31 | d = math.sqrt(dx * dx + dy * dy)
32 | le += d
33 |
34 | return le
35 |
36 |
37 | def get_target_point(path, targetL):
38 | le = 0
39 | ti = 0
40 | lastPairLen = 0
41 | for i in range(len(path) - 1):
42 | dx = path[i + 1][0] - path[i][0]
43 | dy = path[i + 1][1] - path[i][1]
44 | d = math.sqrt(dx * dx + dy * dy)
45 | le += d
46 | if le >= targetL:
47 | ti = i - 1
48 | lastPairLen = d
49 | break
50 |
51 | partRatio = (le - targetL) / lastPairLen
52 |
53 | x = path[ti][0] + (path[ti + 1][0] - path[ti][0]) * partRatio
54 | y = path[ti][1] + (path[ti + 1][1] - path[ti][1]) * partRatio
55 |
56 | return [x, y, ti]
57 |
58 |
59 | def line_collision_check(first, second, obstacleList):
60 | # Line Equation
61 |
62 | x1 = first[0]
63 | y1 = first[1]
64 | x2 = second[0]
65 | y2 = second[1]
66 |
67 | try:
68 | a = y2 - y1
69 | b = -(x2 - x1)
70 | c = y2 * (x2 - x1) - x2 * (y2 - y1)
71 | except ZeroDivisionError:
72 | return False
73 |
74 | for (ox, oy, size) in obstacleList:
75 | d = abs(a * ox + b * oy + c) / (math.sqrt(a * a + b * b))
76 | if d <= size:
77 | return False
78 |
79 | return True # OK
80 |
81 |
82 | def path_smoothing(path, max_iter, obstacle_list):
83 | le = get_path_length(path)
84 |
85 | for i in range(max_iter):
86 | # Sample two points
87 | pickPoints = [random.uniform(0, le), random.uniform(0, le)]
88 | pickPoints.sort()
89 | first = get_target_point(path, pickPoints[0])
90 | second = get_target_point(path, pickPoints[1])
91 |
92 | if first[2] <= 0 or second[2] <= 0:
93 | continue
94 |
95 | if (second[2] + 1) > len(path):
96 | continue
97 |
98 | if second[2] == first[2]:
99 | continue
100 |
101 | # collision check
102 | if not line_collision_check(first, second, obstacle_list):
103 | continue
104 |
105 | # Create New path
106 | newPath = []
107 | newPath.extend(path[:first[2] + 1])
108 | newPath.append([first[0], first[1]])
109 | newPath.append([second[0], second[1]])
110 | newPath.extend(path[second[2] + 1:])
111 | path = newPath
112 | le = get_path_length(path)
113 |
114 | return path
115 |
116 |
117 | def main():
118 | # ====Search Path with RRT====
119 | # Parameter
120 | obstacleList = [
121 | (5, 5, 1),
122 | (3, 6, 2),
123 | (3, 8, 2),
124 | (3, 10, 2),
125 | (7, 5, 2),
126 | (9, 5, 2)
127 | ] # [x,y,size]
128 | rrt = RRT(start=[0, 0], goal=[6, 10],
129 | rand_area=[-2, 15], obstacle_list=obstacleList)
130 | path = rrt.planning(animation=show_animation)
131 |
132 | # Path smoothing
133 | maxIter = 1000
134 | smoothedPath = path_smoothing(path, maxIter, obstacleList)
135 |
136 | # Draw final path
137 | if show_animation:
138 | rrt.draw_graph()
139 | plt.plot([x for (x, y) in path], [y for (x, y) in path], '-r')
140 |
141 | plt.plot([x for (x, y) in smoothedPath], [
142 | y for (x, y) in smoothedPath], '-c')
143 |
144 | plt.grid(True)
145 | plt.pause(0.01) # Need for Mac
146 | plt.show()
147 |
148 |
149 | if __name__ == '__main__':
150 | main()
151 |
--------------------------------------------------------------------------------
/PathPlanning/RRTDubins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/RRTDubins/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/RRTStar/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/RRTStar/Figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/RRTStar/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/RRTStar/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/RRTStarReedsShepp/figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/RRTStarReedsShepp/figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/Figure_1.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/Figure_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/Figure_2.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/Figure_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/Figure_3.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/Figure_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/Figure_4.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/Figure_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/Figure_5.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/Figure_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/Figure_6.png
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/StateLatticePlanner/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/StateLatticePlanner/lookuptable.csv:
--------------------------------------------------------------------------------
1 | x,y,yaw,s,km,kf
2 | 1.0,0.0,0.0,1.0,0.0,0.0
3 | 0.9734888894493215,-0.009758406565994977,0.5358080146312756,0.9922329557399788,-0.10222538550473198,3.0262632253145982
4 | 10.980728996433243,-0.0003093605787364978,0.522622783944529,11.000391678142623,0.00010296091030877934,0.2731556687244648
5 | 16.020309241920156,0.0001292339008200291,0.5243399938698222,16.100019813021202,0.00013263212395994706,0.18999138959173634
6 | 20.963495745193626,-0.00033031017429944326,0.5226120033275024,21.10082901143343,0.00011687467551566884,0.14550546012583987
7 | 6.032553475650599,2.008504211720188,0.5050517859971599,6.400329805864408,0.1520002249689879,-0.13105940607691127
8 | 10.977487445230075,2.0078696810700034,0.5263634407901872,11.201040572298973,0.04895863722280565,0.08356555007223682
9 | 15.994057699325753,2.025659106131227,0.5303858891065698,16.200300421483128,0.0235708657178127,0.10002225103921249
10 | 20.977228843605943,2.0281289825388513,0.5300376140865567,21.20043308669372,0.013795675421657671,0.09331700188063087
11 | 25.95453914157977,1.9926432818499131,0.5226203078411618,26.200880299840527,0.00888830054451281,0.0830622000626594
12 | 0.9999999999999999,0.0,0.0,1.0,0.0,0.0
13 | 5.999999999670752,5.231312388722274e-05,1.4636120911014667e-05,5.996117185283419,4.483756968024291e-06,-3.4422519205046243e-06
14 | 10.999749470720566,-0.011978787043239986,0.022694802592583763,10.99783855994015,-0.00024209503125174496,0.01370491008661795
15 | 15.999885224357776,-0.018937230084507616,0.011565580878015763,15.99839381389597,-0.0002086399372890716,0.005267247862667496
16 | 20.999882688881286,-0.030304200126461317,0.009117836885732596,20.99783120184498,-0.00020013159571184735,0.0034529188783636866
17 | 25.999911270030413,-0.025754431694664327,0.0074809531598503615,25.99674782258235,-0.0001111138115390646,0.0021946603965658368
18 | 10.952178818975062,1.993067260835455,0.0045572480669707136,11.17961498195845,0.04836813285436623,-0.19328716251760758
19 | 16.028306009258,2.0086286208315407,0.009306594796759554,16.122411866381054,0.02330689045417979,-0.08877002085985948
20 | 20.971603115419715,1.9864158336174966,0.007016819403167119,21.093006725076872,0.013439123130720928,-0.05238318300611623
21 | 25.997019676818372,1.9818581321430093,0.007020172975955249,26.074021794586585,0.00876496148602802,-0.03362579291686346
22 | 16.017903482982604,4.009490840390534,-5.293114796312698e-05,16.600937712976638,0.044543450568614244,-0.17444651314466567
23 | 20.98845988414163,3.956600199823583,-0.01050744134070728,21.40149119463485,0.02622674388276059,-0.10625681152519345
24 | 25.979448381017914,3.9968223055054977,-0.00012819253386682928,26.30504721211744,0.017467093413146118,-0.06914750106424669
25 | 25.96268055563514,5.9821266846168,4.931311239565056e-05,26.801388563459287,0.025426008913226557,-0.10047663078001536
26 |
--------------------------------------------------------------------------------
/PathPlanning/VisibilityRoadMap/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/VisibilityRoadMap/__init__.py
--------------------------------------------------------------------------------
/PathPlanning/VisibilityRoadMap/geometry.py:
--------------------------------------------------------------------------------
1 | class Geometry:
2 | class Point:
3 | def __init__(self, x, y):
4 | self.x = x
5 | self.y = y
6 |
7 | @staticmethod
8 | def is_seg_intersect(p1, q1, p2, q2):
9 |
10 | def on_segment(p, q, r):
11 | if ((q.x <= max(p.x, r.x)) and (q.x >= min(p.x, r.x)) and
12 | (q.y <= max(p.y, r.y)) and (q.y >= min(p.y, r.y))):
13 | return True
14 | return False
15 |
16 | def orientation(p, q, r):
17 | val = (float(q.y - p.y) * (r.x - q.x)) - (
18 | float(q.x - p.x) * (r.y - q.y))
19 | if val > 0:
20 | return 1
21 | if val < 0:
22 | return 2
23 | return 0
24 |
25 | # Find the 4 orientations required for
26 | # the general and special cases
27 | o1 = orientation(p1, q1, p2)
28 | o2 = orientation(p1, q1, q2)
29 | o3 = orientation(p2, q2, p1)
30 | o4 = orientation(p2, q2, q1)
31 |
32 | if (o1 != o2) and (o3 != o4):
33 | return True
34 | if (o1 == 0) and on_segment(p1, p2, q1):
35 | return True
36 | if (o2 == 0) and on_segment(p1, q2, q1):
37 | return True
38 | if (o3 == 0) and on_segment(p2, p1, q2):
39 | return True
40 | if (o4 == 0) and on_segment(p2, q1, q2):
41 | return True
42 |
43 | return False
--------------------------------------------------------------------------------
/PathPlanning/VoronoiRoadMap/dijkstra_search.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Dijkstra Search library
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import matplotlib.pyplot as plt
10 | import math
11 | import numpy as np
12 |
13 |
14 | class DijkstraSearch:
15 | class Node:
16 | """
17 | Node class for dijkstra search
18 | """
19 |
20 | def __init__(self, x, y, cost=None, parent=None, edge_ids=None):
21 | self.x = x
22 | self.y = y
23 | self.cost = cost
24 | self.parent = parent
25 | self.edge_ids = edge_ids
26 |
27 | def __str__(self):
28 | return str(self.x) + "," + str(self.y) + "," + str(
29 | self.cost) + "," + str(self.parent)
30 |
31 | def __init__(self, show_animation):
32 | self.show_animation = show_animation
33 |
34 | def search(self, sx, sy, gx, gy, node_x, node_y, edge_ids_list):
35 | """
36 | Search shortest path
37 |
38 | sx: start x positions [m]
39 | sy: start y positions [m]
40 | gx: goal x position [m]
41 | gx: goal x position [m]
42 | node_x: node x position
43 | node_y: node y position
44 | edge_ids_list: edge_list each item includes a list of edge ids
45 | """
46 |
47 | start_node = self.Node(sx, sy, 0.0, -1)
48 | goal_node = self.Node(gx, gy, 0.0, -1)
49 | current_node = None
50 |
51 | open_set, close_set = dict(), dict()
52 | open_set[self.find_id(node_x, node_y, start_node)] = start_node
53 |
54 | while True:
55 | if self.has_node_in_set(close_set, goal_node):
56 | print("goal is found!")
57 | goal_node.parent = current_node.parent
58 | goal_node.cost = current_node.cost
59 | break
60 | elif not open_set:
61 | print("Cannot find path")
62 | break
63 |
64 | current_id = min(open_set, key=lambda o: open_set[o].cost)
65 | current_node = open_set[current_id]
66 |
67 | # show graph
68 | if self.show_animation and len(
69 | close_set.keys()) % 2 == 0: # pragma: no cover
70 | plt.plot(current_node.x, current_node.y, "xg")
71 | # for stopping simulation with the esc key.
72 | plt.gcf().canvas.mpl_connect(
73 | 'key_release_event',
74 | lambda event: [exit(0) if event.key == 'escape' else None])
75 | plt.pause(0.1)
76 |
77 | # Remove the item from the open set
78 | del open_set[current_id]
79 | # Add it to the closed set
80 | close_set[current_id] = current_node
81 |
82 | # expand search grid based on motion model
83 | for i in range(len(edge_ids_list[current_id])):
84 | n_id = edge_ids_list[current_id][i]
85 | dx = node_x[n_id] - current_node.x
86 | dy = node_y[n_id] - current_node.y
87 | d = math.hypot(dx, dy)
88 | node = self.Node(node_x[n_id], node_y[n_id],
89 | current_node.cost + d, current_id)
90 |
91 | if n_id in close_set:
92 | continue
93 | # Otherwise if it is already in the open set
94 | if n_id in open_set:
95 | if open_set[n_id].cost > node.cost:
96 | open_set[n_id] = node
97 | else:
98 | open_set[n_id] = node
99 |
100 | # generate final course
101 | rx, ry = self.generate_final_path(close_set, goal_node)
102 |
103 | return rx, ry
104 |
105 | @staticmethod
106 | def generate_final_path(close_set, goal_node):
107 | rx, ry = [goal_node.x], [goal_node.y]
108 | parent = goal_node.parent
109 | while parent != -1:
110 | n = close_set[parent]
111 | rx.append(n.x)
112 | ry.append(n.y)
113 | parent = n.parent
114 | rx, ry = rx[::-1], ry[::-1] # reverse it
115 | return rx, ry
116 |
117 | def has_node_in_set(self, target_set, node):
118 | for key in target_set:
119 | if self.is_same_node(target_set[key], node):
120 | return True
121 | return False
122 |
123 | def find_id(self, node_x_list, node_y_list, target_node):
124 | for i, _ in enumerate(node_x_list):
125 | if self.is_same_node_with_xy(node_x_list[i], node_y_list[i],
126 | target_node):
127 | return i
128 |
129 | @staticmethod
130 | def is_same_node_with_xy(node_x, node_y, node_b):
131 | dist = np.hypot(node_x - node_b.x,
132 | node_y - node_b.y)
133 | return dist <= 0.1
134 |
135 | @staticmethod
136 | def is_same_node(node_a, node_b):
137 | dist = np.hypot(node_a.x - node_b.x,
138 | node_b.y - node_b.y)
139 | return dist <= 0.1
140 |
--------------------------------------------------------------------------------
/PathPlanning/VoronoiRoadMap/kdtree.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Kd tree Search library
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import scipy.spatial
10 |
11 |
12 | class KDTree:
13 | """
14 | Nearest neighbor search class with KDTree
15 | """
16 |
17 | def __init__(self, data):
18 | # store kd-tree
19 | self.tree = scipy.spatial.cKDTree(data)
20 |
21 | def search(self, inp, k=1):
22 | """
23 | Search NN
24 |
25 | inp: input data, single frame or multi frame
26 |
27 | """
28 |
29 | if len(inp.shape) >= 2: # multi input
30 | index = []
31 | dist = []
32 |
33 | for i in inp.T:
34 | idist, iindex = self.tree.query(i, k=k)
35 | index.append(iindex)
36 | dist.append(idist)
37 |
38 | return index, dist
39 |
40 | dist, index = self.tree.query(inp, k=k)
41 | return index, dist
42 |
43 | def search_in_distance(self, inp, r):
44 | """
45 | find points with in a distance r
46 | """
47 |
48 | index = self.tree.query_ball_point(inp, r)
49 | return index
50 |
--------------------------------------------------------------------------------
/PathPlanning/VoronoiRoadMap/voronoi_road_map.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Voronoi Road Map Planner
4 |
5 | author: Atsushi Sakai (@Atsushi_twi)
6 |
7 | """
8 |
9 | import math
10 | import numpy as np
11 | import scipy.spatial
12 | import matplotlib.pyplot as plt
13 | from dijkstra_search import DijkstraSearch
14 | from kdtree import KDTree
15 |
16 | show_animation = True
17 |
18 |
19 | class VoronoiRoadMapPlanner:
20 |
21 | def __init__(self):
22 | # parameter
23 | self.N_KNN = 10 # number of edge from one sampled point
24 | self.MAX_EDGE_LEN = 30.0 # [m] Maximum edge length
25 |
26 | def planning(self, sx, sy, gx, gy, ox, oy, robot_radius):
27 | obstacle_tree = KDTree(np.vstack((ox, oy)).T)
28 |
29 | sample_x, sample_y = self.voronoi_sampling(sx, sy, gx, gy, ox, oy)
30 | if show_animation: # pragma: no cover
31 | plt.plot(sample_x, sample_y, ".b")
32 |
33 | road_map_info = self.generate_road_map_info(
34 | sample_x, sample_y, robot_radius, obstacle_tree)
35 |
36 | rx, ry = DijkstraSearch(show_animation).search(sx, sy, gx, gy,
37 | sample_x, sample_y,
38 | road_map_info)
39 | return rx, ry
40 |
41 | def is_collision(self, sx, sy, gx, gy, rr, obstacle_kd_tree):
42 | x = sx
43 | y = sy
44 | dx = gx - sx
45 | dy = gy - sy
46 | yaw = math.atan2(gy - sy, gx - sx)
47 | d = math.hypot(dx, dy)
48 |
49 | if d >= self.MAX_EDGE_LEN:
50 | return True
51 |
52 | D = rr
53 | n_step = round(d / D)
54 |
55 | for i in range(n_step):
56 | ids, dist = obstacle_kd_tree.search(np.array([x, y]).reshape(2, 1))
57 | if dist[0] <= rr:
58 | return True # collision
59 | x += D * math.cos(yaw)
60 | y += D * math.sin(yaw)
61 |
62 | # goal point check
63 | ids, dist = obstacle_kd_tree.search(np.array([gx, gy]).reshape(2, 1))
64 | if dist[0] <= rr:
65 | return True # collision
66 |
67 | return False # OK
68 |
69 | def generate_road_map_info(self, node_x, node_y, rr, obstacle_tree):
70 | """
71 | Road map generation
72 |
73 | node_x: [m] x positions of sampled points
74 | node_y: [m] y positions of sampled points
75 | rr: Robot Radius[m]
76 | obstacle_tree: KDTree object of obstacles
77 | """
78 |
79 | road_map = []
80 | n_sample = len(node_x)
81 | node_tree = KDTree(np.vstack((node_x, node_y)).T)
82 |
83 | for (i, ix, iy) in zip(range(n_sample), node_x, node_y):
84 |
85 | index, dists = node_tree.search(
86 | np.array([ix, iy]).reshape(2, 1), k=n_sample)
87 |
88 | inds = index[0]
89 | edge_id = []
90 |
91 | for ii in range(1, len(inds)):
92 | nx = node_x[inds[ii]]
93 | ny = node_y[inds[ii]]
94 |
95 | if not self.is_collision(ix, iy, nx, ny, rr, obstacle_tree):
96 | edge_id.append(inds[ii])
97 |
98 | if len(edge_id) >= self.N_KNN:
99 | break
100 |
101 | road_map.append(edge_id)
102 |
103 | # plot_road_map(road_map, sample_x, sample_y)
104 |
105 | return road_map
106 |
107 | @staticmethod
108 | def plot_road_map(road_map, sample_x, sample_y): # pragma: no cover
109 |
110 | for i, _ in enumerate(road_map):
111 | for ii in range(len(road_map[i])):
112 | ind = road_map[i][ii]
113 |
114 | plt.plot([sample_x[i], sample_x[ind]],
115 | [sample_y[i], sample_y[ind]], "-k")
116 |
117 | @staticmethod
118 | def voronoi_sampling(sx, sy, gx, gy, ox, oy):
119 | oxy = np.vstack((ox, oy)).T
120 |
121 | # generate voronoi point
122 | vor = scipy.spatial.Voronoi(oxy)
123 | sample_x = [ix for [ix, _] in vor.vertices]
124 | sample_y = [iy for [_, iy] in vor.vertices]
125 |
126 | sample_x.append(sx)
127 | sample_y.append(sy)
128 | sample_x.append(gx)
129 | sample_y.append(gy)
130 |
131 | return sample_x, sample_y
132 |
133 |
134 | def main():
135 | print(__file__ + " start!!")
136 |
137 | # start and goal position
138 | sx = 10.0 # [m]
139 | sy = 10.0 # [m]
140 | gx = 50.0 # [m]
141 | gy = 50.0 # [m]
142 | robot_size = 5.0 # [m]
143 |
144 | ox = []
145 | oy = []
146 |
147 | for i in range(60):
148 | ox.append(i)
149 | oy.append(0.0)
150 | for i in range(60):
151 | ox.append(60.0)
152 | oy.append(i)
153 | for i in range(61):
154 | ox.append(i)
155 | oy.append(60.0)
156 | for i in range(61):
157 | ox.append(0.0)
158 | oy.append(i)
159 | for i in range(40):
160 | ox.append(20.0)
161 | oy.append(i)
162 | for i in range(40):
163 | ox.append(40.0)
164 | oy.append(60.0 - i)
165 |
166 | if show_animation: # pragma: no cover
167 | plt.plot(ox, oy, ".k")
168 | plt.plot(sx, sy, "^r")
169 | plt.plot(gx, gy, "^c")
170 | plt.grid(True)
171 | plt.axis("equal")
172 |
173 | rx, ry = VoronoiRoadMapPlanner().planning(sx, sy, gx, gy, ox, oy,
174 | robot_size)
175 |
176 | assert rx, 'Cannot found path'
177 |
178 | if show_animation: # pragma: no cover
179 | plt.plot(rx, ry, "-r")
180 | plt.pause(0.1)
181 | plt.show()
182 |
183 |
184 | if __name__ == '__main__':
185 | main()
186 |
--------------------------------------------------------------------------------
/PathPlanning/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathPlanning/__init__.py
--------------------------------------------------------------------------------
/PathTracking/cgmres_nmpc/Figure_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathTracking/cgmres_nmpc/Figure_1.png
--------------------------------------------------------------------------------
/PathTracking/cgmres_nmpc/Figure_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathTracking/cgmres_nmpc/Figure_2.png
--------------------------------------------------------------------------------
/PathTracking/cgmres_nmpc/Figure_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathTracking/cgmres_nmpc/Figure_3.png
--------------------------------------------------------------------------------
/PathTracking/cgmres_nmpc/Figure_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathTracking/cgmres_nmpc/Figure_4.png
--------------------------------------------------------------------------------
/PathTracking/lqr_steer_control/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathTracking/lqr_steer_control/__init__.py
--------------------------------------------------------------------------------
/PathTracking/move_to_pose/move_to_pose.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Move to specified pose
4 |
5 | Author: Daniel Ingram (daniel-s-ingram)
6 | Atsushi Sakai(@Atsushi_twi)
7 |
8 | P. I. Corke, "Robotics, Vision & Control", Springer 2017, ISBN 978-3-319-54413-7
9 |
10 | """
11 |
12 | import matplotlib.pyplot as plt
13 | import numpy as np
14 | from random import random
15 |
16 | # simulation parameters
17 | Kp_rho = 9
18 | Kp_alpha = 15
19 | Kp_beta = -3
20 | dt = 0.01
21 |
22 | show_animation = True
23 |
24 |
25 | def move_to_pose(x_start, y_start, theta_start, x_goal, y_goal, theta_goal):
26 | """
27 | rho is the distance between the robot and the goal position
28 | alpha is the angle to the goal relative to the heading of the robot
29 | beta is the angle between the robot's position and the goal position plus the goal angle
30 |
31 | Kp_rho*rho and Kp_alpha*alpha drive the robot along a line towards the goal
32 | Kp_beta*beta rotates the line so that it is parallel to the goal angle
33 | """
34 | x = x_start
35 | y = y_start
36 | theta = theta_start
37 |
38 | x_diff = x_goal - x
39 | y_diff = y_goal - y
40 |
41 | x_traj, y_traj = [], []
42 |
43 | rho = np.hypot(x_diff, y_diff)
44 | while rho > 0.001:
45 | x_traj.append(x)
46 | y_traj.append(y)
47 |
48 | x_diff = x_goal - x
49 | y_diff = y_goal - y
50 |
51 | # Restrict alpha and beta (angle differences) to the range
52 | # [-pi, pi] to prevent unstable behavior e.g. difference going
53 | # from 0 rad to 2*pi rad with slight turn
54 |
55 | rho = np.hypot(x_diff, y_diff)
56 | alpha = (np.arctan2(y_diff, x_diff)
57 | - theta + np.pi) % (2 * np.pi) - np.pi
58 | beta = (theta_goal - theta - alpha + np.pi) % (2 * np.pi) - np.pi
59 |
60 | v = Kp_rho * rho
61 | w = Kp_alpha * alpha + Kp_beta * beta
62 |
63 | if alpha > np.pi / 2 or alpha < -np.pi / 2:
64 | v = -v
65 |
66 | theta = theta + w * dt
67 | x = x + v * np.cos(theta) * dt
68 | y = y + v * np.sin(theta) * dt
69 |
70 | if show_animation: # pragma: no cover
71 | plt.cla()
72 | plt.arrow(x_start, y_start, np.cos(theta_start),
73 | np.sin(theta_start), color='r', width=0.1)
74 | plt.arrow(x_goal, y_goal, np.cos(theta_goal),
75 | np.sin(theta_goal), color='g', width=0.1)
76 | plot_vehicle(x, y, theta, x_traj, y_traj)
77 |
78 |
79 | def plot_vehicle(x, y, theta, x_traj, y_traj): # pragma: no cover
80 | # Corners of triangular vehicle when pointing to the right (0 radians)
81 | p1_i = np.array([0.5, 0, 1]).T
82 | p2_i = np.array([-0.5, 0.25, 1]).T
83 | p3_i = np.array([-0.5, -0.25, 1]).T
84 |
85 | T = transformation_matrix(x, y, theta)
86 | p1 = np.matmul(T, p1_i)
87 | p2 = np.matmul(T, p2_i)
88 | p3 = np.matmul(T, p3_i)
89 |
90 | plt.plot([p1[0], p2[0]], [p1[1], p2[1]], 'k-')
91 | plt.plot([p2[0], p3[0]], [p2[1], p3[1]], 'k-')
92 | plt.plot([p3[0], p1[0]], [p3[1], p1[1]], 'k-')
93 |
94 | plt.plot(x_traj, y_traj, 'b--')
95 |
96 | # for stopping simulation with the esc key.
97 | plt.gcf().canvas.mpl_connect('key_release_event',
98 | lambda event: [exit(0) if event.key == 'escape' else None])
99 |
100 | plt.xlim(0, 20)
101 | plt.ylim(0, 20)
102 |
103 | plt.pause(dt)
104 |
105 |
106 | def transformation_matrix(x, y, theta):
107 | return np.array([
108 | [np.cos(theta), -np.sin(theta), x],
109 | [np.sin(theta), np.cos(theta), y],
110 | [0, 0, 1]
111 | ])
112 |
113 |
114 | def main():
115 |
116 | for i in range(5):
117 | x_start = 20 * random()
118 | y_start = 20 * random()
119 | theta_start = 2 * np.pi * random() - np.pi
120 | x_goal = 20 * random()
121 | y_goal = 20 * random()
122 | theta_goal = 2 * np.pi * random() - np.pi
123 | print("Initial x: %.2f m\nInitial y: %.2f m\nInitial theta: %.2f rad\n" %
124 | (x_start, y_start, theta_start))
125 | print("Goal x: %.2f m\nGoal y: %.2f m\nGoal theta: %.2f rad\n" %
126 | (x_goal, y_goal, theta_goal))
127 | move_to_pose(x_start, y_start, theta_start, x_goal, y_goal, theta_goal)
128 |
129 |
130 | if __name__ == '__main__':
131 | main()
132 |
--------------------------------------------------------------------------------
/PathTracking/rear_wheel_feedback/Figure_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/PathTracking/rear_wheel_feedback/Figure_2.png
--------------------------------------------------------------------------------
/SLAM/EKFSLAM/animation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/SLAM/EKFSLAM/animation.png
--------------------------------------------------------------------------------
/SLAM/FastSLAM1/animation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/SLAM/FastSLAM1/animation.png
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/LaTeX/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore LaTeX generated files
2 | graphSLAM_formulation.*
3 | !graphSLAM_formulation.tex
4 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/LaTeX/graphSLAM.bib:
--------------------------------------------------------------------------------
1 | @article{blanco2010tutorial,
2 | title={A tutorial on ${SE}(3)$ transformation parameterizations and on-manifold optimization},
3 | author={Blanco, Jose-Luis},
4 | journal={University of Malaga, Tech. Rep},
5 | volume={3},
6 | year={2010},
7 | publisher={Citeseer}
8 | }
9 |
10 | @article{grisetti2010tutorial,
11 | title={A tutorial on graph-based {SLAM}},
12 | author={Grisetti, Giorgio and Kummerle, Rainer and Stachniss, Cyrill and Burgard, Wolfram},
13 | journal={IEEE Intelligent Transportation Systems Magazine},
14 | volume={2},
15 | number={4},
16 | pages={31--43},
17 | year={2010},
18 | publisher={IEEE}
19 | }
20 |
21 | @article{thrun2006graph,
22 | title={The graph {SLAM} algorithm with applications to large-scale mapping of urban structures},
23 | author={Thrun, Sebastian and Montemerlo, Michael},
24 | journal={The International Journal of Robotics Research},
25 | volume={25},
26 | number={5-6},
27 | pages={403--429},
28 | year={2006},
29 | publisher={SAGE Publications}
30 | }
31 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/data/README.rst:
--------------------------------------------------------------------------------
1 | Acknowledgments and References
2 | ==============================
3 |
4 | Thanks to Luca Larlone for allowing inclusion of the `Intel dataset `_ in this repo.
5 |
6 | 1. Carlone, L. and Censi, A., 2014. `From angular manifolds to the integer lattice: Guaranteed orientation estimation with application to pose graph optimization `_. IEEE Transactions on Robotics, 30(2), pp.475-492.
7 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphSLAM_formulation.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/SLAM/GraphBasedSLAM/graphSLAM_formulation.pdf
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
7 | """Graph SLAM solver in Python.
8 |
9 | """
10 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/edge/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/load.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
7 | """Functions for loading graphs.
8 |
9 | """
10 |
11 |
12 | import logging
13 |
14 | import numpy as np
15 |
16 | from .edge.edge_odometry import EdgeOdometry
17 | from .graph import Graph
18 | from .pose.se2 import PoseSE2
19 | from .util import upper_triangular_matrix_to_full_matrix
20 | from .vertex import Vertex
21 |
22 |
23 | _LOGGER = logging.getLogger(__name__)
24 |
25 |
26 | def load_g2o_se2(infile):
27 | """Load an :math:`SE(2)` graph from a .g2o file.
28 |
29 | Parameters
30 | ----------
31 | infile : str
32 | The path to the .g2o file
33 |
34 | Returns
35 | -------
36 | Graph
37 | The loaded graph
38 |
39 | """
40 | edges = []
41 | vertices = []
42 |
43 | with open(infile) as f:
44 | for line in f.readlines():
45 | if line.startswith("VERTEX_SE2"):
46 | numbers = line[10:].split()
47 | arr = np.array([float(number) for number in numbers[1:]], dtype=np.float64)
48 | p = PoseSE2(arr[:2], arr[2])
49 | v = Vertex(int(numbers[0]), p)
50 | vertices.append(v)
51 | continue
52 |
53 | if line.startswith("EDGE_SE2"):
54 | numbers = line[9:].split()
55 | arr = np.array([float(number) for number in numbers[2:]], dtype=np.float64)
56 | vertex_ids = [int(numbers[0]), int(numbers[1])]
57 | estimate = PoseSE2(arr[:2], arr[2])
58 | information = upper_triangular_matrix_to_full_matrix(arr[3:], 3)
59 | e = EdgeOdometry(vertex_ids, information, estimate)
60 | edges.append(e)
61 | continue
62 |
63 | if line.strip():
64 | _LOGGER.warning("Line not supported -- '%s'", line.rstrip())
65 |
66 | return Graph(edges, vertices)
67 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/pose/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/pose/se2.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
7 | r"""Representation of a pose in :math:`SE(2)`.
8 |
9 | """
10 |
11 | import math
12 |
13 | import numpy as np
14 |
15 | from ..util import neg_pi_to_pi
16 |
17 |
18 | class PoseSE2(np.ndarray):
19 | r"""A representation of a pose in :math:`SE(2)`.
20 |
21 | Parameters
22 | ----------
23 | position : np.ndarray, list
24 | The position in :math:`\mathbb{R}^2`
25 | orientation : float
26 | The angle of the pose (in radians)
27 |
28 | """
29 | def __new__(cls, position, orientation):
30 | obj = np.array([position[0], position[1], neg_pi_to_pi(orientation)], dtype=np.float64).view(cls)
31 | return obj
32 |
33 | # pylint: disable=arguments-differ
34 | def copy(self):
35 | """Return a copy of the pose.
36 |
37 | Returns
38 | -------
39 | PoseSE2
40 | A copy of the pose
41 |
42 | """
43 | return PoseSE2(self[:2], self[2])
44 |
45 | def to_array(self):
46 | """Return the pose as a numpy array.
47 |
48 | Returns
49 | -------
50 | np.ndarray
51 | The pose as a numpy array
52 |
53 | """
54 | return np.array(self)
55 |
56 | def to_compact(self):
57 | """Return the pose as a compact numpy array.
58 |
59 | Returns
60 | -------
61 | np.ndarray
62 | The pose as a compact numpy array
63 |
64 | """
65 | return np.array(self)
66 |
67 | def to_matrix(self):
68 | """Return the pose as an :math:`SE(2)` matrix.
69 |
70 | Returns
71 | -------
72 | np.ndarray
73 | The pose as an :math:`SE(2)` matrix
74 |
75 | """
76 | return np.array([[np.cos(self[2]), -np.sin(self[2]), self[0]], [np.sin(self[2]), np.cos(self[2]), self[1]], [0., 0., 1.]], dtype=np.float64)
77 |
78 | @classmethod
79 | def from_matrix(cls, matrix):
80 | """Return the pose as an :math:`SE(2)` matrix.
81 |
82 | Parameters
83 | ----------
84 | matrix : np.ndarray
85 | The :math:`SE(2)` matrix that will be converted to a `PoseSE2` instance
86 |
87 | Returns
88 | -------
89 | PoseSE2
90 | The matrix as a `PoseSE2` object
91 |
92 | """
93 | return cls([matrix[0, 2], matrix[1, 2]], math.atan2(matrix[1, 0], matrix[0, 0]))
94 |
95 | # ======================================================================= #
96 | # #
97 | # Properties #
98 | # #
99 | # ======================================================================= #
100 | @property
101 | def position(self):
102 | """Return the pose's position.
103 |
104 | Returns
105 | -------
106 | np.ndarray
107 | The position portion of the pose
108 |
109 | """
110 | return np.array(self[:2])
111 |
112 | @property
113 | def orientation(self):
114 | """Return the pose's orientation.
115 |
116 | Returns
117 | -------
118 | float
119 | The angle of the pose
120 |
121 | """
122 | return self[2]
123 |
124 | @property
125 | def inverse(self):
126 | """Return the pose's inverse.
127 |
128 | Returns
129 | -------
130 | PoseSE2
131 | The pose's inverse
132 |
133 | """
134 | return PoseSE2([-self[0] * np.cos(self[2]) - self[1] * np.sin(self[2]), self[0] * np.sin(self[2]) - self[1] * np.cos([self[2]])], -self[2])
135 |
136 | # ======================================================================= #
137 | # #
138 | # Magic Methods #
139 | # #
140 | # ======================================================================= #
141 | def __add__(self, other):
142 | r"""Add poses (i.e., pose composition): :math:`p_1 \oplus p_2`.
143 |
144 | Parameters
145 | ----------
146 | other : PoseSE2
147 | The other pose
148 |
149 | Returns
150 | -------
151 | PoseSE2
152 | The result of pose composition
153 |
154 | """
155 | return PoseSE2([self[0] + other[0] * np.cos(self[2]) - other[1] * np.sin(self[2]), self[1] + other[0] * np.sin(self[2]) + other[1] * np.cos(self[2])], neg_pi_to_pi(self[2] + other[2]))
156 |
157 | def __sub__(self, other):
158 | r"""Subtract poses (i.e., inverse pose composition): :math:`p_1 \ominus p_2`.
159 |
160 | Parameters
161 | ----------
162 | other : PoseSE2
163 | The other pose
164 |
165 | Returns
166 | -------
167 | PoseSE2
168 | The result of inverse pose composition
169 |
170 | """
171 | return PoseSE2([(self[0] - other[0]) * np.cos(other[2]) + (self[1] - other[1]) * np.sin(other[2]), (other[0] - self[0]) * np.sin(other[2]) + (self[1] - other[1]) * np.cos(other[2])], neg_pi_to_pi(self[2] - other[2]))
172 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/util.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
7 | """Utility functions used throughout the package.
8 |
9 | """
10 |
11 | import numpy as np
12 |
13 |
14 | TWO_PI = 2 * np.pi
15 |
16 |
17 | def neg_pi_to_pi(angle):
18 | r"""Normalize ``angle`` to be in :math:`[-\pi, \pi)`.
19 |
20 | Parameters
21 | ----------
22 | angle : float
23 | An angle (in radians)
24 |
25 | Returns
26 | -------
27 | float
28 | The angle normalized to :math:`[-\pi, \pi)`
29 |
30 | """
31 | return (angle + np.pi) % (TWO_PI) - np.pi
32 |
33 |
34 | def solve_for_edge_dimensionality(n):
35 | r"""Solve for the dimensionality of an edge.
36 |
37 | In a .g2o file, an edge is specified as `` ``, where only the upper triangular portion of the matrix is provided.
38 |
39 | This solves the problem:
40 |
41 | .. math::
42 |
43 | d + \frac{d (d + 1)}{2} = n
44 |
45 | Returns
46 | -------
47 | int
48 | The dimensionality of the edge
49 |
50 | """
51 | return int(round(np.sqrt(2 * n + 2.25) - 1.5))
52 |
53 |
54 | def upper_triangular_matrix_to_full_matrix(arr, n):
55 | """Given an upper triangular matrix, return the full matrix.
56 |
57 | Parameters
58 | ----------
59 | arr : np.ndarray
60 | The upper triangular portion of the matrix
61 | n : int
62 | The size of the matrix
63 |
64 | Returns
65 | -------
66 | mat : np.ndarray
67 | The full matrix
68 |
69 | """
70 | triu0 = np.triu_indices(n, 0)
71 | triu1 = np.triu_indices(n, 1)
72 | tril1 = np.tril_indices(n, -1)
73 |
74 | mat = np.zeros((n, n), dtype=np.float64)
75 | mat[triu0] = arr
76 | mat[tril1] = mat[triu1]
77 |
78 | return mat
79 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/graphslam/vertex.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2020 Jeff Irion and contributors
2 | #
3 | # This file originated from the `graphslam` package:
4 | #
5 | # https://github.com/JeffLIrion/python-graphslam
6 |
7 | """A ``Vertex`` class.
8 |
9 | """
10 |
11 | import matplotlib.pyplot as plt
12 |
13 |
14 | # pylint: disable=too-few-public-methods
15 | class Vertex:
16 | """A class for representing a vertex in Graph SLAM.
17 |
18 | Parameters
19 | ----------
20 | vertex_id : int
21 | The vertex's unique ID
22 | pose : graphslam.pose.se2.PoseSE2
23 | The pose associated with the vertex
24 | vertex_index : int, None
25 | The vertex's index in the graph's ``vertices`` list
26 |
27 | Attributes
28 | ----------
29 | id : int
30 | The vertex's unique ID
31 | index : int, None
32 | The vertex's index in the graph's ``vertices`` list
33 | pose : graphslam.pose.se2.PoseSE2
34 | The pose associated with the vertex
35 |
36 | """
37 | def __init__(self, vertex_id, pose, vertex_index=None):
38 | self.id = vertex_id
39 | self.pose = pose
40 | self.index = vertex_index
41 |
42 | def to_g2o(self):
43 | """Export the vertex to the .g2o format.
44 |
45 | Returns
46 | -------
47 | str
48 | The vertex in .g2o format
49 |
50 | """
51 | return "VERTEX_SE2 {} {} {} {}\n".format(self.id, self.pose[0], self.pose[1], self.pose[2])
52 |
53 | def plot(self, color='r', marker='o', markersize=3):
54 | """Plot the vertex.
55 |
56 | Parameters
57 | ----------
58 | color : str
59 | The color that will be used to plot the vertex
60 | marker : str
61 | The marker that will be used to plot the vertex
62 | markersize : int
63 | The size of the plotted vertex
64 |
65 | """
66 | x, y = self.pose.position
67 | plt.plot(x, y, color=color, marker=marker, markersize=markersize)
68 |
--------------------------------------------------------------------------------
/SLAM/GraphBasedSLAM/images/Graph_SLAM_optimization.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/SLAM/GraphBasedSLAM/images/Graph_SLAM_optimization.gif
--------------------------------------------------------------------------------
/SLAM/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/SLAM/__init__.py
--------------------------------------------------------------------------------
/SLAM/iterative_closest_point/iterative_closest_point.py:
--------------------------------------------------------------------------------
1 | """
2 | Iterative Closest Point (ICP) SLAM example
3 | author: Atsushi Sakai (@Atsushi_twi), Göktuğ Karakaşlı
4 | """
5 |
6 | import math
7 |
8 | import matplotlib.pyplot as plt
9 | import numpy as np
10 |
11 | # ICP parameters
12 | EPS = 0.0001
13 | MAX_ITER = 100
14 |
15 | show_animation = True
16 |
17 |
18 | def icp_matching(previous_points, current_points):
19 | """
20 | Iterative Closest Point matching
21 | - input
22 | previous_points: 2D points in the previous frame
23 | current_points: 2D points in the current frame
24 | - output
25 | R: Rotation matrix
26 | T: Translation vector
27 | """
28 | H = None # homogeneous transformation matrix
29 |
30 | dError = 1000.0
31 | preError = 1000.0
32 | count = 0
33 |
34 | while dError >= EPS:
35 | count += 1
36 |
37 | if show_animation: # pragma: no cover
38 | plt.cla()
39 | # for stopping simulation with the esc key.
40 | plt.gcf().canvas.mpl_connect('key_release_event',
41 | lambda event: [exit(0) if event.key == 'escape' else None])
42 | plt.plot(previous_points[0, :], previous_points[1, :], ".r")
43 | plt.plot(current_points[0, :], current_points[1, :], ".b")
44 | plt.plot(0.0, 0.0, "xr")
45 | plt.axis("equal")
46 | plt.pause(0.1)
47 |
48 | indexes, error = nearest_neighbor_association(previous_points, current_points)
49 | Rt, Tt = svd_motion_estimation(previous_points[:, indexes], current_points)
50 |
51 | # update current points
52 | current_points = (Rt @ current_points) + Tt[:, np.newaxis]
53 |
54 | H = update_homogeneous_matrix(H, Rt, Tt)
55 |
56 | dError = abs(preError - error)
57 | preError = error
58 | print("Residual:", error)
59 |
60 | if dError <= EPS:
61 | print("Converge", error, dError, count)
62 | break
63 | elif MAX_ITER <= count:
64 | print("Not Converge...", error, dError, count)
65 | break
66 |
67 | R = np.array(H[0:2, 0:2])
68 | T = np.array(H[0:2, 2])
69 |
70 | return R, T
71 |
72 |
73 | def update_homogeneous_matrix(Hin, R, T):
74 |
75 | H = np.zeros((3, 3))
76 |
77 | H[0, 0] = R[0, 0]
78 | H[1, 0] = R[1, 0]
79 | H[0, 1] = R[0, 1]
80 | H[1, 1] = R[1, 1]
81 | H[2, 2] = 1.0
82 |
83 | H[0, 2] = T[0]
84 | H[1, 2] = T[1]
85 |
86 | if Hin is None:
87 | return H
88 | else:
89 | return Hin @ H
90 |
91 |
92 | def nearest_neighbor_association(previous_points, current_points):
93 |
94 | # calc the sum of residual errors
95 | delta_points = previous_points - current_points
96 | d = np.linalg.norm(delta_points, axis=0)
97 | error = sum(d)
98 |
99 | # calc index with nearest neighbor assosiation
100 | d = np.linalg.norm(np.repeat(current_points, previous_points.shape[1], axis=1)
101 | - np.tile(previous_points, (1, current_points.shape[1])), axis=0)
102 | indexes = np.argmin(d.reshape(current_points.shape[1], previous_points.shape[1]), axis=1)
103 |
104 | return indexes, error
105 |
106 |
107 | def svd_motion_estimation(previous_points, current_points):
108 | pm = np.mean(previous_points, axis=1)
109 | cm = np.mean(current_points, axis=1)
110 |
111 | p_shift = previous_points - pm[:, np.newaxis]
112 | c_shift = current_points - cm[:, np.newaxis]
113 |
114 | W = c_shift @ p_shift.T
115 | u, s, vh = np.linalg.svd(W)
116 |
117 | R = (u @ vh).T
118 | t = pm - (R @ cm)
119 |
120 | return R, t
121 |
122 |
123 | def main():
124 | print(__file__ + " start!!")
125 |
126 | # simulation parameters
127 | nPoint = 1000
128 | fieldLength = 50.0
129 | motion = [0.5, 2.0, np.deg2rad(-10.0)] # movement [x[m],y[m],yaw[deg]]
130 |
131 | nsim = 3 # number of simulation
132 |
133 | for _ in range(nsim):
134 |
135 | # previous points
136 | px = (np.random.rand(nPoint) - 0.5) * fieldLength
137 | py = (np.random.rand(nPoint) - 0.5) * fieldLength
138 | previous_points = np.vstack((px, py))
139 |
140 | # current points
141 | cx = [math.cos(motion[2]) * x - math.sin(motion[2]) * y + motion[0]
142 | for (x, y) in zip(px, py)]
143 | cy = [math.sin(motion[2]) * x + math.cos(motion[2]) * y + motion[1]
144 | for (x, y) in zip(px, py)]
145 | current_points = np.vstack((cx, cy))
146 |
147 | R, T = icp_matching(previous_points, current_points)
148 | print("R:", R)
149 | print("T:", T)
150 |
151 |
152 | if __name__ == '__main__':
153 | main()
154 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-slate
2 | show_downloads: true
3 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | os: Visual Studio 2019
2 |
3 | environment:
4 | global:
5 | # SDK v7.0 MSVC Express 2008's SetEnv.cmd script will fail if the
6 | # /E:ON and /V:ON options are not enabled in the batch script intepreter
7 | # See: http://stackoverflow.com/a/13751649/163740
8 | CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\appveyor\\run_with_env.cmd"
9 |
10 | matrix:
11 | - MINICONDA: C:\Miniconda38-x64
12 | PYTHON_VERSION: "3.8"
13 |
14 | init:
15 | - "ECHO %MINICONDA% %PYTHON_VERSION% %PYTHON_ARCH%"
16 |
17 | install:
18 | # If there is a newer build queued for the same PR, cancel this one.
19 | # The AppVeyor 'rollout builds' option is supposed to serve the same
20 | # purpose but it is problematic because it tends to cancel builds pushed
21 | # directly to master instead of just PR builds (or the converse).
22 | # credits: JuliaLang developers.
23 | - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
24 | https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
25 | Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
26 | throw "There are newer queued builds for this pull request, failing early." }
27 | - ECHO "Filesystem root:"
28 | - ps: "ls \"C:/\""
29 |
30 | # Prepend newly installed Python to the PATH of this build (this cannot be
31 | # done from inside the powershell script as it would require to restart
32 | # the parent CMD process).
33 | - SET PATH=%MINICONDA%;%MINICONDA%\\Scripts;%PATH%
34 | - SET PATH=%PYTHON%;%PYTHON%\Scripts;%PYTHON%\Library\bin;%PATH%
35 | - SET PATH=%PATH%;C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin
36 | - conda config --set always_yes yes --set changeps1 no
37 | - conda update -q conda
38 | - conda info -a
39 | - conda env create -f C:\\projects\pythonrobotics\environment.yml
40 | - activate python_robotics
41 |
42 | # Check that we have the expected version and architecture for Python
43 | - "python --version"
44 | - "python -c \"import struct; print(struct.calcsize('P') * 8)\""
45 |
46 | build: off
47 |
48 | test_script:
49 | - "python -Wignore -m unittest discover tests"
50 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line.
5 | SPHINXOPTS =
6 | SPHINXBUILD = sphinx-build
7 | SPHINXPROJ = PythonRobotics
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ## Python Robotics Documentation
2 |
3 | This folder contains documentation for the Python Robotics project.
4 |
5 |
6 | ### Build the Documentation
7 |
8 | #### Install Sphinx and Theme
9 |
10 | ```
11 | pip install sphinx sphinx-autobuild sphinx-rtd-theme
12 | ```
13 |
14 | #### Building the Docs
15 |
16 | In the `docs/` folder:
17 | ```
18 | make html
19 | ```
20 |
21 | if you want to building each time a file is changed:
22 |
23 | ```
24 | sphinx-autobuild . _build/html
25 | ```
26 |
27 | ### Jupyter notebook integration
28 |
29 | When you want to generate rst files from each jupyter notebooks,
30 |
31 | you can use
32 |
33 | ```
34 | cd path/to/docs
35 | python jupyternotebook2rst.py
36 | ```
37 |
38 |
--------------------------------------------------------------------------------
/docs/_static/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/_static/.gitkeep
--------------------------------------------------------------------------------
/docs/getting_started.rst:
--------------------------------------------------------------------------------
1 | .. _getting_started:
2 |
3 | Getting Started
4 | ===============
5 |
6 | What is this?
7 | -------------
8 |
9 | This is an Open Source Software (OSS) project: PythonRobotics, which is a Python code collection of robotics algorithms.
10 |
11 | The focus of the project is on autonomous navigation, and the goal is for beginners in robotics to understand the basic ideas behind each algorithm.
12 |
13 | In this project, the algorithms which are practical and widely used in both academia and industry are selected.
14 |
15 | Each sample code is written in Python3 and only depends on some standard modules for readability and ease of use.
16 |
17 | It includes intuitive animations to understand the behavior of the simulation.
18 |
19 |
20 | See this paper for more details:
21 |
22 | - PythonRobotics: a Python code collection of robotics algorithms: https://arxiv.org/abs/1808.10703
23 |
24 |
25 | Requirements
26 | -------------
27 |
28 | - Python 3.6.x
29 | - numpy
30 | - scipy
31 | - matplotlib
32 | - pandas
33 | - `cvxpy`_
34 |
35 | .. _cvxpy: http://www.cvxpy.org/en/latest/
36 |
37 |
38 | How to use
39 | ----------
40 |
41 | 1. Install the required libraries. You can use environment.yml with
42 | conda command.
43 |
44 | 2. Clone this repo.
45 |
46 | 3. Execute python script in each directory.
47 |
48 | 4. Add star to this repo if you like it 😃.
49 |
50 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | .. PythonRobotics documentation master file, created by
2 | sphinx-quickstart on Sat Sep 15 13:15:55 2018.
3 | You can adapt this file completely to your liking, but it should at least
4 | contain the root `toctree` directive.
5 |
6 | Welcome to PythonRobotics's documentation!
7 | ==========================================
8 |
9 | Python codes for robotics algorithm. The project is on `GitHub`_.
10 |
11 | This is a Python code collection of robotics algorithms, especially for autonomous navigation.
12 |
13 | Features:
14 |
15 | 1. Easy to read for understanding each algorithm's basic idea.
16 |
17 | 2. Widely used and practical algorithms are selected.
18 |
19 | 3. Minimum dependency.
20 |
21 | See this paper for more details:
22 |
23 | - `[1808.10703] PythonRobotics: a Python code collection of robotics
24 | algorithms`_ (`BibTeX`_)
25 |
26 |
27 | .. _`[1808.10703] PythonRobotics: a Python code collection of robotics algorithms`: https://arxiv.org/abs/1808.10703
28 | .. _BibTeX: https://github.com/AtsushiSakai/PythonRoboticsPaper/blob/master/python_robotics.bib
29 | .. _GitHub: https://github.com/AtsushiSakai/PythonRobotics
30 |
31 |
32 | .. toctree::
33 | :maxdepth: 2
34 | :caption: Guide
35 |
36 | getting_started
37 | modules/introduction
38 | modules/localization
39 | modules/mapping
40 | modules/slam
41 | modules/path_planning
42 | modules/path_tracking
43 | modules/arm_navigation
44 | modules/aerial_navigation
45 | modules/appendix
46 |
47 |
48 | Indices and tables
49 | ==================
50 |
51 | * :ref:`genindex`
52 | * :ref:`modindex`
53 | * :ref:`search`
54 |
--------------------------------------------------------------------------------
/docs/jupyternotebook2rst.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | Jupyter notebook converter to rst file
4 |
5 | author: Atsushi Sakai
6 |
7 | """
8 | import subprocess
9 | import os.path
10 | import os
11 | import glob
12 |
13 |
14 | NOTEBOOK_DIR = "../"
15 |
16 |
17 | def get_notebook_path_list(ndir):
18 | path = glob.glob(ndir + "**/*.ipynb", recursive=True)
19 | return path
20 |
21 |
22 | def convert_rst(rstpath):
23 |
24 | with open(rstpath, "r") as bfile:
25 | filedata = bfile.read()
26 |
27 | # convert from code directive to code-block
28 | # because showing code in Sphinx
29 | before = ".. code:: ipython3"
30 | after = ".. code-block:: ipython3"
31 | filedata = filedata.replace(before, after)
32 |
33 | with open(rstpath, "w") as afile:
34 | afile.write(filedata)
35 |
36 |
37 | def generate_rst(npath):
38 | print("====Start generating rst======")
39 |
40 | # generate dir
41 | dirpath = os.path.dirname(npath)
42 | # print(dirpath)
43 |
44 | rstpath = os.path.abspath("./modules/" + npath[3:-5] + "rst")
45 | # print(rstpath)
46 |
47 | basename = os.path.basename(rstpath)
48 |
49 | cmd = "jupyter nbconvert --to rst "
50 | cmd += npath
51 | print(cmd)
52 | subprocess.call(cmd, shell=True)
53 |
54 | rstpath = dirpath + "/" + basename
55 | convert_rst(rstpath)
56 |
57 | # clean up old files
58 | cmd = "rm -rf "
59 | cmd += "./modules/"
60 | cmd += basename[:-4]
61 | cmd += "*"
62 | # print(cmd)
63 | subprocess.call(cmd, shell=True)
64 |
65 | # move files to module dir
66 | cmd = "mv "
67 | cmd += dirpath
68 | cmd += "/*.rst ./modules/"
69 | print(cmd)
70 | subprocess.call(cmd, shell=True)
71 |
72 | cmd = "mv "
73 | cmd += dirpath
74 | cmd += "/*_files ./modules/"
75 | print(cmd)
76 | subprocess.call(cmd, shell=True)
77 |
78 |
79 | def main():
80 | print("start!!")
81 |
82 | notebook_path_list = get_notebook_path_list(NOTEBOOK_DIR)
83 | # print(notebook_path_list)
84 |
85 | for npath in notebook_path_list:
86 | if "template" not in npath:
87 | generate_rst(npath)
88 |
89 | print("done!!")
90 |
91 |
92 | if __name__ == '__main__':
93 | main()
94 |
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 | set SPHINXPROJ=PythonRobotics
13 |
14 | if "%1" == "" goto help
15 |
16 | %SPHINXBUILD% >NUL 2>NUL
17 | if errorlevel 9009 (
18 | echo.
19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
20 | echo.installed, then set the SPHINXBUILD environment variable to point
21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
22 | echo.may add the Sphinx directory to PATH.
23 | echo.
24 | echo.If you don't have Sphinx installed, grab it from
25 | echo.http://sphinx-doc.org/
26 | exit /b 1
27 | )
28 |
29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
30 | goto end
31 |
32 | :help
33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
34 |
35 | :end
36 | popd
37 |
--------------------------------------------------------------------------------
/docs/modules/FastSLAM1_files/FastSLAM1_12_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/FastSLAM1_files/FastSLAM1_12_0.png
--------------------------------------------------------------------------------
/docs/modules/FastSLAM1_files/FastSLAM1_12_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/FastSLAM1_files/FastSLAM1_12_1.png
--------------------------------------------------------------------------------
/docs/modules/FastSLAM1_files/FastSLAM1_1_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/FastSLAM1_files/FastSLAM1_1_0.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_2_files/Kalmanfilter_basics_2_5_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_2_files/Kalmanfilter_basics_2_5_0.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_14_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_14_1.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_16_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_16_0.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_19_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_19_1.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_21_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_21_1.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_22_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_22_0.png
--------------------------------------------------------------------------------
/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_28_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Kalmanfilter_basics_files/Kalmanfilter_basics_28_1.png
--------------------------------------------------------------------------------
/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_12_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_12_0.png
--------------------------------------------------------------------------------
/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_5_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_5_0.png
--------------------------------------------------------------------------------
/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_7_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_7_0.png
--------------------------------------------------------------------------------
/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_9_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/Planar_Two_Link_IK_files/Planar_Two_Link_IK_9_0.png
--------------------------------------------------------------------------------
/docs/modules/aerial_navigation.rst:
--------------------------------------------------------------------------------
1 | .. _aerial_navigation:
2 |
3 | Aerial Navigation
4 | =================
5 |
6 | Drone 3d trajectory following
7 | -----------------------------
8 |
9 | This is a 3d trajectory following simulation for a quadrotor.
10 |
11 | |3|
12 |
13 | rocket powered landing
14 | -----------------------------
15 |
16 | .. include:: rocket_powered_landing.rst
17 |
18 | .. |3| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/AerialNavigation/drone_3d_trajectory_following/animation.gif
19 |
--------------------------------------------------------------------------------
/docs/modules/appendix.rst:
--------------------------------------------------------------------------------
1 | .. _appendix:
2 |
3 | Appendix
4 | ==============
5 |
6 | .. include:: Kalmanfilter_basics.rst
7 |
8 | .. include:: Kalmanfilter_basics_2.rst
9 |
10 |
--------------------------------------------------------------------------------
/docs/modules/arm_navigation.rst:
--------------------------------------------------------------------------------
1 | .. _arm_navigation:
2 |
3 | Arm Navigation
4 | ==============
5 |
6 | .. include:: Planar_Two_Link_IK.rst
7 |
8 |
9 | N joint arm to point control
10 | ----------------------------
11 |
12 | N joint arm to a point control simulation.
13 |
14 | This is a interactive simulation.
15 |
16 | You can set the goal position of the end effector with left-click on the
17 | ploting area.
18 |
19 | |n_joints_arm|
20 |
21 | In this simulation N = 10, however, you can change it.
22 |
23 | Arm navigation with obstacle avoidance
24 | --------------------------------------
25 |
26 | Arm navigation with obstacle avoidance simulation.
27 |
28 | |obstacle|
29 |
30 | .. |n_joints_arm| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/ArmNavigation/n_joint_arm_to_point_control/animation.gif
31 | .. |obstacle| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/ArmNavigation/arm_obstacle_navigation/animation.gif
32 |
--------------------------------------------------------------------------------
/docs/modules/cgmres_nmpc.rst:
--------------------------------------------------------------------------------
1 |
2 | Nonlinear Model Predictive Control with C-GMRES
3 | -----------------------------------------------
4 |
5 | .. code-block:: ipython3
6 |
7 | from IPython.display import Image
8 | Image(filename="Figure_4.png",width=600)
9 |
10 |
11 |
12 |
13 | .. image:: cgmres_nmpc_files/cgmres_nmpc_1_0.png
14 | :width: 600px
15 |
16 |
17 |
18 | .. code-block:: ipython3
19 |
20 | Image(filename="Figure_1.png",width=600)
21 |
22 |
23 |
24 |
25 | .. image:: cgmres_nmpc_files/cgmres_nmpc_2_0.png
26 | :width: 600px
27 |
28 |
29 |
30 | .. code-block:: ipython3
31 |
32 | Image(filename="Figure_2.png",width=600)
33 |
34 |
35 |
36 |
37 | .. image:: cgmres_nmpc_files/cgmres_nmpc_3_0.png
38 | :width: 600px
39 |
40 |
41 |
42 | .. code-block:: ipython3
43 |
44 | Image(filename="Figure_3.png",width=600)
45 |
46 |
47 |
48 |
49 | .. image:: cgmres_nmpc_files/cgmres_nmpc_4_0.png
50 | :width: 600px
51 |
52 |
53 |
54 | .. figure:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/cgmres_nmpc/animation.gif
55 | :alt: gif
56 |
57 | gif
58 |
59 | Mathematical Formulation
60 | ~~~~~~~~~~~~~~~~~~~~~~~~
61 |
62 | Motion model is
63 |
64 | .. math:: \dot{x}=vcos\theta
65 |
66 | .. math:: \dot{y}=vsin\theta
67 |
68 | .. math:: \dot{\theta}=\frac{v}{WB}sin(u_{\delta})
69 |
70 | \ (tan is not good for optimization)
71 |
72 | .. math:: \dot{v}=u_a
73 |
74 | Cost function is
75 |
76 | .. math:: J=\frac{1}{2}(u_a^2+u_{\delta}^2)-\phi_a d_a-\phi_\delta d_\delta
77 |
78 | Input constraints are
79 |
80 | .. math:: |u_a| \leq u_{a,max}
81 |
82 | .. math:: |u_\delta| \leq u_{\delta,max}
83 |
84 | So, Hamiltonian is
85 |
86 | .. math::
87 |
88 | J=\frac{1}{2}(u_a^2+u_{\delta}^2)-\phi_a d_a-\phi_\delta d_\delta\\ +\lambda_1vcos\theta+\lambda_2vsin\theta+\lambda_3\frac{v}{WB}sin(u_{\delta})+\lambda_4u_a\\
89 | +\rho_1(u_a^2+d_a^2+u_{a,max}^2)+\rho_2(u_\delta^2+d_\delta^2+u_{\delta,max}^2)
90 |
91 | Partial differential equations of the Hamiltonian are:
92 |
93 | :math:`\begin{equation*} \frac{\partial H}{\partial \bf{x}}=\\ \begin{bmatrix} \frac{\partial H}{\partial x}= 0&\\ \frac{\partial H}{\partial y}= 0&\\ \frac{\partial H}{\partial \theta}= -\lambda_1vsin\theta+\lambda_2vcos\theta&\\ \frac{\partial H}{\partial v}=-\lambda_1cos\theta+\lambda_2sin\theta+\lambda_3\frac{1}{WB}sin(u_{\delta})&\\ \end{bmatrix} \\ \end{equation*}`
94 |
95 | :math:`\begin{equation*} \frac{\partial H}{\partial \bf{u}}=\\ \begin{bmatrix} \frac{\partial H}{\partial u_a}= u_a+\lambda_4+2\rho_1u_a&\\ \frac{\partial H}{\partial u_\delta}= u_\delta+\lambda_3\frac{v}{WB}cos(u_{\delta})+2\rho_2u_\delta&\\ \frac{\partial H}{\partial d_a}= -\phi_a+2\rho_1d_a&\\ \frac{\partial H}{\partial d_\delta}=-\phi_\delta+2\rho_2d_\delta&\\ \end{bmatrix} \\ \end{equation*}`
96 |
97 | Ref
98 | ~~~
99 |
100 | - `Shunichi09/nonlinear_control: Implementing the nonlinear model
101 | predictive control, sliding mode
102 | control `__
103 |
104 | - `非線形モデル予測制御におけるCGMRES法をpythonで実装する -
105 | Qiita `__
106 |
--------------------------------------------------------------------------------
/docs/modules/cgmres_nmpc_files/cgmres_nmpc_1_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/cgmres_nmpc_files/cgmres_nmpc_1_0.png
--------------------------------------------------------------------------------
/docs/modules/cgmres_nmpc_files/cgmres_nmpc_2_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/cgmres_nmpc_files/cgmres_nmpc_2_0.png
--------------------------------------------------------------------------------
/docs/modules/cgmres_nmpc_files/cgmres_nmpc_3_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/cgmres_nmpc_files/cgmres_nmpc_3_0.png
--------------------------------------------------------------------------------
/docs/modules/cgmres_nmpc_files/cgmres_nmpc_4_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/cgmres_nmpc_files/cgmres_nmpc_4_0.png
--------------------------------------------------------------------------------
/docs/modules/extended_kalman_filter_localization.rst:
--------------------------------------------------------------------------------
1 |
2 | Extended Kalman Filter Localization
3 | -----------------------------------
4 |
5 | .. code-block:: ipython3
6 |
7 | from IPython.display import Image
8 | Image(filename="ekf.png",width=600)
9 |
10 |
11 |
12 |
13 | .. image:: extended_kalman_filter_localization_files/extended_kalman_filter_localization_1_0.png
14 | :width: 600px
15 |
16 |
17 |
18 | .. figure:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Localization/extended_kalman_filter/animation.gif
19 | :alt: EKF
20 |
21 | EKF
22 |
23 | This is a sensor fusion localization with Extended Kalman Filter(EKF).
24 |
25 | The blue line is true trajectory, the black line is dead reckoning
26 | trajectory,
27 |
28 | the green point is positioning observation (ex. GPS), and the red line
29 | is estimated trajectory with EKF.
30 |
31 | The red ellipse is estimated covariance ellipse with EKF.
32 |
33 | Code: `PythonRobotics/extended_kalman_filter.py at master ·
34 | AtsushiSakai/PythonRobotics `__
35 |
36 | Filter design
37 | ~~~~~~~~~~~~~
38 |
39 | In this simulation, the robot has a state vector includes 4 states at
40 | time :math:`t`.
41 |
42 | .. math:: \textbf{x}_t=[x_t, y_t, \phi_t, v_t]
43 |
44 | x, y are a 2D x-y position, :math:`\phi` is orientation, and v is
45 | velocity.
46 |
47 | In the code, “xEst” means the state vector.
48 | `code `__
49 |
50 | And, :math:`P_t` is covariace matrix of the state,
51 |
52 | :math:`Q` is covariance matrix of process noise,
53 |
54 | :math:`R` is covariance matrix of observation noise at time :math:`t`
55 |
56 |
57 |
58 | The robot has a speed sensor and a gyro sensor.
59 |
60 | So, the input vecor can be used as each time step
61 |
62 | .. math:: \textbf{u}_t=[v_t, \omega_t]
63 |
64 | Also, the robot has a GNSS sensor, it means that the robot can observe
65 | x-y position at each time.
66 |
67 | .. math:: \textbf{z}_t=[x_t,y_t]
68 |
69 | The input and observation vector includes sensor noise.
70 |
71 | In the code, “observation” function generates the input and observation
72 | vector with noise
73 | `code `__
74 |
75 | Motion Model
76 | ~~~~~~~~~~~~
77 |
78 | The robot model is
79 |
80 | .. math:: \dot{x} = vcos(\phi)
81 |
82 | .. math:: \dot{y} = vsin((\phi)
83 |
84 | .. math:: \dot{\phi} = \omega
85 |
86 | So, the motion model is
87 |
88 | .. math:: \textbf{x}_{t+1} = F\textbf{x}_t+B\textbf{u}_t
89 |
90 | where
91 |
92 | :math:`\begin{equation*} F= \begin{bmatrix} 1 & 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 \\ \end{bmatrix} \end{equation*}`
93 |
94 | :math:`\begin{equation*} B= \begin{bmatrix} cos(\phi)dt & 0\\ sin(\phi)dt & 0\\ 0 & dt\\ 1 & 0\\ \end{bmatrix} \end{equation*}`
95 |
96 | :math:`dt` is a time interval.
97 |
98 | This is implemented at
99 | `code `__
100 |
101 | Its Jacobian matrix is
102 |
103 | :math:`\begin{equation*} J_F= \begin{bmatrix} \frac{dx}{dx}& \frac{dx}{dy} & \frac{dx}{d\phi} & \frac{dx}{dv}\\ \frac{dy}{dx}& \frac{dy}{dy} & \frac{dy}{d\phi} & \frac{dy}{dv}\\ \frac{d\phi}{dx}& \frac{d\phi}{dy} & \frac{d\phi}{d\phi} & \frac{d\phi}{dv}\\ \frac{dv}{dx}& \frac{dv}{dy} & \frac{dv}{d\phi} & \frac{dv}{dv}\\ \end{bmatrix} \end{equation*}`
104 |
105 | :math:`\begin{equation*} = \begin{bmatrix} 1& 0 & -v sin(\phi)dt & cos(\phi)dt\\ 0 & 1 & v cos(\phi)dt & sin(\phi) dt\\ 0 & 0 & 1 & 0\\ 0 & 0 & 0 & 1\\ \end{bmatrix} \end{equation*}`
106 |
107 | Observation Model
108 | ~~~~~~~~~~~~~~~~~
109 |
110 | The robot can get x-y position infomation from GPS.
111 |
112 | So GPS Observation model is
113 |
114 | .. math:: \textbf{z}_{t} = H\textbf{x}_t
115 |
116 | where
117 |
118 | :math:`\begin{equation*} B= \begin{bmatrix} 1 & 0 & 0& 0\\ 0 & 1 & 0& 0\\ \end{bmatrix} \end{equation*}`
119 |
120 | Its Jacobian matrix is
121 |
122 | :math:`\begin{equation*} J_H= \begin{bmatrix} \frac{dx}{dx}& \frac{dx}{dy} & \frac{dx}{d\phi} & \frac{dx}{dv}\\ \frac{dy}{dx}& \frac{dy}{dy} & \frac{dy}{d\phi} & \frac{dy}{dv}\\ \end{bmatrix} \end{equation*}`
123 |
124 | :math:`\begin{equation*} = \begin{bmatrix} 1& 0 & 0 & 0\\ 0 & 1 & 0 & 0\\ \end{bmatrix} \end{equation*}`
125 |
126 | Extented Kalman Filter
127 | ~~~~~~~~~~~~~~~~~~~~~~
128 |
129 | Localization process using Extendted Kalman Filter:EKF is
130 |
131 | === Predict ===
132 |
133 | :math:`x_{Pred} = Fx_t+Bu_t`
134 |
135 | :math:`P_{Pred} = J_FP_t J_F^T + Q`
136 |
137 | === Update ===
138 |
139 | :math:`z_{Pred} = Hx_{Pred}`
140 |
141 | :math:`y = z - z_{Pred}`
142 |
143 | :math:`S = J_H P_{Pred}.J_H^T + R`
144 |
145 | :math:`K = P_{Pred}.J_H^T S^{-1}`
146 |
147 | :math:`x_{t+1} = x_{Pred} + Ky`
148 |
149 | :math:`P_{t+1} = ( I - K J_H) P_{Pred}`
150 |
151 | Ref:
152 | ~~~~
153 |
154 | - `PROBABILISTIC-ROBOTICS.ORG `__
155 |
--------------------------------------------------------------------------------
/docs/modules/extended_kalman_filter_localization_files/extended_kalman_filter_localization_1_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/extended_kalman_filter_localization_files/extended_kalman_filter_localization_1_0.png
--------------------------------------------------------------------------------
/docs/modules/introduction.rst:
--------------------------------------------------------------------------------
1 |
2 | Introduction
3 | ============
4 |
5 | Ref
6 | ---
7 |
--------------------------------------------------------------------------------
/docs/modules/localization.rst:
--------------------------------------------------------------------------------
1 | .. _localization:
2 |
3 | Localization
4 | ============
5 |
6 | .. include:: extended_kalman_filter_localization.rst
7 |
8 |
9 | Unscented Kalman Filter localization
10 | ------------------------------------
11 |
12 | |2|
13 |
14 | This is a sensor fusion localization with Unscented Kalman Filter(UKF).
15 |
16 | The lines and points are same meaning of the EKF simulation.
17 |
18 | Ref:
19 |
20 | - `Discriminatively Trained Unscented Kalman Filter for Mobile Robot
21 | Localization`_
22 |
23 | Particle filter localization
24 | ----------------------------
25 |
26 | |3|
27 |
28 | This is a sensor fusion localization with Particle Filter(PF).
29 |
30 | The blue line is true trajectory, the black line is dead reckoning
31 | trajectory,
32 |
33 | and the red line is estimated trajectory with PF.
34 |
35 | It is assumed that the robot can measure a distance from landmarks
36 | (RFID).
37 |
38 | This measurements are used for PF localization.
39 |
40 | Ref:
41 |
42 | - `PROBABILISTIC ROBOTICS`_
43 |
44 | Histogram filter localization
45 | -----------------------------
46 |
47 | |4|
48 |
49 | This is a 2D localization example with Histogram filter.
50 |
51 | The red cross is true position, black points are RFID positions.
52 |
53 | The blue grid shows a position probability of histogram filter.
54 |
55 | In this simulation, x,y are unknown, yaw is known.
56 |
57 | The filter integrates speed input and range observations from RFID for
58 | localization.
59 |
60 | Initial position is not needed.
61 |
62 | Ref:
63 |
64 | - `PROBABILISTIC ROBOTICS`_
65 |
66 | .. _PROBABILISTIC ROBOTICS: http://www.probabilistic-robotics.org/
67 | .. _Discriminatively Trained Unscented Kalman Filter for Mobile Robot Localization: https://www.researchgate.net/publication/267963417_Discriminatively_Trained_Unscented_Kalman_Filter_for_Mobile_Robot_Localization
68 |
69 | .. |2| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Localization/unscented_kalman_filter/animation.gif
70 | .. |3| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Localization/particle_filter/animation.gif
71 | .. |4| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Localization/histogram_filter/animation.gif
72 |
--------------------------------------------------------------------------------
/docs/modules/mapping.rst:
--------------------------------------------------------------------------------
1 | .. _mapping:
2 |
3 | Mapping
4 | =======
5 |
6 | Gaussian grid map
7 | -----------------
8 |
9 | This is a 2D Gaussian grid mapping example.
10 |
11 | |2|
12 |
13 | Ray casting grid map
14 | --------------------
15 |
16 | This is a 2D ray casting grid mapping example.
17 |
18 | |3|
19 |
20 | k-means object clustering
21 | -------------------------
22 |
23 | This is a 2D object clustering with k-means algorithm.
24 |
25 | |4|
26 |
27 | Object shape recognition using circle fitting
28 | ---------------------------------------------
29 |
30 | This is an object shape recognition using circle fitting.
31 |
32 | |5|
33 |
34 | The blue circle is the true object shape.
35 |
36 | The red crosses are observations from a ranging sensor.
37 |
38 | The red circle is the estimated object shape using circle fitting.
39 |
40 | .. |2| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Mapping/gaussian_grid_map/animation.gif
41 | .. |3| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Mapping/raycasting_grid_map/animation.gif
42 | .. |4| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Mapping/kmeans_clustering/animation.gif
43 | .. |5| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/Mapping/circle_fitting/animation.gif
44 |
--------------------------------------------------------------------------------
/docs/modules/path_tracking.rst:
--------------------------------------------------------------------------------
1 | .. _path_tracking:
2 |
3 | Path tracking
4 | =============
5 |
6 | move to a pose control
7 | ----------------------
8 |
9 | This is a simulation of moving to a pose control
10 |
11 | |2|
12 |
13 | Ref:
14 |
15 | - `P. I. Corke, "Robotics, Vision and Control" \| SpringerLink
16 | p102 `__
17 |
18 | Pure pursuit tracking
19 | ---------------------
20 |
21 | Path tracking simulation with pure pursuit steering control and PID
22 | speed control.
23 |
24 | |3|
25 |
26 | The red line is a target course, the green cross means the target point
27 | for pure pursuit control, the blue line is the tracking.
28 |
29 | Ref:
30 |
31 | - `A Survey of Motion Planning and Control Techniques for Self-driving
32 | Urban Vehicles `__
33 |
34 | Stanley control
35 | ---------------
36 |
37 | Path tracking simulation with Stanley steering control and PID speed
38 | control.
39 |
40 | |4|
41 |
42 | Ref:
43 |
44 | - `Stanley: The robot that won the DARPA grand
45 | challenge `__
46 |
47 | - `Automatic Steering Methods for Autonomous Automobile Path
48 | Tracking `__
49 |
50 | Rear wheel feedback control
51 | ---------------------------
52 |
53 | Path tracking simulation with rear wheel feedback steering control and
54 | PID speed control.
55 |
56 | |5|
57 |
58 | Ref:
59 |
60 | - `A Survey of Motion Planning and Control Techniques for Self-driving
61 | Urban Vehicles `__
62 |
63 | .. _linearquadratic-regulator-(lqr)-steering-control:
64 |
65 | Linear–quadratic regulator (LQR) steering control
66 | -------------------------------------------------
67 |
68 | Path tracking simulation with LQR steering control and PID speed
69 | control.
70 |
71 | |6|
72 |
73 | Ref:
74 |
75 | - `ApolloAuto/apollo: An open autonomous driving
76 | platform `__
77 |
78 | .. _linearquadratic-regulator-(lqr)-speed-and-steering-control:
79 |
80 | Linear–quadratic regulator (LQR) speed and steering control
81 | -----------------------------------------------------------
82 |
83 | Path tracking simulation with LQR speed and steering control.
84 |
85 | |7|
86 |
87 | Ref:
88 |
89 | - `Towards fully autonomous driving: Systems and algorithms - IEEE
90 | Conference
91 | Publication `__
92 |
93 | .. include:: Model_predictive_speed_and_steering_control.rst
94 |
95 | Ref:
96 |
97 | - `notebook `__
98 |
99 |
100 | .. include:: cgmres_nmpc.rst
101 |
102 | .. |2| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/move_to_pose/animation.gif
103 | .. |3| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/pure_pursuit/animation.gif
104 | .. |4| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/stanley_controller/animation.gif
105 | .. |5| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/rear_wheel_feedback/animation.gif
106 | .. |6| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/lqr_steer_control/animation.gif
107 | .. |7| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathTracking/lqr_speed_steer_control/animation.gif
108 |
--------------------------------------------------------------------------------
/docs/modules/rrt_star.rst:
--------------------------------------------------------------------------------
1 |
2 | Simulation
3 | ^^^^^^^^^^
4 |
5 | .. code-block:: ipython3
6 |
7 | from IPython.display import Image
8 | Image(filename="Figure_1.png",width=600)
9 |
10 |
11 |
12 |
13 | .. image:: rrt_star_files/rrt_star_1_0.png
14 | :width: 600px
15 |
16 |
17 |
18 | .. figure:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/PathPlanning/RRTstar/animation.gif
19 | :alt: gif
20 |
21 | gif
22 |
23 | Ref
24 | ^^^
25 |
26 | - `Sampling-based Algorithms for Optimal Motion
27 | Planning `__
28 |
--------------------------------------------------------------------------------
/docs/modules/rrt_star_files/rrt_star_1_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/docs/modules/rrt_star_files/rrt_star_1_0.png
--------------------------------------------------------------------------------
/docs/modules/slam.rst:
--------------------------------------------------------------------------------
1 | .. _slam:
2 |
3 | SLAM
4 | ====
5 |
6 | Simultaneous Localization and Mapping(SLAM) examples
7 |
8 | .. _iterative-closest-point-(icp)-matching:
9 |
10 | Iterative Closest Point (ICP) Matching
11 | --------------------------------------
12 |
13 | This is a 2D ICP matching example with singular value decomposition.
14 |
15 | It can calculate a rotation matrix and a translation vector between
16 | points to points.
17 |
18 | |3|
19 |
20 | Ref:
21 |
22 | - `Introduction to Mobile Robotics: Iterative Closest Point Algorithm`_
23 |
24 | EKF SLAM
25 | --------
26 |
27 | This is an Extended Kalman Filter based SLAM example.
28 |
29 | The blue line is ground truth, the black line is dead reckoning, the red
30 | line is the estimated trajectory with EKF SLAM.
31 |
32 | The green crosses are estimated landmarks.
33 |
34 | |4|
35 |
36 | Ref:
37 |
38 | - `PROBABILISTIC ROBOTICS`_
39 |
40 | .. include:: FastSLAM1.rst
41 |
42 | |5|
43 |
44 | Ref:
45 |
46 | - `PROBABILISTIC ROBOTICS`_
47 |
48 | - `SLAM simulations by Tim Bailey`_
49 |
50 | FastSLAM 2.0
51 | ------------
52 |
53 | This is a feature based SLAM example using FastSLAM 2.0.
54 |
55 | The animation has the same meanings as one of FastSLAM 1.0.
56 |
57 | |6|
58 |
59 | Ref:
60 |
61 | - `PROBABILISTIC ROBOTICS`_
62 |
63 | - `SLAM simulations by Tim Bailey`_
64 |
65 | Graph based SLAM
66 | ----------------
67 |
68 | This is a graph based SLAM example.
69 |
70 | The blue line is ground truth.
71 |
72 | The black line is dead reckoning.
73 |
74 | The red line is the estimated trajectory with Graph based SLAM.
75 |
76 | The black stars are landmarks for graph edge generation.
77 |
78 | |7|
79 |
80 | Ref:
81 |
82 | - `A Tutorial on Graph-Based SLAM`_
83 |
84 | .. _`Introduction to Mobile Robotics: Iterative Closest Point Algorithm`: https://cs.gmu.edu/~kosecka/cs685/cs685-icp.pdf
85 | .. _PROBABILISTIC ROBOTICS: http://www.probabilistic-robotics.org/
86 | .. _SLAM simulations by Tim Bailey: http://www-personal.acfr.usyd.edu.au/tbailey/software/slam_simulations.htm
87 | .. _A Tutorial on Graph-Based SLAM: http://www2.informatik.uni-freiburg.de/~stachnis/pdf/grisetti10titsmag.pdf
88 |
89 | .. |3| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/SLAM/iterative_closest_point/animation.gif
90 | .. |4| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/SLAM/EKFSLAM/animation.gif
91 | .. |5| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/SLAM/FastSLAM1/animation.gif
92 | .. |6| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/SLAM/FastSLAM2/animation.gif
93 | .. |7| image:: https://github.com/AtsushiSakai/PythonRoboticsGifs/raw/master/SLAM/GraphBasedSLAM/animation.gif
94 |
--------------------------------------------------------------------------------
/environment.yml:
--------------------------------------------------------------------------------
1 | name: python_robotics
2 | dependencies:
3 | - python
4 | - pip
5 | - matplotlib
6 | - scipy
7 | - numpy
8 | - pandas
9 | - coverage
10 | - pip:
11 | - cvxpy
12 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/icon.png
--------------------------------------------------------------------------------
/mypy.ini:
--------------------------------------------------------------------------------
1 | [mypy]
2 | ignore_missing_imports = True
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | pandas
3 | scipy
4 | matplotlib
5 | cvxpy
6 |
--------------------------------------------------------------------------------
/rundiffstylecheck.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo "$(basename $0) start!"
3 | VERSION=v0.1.6
4 | wget https://github.com/AtsushiSakai/DiffSentinel/archive/${VERSION}.zip
5 | unzip ${VERSION}.zip
6 | ./DiffSentinel*/starter.sh HEAD origin/master
7 | check_result=$?
8 | rm -rf ${VERSION}.zip DiffSentinel*
9 | if [[ ${check_result} -ne 0 ]];
10 | then
11 | echo "Error: Your changes contain pycodestyle errors."
12 | exit 1
13 | fi
14 | echo "$(basename $0) done!"
15 | exit 0
16 |
--------------------------------------------------------------------------------
/runtests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | echo "Run test suites! "
3 | #python -m unittest discover tests
4 | #python -Wignore -m unittest discover tests #ignore warning
5 | coverage run -m unittest discover tests # generate coverage file
6 |
--------------------------------------------------------------------------------
/tests/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LiHongbo97/PythonRobotics/3607d72b60cd500806e0f026ac8beb82850a01f9/tests/.gitkeep
--------------------------------------------------------------------------------
/tests/test_LQR_planner.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from unittest import TestCase
3 |
4 | sys.path.append("./PathPlanning/LQRPlanner")
5 |
6 | from PathPlanning.LQRPlanner import LQRplanner as m
7 |
8 | print(__file__)
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.SHOW_ANIMATION = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_a_star.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(__file__) + "/../")
5 | try:
6 | from PathPlanning.AStar import a_star as m
7 | except:
8 | raise
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
17 |
18 | if __name__ == '__main__': # pragma: no cover
19 | test = Test()
20 | test.test1()
21 |
--------------------------------------------------------------------------------
/tests/test_batch_informed_rrt_star.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(__file__) + "/../")
5 | try:
6 | from PathPlanning.BatchInformedRRTStar import batch_informed_rrtstar as m
7 | except:
8 | raise
9 |
10 | print(__file__)
11 |
12 |
13 | class Test(TestCase):
14 |
15 | def test1(self):
16 | m.show_animation = False
17 | m.main(maxIter=10)
18 |
19 |
20 | if __name__ == '__main__': # pragma: no cover
21 | test = Test()
22 | test.test1()
23 |
--------------------------------------------------------------------------------
/tests/test_bezier_path.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | sys.path.append("./PathPlanning/BezierPath/")
5 |
6 | from PathPlanning.BezierPath import bezier_path as m
7 |
8 | print(__file__)
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 | m.main2()
17 |
--------------------------------------------------------------------------------
/tests/test_cgmres_nmpc.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | if 'cvxpy' in sys.modules: # pragma: no cover
5 | sys.path.append("./PathTracking/cgmres_nmpc/")
6 |
7 | from PathTracking.cgmres_nmpc import cgmres_nmpc as m
8 |
9 | print(__file__)
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_circle_fitting.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Mapping.circle_fitting import circle_fitting as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_closed_loop_rrt_star_car.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
6 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
7 | "/../PathPlanning/ClosedLoopRRTStar/")
8 | try:
9 | from PathPlanning.ClosedLoopRRTStar import closed_loop_rrt_star_car as m
10 | except:
11 | raise
12 |
13 |
14 | print(__file__)
15 |
16 |
17 | class Test(TestCase):
18 |
19 | def test1(self):
20 | m.show_animation = False
21 | m.main(gx=1.0, gy=0.0, gyaw=0.0, max_iter=5)
22 |
23 |
24 | if __name__ == '__main__': # pragma: no cover
25 | test = Test()
26 | test.test1()
27 |
--------------------------------------------------------------------------------
/tests/test_dijkstra.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from PathPlanning.Dijkstra import dijkstra as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_drone_3d_trajectory_following.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(__file__) + "/../AerialNavigation/drone_3d_trajectory_following/")
6 |
7 | import drone_3d_trajectory_following as m
8 |
9 | print(__file__)
10 |
11 |
12 | class Test(TestCase):
13 |
14 | def test1(self):
15 | m.show_animation = False
16 | m.main()
17 |
--------------------------------------------------------------------------------
/tests/test_dubins_path_planning.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import numpy as np
4 |
5 | from PathPlanning.DubinsPath import dubins_path_planning
6 |
7 |
8 | class Test(TestCase):
9 |
10 | @staticmethod
11 | def check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x, end_y, end_yaw):
12 | assert (abs(px[0] - start_x) <= 0.01)
13 | assert (abs(py[0] - start_y) <= 0.01)
14 | assert (abs(pyaw[0] - start_yaw) <= 0.01)
15 | print("x", px[-1], end_x)
16 | assert (abs(px[-1] - end_x) <= 0.01)
17 | print("y", py[-1], end_y)
18 | assert (abs(py[-1] - end_y) <= 0.01)
19 | print("yaw", pyaw[-1], end_yaw)
20 | assert (abs(pyaw[-1] - end_yaw) <= 0.01)
21 |
22 | def test1(self):
23 | start_x = 1.0 # [m]
24 | start_y = 1.0 # [m]
25 | start_yaw = np.deg2rad(45.0) # [rad]
26 |
27 | end_x = -3.0 # [m]
28 | end_y = -3.0 # [m]
29 | end_yaw = np.deg2rad(-45.0) # [rad]
30 |
31 | curvature = 1.0
32 |
33 | px, py, pyaw, mode, clen = dubins_path_planning.dubins_path_planning(
34 | start_x, start_y, start_yaw, end_x, end_y, end_yaw, curvature)
35 |
36 | self.check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x, end_y, end_yaw)
37 |
38 | def test2(self):
39 | dubins_path_planning.show_animation = False
40 | dubins_path_planning.main()
41 |
42 | def test3(self):
43 | N_TEST = 10
44 |
45 | for i in range(N_TEST):
46 | start_x = (np.random.rand() - 0.5) * 10.0 # [m]
47 | start_y = (np.random.rand() - 0.5) * 10.0 # [m]
48 | start_yaw = np.deg2rad((np.random.rand() - 0.5) * 180.0) # [rad]
49 |
50 | end_x = (np.random.rand() - 0.5) * 10.0 # [m]
51 | end_y = (np.random.rand() - 0.5) * 10.0 # [m]
52 | end_yaw = np.deg2rad((np.random.rand() - 0.5) * 180.0) # [rad]
53 |
54 | curvature = 1.0 / (np.random.rand() * 5.0)
55 |
56 | px, py, pyaw, mode, clen = dubins_path_planning.dubins_path_planning(
57 | start_x, start_y, start_yaw, end_x, end_y, end_yaw, curvature)
58 |
59 | self.check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x, end_y, end_yaw)
60 |
--------------------------------------------------------------------------------
/tests/test_dynamic_window_approach.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(__file__) + "/../")
6 | try:
7 | from PathPlanning.DynamicWindowApproach import dynamic_window_approach as m
8 | except ImportError:
9 | raise
10 |
11 | print(__file__)
12 |
13 |
14 | class TestDynamicWindowApproach(TestCase):
15 | def test_main1(self):
16 | m.show_animation = False
17 | m.main(gx=1.0, gy=1.0)
18 |
19 | def test_main2(self):
20 | m.show_animation = False
21 | m.main(gx=1.0, gy=1.0, robot_type=m.RobotType.rectangle)
22 |
23 |
24 | if __name__ == '__main__': # pragma: no cover
25 | test = TestDynamicWindowApproach()
26 | test.test_main1()
27 | test.test_main2()
28 |
--------------------------------------------------------------------------------
/tests/test_ekf_slam.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from SLAM.EKFSLAM import ekf_slam as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_eta3_spline_path.py:
--------------------------------------------------------------------------------
1 |
2 | from unittest import TestCase
3 | from PathPlanning.Eta3SplinePath import eta3_spline_path as m
4 |
5 |
6 | class Test(TestCase):
7 |
8 | def test1(self):
9 | m.show_animation = False
10 | m.main()
11 |
--------------------------------------------------------------------------------
/tests/test_extended_kalman_filter.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Localization.extended_kalman_filter import extended_kalman_filter as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_fast_slam1.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
5 | try:
6 | from SLAM.FastSLAM1 import fast_slam1 as m
7 | except:
8 | raise
9 |
10 |
11 | print(__file__)
12 |
13 |
14 | class Test(TestCase):
15 |
16 | def test1(self):
17 | m.show_animation = False
18 | m.SIM_TIME = 3.0
19 | m.main()
20 |
21 |
22 | if __name__ == '__main__': # pragma: no cover
23 | test = Test()
24 | test.test1()
25 |
--------------------------------------------------------------------------------
/tests/test_fast_slam2.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
5 | try:
6 | from SLAM.FastSLAM2 import fast_slam2 as m
7 | except:
8 | raise
9 |
10 |
11 | print(__file__)
12 |
13 |
14 | class Test(TestCase):
15 |
16 | def test1(self):
17 | m.show_animation = False
18 | m.SIM_TIME = 3.0
19 | m.main()
20 |
21 |
22 | if __name__ == '__main__': # pragma: no cover
23 | test = Test()
24 | test.test1()
25 |
--------------------------------------------------------------------------------
/tests/test_frenet_optimal_trajectory.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | import os
5 | sys.path.append("./PathPlanning/FrenetOptimalTrajectory/")
6 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
7 | try:
8 | from PathPlanning.FrenetOptimalTrajectory import frenet_optimal_trajectory as m
9 | except:
10 | raise
11 |
12 |
13 | print(__file__)
14 |
15 |
16 | class Test(TestCase):
17 |
18 | def test1(self):
19 | m.show_animation = False
20 | m.SIM_LOOP = 5
21 | m.main()
22 |
23 |
24 | if __name__ == '__main__': # pragma: no cover
25 | test = Test()
26 | test.test1()
27 |
--------------------------------------------------------------------------------
/tests/test_gaussian_grid_map.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Mapping.gaussian_grid_map import gaussian_grid_map as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_graph_based_slam.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
5 | try:
6 | from SLAM.GraphBasedSLAM import graph_based_slam as m
7 | except:
8 | raise
9 |
10 |
11 | print(__file__)
12 |
13 |
14 | class Test(TestCase):
15 |
16 | def test1(self):
17 | m.show_animation = False
18 | m.SIM_TIME = 20.0
19 | m.main()
20 |
21 |
22 | if __name__ == '__main__': # pragma: no cover
23 | test = Test()
24 | test.test1()
25 |
--------------------------------------------------------------------------------
/tests/test_grid_map_lib.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import unittest
4 |
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../Mapping/grid_map_lib")
6 | try:
7 | from grid_map_lib import GridMap
8 | except ImportError:
9 | raise
10 |
11 |
12 | class MyTestCase(unittest.TestCase):
13 |
14 | def test_position_set(self):
15 | grid_map = GridMap(100, 120, 0.5, 10.0, -0.5)
16 |
17 | grid_map.set_value_from_xy_pos(10.1, -1.1, 1.0)
18 | grid_map.set_value_from_xy_pos(10.1, -0.1, 1.0)
19 | grid_map.set_value_from_xy_pos(10.1, 1.1, 1.0)
20 | grid_map.set_value_from_xy_pos(11.1, 0.1, 1.0)
21 | grid_map.set_value_from_xy_pos(10.1, 0.1, 1.0)
22 | grid_map.set_value_from_xy_pos(9.1, 0.1, 1.0)
23 |
24 | self.assertEqual(True, True)
25 |
26 | def test_polygon_set(self):
27 | ox = [0.0, 20.0, 50.0, 100.0, 130.0, 40.0]
28 | oy = [0.0, -20.0, 0.0, 30.0, 60.0, 80.0]
29 |
30 | grid_map = GridMap(600, 290, 0.7, 60.0, 30.5)
31 |
32 | grid_map.set_value_from_polygon(ox, oy, 1.0, inside=False)
33 |
34 | self.assertEqual(True, True)
35 |
36 |
37 | if __name__ == '__main__':
38 | unittest.main()
39 |
--------------------------------------------------------------------------------
/tests/test_histogram_filter.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | import os
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
6 | try:
7 | from Localization.histogram_filter import histogram_filter as m
8 | except:
9 | raise
10 |
11 |
12 | print(__file__)
13 |
14 |
15 | class Test(TestCase):
16 |
17 | def test1(self):
18 | m.show_animation = False
19 | m.SIM_TIME = 1.0
20 | m.main()
21 |
22 |
23 | if __name__ == '__main__':
24 | test = Test()
25 | test.test1()
26 |
--------------------------------------------------------------------------------
/tests/test_hybrid_a_star.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(__file__) + "/../")
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
6 | + "/../PathPlanning/HybridAStar")
7 | try:
8 | from PathPlanning.HybridAStar import hybrid_a_star as m
9 | except Exception:
10 | raise
11 |
12 |
13 | class Test(TestCase):
14 |
15 | def test1(self):
16 | m.show_animation = False
17 | m.main()
18 |
19 |
20 | if __name__ == '__main__': # pragma: no cover
21 | test = Test()
22 | test.test1()
23 |
--------------------------------------------------------------------------------
/tests/test_informed_rrt_star.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
6 | + "/../PathPlanning/InformedRRTStar/")
7 | try:
8 | from PathPlanning.InformedRRTStar import informed_rrt_star as m
9 | except:
10 | raise
11 |
12 | print(__file__)
13 |
14 |
15 | class Test(TestCase):
16 |
17 | def test1(self):
18 | m.show_animation = False
19 | m.main()
20 |
21 |
22 | if __name__ == '__main__': # pragma: no cover
23 | test = Test()
24 | test.test1()
25 |
--------------------------------------------------------------------------------
/tests/test_inverted_pendulum_mpc_control.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | if 'cvxpy' in sys.modules: # pragma: no cover
5 | sys.path.append("./InvertedPendulumCart/inverted_pendulum_mpc_control/")
6 |
7 | import inverted_pendulum_mpc_control as m
8 |
9 | print(__file__)
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_iterative_closest_point.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from SLAM.iterative_closest_point import iterative_closest_point as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_kmeans_clustering.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Mapping.kmeans_clustering import kmeans_clustering as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_lqr_rrt_star.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
6 | + "/../PathPlanning/LQRRRTStar/")
7 | try:
8 | from PathPlanning.LQRRRTStar import lqr_rrt_star as m
9 | except:
10 | raise
11 |
12 | print(__file__)
13 |
14 |
15 | class Test(TestCase):
16 |
17 | def test1(self):
18 | m.show_animation = False
19 | m.main(maxIter=5)
20 |
21 |
22 | if __name__ == '__main__': # pragma: no cover
23 | test = Test()
24 | test.test1()
25 |
--------------------------------------------------------------------------------
/tests/test_lqr_speed_steer_control.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | sys.path.append("./PathTracking/lqr_speed_steer_control/")
5 |
6 | from PathTracking.lqr_speed_steer_control import lqr_speed_steer_control as m
7 |
8 | print(__file__)
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_lqr_steer_control.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | sys.path.append("./PathTracking/lqr_steer_control/")
5 |
6 | from PathTracking.lqr_steer_control import lqr_steer_control as m
7 |
8 | print(__file__)
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_model_predictive_speed_and_steer_control.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | if 'cvxpy' in sys.modules: # pragma: no cover
5 | sys.path.append("./PathTracking/model_predictive_speed_and_steer_control/")
6 |
7 | from PathTracking.model_predictive_speed_and_steer_control import model_predictive_speed_and_steer_control as m
8 |
9 | print(__file__)
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 | m.main2()
17 |
--------------------------------------------------------------------------------
/tests/test_move_to_pose.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from PathTracking.move_to_pose import move_to_pose as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_n_joint_arm_to_point_control.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(__file__) + "/../ArmNavigation/n_joint_arm_to_point_control/")
6 |
7 | import n_joint_arm_to_point_control as m
8 |
9 | print(__file__)
10 |
11 |
12 | class Test(TestCase):
13 |
14 | def test1(self):
15 | m.show_animation = False
16 | m.animation()
17 |
--------------------------------------------------------------------------------
/tests/test_particle_filter.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Localization.particle_filter import particle_filter as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_potential_field_planning.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from PathPlanning.PotentialFieldPlanning import potential_field_planning as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_probabilistic_road_map.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from PathPlanning.ProbabilisticRoadMap import probabilistic_road_map
3 |
4 |
5 | class Test(TestCase):
6 |
7 | def test1(self):
8 | probabilistic_road_map.show_animation = False
9 | probabilistic_road_map.main()
10 |
--------------------------------------------------------------------------------
/tests/test_pure_pursuit.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from PathTracking.pure_pursuit import pure_pursuit as m
3 |
4 | print("pure_pursuit test")
5 |
6 |
7 | class Test(TestCase):
8 |
9 | def test1(self):
10 | m.show_animation = False
11 | m.main()
12 |
--------------------------------------------------------------------------------
/tests/test_quintic_polynomials_planner.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from PathPlanning.QuinticPolynomialsPlanner import quintic_polynomials_planner as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_raycasting_grid_map.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Mapping.raycasting_grid_map import raycasting_grid_map as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_rear_wheel_feedback.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | sys.path.append("./PathTracking/rear_wheel_feedback/")
5 |
6 | from PathTracking.rear_wheel_feedback import rear_wheel_feedback as m
7 |
8 | print(__file__)
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_reeds_shepp_path_planning.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | from PathPlanning.ReedsSheppPath import reeds_shepp_path_planning as m
3 |
4 | import numpy as np
5 |
6 |
7 | class Test(TestCase):
8 |
9 | @staticmethod
10 | def check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x, end_y, end_yaw):
11 | assert (abs(px[0] - start_x) <= 0.01)
12 | assert (abs(py[0] - start_y) <= 0.01)
13 | assert (abs(pyaw[0] - start_yaw) <= 0.01)
14 | print("x", px[-1], end_x)
15 | assert (abs(px[-1] - end_x) <= 0.01)
16 | print("y", py[-1], end_y)
17 | assert (abs(py[-1] - end_y) <= 0.01)
18 | print("yaw", pyaw[-1], end_yaw)
19 | assert (abs(pyaw[-1] - end_yaw) <= 0.01)
20 |
21 | def test1(self):
22 | m.show_animation = False
23 | m.main()
24 |
25 | def test2(self):
26 | N_TEST = 10
27 |
28 | for i in range(N_TEST):
29 | start_x = (np.random.rand() - 0.5) * 10.0 # [m]
30 | start_y = (np.random.rand() - 0.5) * 10.0 # [m]
31 | start_yaw = np.deg2rad((np.random.rand() - 0.5) * 180.0) # [rad]
32 |
33 | end_x = (np.random.rand() - 0.5) * 10.0 # [m]
34 | end_y = (np.random.rand() - 0.5) * 10.0 # [m]
35 | end_yaw = np.deg2rad((np.random.rand() - 0.5) * 180.0) # [rad]
36 |
37 | curvature = 1.0 / (np.random.rand() * 5.0)
38 |
39 | px, py, pyaw, mode, clen = m.reeds_shepp_path_planning(
40 | start_x, start_y, start_yaw, end_x, end_y, end_yaw, curvature)
41 |
42 | self.check_edge_condition(px, py, pyaw, start_x, start_y, start_yaw, end_x, end_y, end_yaw)
43 |
--------------------------------------------------------------------------------
/tests/test_rocket_powered_landing.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 |
5 | if 'cvxpy' in sys.modules: # pragma: no cover
6 | sys.path.append("./AerialNavigation/rocket_powered_landing/")
7 |
8 | from AerialNavigation.rocket_powered_landing import rocket_powered_landing as m
9 | print(__file__)
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_rrt.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/../")
6 | try:
7 | from PathPlanning.RRT import rrt as m
8 | from PathPlanning.RRT import rrt_with_pathsmoothing as m1
9 | except ImportError:
10 | raise
11 |
12 |
13 | print(__file__)
14 |
15 |
16 | class Test(TestCase):
17 |
18 | def test1(self):
19 | m.show_animation = False
20 | m.main(gx=1.0, gy=1.0)
21 |
22 | def test2(self):
23 | m1.show_animation = False
24 | m1.main()
25 |
26 |
27 | if __name__ == '__main__': # pragma: no cover
28 | test = Test()
29 | test.test1()
30 | test.test2()
31 |
--------------------------------------------------------------------------------
/tests/test_rrt_dubins.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 | import sys
3 | import os
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
5 | "/../PathPlanning/RRTDubins/")
6 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
7 | "/../PathPlanning/DubinsPath/")
8 |
9 | try:
10 | import rrt_dubins as m
11 | except:
12 | raise
13 |
14 |
15 | print(__file__)
16 |
17 |
18 | class Test(TestCase):
19 |
20 | def test1(self):
21 | m.show_animation = False
22 | m.main()
23 |
24 |
25 | if __name__ == '__main__': # pragma: no cover
26 | test = Test()
27 | test.test1()
28 |
--------------------------------------------------------------------------------
/tests/test_rrt_star.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
6 | "/../PathPlanning/RRTStar/")
7 |
8 | try:
9 | import rrt_star as m
10 | except ImportError:
11 | raise
12 |
13 |
14 | print(__file__)
15 |
16 |
17 | class Test(TestCase):
18 |
19 | def test1(self):
20 | m.show_animation = False
21 | m.main()
22 |
23 |
24 | if __name__ == '__main__': # pragma: no cover
25 | test = Test()
26 | test.test1()
27 |
--------------------------------------------------------------------------------
/tests/test_rrt_star_dubins.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | import os
5 |
6 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
7 | + "/../PathPlanning/RRTStarDubins/")
8 |
9 | try:
10 | import rrt_star_dubins as m
11 | except:
12 | raise
13 |
14 | print(__file__)
15 |
16 |
17 | class Test(TestCase):
18 |
19 | def test1(self):
20 | m.show_animation = False
21 | m.main()
22 |
23 |
24 | if __name__ == '__main__': # pragma: no cover
25 | test = Test()
26 | test.test1()
27 |
--------------------------------------------------------------------------------
/tests/test_rrt_star_reeds_shepp.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | from unittest import TestCase
4 |
5 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
6 | + "/../PathPlanning/RRTStarReedsShepp/")
7 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
8 | + "/../PathPlanning/ReedsSheppPath/")
9 |
10 | try:
11 | import rrt_star_reeds_shepp as m
12 | except:
13 | raise
14 |
15 |
16 | print(__file__)
17 |
18 |
19 | class Test(TestCase):
20 |
21 | def test1(self):
22 | m.show_animation = False
23 | m.main(max_iter=5)
24 |
25 |
26 | if __name__ == '__main__': # pragma: no cover
27 | test = Test()
28 | test.test1()
29 |
--------------------------------------------------------------------------------
/tests/test_stanley_controller.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 | sys.path.append("./PathTracking/stanley_controller/")
5 |
6 | from PathTracking.stanley_controller import stanley_controller as m
7 |
8 | print(__file__)
9 |
10 |
11 | class Test(TestCase):
12 |
13 | def test1(self):
14 | m.show_animation = False
15 | m.main()
16 |
--------------------------------------------------------------------------------
/tests/test_state_lattice_planner.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | import sys
4 |
5 | import os
6 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
7 | "/../PathPlanning/ModelPredictiveTrajectoryGenerator/")
8 | sys.path.append(os.path.dirname(os.path.abspath(__file__)) +
9 | "/../PathPlanning/StateLatticePlanner/")
10 |
11 | try:
12 | import state_lattice_planner as m
13 | import model_predictive_trajectory_generator as m2
14 | except:
15 | raise
16 |
17 | print(__file__)
18 |
19 |
20 | class Test(TestCase):
21 |
22 | def test1(self):
23 | m.show_animation = False
24 | m2.show_animation = False
25 | m.main()
26 |
27 |
28 | if __name__ == '__main__': # pragma: no cover
29 | test = Test()
30 | test.test1()
31 |
--------------------------------------------------------------------------------
/tests/test_two_joint_arm_to_point_control.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from ArmNavigation.two_joint_arm_to_point_control import two_joint_arm_to_point_control as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.animation()
13 |
--------------------------------------------------------------------------------
/tests/test_unscented_kalman_filter.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase
2 |
3 | from Localization.unscented_kalman_filter import unscented_kalman_filter as m
4 |
5 | print(__file__)
6 |
7 |
8 | class Test(TestCase):
9 |
10 | def test1(self):
11 | m.show_animation = False
12 | m.main()
13 |
--------------------------------------------------------------------------------
/tests/test_visibility_road_map_planner.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
4 | + "/../PathPlanning/VoronoiRoadMap/")
5 |
6 | from unittest import TestCase
7 | from PathPlanning.VoronoiRoadMap import voronoi_road_map as m
8 |
9 |
10 | print(__file__)
11 |
12 |
13 | class Test(TestCase):
14 |
15 | def test1(self):
16 | m.show_animation = False
17 | m.main()
18 |
--------------------------------------------------------------------------------
/tests/test_voronoi_road_map_planner.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 |
4 | sys.path.append(os.path.dirname(os.path.abspath(__file__))
5 | + "/../PathPlanning/VisibilityRoadMap/")
6 |
7 | from unittest import TestCase
8 | from PathPlanning.VisibilityRoadMap import visibility_road_map as m
9 |
10 | print(__file__)
11 |
12 |
13 | class Test(TestCase):
14 |
15 | def test1(self):
16 | m.show_animation = False
17 | m.main()
18 |
--------------------------------------------------------------------------------