35 from tf.transformations
import quaternion_from_euler
40 from math
import sqrt, atan2
42 from nav_msgs.msg
import OccupancyGrid, Path
43 from geometry_msgs.msg
import PolygonStamped, PointStamped, PoseWithCovarianceStamped, PoseStamped
45 from python_qt_binding.QtCore
import Signal, Slot, QPointF, qWarning, Qt
46 from python_qt_binding.QtGui
import QPixmap, QImage, QPainterPath, QPen, QPolygonF, QColor, qRgb, QTransform
47 from python_qt_binding.QtWidgets
import QWidget, QGraphicsView, QGraphicsScene, QVBoxLayout, QHBoxLayout, QPushButton, \
52 from .list_dialog
import ListDialog
56 msg_types = [OccupancyGrid, Path, PolygonStamped, PointStamped]
57 msg_type, array = get_field_type(topic)
59 if not array
and msg_type
in msg_types:
77 def __init__(self, map_topic='/map', paths=None, polygons=None):
78 super(NavViewWidget, self).
__init__()
80 paths = [
'/move_base/NavFn/plan',
'/move_base/TrajectoryPlannerROS/local_plan']
82 polygons = [
'/move_base/local_costmap/robot_footprint']
87 self.setAcceptDrops(
True)
88 self.setWindowTitle(
'Navigation Viewer')
116 if not e.mimeData().hasText():
117 if not hasattr(e.source(),
'selectedItems')
or len(e.source().selectedItems()) == 0:
118 qWarning(
'NavView.dragEnterEvent(): not hasattr(event.source(), selectedItems) or '
119 'len(event.source().selectedItems()) == 0')
121 item = e.source().selectedItems()[0]
122 topic_name = item.data(0, Qt.UserRole)
123 if topic_name
is None:
124 qWarning(
'NavView.dragEnterEvent(): not hasattr(item, ros_topic_name_)')
128 topic_name = str(e.mimeData().text())
131 e.acceptProposedAction()
134 if e.mimeData().hasText():
135 topic_name = str(e.mimeData().text())
137 dropped_item = e.source().selectedItems()[0]
138 topic_name = str(dropped_item.data(0, Qt.UserRole))
140 topic_type, array = get_field_type(topic_name)
142 if topic_type
is OccupancyGrid:
147 elif topic_type
is Path:
148 self.
paths.append(topic_name)
150 elif topic_type
is PolygonStamped:
155 instance_settings.set_value(
"map_topic", self.
map_topic)
156 instance_settings.set_value(
"paths", self.
paths)
157 instance_settings.set_value(
"polygons", self.
polygons)
161 self.
map_topic = instance_settings.value(
"map_topic",
"/map")
166 self.
paths = instance_settings.value(
"paths", [])
171 self.
polygons = instance_settings.value(
"polygons", [])
179 Callback when the configuration button is clicked
182 map_topics = sorted(rostopic.find_by_type(
'nav_msgs/OccupancyGrid'))
187 map_topic, ok = QInputDialog.getItem(self,
"Select map topic name",
"Topic name",
195 path_topics = sorted(rostopic.find_by_type(
'nav_msgs/Path'))
196 path_topics = [(topic, topic
in self.
paths)
for topic
in path_topics]
197 dialog =
ListDialog(
"Select path topic(s)", path_topics, self)
198 paths, ok = dialog.exec_()
203 diff = set(paths).symmetric_difference(set(self.
paths))
209 polygon_topics = sorted(rostopic.find_by_type(
'geometry_msgs/PolygonStamped'))
210 polygon_topics = [(topic, topic
in self.
polygons)
for topic
in polygon_topics]
211 dialog =
ListDialog(
"Select polygon topic(s)", polygon_topics, self)
212 polygons, ok = dialog.exec_()
217 diff = set(polygons).symmetric_difference(set(self.
polygons))
223 rospy.logdebug(
"New configuration is different, creating a new nav_view")
228 map_changed = Signal()
229 path_changed = Signal(str)
230 polygon_changed = Signal(str)
232 def __init__(self, map_topic='/map', paths=None, polygons=None, tf_listener=None, parent=None):
235 paths = [
'/move_base/SBPLLatticePlanner/plan',
'/move_base/TrajectoryPlannerROS/local_plan']
237 polygons = [
'/move_base/local_costmap/robot_footprint']
238 if tf_listener
is None:
249 self.destroyed.connect(self.
close)
252 self.setDragMode(QGraphicsView.ScrollHandDrag)
269 self.
_colors = [(238, 34, 116), (68, 134, 252), (236, 228, 46), (102, 224, 18), (242, 156, 6), (240, 64, 10),
280 for poly
in polygons:
284 self.
_pose_pub = rospy.Publisher(
'/initialpose', PoseWithCovarianceStamped, queue_size=100)
285 self.
_goal_pub = rospy.Publisher(
'/move_base_simple/goal', PoseStamped, queue_size=100)
287 self.
_pose_pub = rospy.Publisher(
'/initialpose', PoseWithCovarianceStamped)
288 self.
_goal_pub = rospy.Publisher(
'/move_base_simple/goal', PoseStamped)
290 self.setScene(self.
_scene)
300 item.setAcceptDrops(
True)
301 item.dragEnterEvent = c
315 delta = event.angleDelta().y()
316 except AttributeError:
317 delta = event.delta()
319 self.scale(1.15, 1.15)
321 self.scale(0.85, 0.85)
324 map_hash = hash(msg.data)
326 rospy.logdebug(
"Skipping map cb, because the map is the same")
337 a = numpy.array(msg.data, dtype=numpy.uint8, copy=
False, order=
'C')
341 a = numpy.append(a, e, axis=1)
342 image = QImage(a.reshape((a.shape[0] * a.shape[1])), self.
map_width, self.
map_height, QImage.Format_Indexed8)
344 for i
in reversed(range(101)):
345 image.setColor(100 - i, qRgb(i * 2.55, i * 2.55, i * 2.55))
346 image.setColor(101, qRgb(255, 0, 0))
347 image.setColor(255, qRgb(200, 200, 200))
362 if not (msg.header.frame_id == self.
frame_id or msg.header.frame_id ==
''):
364 self.
_tf.waitForTransform(msg.header.frame_id, self.
frame_id, rospy.Time(), rospy.Duration(10))
365 data = [self.
_tf.transformPose(self.
frame_id, pose)
for pose
in msg.poses]
367 rospy.logerr(
"TF Error")
373 start = data[0].pose.position
377 pt = pose.pose.position
383 path.color = random.choice(self.
_colors)
384 self.
_colors.remove(path.color)
387 path.sub = rospy.Subscriber(path.name, Path, path.cb)
398 if not (msg.header.frame_id == self.
frame_id or msg.header.frame_id ==
''):
400 self.
_tf.waitForTransform(msg.header.frame_id, self.
frame_id, rospy.Time(), rospy.Duration(10))
402 for pt
in msg.polygon.points:
404 ps.header.frame_id = msg.header.frame_id
408 points_stamped.append(ps)
411 for pt
in points_stamped:
412 point = self.
_tf.transformPoint(self.
frame_id, pt).point
413 trans_pts.append((point.x, point.y))
415 rospy.logerr(
"TF Error")
418 trans_pts = [(pt.x, pt.y)
for pt
in msg.polygon.points]
420 if len(trans_pts) > 0:
421 trans_pts.append(trans_pts[0])
423 poly.path = QPolygonF(pts)
427 poly.color = random.choice(self.
_colors)
428 self.
_colors.remove(poly.color)
431 poly.sub = rospy.Subscriber(poly.name, PolygonStamped, poly.cb)
439 self.setDragMode(QGraphicsView.NoDrag)
442 self.setDragMode(QGraphicsView.ScrollHandDrag)
448 self.setDragMode(QGraphicsView.NoDrag)
451 self.setDragMode(QGraphicsView.ScrollHandDrag)
454 p = self.mapToScene(e.x(), e.y())
456 mag = sqrt(pow(v[0], 2) + pow(v[1], 2))
457 v = (v[0]/mag, v[1]/mag)
464 res = (v[0]*25, v[1]*25)
467 pen = QPen(QColor(
"red"))
469 pen = QPen(QColor(
"green"))
475 angle = atan2(u[0], u[1])
476 quat = quaternion_from_euler(0, 0, angle)
484 p = self.mapToScene(e.x(), e.y())
496 msg.header.stamp = rospy.Time.now()
498 msg.pose.position.x = map_p[0]
499 msg.pose.position.y = map_p[1]
500 msg.pose.orientation.z = quat[2]
501 msg.pose.orientation.w = quat[3]
509 msg = PoseWithCovarianceStamped()
511 msg.header.stamp = rospy.Time.now()
514 msg.pose.pose.orientation.z = quat[2]
515 msg.pose.pose.orientation.w = quat[3]
516 msg.pose.pose.position.x = map_p[0]
517 msg.pose.pose.position.y = map_p[1]
524 for p
in self.
_paths.values():
532 super(NavView, self).
close()
538 pixmap = QPixmap.fromImage(self.
_map)
552 if name
in self.
_paths.keys():
553 old_item = self.
_paths[name].item
556 pen=QPen(QColor(*self.
_paths[name].color)))
559 self.
_scene.removeItem(old_item)
567 pen=QPen(QColor(*self.
_polygons[name].color)))
570 self.
_scene.removeItem(old_item)
574 Mirror any QItem to have correct orientation
578 item.setTransform(QTransform().scale(1, -1).translate(0, -self.
map_height))
582 Convert point from Qt to map coordinates
584 :param point: tuple or list
597 Convert point from map to qt coordinates
599 :param point: tuple or list