Skip to content

Commit

Permalink
GNN-dynamic models + GNN-dynamic-planner
Browse files Browse the repository at this point in the history
  • Loading branch information
ruipengZ committed Mar 16, 2023
1 parent e7506bb commit 5410891
Show file tree
Hide file tree
Showing 19 changed files with 285 additions and 28 deletions.
Binary file added data/weights/dynamic/2arms/weights_gnn.pt
Binary file not shown.
Binary file added data/weights/dynamic/2arms/weights_head.pt
Binary file not shown.
Binary file added data/weights/dynamic/3arms/weights_gnn.pt
Binary file not shown.
Binary file added data/weights/dynamic/3arms/weights_head.pt
Binary file not shown.
Binary file added data/weights/dynamic/3kuka/weights_gnn.pt
Binary file not shown.
Binary file added data/weights/dynamic/3kuka/weights_head.pt
Binary file not shown.
Binary file added data/weights/dynamic/kuka4/weights_gnn.pt
Binary file not shown.
Binary file added data/weights/dynamic/kuka4/weights_head.pt
Binary file not shown.
Binary file added data/weights/dynamic/kuka5/weights_gnn.pt
Binary file not shown.
Binary file added data/weights/dynamic/kuka5/weights_head.pt
Binary file not shown.
Binary file added data/weights/dynamic/kuka7/weights_gnn.pt
Binary file not shown.
Binary file added data/weights/dynamic/kuka7/weights_head.pt
Binary file not shown.
195 changes: 195 additions & 0 deletions examples/dynamic_gnn_planner.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"pycharm": {
"name": "#%% md\n"
}
},
"source": [
"> [GNN-Dynamic](https://arxiv.org/abs/2210.08408) - it plans a trajectory by prioritizing the edge to explore based on a Graph Neural Network heuristic function in a dynamic environment."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"/Users/ruipeng/Desktop/lemp\n"
]
}
],
"source": [
"cd .."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false,
"jupyter": {
"outputs_hidden": false
},
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"pybullet build time: Mar 16 2022 13:26:26\n"
]
}
],
"source": [
"from environment.dynamic.simple2arm_env import Simple2ArmEnv\n",
"from objects.dynamic_object import DynamicObject\n",
"from robot.abstract_robot import DynamicRobotFactory\n",
"from objects.trajectory import WaypointLinearTrajectory\n",
"from robot.simple2arm_robot import Simple2ArmRobot\n",
"from planner.sipp_planner import SippPlanner\n",
"from utils.utils import seed_everything\n",
"\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"waypoints = np.linspace(0, np.pi/4, 40).tolist()\n",
"waypoints = [np.array([p,p]) for p in waypoints]\n",
"traj = WaypointLinearTrajectory(waypoints=waypoints)\n",
"robot = Simple2ArmRobot(base_position=(1, 1, 0), base_orientation=(0, 0, 0, 1))\n",
"objs = DynamicObject(item=robot, trajectory=traj)\n",
"\n",
"env = Simple2ArmEnv(objects=[objs])\n",
"num_samples = 1000\n",
"max_num_samples = 2000"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%%\n"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"# visualize environment\n",
"%matplotlib inline\n",
"import matplotlib.pyplot as plt\n",
"env.load()\n",
"plt.imshow(env.render())\n",
"plt.show()"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%%\n"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"from planner.learned.GNN_dynamic_planner import GNNDynamicPlanner"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%%\n"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"weights_path = ['data/weights/dynamic/2arms/weights_gnn.pt','data/weights/dynamic/2arms/weights_head.pt']"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%%\n"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [
"start, goal = np.array([0.] * 2), np.array([np.pi*7/8, 0])\n",
"# start, goal = np.array([0.] * 2), np.array([0, 0])\n",
"seed_everything(42)\n",
"model_args = dict(config_size=env.robot.config_dim, embed_size=32, obs_size=9)\n",
"planner = GNNDynamicPlanner(num_samples=num_samples, max_num_samples=max_num_samples, model_args=model_args, use_bctc=False, stop_when_success=True)\n",
"# load trained weights\n",
"planner.load_model(weights_path)\n",
"result = planner.plan(env, start, goal, timeout=('time', 1000))\n",
"if result.solution:\n",
" print('success', result)\n",
"else:\n",
" print('fail')"
],
"metadata": {
"collapsed": false,
"pycharm": {
"name": "#%%\n"
}
}
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"pycharm": {
"name": "#%%\n"
}
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "PyCharm (GNN-Dynamic)",
"language": "python",
"name": "pycharm-e8cae0ef"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.7"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
79 changes: 61 additions & 18 deletions planner/learned/GNN_dynamic_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,37 @@
from tqdm import tqdm as tqdm
import numpy as np

from utils.utils import DotDict

class GNNDynamicPlanner(LearnedPlanner):
def __init__(self, num_samples, max_num_samples, use_bctc, model_path=None, k_neighbors=50, num_samples_add=200, **kwargs):
self.model = [GNNet(config_size=2, embed_size=32, obs_size=9, use_obstacles=True), PolicyHead(embed_size=32), PositionalEncoder(embed_size=32), PositionalEncoder(embed_size=9)]
def __init__(self, num_samples, max_num_samples, model_args, use_bctc=False, model_path=None, k_neighbors=50, num_samples_add=200, **kwargs):
model_args = DotDict(model_args)
self.model = [
GNNet(config_size=model_args.config_size, embed_size=model_args.embed_size, obs_size=model_args.obs_size, use_obstacles=True),
PolicyHead(embed_size=model_args.embed_size),
PositionalEncoder(embed_size=model_args.embed_size),
PositionalEncoder(embed_size=model_args.obs_size)]

self.num_samples = num_samples
self.num_samples_add = num_samples_add
self.max_num_samples = max_num_samples
self.use_bctc = use_bctc
self.model_path = model_path
self.k_neigbors = k_neighbors



super(GNNDynamicPlanner, self).__init__(self.model, **kwargs)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
for model_ in self.model:
model_.to(self.device)

def _num_node(self):
## return nodes on the graph
return self.num_samples

def _plan(self, env, start, goal, timeout, **kwargs):
assert not self.loaded and self.model_path is None, "model not loaded and model_path is None"
# assert not self.loaded and self.model_path is None, "model not loaded and model_path is None"

if not self.loaded and self.model_path is not None:
self.load_model(self.model_path)
Expand All @@ -43,16 +56,30 @@ def _plan(self, env, start, goal, timeout, **kwargs):

model_gnn, model_head, model_pe_gnn, model_pe_head = self.model

self.speed = 1/(len(env.object.trajectory.waypoints)-1)
num_objects = len(env.objects)
self.len_traj = len(env.objects[0].trajectory.waypoints)


obs_traj = []
for i in range(self.len_traj):
## set config
for j in range(num_objects):
env.objects[j].item.set_config(env.objects[j].trajectory.waypoints[i])
obs_traj.append(np.concatenate([np.array(env.objects[j].item.get_workspace_observation()) for j in range(num_objects)], axis=0)[:, np.newaxis])

all_traj = np.concatenate(obs_traj, axis=1).transpose()
self.obs_traj = all_traj

self.speed = 1/(self.len_traj/2-1)

path = self._explore(env, start, goal,
model_gnn, model_head, model_pe_gnn, model_pe_head, t_max=self.max_num_samples, k=self.k_neigbors, n_sample=self.num_samples_add)


return create_dot_dict(solution=path)

def create_graph(self):
graph_data = knn_graph_from_points(self.points, self.k_neighbors)
def create_graph(self, points, k):
graph_data = knn_graph_from_points(points, k)
self.edges = graph_data.edges
self.edge_index = graph_data.edge_index
self.edge_cost = graph_data.edge_cost
Expand Down Expand Up @@ -92,12 +119,12 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
path = []
while not success and (len(points) - 2) <= t_max:

x = torch.arange(len(env.obs_points))
x = torch.arange(self.len_traj)
pe = model_pe_gnn(x).to(self.device)

edge_feat, node_feat = model_gnn(**data.to(self.device).to_dict(),
pos_enc=pe,
obstacles=torch.FloatTensor(env.obs_points).to(self.device),
obstacles=torch.FloatTensor(self.obs_traj).to(self.device),
loop=5)

# explore using the head network
Expand All @@ -106,7 +133,7 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
time_tick = 0

costs = {0: 0.}
path = [(0, 0)] # (node, time_cost)
path = [(data.v[0].numpy(), 0)] # (node, time_cost)

success = False
stay_counter = 0
Expand All @@ -117,8 +144,8 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
print('hello')
edges = edge_feat[cur_node, nonzero_indices, :]
offsets = torch.LongTensor([-2, -1, 0, 1, 2])
time_window = torch.clip(int(time_tick) + offsets, 0, env.obs_points.shape[0] - 1)
obs = torch.FloatTensor(env.obs_points[time_window].flatten())[None, :].repeat(edges.shape[0], 1).to(
time_window = torch.clip(int(time_tick) + offsets, 0, self.obs_traj.shape[0] - 1)
obs = torch.FloatTensor(self.obs_traj[time_window].flatten())[None, :].repeat(edges.shape[0], 1).to(
self.device)
pos_enc_tw = model_pe_head(time_window).flatten()[None, :].repeat(edges.shape[0], 1).to(self.device)

Expand All @@ -139,11 +166,12 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
continue

############ Take the step #########
if env._edge_fp(self.to_np(data.v[cur_node]), self.to_np(data.v[next_node]), time_tick):
dist = np.linalg.norm(self.to_np(data.v[next_node]) - self.to_np(data.v[cur_node]))
duration = int(np.ceil(dist / self.speed))
if env.edge_fp(self.to_np(data.v[cur_node]), self.to_np(data.v[next_node]), time_tick, time_tick+duration):
# step forward
success_one_step = True

dist = np.linalg.norm(self.to_np(data.v[next_node]) - self.to_np(data.v[cur_node]))
if dist == 0: # stay
if stay_counter > 0:
mask.remove(idx)
Expand All @@ -152,11 +180,11 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
stay_counter += 1
costs[next_node] = costs[cur_node] + self.speed
time_tick += 1
path.append((next_node, time_tick))
path.append((data.v[next_node].numpy(), time_tick))
else:
costs[next_node] = costs[cur_node] + dist
time_tick += int(np.ceil(dist / self.speed))
path.append((next_node, time_tick))
path.append((data.v[next_node].numpy(), time_tick))
####### there is no way back ###########
edge_feat[:, cur_node, :] = 0
stay_counter = 0
Expand All @@ -173,12 +201,13 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
success = False
break

elif env.in_goal_region(self.to_np(data.v[cur_node])):
elif env.robot.in_goal_region(self.to_np(data.v[cur_node]), goal):
success = True
break

if not success:
# print('----------------------------------------resample----------------------------------------!')
new_points = env.uniform_sample_mine(n_sample)
new_points = env.robot.sample_n_free_points(n_sample)
original_points = data.v.cpu()
points = torch.cat(
(original_points[:-1, :], torch.FloatTensor(new_points), original_points[[-1], :]), dim=0)
Expand All @@ -195,6 +224,20 @@ def _explore(self, env, start, goal, model_gnn, model_head, model_pe_gnn, model_
if not success:
return None
else:
return path
# print(path)
return self.generatePath(path)

def generatePath(self, discrete_path):

path = []
for i in range(len(discrete_path)-1):
path.append(discrete_path[i][0])
prev_point, prev_t = discrete_path[i][0], discrete_path[i][1]
next_point, next_t = discrete_path[i+1][0], discrete_path[i+1][1]
for k in range(1, next_t - prev_t):
path.append(prev_point + (next_point - prev_point)/np.linalg.norm(next_point - prev_point) * self.speed * k)
path.append(discrete_path[-1][0])

return path


10 changes: 7 additions & 3 deletions planner/learned_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ def __init__(self, model, **kwargs):
self.model = model
self.loaded = False

def _num_node(self):
## return nodes on the graph
raise NotImplementedError

def save_model(self, path):
if isinstance(self.model, list):
if not isinstance(path, list):
Expand All @@ -20,9 +24,9 @@ def load_model(self, path):
if isinstance(self.model, list):
if not isinstance(path, list):
raise ValueError("paths should be a list of paths")
for _, m in enumerate(self.model):
if path[_] is not None:
m.load_state_dict(torch.load(path[_]))
for _, p in enumerate(path):
if self.model[_] is not None:
self.model[_].load_state_dict(torch.load(p, map_location=torch.device('cpu')))
else:
self.model.load_state_dict(torch.load(path))
self.loaded = True
Expand Down
Loading

0 comments on commit 5410891

Please sign in to comment.