python-motion-planning 1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- curve_generation/__init__.py +9 -0
- curve_generation/bezier_curve.py +131 -0
- curve_generation/bspline_curve.py +271 -0
- curve_generation/cubic_spline.py +128 -0
- curve_generation/curve.py +64 -0
- curve_generation/dubins_curve.py +348 -0
- curve_generation/fem_pos_smooth.py +114 -0
- curve_generation/polynomial_curve.py +226 -0
- curve_generation/reeds_shepp.py +736 -0
- global_planner/__init__.py +3 -0
- global_planner/evolutionary_search/__init__.py +4 -0
- global_planner/evolutionary_search/aco.py +186 -0
- global_planner/evolutionary_search/evolutionary_search.py +87 -0
- global_planner/evolutionary_search/pso.py +356 -0
- global_planner/graph_search/__init__.py +28 -0
- global_planner/graph_search/a_star.py +124 -0
- global_planner/graph_search/d_star.py +291 -0
- global_planner/graph_search/d_star_lite.py +188 -0
- global_planner/graph_search/dijkstra.py +77 -0
- global_planner/graph_search/gbfs.py +78 -0
- global_planner/graph_search/graph_search.py +87 -0
- global_planner/graph_search/jps.py +165 -0
- global_planner/graph_search/lazy_theta_star.py +114 -0
- global_planner/graph_search/lpa_star.py +230 -0
- global_planner/graph_search/s_theta_star.py +133 -0
- global_planner/graph_search/theta_star.py +171 -0
- global_planner/graph_search/voronoi.py +200 -0
- global_planner/sample_search/__init__.py +6 -0
- global_planner/sample_search/informed_rrt.py +152 -0
- global_planner/sample_search/rrt.py +151 -0
- global_planner/sample_search/rrt_connect.py +147 -0
- global_planner/sample_search/rrt_star.py +77 -0
- global_planner/sample_search/sample_search.py +135 -0
- local_planner/__init__.py +19 -0
- local_planner/apf.py +144 -0
- local_planner/ddpg.py +630 -0
- local_planner/dqn.py +687 -0
- local_planner/dwa.py +212 -0
- local_planner/local_planner.py +262 -0
- local_planner/lqr.py +146 -0
- local_planner/mpc.py +214 -0
- local_planner/pid.py +158 -0
- local_planner/rpp.py +147 -0
- python_motion_planning-1.0.dist-info/LICENSE +674 -0
- python_motion_planning-1.0.dist-info/METADATA +873 -0
- python_motion_planning-1.0.dist-info/RECORD +65 -0
- python_motion_planning-1.0.dist-info/WHEEL +5 -0
- python_motion_planning-1.0.dist-info/top_level.txt +4 -0
- utils/__init__.py +19 -0
- utils/agent/__init__.py +0 -0
- utils/agent/agent.py +135 -0
- utils/environment/__init__.py +0 -0
- utils/environment/env.py +134 -0
- utils/environment/node.py +85 -0
- utils/environment/point2d.py +96 -0
- utils/environment/pose2d.py +91 -0
- utils/helper/__init__.py +3 -0
- utils/helper/math_helper.py +65 -0
- utils/planner/__init__.py +0 -0
- utils/planner/control_factory.py +31 -0
- utils/planner/curve_factory.py +29 -0
- utils/planner/planner.py +40 -0
- utils/planner/search_factory.py +51 -0
- utils/plot/__init__.py +0 -0
- utils/plot/plot.py +274 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
curve_generation/__init__.py,sha256=6eeN0NBlAndBhjk-ol3Yaa-0om2lNG7n43BASRfeV9k,370
|
|
2
|
+
curve_generation/bezier_curve.py,sha256=QVMsXceZ7KNJjFJL0zwagqRujO4Zx-FtwdinDzdyaVo,3637
|
|
3
|
+
curve_generation/bspline_curve.py,sha256=2yHMYUbqg2aUQ3GXMN2lO44OLOfsqPH2Rk5sZT7mkCc,9332
|
|
4
|
+
curve_generation/cubic_spline.py,sha256=gW4vkGCmtJzpC7aRhGcYPdJLAAi-UKCOFqmqnqBno4Q,3378
|
|
5
|
+
curve_generation/curve.py,sha256=xf7G5lDGTCVUBoSK8Oa14fmdf_AANOZVqdSmAKlwu-A,1543
|
|
6
|
+
curve_generation/dubins_curve.py,sha256=3U6jYRzuc9MrE9fjOCoLE3hSRvd65dZkksrgYH5rglI,10987
|
|
7
|
+
curve_generation/fem_pos_smooth.py,sha256=WAHZym4_E2Mp0Uk4w8olQ6c_KigJo79nqAs-KscXyJ0,3388
|
|
8
|
+
curve_generation/polynomial_curve.py,sha256=9eM55opnFuwCPY6ErJY3knH-MMhvT6g7PwhSiAGMP78,7504
|
|
9
|
+
curve_generation/reeds_shepp.py,sha256=hWJDbBhRuHgtcHzGUJefqv_IuO7W8irrBMxCQOGeFkM,21427
|
|
10
|
+
global_planner/__init__.py,sha256=cx2kwds-up49JOu1LGjJkviEOAlG1-mV3A7QvNDfRoI,93
|
|
11
|
+
global_planner/evolutionary_search/__init__.py,sha256=2MIP1t74TmKwRVGbSxDSy38rMObDjjpqNn-tHB8fCEc,70
|
|
12
|
+
global_planner/evolutionary_search/aco.py,sha256=fPvEEOLLo-W7k6jvgZxUtN4V59vnaHLi4_hqh9odRF4,6997
|
|
13
|
+
global_planner/evolutionary_search/evolutionary_search.py,sha256=WbftOQQ--XivulEFYCT_CFX2FylIvSVxysxbqQkxnl4,2697
|
|
14
|
+
global_planner/evolutionary_search/pso.py,sha256=i_QqU6_NUgiXI4rPZrtAskpjrE35IZdi0kyVJU1IQ-w,13673
|
|
15
|
+
global_planner/graph_search/__init__.py,sha256=5wAp5GsaPfyWmfAPiXtdjaWK1VGQp1nBUZXn7MCCXdQ,745
|
|
16
|
+
global_planner/graph_search/a_star.py,sha256=81g0syj09DrpmQcbVX3rKlinXMDzQB7_0Vq_s8s4UxY,3821
|
|
17
|
+
global_planner/graph_search/d_star.py,sha256=amBzAXM895FqpeVA-J3PbYgKusZM0ujquzjLzPkHwzA,10445
|
|
18
|
+
global_planner/graph_search/d_star_lite.py,sha256=iI8hA5GbrTtpIqr9pY6SVB55QyxPHe4F3vYcTJe2RyE,6770
|
|
19
|
+
global_planner/graph_search/dijkstra.py,sha256=ssQg5eIyFBVVXH_MMAblCEJWvh6egEfGln_BMShDK7E,2451
|
|
20
|
+
global_planner/graph_search/gbfs.py,sha256=2x43OjShaFv7nox1AxzARIWsxS0lv5zWWZy0uikxsLU,2531
|
|
21
|
+
global_planner/graph_search/graph_search.py,sha256=QmIBScvOfYI_Ud4qQ3lh3Di3Tw-ZuiUV343w7zj6X_c,2675
|
|
22
|
+
global_planner/graph_search/jps.py,sha256=fxYBClawoj-sk9dNsqKOwNA7X6jsRqEdi0Nvr_qD0uo,5291
|
|
23
|
+
global_planner/graph_search/lazy_theta_star.py,sha256=S-1-eUAzhRsjhms1sAACFmh7EtsFcNGnl5T8f4Kwip8,3884
|
|
24
|
+
global_planner/graph_search/lpa_star.py,sha256=TpD4jqQvdn9cFWphhpyIibQmqeAI8gDrtHw3T2Unc6E,7474
|
|
25
|
+
global_planner/graph_search/s_theta_star.py,sha256=Q9mUgVwfIsd3vC0x1xQVtSabcjaUFunICvBRElJmDWs,4372
|
|
26
|
+
global_planner/graph_search/theta_star.py,sha256=_kTcY1fTiKO5TxgNcBrsD9Z6Xh3e_OgyTuZMVWUJ0jg,5410
|
|
27
|
+
global_planner/graph_search/voronoi.py,sha256=KcdnLz9RrqlB-MLkDxQBhZn2u3y6Jz3583yNVAj71Fo,6685
|
|
28
|
+
global_planner/sample_search/__init__.py,sha256=0foVXexrJxGDiECtc8BWGADzjsgGS_BfvMfecMHFxAI,188
|
|
29
|
+
global_planner/sample_search/informed_rrt.py,sha256=pTKXfovP_0H1hVcWpFp-p2upyOAVnKYNKYwbVJ2pei4,5588
|
|
30
|
+
global_planner/sample_search/rrt.py,sha256=YXrHvR8NdlmrSoVFhohNZEDp0rBgidX6o2Q4RKZv93o,5280
|
|
31
|
+
global_planner/sample_search/rrt_connect.py,sha256=qvBPNe-gaA0Xi_S9SraAvq0DzTdDzDcUCZDMRUBK514,5553
|
|
32
|
+
global_planner/sample_search/rrt_star.py,sha256=0VLXlGuhndIxZT_FZMS-11avlJB4lDsjp5PbQgGVPvs,2892
|
|
33
|
+
global_planner/sample_search/sample_search.py,sha256=rgeEfLe8kkbXx5pSa80BTG_nOb0l7VNrG3L2H718wnM,4243
|
|
34
|
+
local_planner/__init__.py,sha256=ojbK5WMquubPDMADURk78qRIP4vGI_yaE7MX86gM-0A,304
|
|
35
|
+
local_planner/apf.py,sha256=dc2h8PCyihlwdmyICQ22uKhbOvYA9t2BGqWuZK7o9Ss,5586
|
|
36
|
+
local_planner/ddpg.py,sha256=R7jcve4bokTZzyl2qYbvtb3NWxRAbCzkgZ-22scpHvo,29726
|
|
37
|
+
local_planner/dqn.py,sha256=Y2HH0GaKR1mG8JwLfDBRa_3oCttzmfid13osad5xl_0,28026
|
|
38
|
+
local_planner/dwa.py,sha256=HWkVVDFmjWv_n4uT3vdQpe9_SJdF07lt-q6SWGFyr3E,8120
|
|
39
|
+
local_planner/local_planner.py,sha256=AmaVt3STFIq4kQi6f-eEn_fwAdfno13vPdAYlRubZC8,10543
|
|
40
|
+
local_planner/lqr.py,sha256=jxlTFEFIFlf5k9rdXWVhwiVKWSy4_EBSZAH1H9AWPes,5444
|
|
41
|
+
local_planner/mpc.py,sha256=GZa7jtLr5Dhds1vmxn5TdxZVSO5KyO8yz1md3IaA5kQ,8209
|
|
42
|
+
local_planner/pid.py,sha256=kfvXCGkNFcPnqehbIdByHTaa7ZN7DUDfefrScFTeCL8,5939
|
|
43
|
+
local_planner/rpp.py,sha256=GLfjGoqjy0L4uTRVk7ZywM8xtBmfw7ZVuB5JA4kweUg,5809
|
|
44
|
+
utils/__init__.py,sha256=ebv1k_btXnz6YbTqaokEma0Xr3J5WKkiCw3V1ESEQ4I,632
|
|
45
|
+
utils/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
46
|
+
utils/agent/agent.py,sha256=vv_cP6ZJ2sZSpot5txi5YdQmVQRRUURVyx2CX2uyn9s,3978
|
|
47
|
+
utils/environment/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
48
|
+
utils/environment/env.py,sha256=_XVn6z6x46b-EEUHPrgY3UHPkN25fgv54TWU3ZXDkNg,3838
|
|
49
|
+
utils/environment/node.py,sha256=1kLobhv7wxuG2KatSNcuaIRDKcokox9FG8dpCbjvH9s,2279
|
|
50
|
+
utils/environment/point2d.py,sha256=fKwF3GctvVWsX_QcLX9W7LRma4G1QoNWnseAoqsleBM,2517
|
|
51
|
+
utils/environment/pose2d.py,sha256=zDuwm4EDMO0-LAfnLp3RvvNl46TmP2Ap46ZgolwItNQ,2478
|
|
52
|
+
utils/helper/__init__.py,sha256=1n8T8SlUeRKDWiZf3nsdEDA4h99xXyegvPLV08c_D2c,63
|
|
53
|
+
utils/helper/math_helper.py,sha256=tFfeyKJYsD4e7LPtyie4nrTVv3QBLipDcSUHejNIl48,2160
|
|
54
|
+
utils/planner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
55
|
+
utils/planner/control_factory.py,sha256=eLiwsRxtRFi3JCPlMunRKZO1gIPN-Wm5X6Zn5AXby18,1035
|
|
56
|
+
utils/planner/curve_factory.py,sha256=U-rm_sRMpxgLkXko6-VyzhXlOVG8UP86-05Mugf_lOM,956
|
|
57
|
+
utils/planner/planner.py,sha256=4ysHWj8Ho8O8v9KqCHCBS5Nb_5vzm-hbiIvkHCtmi4s,1065
|
|
58
|
+
utils/planner/search_factory.py,sha256=cD1znMcR8wMbdM1D23TpiplfZfQTS_Z01QnKYTMpO6E,1816
|
|
59
|
+
utils/plot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
|
+
utils/plot/plot.py,sha256=BMHGFL2n8P0bgxCrWiVEZly_DPXx-NMKZQh2_BNzEFc,10207
|
|
61
|
+
python_motion_planning-1.0.dist-info/LICENSE,sha256=a4N8el8H0UrdAJzCeVgj9HklpT3VkZfsy4aL1SAifJE,35793
|
|
62
|
+
python_motion_planning-1.0.dist-info/METADATA,sha256=oke5gV_-Rzuzl3ZkC7-s2eeE8RAM7KmSYUCMUlp8npc,57991
|
|
63
|
+
python_motion_planning-1.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
64
|
+
python_motion_planning-1.0.dist-info/top_level.txt,sha256=FhASGG2D6Eo2pNwFLbE9t3mTAVX5Vd6GreG_uV4bN28,52
|
|
65
|
+
python_motion_planning-1.0.dist-info/RECORD,,
|
utils/__init__.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .helper import MathHelper
|
|
2
|
+
from .agent.agent import Robot
|
|
3
|
+
from .environment.env import Env, Grid, Map
|
|
4
|
+
from .environment.node import Node
|
|
5
|
+
from .environment.point2d import Point2D
|
|
6
|
+
from .environment.pose2d import Pose2D
|
|
7
|
+
from .plot.plot import Plot
|
|
8
|
+
from .planner.planner import Planner
|
|
9
|
+
from .planner.search_factory import SearchFactory
|
|
10
|
+
from .planner.curve_factory import CurveFactory
|
|
11
|
+
from .planner.control_factory import ControlFactory
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"MathHelper",
|
|
15
|
+
"Env", "Grid", "Map", "Node", "Point2D", "Pose2D",
|
|
16
|
+
"Plot",
|
|
17
|
+
"Planner", "SearchFactory", "CurveFactory", "ControlFactory",
|
|
18
|
+
"Robot"
|
|
19
|
+
]
|
utils/agent/__init__.py
ADDED
|
File without changes
|
utils/agent/agent.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@file: agent.py
|
|
3
|
+
@breif: Class for agent
|
|
4
|
+
@author: Yang Haodong, Wu Maojia
|
|
5
|
+
@update: 2024.3.29
|
|
6
|
+
"""
|
|
7
|
+
import math
|
|
8
|
+
import numpy as np
|
|
9
|
+
from abc import abstractmethod, ABC
|
|
10
|
+
|
|
11
|
+
class Agent(ABC):
|
|
12
|
+
"""
|
|
13
|
+
Abstract class for agent.
|
|
14
|
+
|
|
15
|
+
Parameters:
|
|
16
|
+
px (float): initial x-position
|
|
17
|
+
py (float): initial y-position
|
|
18
|
+
theta (float): initial pose angle
|
|
19
|
+
"""
|
|
20
|
+
def __init__(self, px, py, theta) -> None:
|
|
21
|
+
self.px = px
|
|
22
|
+
self.py = py
|
|
23
|
+
self.theta = theta
|
|
24
|
+
self.parameters = None
|
|
25
|
+
|
|
26
|
+
def setParameters(self, **parameters) -> None:
|
|
27
|
+
# other customer parameters
|
|
28
|
+
self.parameters = parameters
|
|
29
|
+
for param, val in parameters.items():
|
|
30
|
+
setattr(self, param, val)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def position(self):
|
|
34
|
+
return (self.px, self.py)
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def kinematic(self, u, dt):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def state(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Robot(Agent):
|
|
47
|
+
"""
|
|
48
|
+
Class for robot.
|
|
49
|
+
|
|
50
|
+
Parameters:
|
|
51
|
+
px (float): initial x-position
|
|
52
|
+
py (float): initial y-position
|
|
53
|
+
theta (float): initial pose angle
|
|
54
|
+
v (float): linear velocity
|
|
55
|
+
w (float): angular velocity
|
|
56
|
+
"""
|
|
57
|
+
def __init__(self, px, py, theta, v, w) -> None:
|
|
58
|
+
super().__init__(px, py, theta)
|
|
59
|
+
# velocity
|
|
60
|
+
self.v = v
|
|
61
|
+
self.w = w
|
|
62
|
+
# history
|
|
63
|
+
self.history_pose = []
|
|
64
|
+
|
|
65
|
+
def __str__(self) -> str:
|
|
66
|
+
return "Robot"
|
|
67
|
+
|
|
68
|
+
def kinematic(self, u: np.ndarray, dt: float, replace: bool = True):
|
|
69
|
+
"""
|
|
70
|
+
Run robot kinematic once.
|
|
71
|
+
|
|
72
|
+
Parameters:
|
|
73
|
+
u (np.ndarray): control command with [v, w]
|
|
74
|
+
dt (float): simulation time
|
|
75
|
+
replace (bool): update-self if true else return a new Robot object
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
robot (Robot): a new robot object
|
|
79
|
+
"""
|
|
80
|
+
new_state = self.lookforward(self.state, u, dt).squeeze().tolist()
|
|
81
|
+
if replace:
|
|
82
|
+
self.history_pose.append((self.px, self.py, self.theta))
|
|
83
|
+
self.px, self.py, self.theta = new_state[0], new_state[1], new_state[2]
|
|
84
|
+
self.v, self.w = new_state[3], new_state[4]
|
|
85
|
+
else:
|
|
86
|
+
new_robot = Robot(new_state[0], new_state[1], new_state[2],
|
|
87
|
+
new_state[3], new_state[4])
|
|
88
|
+
new_robot.setParameters(self.parameters)
|
|
89
|
+
return new_robot
|
|
90
|
+
|
|
91
|
+
def lookforward(self, state: np.ndarray, u: np.ndarray, dt: float) -> np.ndarray:
|
|
92
|
+
"""
|
|
93
|
+
Run robot kinematic once but do not update.
|
|
94
|
+
|
|
95
|
+
Parameters:
|
|
96
|
+
state (np.ndarray): robot state with [x, y, theta, v, w]
|
|
97
|
+
u (np.ndarray): control command with [v, w]
|
|
98
|
+
dt (float): simulation time
|
|
99
|
+
obstacles (set): set of obstacles with (x, y)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
new_state (np.ndarray (5x1)): new robot state with [x, y, theta, v, w]
|
|
103
|
+
"""
|
|
104
|
+
F = np.array([[1, 0, 0, 0, 0],
|
|
105
|
+
[0, 1, 0, 0, 0],
|
|
106
|
+
[0, 0, 1, 0, 0],
|
|
107
|
+
[0, 0, 0, 0, 0],
|
|
108
|
+
[0, 0, 0, 0, 0]])
|
|
109
|
+
B = np.array([[dt * math.cos(state[2]), 0],
|
|
110
|
+
[dt * math.sin(state[2]), 0],
|
|
111
|
+
[ 0, dt],
|
|
112
|
+
[ 1, 0],
|
|
113
|
+
[ 0, 1]])
|
|
114
|
+
new_state = F @ state + B @ u
|
|
115
|
+
|
|
116
|
+
return new_state
|
|
117
|
+
|
|
118
|
+
def reset(self) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Reset the state.
|
|
121
|
+
"""
|
|
122
|
+
self.v = 0
|
|
123
|
+
self.w = 0
|
|
124
|
+
self.history_pose = []
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def state(self) -> None:
|
|
128
|
+
"""
|
|
129
|
+
Get the state.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
state (np.ndarray (5x1)): robot state with [x, y, theta, v, w]
|
|
133
|
+
"""
|
|
134
|
+
state = np.array([[self.px], [self.py], [self.theta], [self.v], [self.w]])
|
|
135
|
+
return state
|
|
File without changes
|
utils/environment/env.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@file: env.py
|
|
3
|
+
@breif: 2-dimension environment
|
|
4
|
+
@author: Winter
|
|
5
|
+
@update: 2023.1.13
|
|
6
|
+
"""
|
|
7
|
+
from math import sqrt
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from scipy.spatial import cKDTree
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from .node import Node
|
|
13
|
+
|
|
14
|
+
class Env(ABC):
|
|
15
|
+
"""
|
|
16
|
+
Class for building 2-d workspace of robots.
|
|
17
|
+
|
|
18
|
+
Parameters:
|
|
19
|
+
x_range (int): x-axis range of enviroment
|
|
20
|
+
y_range (int): y-axis range of environmet
|
|
21
|
+
eps (float): tolerance for float comparison
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> from python_motion_planning.utils import Env
|
|
25
|
+
>>> env = Env(30, 40)
|
|
26
|
+
"""
|
|
27
|
+
def __init__(self, x_range: int, y_range: int, eps: float = 1e-6) -> None:
|
|
28
|
+
# size of environment
|
|
29
|
+
self.x_range = x_range
|
|
30
|
+
self.y_range = y_range
|
|
31
|
+
self.eps = eps
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def grid_map(self) -> set:
|
|
35
|
+
return {(i, j) for i in range(self.x_range) for j in range(self.y_range)}
|
|
36
|
+
|
|
37
|
+
@abstractmethod
|
|
38
|
+
def init(self) -> None:
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
class Grid(Env):
|
|
42
|
+
"""
|
|
43
|
+
Class for discrete 2-d grid map.
|
|
44
|
+
"""
|
|
45
|
+
def __init__(self, x_range: int, y_range: int) -> None:
|
|
46
|
+
super().__init__(x_range, y_range)
|
|
47
|
+
# allowed motions
|
|
48
|
+
self.motions = [Node((-1, 0), None, 1, None), Node((-1, 1), None, sqrt(2), None),
|
|
49
|
+
Node((0, 1), None, 1, None), Node((1, 1), None, sqrt(2), None),
|
|
50
|
+
Node((1, 0), None, 1, None), Node((1, -1), None, sqrt(2), None),
|
|
51
|
+
Node((0, -1), None, 1, None), Node((-1, -1), None, sqrt(2), None)]
|
|
52
|
+
# obstacles
|
|
53
|
+
self.obstacles = None
|
|
54
|
+
self.obstacles_tree = None
|
|
55
|
+
self.init()
|
|
56
|
+
|
|
57
|
+
def init(self) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Initialize grid map.
|
|
60
|
+
"""
|
|
61
|
+
x, y = self.x_range, self.y_range
|
|
62
|
+
obstacles = set()
|
|
63
|
+
|
|
64
|
+
# boundary of environment
|
|
65
|
+
for i in range(x):
|
|
66
|
+
obstacles.add((i, 0))
|
|
67
|
+
obstacles.add((i, y - 1))
|
|
68
|
+
for i in range(y):
|
|
69
|
+
obstacles.add((0, i))
|
|
70
|
+
obstacles.add((x - 1, i))
|
|
71
|
+
|
|
72
|
+
# user-defined obstacles
|
|
73
|
+
for i in range(10, 21):
|
|
74
|
+
obstacles.add((i, 15))
|
|
75
|
+
for i in range(15):
|
|
76
|
+
obstacles.add((20, i))
|
|
77
|
+
for i in range(15, 30):
|
|
78
|
+
obstacles.add((30, i))
|
|
79
|
+
for i in range(16):
|
|
80
|
+
obstacles.add((40, i))
|
|
81
|
+
|
|
82
|
+
self.obstacles = obstacles
|
|
83
|
+
self.obstacles_tree = cKDTree(np.array(list(obstacles)))
|
|
84
|
+
|
|
85
|
+
def update(self, obstacles):
|
|
86
|
+
self.obstacles = obstacles
|
|
87
|
+
self.obstacles_tree = cKDTree(np.array(list(obstacles)))
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Map(Env):
|
|
91
|
+
"""
|
|
92
|
+
Class for continuous 2-d map.
|
|
93
|
+
"""
|
|
94
|
+
def __init__(self, x_range: int, y_range: int) -> None:
|
|
95
|
+
super().__init__(x_range, y_range)
|
|
96
|
+
self.boundary = None
|
|
97
|
+
self.obs_circ = None
|
|
98
|
+
self.obs_rect = None
|
|
99
|
+
self.init()
|
|
100
|
+
|
|
101
|
+
def init(self):
|
|
102
|
+
"""
|
|
103
|
+
Initialize map.
|
|
104
|
+
"""
|
|
105
|
+
x, y = self.x_range, self.y_range
|
|
106
|
+
|
|
107
|
+
# boundary of environment
|
|
108
|
+
self.boundary = [
|
|
109
|
+
[0, 0, 1, y],
|
|
110
|
+
[0, y, x, 1],
|
|
111
|
+
[1, 0, x, 1],
|
|
112
|
+
[x, 1, 1, y]
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
# user-defined obstacles
|
|
116
|
+
self.obs_rect = [
|
|
117
|
+
[14, 12, 8, 2],
|
|
118
|
+
[18, 22, 8, 3],
|
|
119
|
+
[26, 7, 2, 12],
|
|
120
|
+
[32, 14, 10, 2]
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
self.obs_circ = [
|
|
124
|
+
[7, 12, 3],
|
|
125
|
+
[46, 20, 2],
|
|
126
|
+
[15, 5, 2],
|
|
127
|
+
[37, 7, 3],
|
|
128
|
+
[37, 23, 3]
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
def update(self, boundary, obs_circ, obs_rect):
|
|
132
|
+
self.boundary = boundary if boundary else self.boundary
|
|
133
|
+
self.obs_circ = obs_circ if obs_circ else self.obs_circ
|
|
134
|
+
self.obs_rect = obs_rect if obs_rect else self.obs_rect
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@file: node.py
|
|
3
|
+
@breif: 2-dimension node data stucture
|
|
4
|
+
@author: Yang Haodong, Wu Maojia
|
|
5
|
+
@update: 2024.3.15
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
class Node(object):
|
|
9
|
+
"""
|
|
10
|
+
Class for searching nodes.
|
|
11
|
+
|
|
12
|
+
Parameters:
|
|
13
|
+
current (tuple): current coordinate
|
|
14
|
+
parent (tuple): coordinate of parent node
|
|
15
|
+
g (float): path cost
|
|
16
|
+
h (float): heuristic cost
|
|
17
|
+
|
|
18
|
+
Examples:
|
|
19
|
+
>>> from env import Node
|
|
20
|
+
>>> node1 = Node((1, 0), (2, 3), 1, 2)
|
|
21
|
+
>>> node2 = Node((1, 0), (2, 5), 2, 8)
|
|
22
|
+
>>> node3 = Node((2, 0), (1, 6), 3, 1)
|
|
23
|
+
...
|
|
24
|
+
>>> node1 + node2
|
|
25
|
+
>>> Node((2, 0), (2, 3), 3, 2)
|
|
26
|
+
...
|
|
27
|
+
>>> node1 == node2
|
|
28
|
+
>>> True
|
|
29
|
+
...
|
|
30
|
+
>>> node1 != node3
|
|
31
|
+
>>> True
|
|
32
|
+
"""
|
|
33
|
+
def __init__(self, current: tuple, parent: tuple = None, g: float = 0, h: float = 0) -> None:
|
|
34
|
+
self.current = current
|
|
35
|
+
self.parent = parent
|
|
36
|
+
self.g = g
|
|
37
|
+
self.h = h
|
|
38
|
+
|
|
39
|
+
def __add__(self, node):
|
|
40
|
+
assert isinstance(node, Node)
|
|
41
|
+
return Node((self.x + node.x, self.y + node.y), self.parent, self.g + node.g, self.h)
|
|
42
|
+
|
|
43
|
+
def __eq__(self, node) -> bool:
|
|
44
|
+
if not isinstance(node, Node):
|
|
45
|
+
return False
|
|
46
|
+
return self.current == node.current
|
|
47
|
+
|
|
48
|
+
def __ne__(self, node) -> bool:
|
|
49
|
+
return not self.__eq__(node)
|
|
50
|
+
|
|
51
|
+
def __lt__(self, node) -> bool:
|
|
52
|
+
assert isinstance(node, Node)
|
|
53
|
+
return self.g + self.h < node.g + node.h or \
|
|
54
|
+
(self.g + self.h == node.g + node.h and self.h < node.h)
|
|
55
|
+
|
|
56
|
+
def __hash__(self) -> int:
|
|
57
|
+
return hash(self.current)
|
|
58
|
+
|
|
59
|
+
def __str__(self) -> str:
|
|
60
|
+
return "Node({}, {}, {}, {})".format(self.current, self.parent, self.g, self.h)
|
|
61
|
+
|
|
62
|
+
def __repr__(self) -> str:
|
|
63
|
+
return self.__str__()
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def x(self) -> float:
|
|
67
|
+
return self.current[0]
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def y(self) -> float:
|
|
71
|
+
return self.current[1]
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def px(self) -> float:
|
|
75
|
+
if self.parent:
|
|
76
|
+
return self.parent[0]
|
|
77
|
+
else:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def py(self) -> float:
|
|
82
|
+
if self.parent:
|
|
83
|
+
return self.parent[1]
|
|
84
|
+
else:
|
|
85
|
+
return None
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@file: point2d.py
|
|
3
|
+
@breif: 2-dimension point data stucture
|
|
4
|
+
@author: Wu Maojia
|
|
5
|
+
@update: 2024.3.15
|
|
6
|
+
"""
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Point2D(object):
|
|
11
|
+
"""
|
|
12
|
+
Class for searching and manipulating 2-dimensional points.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
x: x-coordinate of the 2d point
|
|
16
|
+
y: y-coordinate of the 2d point
|
|
17
|
+
eps: tolerance for float comparison
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
>>> from python_motion_planning import Point2D
|
|
21
|
+
>>> p1 = Point2D(1, 2)
|
|
22
|
+
>>> p2 = Point2D(3, 4)
|
|
23
|
+
...
|
|
24
|
+
>>> p1
|
|
25
|
+
>>> Point2D(1, 2)
|
|
26
|
+
...
|
|
27
|
+
>>> p1 + p2
|
|
28
|
+
>>> Point2D(4, 6)
|
|
29
|
+
...
|
|
30
|
+
>>> p1 - p2
|
|
31
|
+
>>> Point2D(-2, -2)
|
|
32
|
+
...
|
|
33
|
+
>>> p1 == p2
|
|
34
|
+
>>> False
|
|
35
|
+
...
|
|
36
|
+
>>> p1!= p2
|
|
37
|
+
>>> True
|
|
38
|
+
...
|
|
39
|
+
>>> p1.dist(p2)
|
|
40
|
+
>>> 2.8284271247461903
|
|
41
|
+
...
|
|
42
|
+
>>> p1.angle(p2)
|
|
43
|
+
>>> 0.7853981633974483
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, x: float, y: float, eps: float = 1e-6) -> None:
|
|
47
|
+
self.x = x
|
|
48
|
+
self.y = y
|
|
49
|
+
self.eps = eps
|
|
50
|
+
|
|
51
|
+
if abs(self.x - round(self.x)) < self.eps:
|
|
52
|
+
self.x = round(self.x)
|
|
53
|
+
|
|
54
|
+
if abs(self.y - round(self.y)) < self.eps:
|
|
55
|
+
self.y = round(self.y)
|
|
56
|
+
|
|
57
|
+
def __add__(self, point):
|
|
58
|
+
assert isinstance(point, Point2D)
|
|
59
|
+
return Point2D(self.x + point.x, self.y + point.y)
|
|
60
|
+
|
|
61
|
+
def __sub__(self, point):
|
|
62
|
+
assert isinstance(point, Point2D)
|
|
63
|
+
return Point2D(self.x - point.x, self.y - point.y)
|
|
64
|
+
|
|
65
|
+
def __eq__(self, point) -> bool:
|
|
66
|
+
if not isinstance(point, Point2D):
|
|
67
|
+
return False
|
|
68
|
+
return abs(self.x - point.x) < self.eps and abs(self.y - point.y) < self.eps
|
|
69
|
+
|
|
70
|
+
def __ne__(self, point) -> bool:
|
|
71
|
+
return not self.__eq__(point)
|
|
72
|
+
|
|
73
|
+
def __hash__(self) -> int:
|
|
74
|
+
return hash((self.x, self.y))
|
|
75
|
+
|
|
76
|
+
def __str__(self) -> str:
|
|
77
|
+
return "Point2D({}, {})".format(self.x, self.y)
|
|
78
|
+
|
|
79
|
+
def __repr__(self) -> str:
|
|
80
|
+
return self.__str__()
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def from_tuple(point: tuple):
|
|
84
|
+
return Point2D(point[0], point[1])
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def to_tuple(self) -> tuple:
|
|
88
|
+
return int(self.x), int(self.y)
|
|
89
|
+
|
|
90
|
+
def dist(self, point) -> float:
|
|
91
|
+
assert isinstance(point, Point2D)
|
|
92
|
+
return math.hypot(self.x - point.x, self.y - point.y)
|
|
93
|
+
|
|
94
|
+
def angle(self, point) -> float:
|
|
95
|
+
assert isinstance(point, Point2D)
|
|
96
|
+
return math.atan2(point.y - self.y, point.x - self.x)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
@file: pose2d.py
|
|
3
|
+
@breif: 2-dimension pose data stucture
|
|
4
|
+
@author: Wu Maojia
|
|
5
|
+
@update: 2024.3.15
|
|
6
|
+
"""
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Pose2D(object):
|
|
11
|
+
"""
|
|
12
|
+
Class for searching and manipulating 2-dimensional poses.
|
|
13
|
+
|
|
14
|
+
Parameters:
|
|
15
|
+
x: x-coordinate of the 2d pose
|
|
16
|
+
y: y-coordinate of the 2d pose
|
|
17
|
+
theta: orientation of the 2d pose in radians
|
|
18
|
+
eps: tolerance for float comparison
|
|
19
|
+
|
|
20
|
+
Examples:
|
|
21
|
+
>>> from python_motion_planning import Pose2D
|
|
22
|
+
>>> p1 = Pose2D(1, 2)
|
|
23
|
+
>>> p2 = Pose2D(3, 4, 1)
|
|
24
|
+
...
|
|
25
|
+
>>> p1
|
|
26
|
+
>>> Pose2D(1, 2, 0)
|
|
27
|
+
...
|
|
28
|
+
>>> p2
|
|
29
|
+
>>> Pose2D(3, 4, 1)
|
|
30
|
+
...
|
|
31
|
+
>>> p1 + p2
|
|
32
|
+
>>> Pose2D(4, 6, 1)
|
|
33
|
+
...
|
|
34
|
+
>>> p1 - p2
|
|
35
|
+
>>> Pose2D(-2, -2, -1)
|
|
36
|
+
...
|
|
37
|
+
>>> p1 == p2
|
|
38
|
+
>>> False
|
|
39
|
+
...
|
|
40
|
+
>>> p1!= p2
|
|
41
|
+
>>> True
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def __init__(self, x: float, y: float, theta: float = 0, eps: float = 1e-6) -> None:
|
|
45
|
+
self.x = x
|
|
46
|
+
self.y = y
|
|
47
|
+
self.theta = theta
|
|
48
|
+
self.eps = eps
|
|
49
|
+
|
|
50
|
+
if abs(self.x - round(self.x)) < self.eps:
|
|
51
|
+
self.x = round(self.x)
|
|
52
|
+
|
|
53
|
+
if abs(self.y - round(self.y)) < self.eps:
|
|
54
|
+
self.y = round(self.y)
|
|
55
|
+
|
|
56
|
+
if abs(self.theta - round(self.theta)) < self.eps:
|
|
57
|
+
self.theta = round(self.theta)
|
|
58
|
+
|
|
59
|
+
def __add__(self, pose):
|
|
60
|
+
assert isinstance(pose, Pose2D)
|
|
61
|
+
return Pose2D(self.x + pose.x, self.y + pose.y, self.theta + pose.theta)
|
|
62
|
+
|
|
63
|
+
def __sub__(self, pose):
|
|
64
|
+
assert isinstance(pose, Pose2D)
|
|
65
|
+
return Pose2D(self.x - pose.x, self.y - pose.y, self.theta - pose.theta)
|
|
66
|
+
|
|
67
|
+
def __eq__(self, pose) -> bool:
|
|
68
|
+
if not isinstance(pose, Pose2D):
|
|
69
|
+
return False
|
|
70
|
+
return (abs(self.x - pose.x) < self.eps and abs(self.y - pose.y) < self.eps
|
|
71
|
+
and abs(self.theta - pose.theta) < self.eps)
|
|
72
|
+
|
|
73
|
+
def __ne__(self, pose) -> bool:
|
|
74
|
+
return not self.__eq__(pose)
|
|
75
|
+
|
|
76
|
+
def __hash__(self) -> int:
|
|
77
|
+
return hash((self.x, self.y, self.theta))
|
|
78
|
+
|
|
79
|
+
def __str__(self) -> str:
|
|
80
|
+
return "Pose2D({}, {}, {})".format(self.x, self.y, self.theta)
|
|
81
|
+
|
|
82
|
+
def __repr__(self) -> str:
|
|
83
|
+
return self.__str__()
|
|
84
|
+
|
|
85
|
+
@staticmethod
|
|
86
|
+
def from_tuple(pose: tuple):
|
|
87
|
+
return Pose2D(pose[0], pose[1], pose[2])
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def to_tuple(self) -> tuple:
|
|
91
|
+
return self.x, self.y, self.theta
|
utils/helper/__init__.py
ADDED