4 import matplotlib.pyplot
as plt
6 from IPython.display
import HTML, display
8 from matplotlib.animation
import FuncAnimation
10 from matplotlib.patches
import Rectangle
12 from matplotlib
import RcParams, rcParams
13 rcParams[
'figure.figsize'] = (10, 5)
15 from colour
import Color
17 import xml.etree.ElementTree
as ET
21 def __init__(self, center, width, height, name=None):
30 c2tr = np.array([width, height]) / 2
31 c2br = np.array([width, - height]) / 2
40 self.
AA = np.array([[1, 0], [0, 1], [-1, 0], [0, -1]])
41 self.
bb = np.concatenate([c2tr] * 2) + self.
AA.dot(center)
47 size_string = str(self.
widthwidth) +
' ' + str(self.
heightheight) +
' 0.001 0 0 0'
48 pose_string = str(self.
centercenter[0]) +
' ' + str(self.
centercenter[1]) +
' 0 0 0 0'
50 size_string = str(self.
widthwidth) +
' ' + str(self.
heightheight) +
' 0.5 0 0 0'
51 pose_string = str(self.
centercenter[0]) +
' ' + str(self.
centercenter[1]) +
' -0.25 0 0 0'
53 model = ET.SubElement(super,
'model')
55 model.set(
'name', self.
namename)
57 static = ET.SubElement(model,
'static')
60 pose = ET.SubElement(model,
'pose')
61 pose.text = pose_string
63 link = ET.SubElement(model,
'link')
64 link.set(
'name',
'plate_link')
66 link_pose = ET.SubElement(link,
'pose')
67 link_pose.text =
'0 0 0 0 0 0'
69 visual = ET.SubElement(link,
'visual')
70 visual.set(
'name',
'plate_visual')
72 visual_geometry = ET.SubElement(visual,
'geometry')
73 visual_box = ET.SubElement(visual_geometry,
'box')
75 visual_size = ET.SubElement(visual_box,
'size')
76 visual_size.text = size_string
78 material = ET.SubElement(visual,
'material')
80 ambient = ET.SubElement(material,
'ambient')
81 ambient.text =
'0.8 0 0 1'
83 diffuse = ET.SubElement(material,
'diffuse')
84 diffuse.text =
'0.8 0 0 1'
86 specular = ET.SubElement(material,
'specular')
87 specular.text =
'0.1 0.1 0.1 1'
89 emissive = ET.SubElement(material,
'emissive')
90 emissive.text =
'0.8 0 0 1'
92 collision = ET.SubElement(link,
'collision')
93 collision.set(
'name',
'plate_collision')
95 collision_geometry = ET.SubElement(collision,
'geometry')
96 collision_box = ET.SubElement(collision_geometry,
'box')
98 collision_size = ET.SubElement(collision_box,
'size')
99 collision_size.text = size_string
109 if not 'edgecolor' in kwargs:
110 kwargs[
'edgecolor'] =
'black'
113 if not 'facecolor' in kwargs:
114 kwargs[
'facecolor'] =
'none'
121 c2c = np.array([width, height]) / 2
122 bottom_left = center - c2c
123 top_right = center + c2c
126 rect = Rectangle(bottom_left, width, height, **kwargs)
130 ax.scatter(*bottom_left, s=0)
131 ax.scatter(*top_right, s=0)
134 ax.set_aspect(
'equal')
146 if bool_bridge
is None:
147 initial = self.
add_stoneadd_stone([0, 0], 1.2, 1.2,
'initial')
149 goal = self.
add_stoneadd_stone([4, 0], 1.2, 1.2,
'goal')
152 self.
add_stoneadd_stone([1 + 2*i, 0], 1.2, 4.2,
'vertical' + str(i))
153 self.
add_stoneadd_stone([2, -1.5 + 3*i], 3.2, 1.2,
'lateral' + str(i))
156 if any(i != bool(i)
for i
in bool_bridge):
157 raise ValueError(
'Entry bool_bridge must be a list of boolean value.')
162 initial = self.
add_stoneadd_stone([0, 0], 1.2, 1.2,
'initial')
167 center = initial.bottom_right + np.array([width_bridge * 1.5, initial.height / 2])
168 centers = [center + np.array([i * 2 * width_bridge, 0])
for i
in np.where(bool_bridge)[0]]
171 [width_bridge*1.5] * sum(bool_bridge),
172 [initial.height] * sum(bool_bridge),
178 center = initial.center + np.array([initial.width + (len(bool_bridge) * 2 + 1) * width_bridge, 0])
179 goal = self.
add_stoneadd_stone(center, initial.width, initial.height,
'goal')
184 c2g = goal.center - initial.center
185 width = initial.width + c2g[0]
186 center = initial.center + c2g / 2 + np.array([0, (initial.height + height) / 2 + clearance])
187 self.
add_stoneadd_stone(center, width, height,
'lateral')
199 n_stones = len(centers)
200 if n_stones != len(widths)
or n_stones != len(heights):
201 raise ValueError(
'Arguments have incoherent size.')
205 for i
in range(n_stones):
206 stone_name = name
if name
is None else name +
'_' + str(i)
207 stones.append(self.
add_stoneadd_stone(centers[i], widths[i], heights[i], name=stone_name))
218 if stone.name == name:
222 raise ValueError(f
'No stone in the terrain has name {name}.')
225 def plot(self, title=None, **kwargs):
228 if not 'facecolor' in kwargs:
229 kwargs[
'facecolor'] = [0, 1, 0, .1]
232 labels = [
'Stepping stone',
None]
234 stone.plot(label=labels[min(i, 1)], **kwargs)
241 sdf = ET.Element(
'sdf')
242 sdf.set(
'version',
'1.7')
243 model = ET.SubElement(sdf,
'model')
244 model.set(
'name',
'terrain')
246 pose = ET.SubElement(model,
'pose')
247 pose.text =
'0 0 0 0 0 0'
251 model_indicator = ET.SubElement(model,
'model')
252 model_indicator.set(
'name',
'terrain_indicator')
254 pose_indicator = ET.SubElement(model,
'pose')
255 pose_indicator.text =
'0 0 0 0 0 0'
257 model_height = ET.SubElement(model,
'model')
258 model_height.set(
'name',
'terrain_height')
260 pose_height = ET.SubElement(model,
'pose')
261 pose_height.text =
'0 0 0 0 0 0'
264 s.add_to_sdf(model_indicator, indicator=
True)
265 s.add_to_sdf(model_height, indicator=
False)
270 mydata = ET.tostring(sdf, encoding=
'unicode', xml_declaration=
True)
271 f = open(fname + fend,
'w')
276 def animate_footstep_plan(terrain, n_steps, step_span, positions, title=None, outname="footstep_planner", save_anim=False):
279 fig, ax = plt.subplots()
282 terrain.plot(title=title, ax=ax)
285 colors = [
'r',
'b',
'g',
'y']
287 feet = [ax.scatter(0, 0, color=c, zorder=3, label=
'foot ' + str(i))
for (i, c)
in enumerate(colors)]
295 label=
'foot ' + str(i) +
' limits'
296 )
for (i, c)
in enumerate(colors)]
321 ax.legend(loc=
'upper left', bbox_to_anchor=(0, 1.3), ncol=2)
323 def animate(n_steps):
328 for i
in range(len(feet)):
329 feet[i].set_offsets(positions[i][n_steps])
332 c2c = np.ones(2) * step_span / 2
333 for i
in range(len(limits)):
334 limits[i].set_xy(positions[i][n_steps] - c2c)
339 ani = FuncAnimation(fig, animate, frames=n_steps, interval=1e3)
342 ani.save(outname +
".mp4", fps=30, extra_args=[
'-vcodec',
'libx264'])
349 position_left = np.genfromtxt(base_name +
"_position_left" + fend)
351 position_right = np.genfromtxt(base_name +
"_position_right" + fend)
353 stone_left = np.genfromtxt(base_name +
"_stone_left" + fend)
355 stone_right = np.genfromtxt(base_name +
"_stone_right" + fend)
357 return (position_left, position_right, stone_left, stone_right,
True)
362 position_front_left = np.genfromtxt(base_name +
"_position_front_left" + fend)[:, 0:2]
364 position_front_right = np.genfromtxt(base_name +
"_position_front_right" + fend)[:, 0:2]
366 position_rear_left = np.genfromtxt(base_name +
"_position_rear_left" + fend)[:, 0:2]
368 position_rear_right = np.genfromtxt(base_name +
"_position_rear_right" + fend)[:, 0:2]
370 return (position_front_left, position_front_right, position_rear_left, position_rear_right,
True)
376 n_steps = footsteps[0].shape[0]
382 sdf = ET.Element(
'sdf')
383 sdf.set(
'version',
'1.7')
385 model = ET.SubElement(sdf,
'model')
386 model.set(
'name',
'footsteps')
388 static = ET.SubElement(model,
'static')
391 pose = ET.SubElement(model,
'pose')
392 pose.text =
'0 0 0 0 0 0'
394 yellow = Color(
'yellow')
395 colors = list(yellow.range_to(Color(
'green'), len(steps)))
397 for i, (color, step)
in enumerate(zip(colors, steps)):
398 footstep_model = ET.SubElement(model,
'model')
399 footstep_model.set(
'name',
'footstep ' + str(i))
401 static = ET.SubElement(footstep_model,
'static')
404 rgb = color.get_rgb()
405 color_string = str(rgb[0]) +
' ' + str(rgb[1]) +
' ' + str(rgb[2]) +
' 1'
407 link = ET.SubElement(footstep_model,
'link')
408 link.set(
'name',
'footstep_link')
410 link_pose = ET.SubElement(link,
'pose')
411 link_pose.text = str(step[0]) +
' ' + str(step[1]) +
' 0 0 0 0'
413 visual = ET.SubElement(link,
'visual')
414 visual.set(
'name',
'footstep_visual')
416 visual_geometry = ET.SubElement(visual,
'geometry')
418 visual_cylinder = ET.SubElement(visual_geometry,
'cylinder')
419 radius = ET.SubElement(visual_cylinder,
'radius')
422 length = ET.SubElement(visual_cylinder,
'length')
428 material = ET.SubElement(visual,
'material')
431 ambient = ET.SubElement(material,
'ambient')
432 ambient.text = color_string
434 diffuse = ET.SubElement(material,
'diffuse')
435 diffuse.text = color_string
437 specular = ET.SubElement(material,
'specular')
438 specular.text =
'0.1 0.1 0.1 1'
440 emissive = ET.SubElement(material,
'emissive')
441 emissive.text = color_string
451 data = ET.tostring(sdf, encoding=
'unicode', xml_declaration=
True)
452 f = open(name + fend,
'w')
459 footsteps = np.genfromtxt(infname +
'_position_ts' + infend)
def __init__(self, center, width, height, name=None)
def add_to_sdf(self, super, indicator=True)
def plot(self, title=None, **kwargs)
def __init__(self, bool_bridge=None)
def add_stones(self, centers, widths, heights, name=None)
def get_stone_by_name(self, name)
def write_to_sdf(self, fname='terrain_xml', with_height=True)
def add_stone(self, center, width, height, name=None)
def import_decision_variables_quadruped(base_name="footstep_planner")
def import_and_animate_footstep_plan(terrain, step_span, title=None, base_name="footstep_planner")
def import_decision_variables_biped(base_name="footstep_planner")
def transcribe_footsteps_to_sdf(infname='footstep_planner', outfname='footsteps_xml')
def plot_rectangle(center, width, height, ax=None, frame=.1, **kwargs)
def write_footsteps_to_sdf(steps, name='footsteps_xml')
def animate_footstep_plan(terrain, n_steps, step_span, positions, title=None, outname="footstep_planner", save_anim=False)