Loading...
Searching...
No Matches
visualize.py
1#!/usr/bin/env python3
2
3
36
37# Author: Ryan Luna
38
39import sys
40import yaml
41from math import sin, cos
42import numpy as np
43import matplotlib.pyplot as plt
44import matplotlib.animation as animation
45import matplotlib.patches as patches
46
47
48# Reads a YAML world specification from the worldFile.
49# Returns the set of obstacles and the bounds of the world.
50def readWorldFile(worldFile):
51 handle = open(worldFile, "r")
52 # use safe_load instead load
53 dataMap = yaml.safe_load(handle)
54 handle.close()
55
56 # Read in bounds
57 xlower = float(dataMap["bounds"]["x"]["lower"])
58 xupper = float(dataMap["bounds"]["x"]["upper"])
59 ylower = float(dataMap["bounds"]["y"]["lower"])
60 yupper = float(dataMap["bounds"]["y"]["upper"])
61
62 bounds = [[xlower, xupper], [ylower, yupper]]
63
64 obs = {}
65
66 if "obstacles" in dataMap:
67 for r in dataMap["obstacles"]:
68 for k in r.keys():
69 if k not in obs:
70 obs[k] = []
71 obs[k].append(r[k])
72 return obs, bounds
73
74
75def readPathFile(pathFile):
76 # Read in file line by line
77 lines = [line.rstrip() for line in open(pathFile) if len(line.rstrip()) > 0]
78
79 # first line is meta data
80 metadata = lines[0]
81 metadata = metadata.split(" ")
82 if len(metadata) != 5:
83 raise RuntimeError(
84 "Malformed path file. Expected first line with # links, link length, originX, originY, xySlices"
85 )
86
87 numLinks = int(metadata[0])
88 linkLength = float(metadata[1])
89 origin = (float(metadata[2]), float(metadata[3]))
90 slices = int(metadata[4])
91
92 path = []
93
94 for l in lines[1:]:
95 entries = l.split(" ")
96 if len(entries) != numLinks:
97 raise RuntimeError(
98 "Malformed path file. Path entries must have length = # links"
99 )
100 config = [float(e) for e in entries]
101 path.append(config)
102
103 return numLinks, linkLength, origin, path, slices
104
105
106def plotPolygon(axes, data, color, alpha=1.0, edgecolor=None):
107 points = []
108 for d in data:
109 for k in d.keys():
110 if k == "vertex":
111 coords = d[k].strip()
112 if coords.startswith("("):
113 coords = coords[1:-1]
114 coords = coords.strip().split(",")
115 pt = [float(coords[0]), float(coords[1])]
116 points.append(pt)
117
118 else:
119 raise RuntimeError('Expected "vertex", but got ', k)
120
121 if len(points) > 0:
122 arr = np.array(points)
123 axes.add_patch(
124 patches.Polygon(
125 arr, facecolor=color, alpha=alpha, fill=True, edgecolor=edgecolor
126 )
127 )
128
129
130def plotObstacles(axes, obstacles, bounds):
131 for k, v in obstacles.items():
132 for o in v:
133 if k == "polygon":
134 plotPolygon(axes, o, "0.4", edgecolor="0.4")
135 else:
136 raise RuntimeError("Unknown geometry type: ", k)
137
138 plt.axis([bounds[0][0], bounds[0][1], bounds[1][0], bounds[1][1]])
139
140
141def plotChain(axes, angles, origin, color="blue"):
142 x = origin[0]
143 y = origin[1]
144 angle = 0.0
145
146 linkLength = 1.0 / len(angles)
147 for theta in angles:
148 angle += theta
149
150 xN = x + (cos(angle) * linkLength)
151 yN = y + (sin(angle) * linkLength)
152
153 axes.plot([x, xN], [y, yN], "-", color=color)
154
155 x = xN
156 y = yN
157
158
159def plotChainFrame(frame_num, ax, path, obstacles, bounds, origin, rate, slices):
160 ax.clear()
161 plotObstacles(ax, obstacles, bounds)
162
163 # Hold for 1 second at the beginning and end
164 if frame_num < rate:
165 configuration = path[0]
166 elif frame_num >= len(path) + rate:
167 configuration = path[-1]
168 else:
169 configuration = path[frame_num - rate]
170
171 plotChain(ax, configuration, origin)
172
173
174def AnimatePath(obstacles, bounds, numLinks, linkLength, origin, path, slices):
175 fig = plt.figure()
176 axes = plt.axes()
177 framerate = 30
178
179 numFrames = len(path)
180 anim = animation.FuncAnimation(
181 fig,
182 plotChainFrame,
183 fargs=[axes, path, obstacles, bounds, origin, slices, framerate],
184 frames=numFrames + (framerate * 2),
185 interval=(1.0 / framerate) * 1000,
186 blit=False,
187 )
188
189 filename = "animation.mp4"
190 anim.save(filename, fps=framerate)
191
192
193if __name__ == "__main__":
194 worldFile = "./world.yaml"
195 pathFile = "./manipulator_path.txt"
196
197 obs, bounds = readWorldFile(worldFile)
198 numLinks, linkLength, origin, path, slices = readPathFile(pathFile)
199
200 print("Creating animation for planar kinematic chain... ")
201 AnimatePath(obs, bounds, numLinks, linkLength, origin, path, slices)
202 print("Done")