velocity_space_widget.py
Go to the documentation of this file.
1 # Software License Agreement (BSD License)
2 #
3 # Copyright (c) 2018-2019, Locus Robotics
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 #
10 # * Redistributions of source code must retain the above copyright
11 # notice, this list of conditions and the following disclaimer.
12 # * Redistributions in binary form must reproduce the above
13 # copyright notice, this list of conditions and the following
14 # disclaimer in the documentation and/or other materials provided
15 # with the distribution.
16 # * Neither the name of the copyright holder nor the names of its
17 # contributors may be used to endorse or promote products derived
18 # from this software without specific prior written permission.
19 #
20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 
33 from python_qt_binding.QtCore import Qt
34 from python_qt_binding.QtGui import QBrush, QPainter, QPen
35 from python_qt_binding.QtWidgets import QWidget
36 
37 from .util import scale
38 
39 
40 class VelocitySpaceWidget(QWidget):
41  """This widget displays the velocities for each trajectory in the evaluation."""
42 
43  def __init__(self, parent, selection_callback):
44  QWidget.__init__(self, parent)
45  self.evaluation = None
46  self.vel_space = {}
47  self.scores = {}
48  self.n_x = 0
49  self.n_theta = 0
50  self.selected = []
51  self.selection_callback = selection_callback
52  self.vel = None
53 
54  def setVelocity(self, vel):
55  self.vel = vel
56 
57  def setEvaluation(self, evaluation):
58  self.evaluation = evaluation
59  self.selected = []
60  self.vel_space = {}
61  self.scores = {}
62  xs = set()
63  thetas = set()
64  self.x_min = self.x_max = None
65  self.t_min = self.t_max = None
66  for i, twist in enumerate(self.evaluation.twists):
67  v = twist.traj.velocity.x, twist.traj.velocity.theta
68  xs.add(v[0])
69  thetas.add(v[1])
70  self.vel_space[v] = i
71  self.scores[v] = twist.total
72 
73  if self.x_min is None:
74  self.x_min = self.x_max = v[0]
75  self.t_min = self.t_max = v[1]
76  else:
77  self.x_min = min(self.x_min, v[0])
78  self.x_max = max(self.x_max, v[0])
79  self.t_min = min(self.t_min, v[1])
80  self.t_max = max(self.t_max, v[1])
81  self.n_x = len(xs)
82  self.n_theta = len(thetas)
83  self.update()
84 
85  def setSelected(self, selected):
86  self.selected = selected
87  self.update()
88 
89  def paintEvent(self, event):
90  if self.evaluation is None or self.n_x == 0:
91  return
92  rect = event.rect()
93  w, h = rect.width(), rect.height()
94  self.left = w * 0.2
95  self.width = w * 0.7
96  self.top = h * 0.1
97  self.height = h * 0.85
98 
99  horizontal_size = self.width / self.n_x
100  vertical_size = self.height / self.n_theta
101  diameter = max(5, min(horizontal_size, vertical_size))
102  radius = diameter / 2
103 
104  self.qp = QPainter()
105  self.qp.begin(self)
106  self.qp.setBrush(QBrush(Qt.white))
107  self.qp.drawRect(self.left, self.top, self.width, self.height)
108 
109  zx, zy = self.toScreen(0, 0)
110  if zy >= self.top and zy <= self.top + self.height:
111  self.qp.drawLine(self.left, zy, self.left + self.width, zy)
112  if zx >= self.left and zx <= self.left + self.width:
113  self.qp.drawLine(zx, self.top, zx, self.top + self.height)
114 
115  self.qp.drawText(self.left, 0, self.width, self.top, Qt.AlignCenter, 'x')
116  self.qp.drawText(0, 0, self.left * 2, self.top, Qt.AlignCenter, '%.2f' % self.x_min)
117  self.qp.drawText(w - self.left, 0, self.left, self.top, Qt.AlignCenter, '%.2f' % self.x_max)
118 
119  right_center = Qt.AlignVCenter | Qt.AlignRight
120  self.qp.drawText(0, self.top, self.left - radius, self.height, right_center, 'theta')
121  self.qp.drawText(0, 0, self.left - radius, self.top * 2, right_center, '%.2f' % self.t_max)
122  self.qp.drawText(0, h - self.top, self.left - radius, self.top, right_center, '%.2f' % self.t_min)
123 
124  for (x, theta), index in self.vel_space.iteritems():
125  dx, dy = self.toScreen(x, theta)
126  if index in self.selected:
127  self.qp.setBrush(QBrush(self.selected[index]))
128  else:
129  self.qp.setBrush(QBrush())
130 
131  if self.scores[x, theta] < 0.0:
132  self.qp.setPen(QPen(Qt.red))
133  else:
134  self.qp.setPen(QPen(Qt.black))
135  self.qp.drawEllipse(dx - radius, dy - radius, diameter, diameter)
136 
137  if self.vel:
138  self.qp.setBrush(QBrush())
139  dx, dy = self.toScreen(self.vel.x, self.vel.theta)
140  self.qp.drawEllipse(dx - diameter, dy - diameter, diameter * 2, diameter * 2)
141 
142  self.qp.end()
143 
144  def toScreen(self, x, theta):
145  return scale(x, self.x_min, self.x_max, self.width, self.left), \
146  scale(theta, self.t_max, self.t_min, self.height, self.top)
147 
148  def toVelocity(self, x, y):
149  xv = (x - self.left) / self.width * (self.x_max - self.x_min) + self.x_min
150  yv = self.t_max - (y - self.top) / self.height * (self.t_max - self.t_min)
151  return xv, yv
152 
153  def getNearestVelocity(self, xv, tv):
154  best_d = None
155  best = None
156  for (x, theta), index in self.vel_space.iteritems():
157  d = abs(x - xv) + abs(theta - tv)
158  if best_d is None or d < best_d:
159  best_d = d
160  best = index
161  return best
162 
163  def mousePressEvent(self, event):
164  pos = event.pos()
165  xv, tv = self.toVelocity(pos.x(), pos.y())
166 
167  new_selected = list(self.selected)
168  selection = self.getNearestVelocity(xv, tv)
169  if selection in self.selected:
170  new_selected.remove(selection)
171  self.selection_callback(new_selected)
172  else:
173  new_selected.append(selection)
174  self.selection_callback(new_selected)
def scale(v, minval, maxval, dest_size=1.0, offset=0.0)
Definition: util.py:42


rqt_dwb_plugin
Author(s):
autogenerated on Mon Feb 28 2022 23:33:43