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.
Files changed (65) hide show
  1. curve_generation/__init__.py +9 -0
  2. curve_generation/bezier_curve.py +131 -0
  3. curve_generation/bspline_curve.py +271 -0
  4. curve_generation/cubic_spline.py +128 -0
  5. curve_generation/curve.py +64 -0
  6. curve_generation/dubins_curve.py +348 -0
  7. curve_generation/fem_pos_smooth.py +114 -0
  8. curve_generation/polynomial_curve.py +226 -0
  9. curve_generation/reeds_shepp.py +736 -0
  10. global_planner/__init__.py +3 -0
  11. global_planner/evolutionary_search/__init__.py +4 -0
  12. global_planner/evolutionary_search/aco.py +186 -0
  13. global_planner/evolutionary_search/evolutionary_search.py +87 -0
  14. global_planner/evolutionary_search/pso.py +356 -0
  15. global_planner/graph_search/__init__.py +28 -0
  16. global_planner/graph_search/a_star.py +124 -0
  17. global_planner/graph_search/d_star.py +291 -0
  18. global_planner/graph_search/d_star_lite.py +188 -0
  19. global_planner/graph_search/dijkstra.py +77 -0
  20. global_planner/graph_search/gbfs.py +78 -0
  21. global_planner/graph_search/graph_search.py +87 -0
  22. global_planner/graph_search/jps.py +165 -0
  23. global_planner/graph_search/lazy_theta_star.py +114 -0
  24. global_planner/graph_search/lpa_star.py +230 -0
  25. global_planner/graph_search/s_theta_star.py +133 -0
  26. global_planner/graph_search/theta_star.py +171 -0
  27. global_planner/graph_search/voronoi.py +200 -0
  28. global_planner/sample_search/__init__.py +6 -0
  29. global_planner/sample_search/informed_rrt.py +152 -0
  30. global_planner/sample_search/rrt.py +151 -0
  31. global_planner/sample_search/rrt_connect.py +147 -0
  32. global_planner/sample_search/rrt_star.py +77 -0
  33. global_planner/sample_search/sample_search.py +135 -0
  34. local_planner/__init__.py +19 -0
  35. local_planner/apf.py +144 -0
  36. local_planner/ddpg.py +630 -0
  37. local_planner/dqn.py +687 -0
  38. local_planner/dwa.py +212 -0
  39. local_planner/local_planner.py +262 -0
  40. local_planner/lqr.py +146 -0
  41. local_planner/mpc.py +214 -0
  42. local_planner/pid.py +158 -0
  43. local_planner/rpp.py +147 -0
  44. python_motion_planning-1.0.dist-info/LICENSE +674 -0
  45. python_motion_planning-1.0.dist-info/METADATA +873 -0
  46. python_motion_planning-1.0.dist-info/RECORD +65 -0
  47. python_motion_planning-1.0.dist-info/WHEEL +5 -0
  48. python_motion_planning-1.0.dist-info/top_level.txt +4 -0
  49. utils/__init__.py +19 -0
  50. utils/agent/__init__.py +0 -0
  51. utils/agent/agent.py +135 -0
  52. utils/environment/__init__.py +0 -0
  53. utils/environment/env.py +134 -0
  54. utils/environment/node.py +85 -0
  55. utils/environment/point2d.py +96 -0
  56. utils/environment/pose2d.py +91 -0
  57. utils/helper/__init__.py +3 -0
  58. utils/helper/math_helper.py +65 -0
  59. utils/planner/__init__.py +0 -0
  60. utils/planner/control_factory.py +31 -0
  61. utils/planner/curve_factory.py +29 -0
  62. utils/planner/planner.py +40 -0
  63. utils/planner/search_factory.py +51 -0
  64. utils/plot/__init__.py +0 -0
  65. 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,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.8.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,4 @@
1
+ curve_generation
2
+ global_planner
3
+ local_planner
4
+ utils
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
+ ]
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
@@ -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
@@ -0,0 +1,3 @@
1
+ from .math_helper import MathHelper
2
+
3
+ __all__ = ["MathHelper"]