Skip to content

Commit 2a1abb2

Browse files
committed
some cleanup
1 parent 7a7e507 commit 2a1abb2

File tree

2 files changed

+74
-64
lines changed

2 files changed

+74
-64
lines changed

PathPlanning/TimeBasedPathPlanning/ConflictBasedSearch.py

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
ObstacleArrangement,
1111
Position,
1212
)
13+
from typing import Optional
1314
from copy import deepcopy
1415
from PathPlanning.TimeBasedPathPlanning.BaseClasses import MultiAgentPlanner, StartAndGoal
1516
from PathPlanning.TimeBasedPathPlanning.Node import NodePath
@@ -33,7 +34,7 @@ def plan(grid: Grid, start_and_goals: list[StartAndGoal], single_agent_planner_c
3334

3435
initial_solution: dict[AgentId, NodePath] = {}
3536

36-
# Generate initial solution (no reservations for robots)
37+
# Generate initial solution (no constraints)
3738
for start_and_goal in start_and_goals:
3839
path = single_agent_planner_class.plan(grid, start_and_goal.start, start_and_goal.goal, start_and_goal.index, verbose)
3940
initial_solution[AgentId(start_and_goal.index)] = path
@@ -44,126 +45,135 @@ def plan(grid: Grid, start_and_goals: list[StartAndGoal], single_agent_planner_c
4445
print(f"\nAgent {agent_idx} path:\n {path}")
4546

4647
constraint_tree = ConstraintTree(initial_solution)
47-
4848
attempted_constraint_combos = set()
4949

5050
while constraint_tree.nodes_to_expand:
5151
constraint_tree_node = constraint_tree.get_next_node_to_expand()
5252
ancestor_constraints = constraint_tree.get_ancestor_constraints(constraint_tree_node.parent_idx)
5353

54-
# print(f"Expanded node: {constraint_tree_node.constraint} with parent: {constraint_tree_node.parent_idx}")
55-
# print(f"\tAncestor constraints: {ancestor_constraints}")
56-
5754
if verbose:
5855
print(f"Expanding node with constraint {constraint_tree_node.constraint} and parent {constraint_tree_node.parent_idx}")
56+
print(f"\tCOST: {constraint_tree_node.cost}")
5957

6058
if constraint_tree_node is None:
6159
raise RuntimeError("No more nodes to expand in the constraint tree.")
6260
if not constraint_tree_node.constraint:
6361
# This means we found a solution!
62+
print(f"Found a path with constraints after {constraint_tree.expanded_node_count()} expansions:")
63+
print(f"Final cost: {constraint_tree_node.cost}")
6464
return (start_and_goals, [constraint_tree_node.paths[start_and_goal.index] for start_and_goal in start_and_goals])
6565

6666
if not isinstance(constraint_tree_node.constraint, ForkingConstraint):
6767
raise ValueError(f"Expected a ForkingConstraint, but got: {constraint_tree_node.constraint}")
6868

69-
# TODO: contents of this loop should probably be in a helper?
7069
for constrained_agent in constraint_tree_node.constraint.constrained_agents:
71-
if verbose:
72-
print(f"\nOuter loop step for agent {constrained_agent}")
73-
7470
applied_constraint = AppliedConstraint(constrained_agent.constraint, constrained_agent.agent)
7571

76-
# TODO: check type of other constraints somewhere
7772
all_constraints = deepcopy(ancestor_constraints) # TODO - no deepcopy pls
7873
all_constraints.append(applied_constraint)
7974

80-
num_expansions = constraint_tree.expanded_node_count()
81-
if num_expansions % 50 == 0:
82-
print(f"Expanded {num_expansions} nodes so far...")
83-
print(f"\tlen of constraints {len(all_constraints)}")
84-
85-
# Skip if we have already tried this set of constraints
86-
constraint_hash = hash(frozenset(all_constraints))
87-
if constraint_hash in attempted_constraint_combos:
88-
if verbose:
89-
print(f"\tSkipping already attempted constraint combination: {all_constraints}")
75+
new_path = ConflictBasedSearch.plan_for_agent(constrained_agent, all_constraints, constraint_tree, attempted_constraint_combos, grid, single_agent_planner_class, start_and_goals)
76+
if not new_path:
9077
continue
91-
else:
92-
attempted_constraint_combos.add(constraint_hash)
9378

94-
if verbose:
95-
print(f"\tall constraints: {all_constraints}")
96-
97-
grid.clear_constraint_points()
98-
grid.apply_constraint_points(all_constraints)
99-
100-
# Just plan for agent with new constraint
101-
start_and_goal = ConflictBasedSearch.find_by_index(start_and_goals, constrained_agent.agent)
102-
try:
103-
if verbose:
104-
print("\tplanning for: {}", start_and_goal)
105-
new_path = single_agent_planner_class.plan(grid, start_and_goal.start, start_and_goal.goal, start_and_goal.index, verbose)
106-
except Exception as e:
107-
continue
108-
109-
applied_constraint_parent = deepcopy(constraint_tree_node) #TODO: not sure if deepcopy is actually needed
110-
paths: dict[AgentId, NodePath] = deepcopy(constraint_tree_node.paths) # TODO: not sure if deepcopy is actually needed
79+
# Deepcopy to update with applied constraint and new paths
80+
applied_constraint_parent = deepcopy(constraint_tree_node)
81+
# TODO: could have a map under the hood to make these copies cheaper
82+
paths: dict[AgentId, NodePath] = deepcopy(constraint_tree_node.paths)
11183
paths[constrained_agent.agent] = new_path
11284

113-
# for (agent_idx, path) in paths.items():
114-
# print(f"\nAgent {agent_idx} path:\n {path}")
85+
if verbose:
86+
for (agent_idx, path) in paths.items():
87+
print(f"\nAgent {agent_idx} path:\n {path}")
11588

11689
applied_constraint_parent.constraint = applied_constraint
90+
# applied_constraint_parent.paths = paths
11791
parent_idx = constraint_tree.add_expanded_node(applied_constraint_parent)
11892

11993
new_constraint_tree_node = ConstraintTreeNode(paths, parent_idx, all_constraints)
12094
if new_constraint_tree_node.constraint is None:
12195
# This means we found a solution!
122-
print(f"Found a path with constraints after {num_expansions} expansions:")
96+
print(f"Found a path with constraints after {constraint_tree.expanded_node_count()} expansions:")
12397
for constraint in all_constraints:
12498
print(f"\t{constraint}")
125-
# return (start_and_goals, [paths[AgentId(i)] for i in range(len(start_and_goals))])
99+
print(f"Final cost: {constraint_tree_node.cost}")
126100
return (start_and_goals, paths.values())
127101

128-
# if verbose:
129-
# print(f"Adding new constraint tree node with constraint: {new_constraint_tree_node.constraint}")
102+
if verbose:
103+
print(f"Adding new constraint tree node with constraint: {new_constraint_tree_node.constraint}")
130104
constraint_tree.add_node_to_tree(new_constraint_tree_node)
131105

132106
raise RuntimeError("No solution found")
133107

134-
# TODO: bad function name
135-
def find_by_index(start_and_goal_list: list[StartAndGoal], target_index: AgentId) -> AgentId:
108+
def get_agents_start_and_goal(start_and_goal_list: list[StartAndGoal], target_index: AgentId) -> StartAndGoal:
136109
for item in start_and_goal_list:
137110
if item.index == target_index:
138111
return item
139112
raise RuntimeError(f"Could not find agent with index {target_index} in {start_and_goal_list}")
140113

114+
115+
def plan_for_agent(constrained_agent: ConstraintTreeNode,
116+
all_constraints: list[AppliedConstraint],
117+
constraint_tree: ConstraintTree,
118+
attempted_constraint_combos: set,
119+
grid: Grid,
120+
single_agent_planner_class: SingleAgentPlanner,
121+
start_and_goals: list[StartAndGoal]) -> Optional[tuple[list[StartAndGoal], list[NodePath]]]:
122+
123+
num_expansions = constraint_tree.expanded_node_count()
124+
if num_expansions % 50 == 0:
125+
print(f"Expanded {num_expansions} nodes so far...")
126+
print(f"\tNumber of constraints on expanded node: {len(all_constraints)}")
127+
128+
# Skip if we have already tried this set of constraints
129+
constraint_hash = hash(frozenset(all_constraints))
130+
if constraint_hash in attempted_constraint_combos:
131+
if verbose:
132+
print(f"\tSkipping already attempted constraint combination: {all_constraints}")
133+
return None
134+
else:
135+
attempted_constraint_combos.add(constraint_hash)
136+
137+
if verbose:
138+
print(f"\tall constraints: {all_constraints}")
139+
140+
grid.clear_constraint_points()
141+
grid.apply_constraint_points(all_constraints)
142+
143+
# Just plan for agent with new constraint
144+
start_and_goal = ConflictBasedSearch.get_agents_start_and_goal(start_and_goals, constrained_agent.agent)
145+
try:
146+
if verbose:
147+
print("\tplanning for: {}", start_and_goal)
148+
new_path = single_agent_planner_class.plan(grid, start_and_goal.start, start_and_goal.goal, start_and_goal.index, verbose)
149+
return new_path
150+
except Exception as e:
151+
print(f"Error: {e}")
152+
return None
153+
141154
# TODO
142-
# * still discrepancies between sipp and A*
143155
# * fan out across multiple threads
144156
# * somehow test/check that high level tree is doing what you want
145-
# * SIPP stinks at 3 robots in the hallway case
146157
verbose = False
147158
show_animation = True
148159
np.random.seed(42) # For reproducibility
149160
def main():
150161
grid_side_length = 21
151162

152-
# TODO: bug somewhere where it expects agent ids to match indices
153163
# start_and_goals = [StartAndGoal(i, Position(1, i), Position(19, 19-i)) for i in range(1, 12)]
154-
# start_and_goals = [StartAndGoal(i, Position(1, 8+i), Position(19, 19-i)) for i in range(6)]
164+
start_and_goals = [StartAndGoal(i, Position(1, 8+i), Position(19, 19-i)) for i in range(5)]
155165
# start_and_goals = [StartAndGoal(i, Position(1, 2*i), Position(19, 19-i)) for i in range(4)]
156166

157167
# generate random start and goals
158-
start_and_goals: list[StartAndGoal] = []
159-
for i in range(40):
160-
start = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
161-
goal = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
162-
while any([start_and_goal.start == start for start_and_goal in start_and_goals]):
163-
start = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
164-
goal = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
168+
# start_and_goals: list[StartAndGoal] = []
169+
# for i in range(40):
170+
# start = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
171+
# goal = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
172+
# while any([start_and_goal.start == start for start_and_goal in start_and_goals]):
173+
# start = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
174+
# goal = Position(np.random.randint(0, grid_side_length), np.random.randint(0, grid_side_length))
165175

166-
start_and_goals.append(StartAndGoal(i, start, goal))
176+
# start_and_goals.append(StartAndGoal(i, start, goal))
167177

168178
# hallway cross
169179
# start_and_goals = [StartAndGoal(0, Position(6, 10), Position(13, 10)),
@@ -183,8 +193,8 @@ def main():
183193
obstacle_avoid_points=obstacle_avoid_points,
184194
# obstacle_arrangement=ObstacleArrangement.TEMPORARY_OBSTACLE,
185195
# obstacle_arrangement=ObstacleArrangement.HALLWAY,
186-
# obstacle_arrangement=ObstacleArrangement.NARROW_CORRIDOR,
187-
obstacle_arrangement=ObstacleArrangement.NONE,
196+
obstacle_arrangement=ObstacleArrangement.NARROW_CORRIDOR,
197+
# obstacle_arrangement=ObstacleArrangement.NONE,
188198
# obstacle_arrangement=ObstacleArrangement.ARRANGEMENT1,
189199
# obstacle_arrangement=ObstacleArrangement.RANDOM,
190200
)

PathPlanning/TimeBasedPathPlanning/ConstraintTree.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ def add_expanded_node(self, node: ConstraintTreeNode) -> int:
150150
"""
151151
Add an expanded node to the tree. Returns the index of this node in the expanded nodes dictionary.
152152
"""
153-
parent_idx = len(self.expanded_nodes)
154-
self.expanded_nodes[parent_idx] = node
155-
return parent_idx
153+
node_idx = len(self.expanded_nodes)
154+
self.expanded_nodes[node_idx] = node
155+
return node_idx
156156

157157
def expanded_node_count(self) -> int:
158158
return len(self.expanded_nodes)

0 commit comments

Comments
 (0)