diff --git a/.gitignore b/.gitignore index 5d039d2..1335cd5 100644 --- a/.gitignore +++ b/.gitignore @@ -127,4 +127,5 @@ dmypy.json # Pyre type checker .pyre/ -.vscode/ \ No newline at end of file +.vscode/ +test.py diff --git a/RDA_planner/mpc.py b/RDA_planner/mpc.py index 55e26f0..b388fa4 100644 --- a/RDA_planner/mpc.py +++ b/RDA_planner/mpc.py @@ -414,6 +414,12 @@ def convert_inequal_polygon(self, vertex, velocity=np.zeros((2, 1))): def gen_inequal_global(self, vertex): + convex_flag, order = self.is_convex_and_ordered(vertex) + + assert convex_flag, 'The polygon constructed by vertex is not convex. Please check the vertex.' + + if order == 'CW': vertex = vertex[:, ::-1] + temp_vertex = np.c_[vertex, vertex[0:2, 0]] point_num = vertex.shape[1] @@ -438,4 +444,43 @@ def gen_inequal_global(self, vertex): return A, b + def cross_product(self, o, a, b): + """Compute the cross product of vectors OA and OB. + A positive cross product indicates a counter-clockwise turn, + a negative indicates a clockwise turn, and zero indicates a collinear point.""" + return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]) + + def is_convex_and_ordered(self, points): + """Determine if the polygon is convex and return the order (CW or CCW). + + Args: + points (np.ndarray): A 2xN NumPy array representing the vertices of the polygon. + + Returns: + (bool, str): A tuple where the first element is True if the polygon is convex, + and the second element is 'CW' or 'CCW' based on the order. + If not convex, returns (False, None). + """ + n = points.shape[1] # Number of points + if n < 3: + return False, None # A polygon must have at least 3 points + + # Initialize the direction for the first cross product + direction = 0 + + for i in range(n): + o = points[:, i] + a = points[:, (i + 1) % n] + b = points[:, (i + 2) % n] + + cross = self.cross_product(o, a, b) + + if cross != 0: # Only consider non-collinear points + if direction == 0: + direction = 1 if cross > 0 else -1 + elif (cross > 0 and direction < 0) or (cross < 0 and direction > 0): + return False, None # Not convex + + return True, 'CCW' if direction > 0 else 'CW' + diff --git a/README.md b/README.md index 3e59d67..2b57c03 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ pip install -e . **Dynamic obstacles avoidance (example/dynamic_obs.py)** | | |:-------------------------:|:-------------------------:|:-------------------------:| -**Note:** You can customize the scenario by modifying the parameters in the corresponding yaml file as introduced in [ir_sim](https://github.com/hanruihua/ir_sim). For the polygon obstacles, please make sure the order of the vertices is Counterclockwise. +**Note:** You can customize the scenario by modifying the parameters in the corresponding yaml file as introduced in [ir_sim](https://github.com/hanruihua/ir_sim). For the polygon obstacles, please make sure the obstacles are convex (CCW order is not necessary now). ## Citation diff --git a/example/path_track/animation/path_track_diff.gif b/example/path_track/animation/path_track_diff.gif new file mode 100644 index 0000000..161c7f0 Binary files /dev/null and b/example/path_track/animation/path_track_diff.gif differ diff --git a/example/path_track/path_track.yaml b/example/path_track/path_track.yaml index 3a9bb9b..e64f737 100644 --- a/example/path_track/path_track.yaml +++ b/example/path_track/path_track.yaml @@ -31,7 +31,7 @@ obstacle: - number: 1 distribution: {name: 'manual'} shape: - - {name: 'polygon', vertices: [[31, 24], [33, 24], [33, 28], [31, 28]]} + - {name: 'polygon', vertices: [[31, 28], [33, 28], [33, 24], [31, 24]]} state: - [0, 0, 0] diff --git a/example/path_track/path_track_diff.py b/example/path_track/path_track_diff.py index ba79731..f982b80 100644 --- a/example/path_track/path_track_diff.py +++ b/example/path_track/path_track_diff.py @@ -40,7 +40,7 @@ def main(): print('arrive at the goal') break - env.end(ani_name='path_track', show_traj=True, show_trail=True, ending_time=10, ani_kwargs={'subrectangles':True}) + env.end(ani_name='path_track_diff', show_traj=True, show_trail=True, ending_time=10) if __name__ == '__main__': main() \ No newline at end of file diff --git a/setup.py b/setup.py index 02da8bd..00e95b4 100644 --- a/setup.py +++ b/setup.py @@ -4,12 +4,12 @@ setup( name='RDA_planner', py_modules=['RDA_planner'], - version= '2.1', + version= '2.2', install_requires=[ 'cvxpy==1.5.2', 'numpy', 'pathos', - 'ir_sim==2.1.1', + 'ir_sim==2.1.2', 'matplotlib', 'gctl==1.1', 'opencv-python',