コード例 #1
0
class MyPlugin(Plugin):

    def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('MyPlugin')
        rospy.loginfo("Remora: Basic Controller Online")

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which is a sibling of this file
        # in this example the .ui and .py file are in the same folder
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'remora_gui.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('MyPluginUi')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        self._widget.setWindowTitle('Basic Console')
        if context.serial_number() > 1:
          self._widget.setWindowTitle(self._widget.windowTitle()+(' (%d)'% context.serial_number()))

        # Add widget to the user interface
        context.add_widget(self._widget)
        # Register all subsribers here
        return

    def shutdown_plugin(self):
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #2
0
class PyConsole(Plugin):
    """
    Plugin providing an interactive Python console
    """
    def __init__(self, context):
        super(PyConsole, self).__init__(context)
        self.setObjectName('PyConsole')

        self._context = context
        self._use_spyderlib = _has_spyderlib
        self._console_widget = None
        self._widget = QWidget()
        self._widget.setLayout(QVBoxLayout())
        self._widget.layout().setContentsMargins(0, 0, 0, 0)
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        self._context.add_widget(self._widget)

    def _switch_console_widget(self):
        self._widget.layout().removeWidget(self._console_widget)
        self.shutdown_console_widget()

        if _has_spyderlib and self._use_spyderlib:
            self._console_widget = SpyderConsoleWidget(self._context)
            self._widget.setWindowTitle('SpyderConsole')
        else:
            self._console_widget = PyConsoleWidget(self._context)
            self._widget.setWindowTitle('PyConsole')
        if self._context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % self._context.serial_number()))

        self._widget.layout().addWidget(self._console_widget)

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('use_spyderlib', self._use_spyderlib)

    def restore_settings(self, plugin_settings, instance_settings):
        self._use_spyderlib = _has_spyderlib and (instance_settings.value('use_spyderlib', True) in [True, 'true'])
        self._switch_console_widget()

    def trigger_configuration(self):
        options = [
            {'title': 'SpyderConsole', 'description': 'Advanced Python console with tab-completion (needs spyderlib to be installed).', 'enabled': _has_spyderlib},
            {'title': 'PyConsole', 'description': 'Simple Python console.'},
        ]
        dialog = SimpleSettingsDialog(title='PyConsole Options')
        dialog.add_exclusive_option_group(title='Console Type', options=options, selected_index=int(not self._use_spyderlib))
        console_type = dialog.get_settings()[0]
        new_use_spyderlib = {0: True, 1: False}.get(console_type['selected_index'], self._use_spyderlib)
        if self._use_spyderlib != new_use_spyderlib:
            self._use_spyderlib = new_use_spyderlib
            self._switch_console_widget()

    def shutdown_console_widget(self):
        if self._console_widget is not None and hasattr(self._console_widget, 'shutdown'):
            self._console_widget.shutdown()

    def shutdown_plugin(self):
        self.shutdown_console_widget()
class MyPlugin(Plugin):
    def __init__(self, context):

        #Creating a QObject
        super(MyPlugin, self).__init__(context)
        self.setObjectName('MyPlugin')

        #Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        parser.add_argument("-q", "--quiet", action="store_true", dest="quiet")
        #help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        #Creating QWidget
        self._widget = QWidget()
        ui_file = os.path.join(rospkg.RosPack().get_path('user_interface'),
                               'resource', 'user_interface_gui.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('MyPluginUi')

        #ROS publishers and subscribers--------------->
        self.coord_to_gui_sub = rospy.Subscriber('/coord_to_gui', String,
                                                 self.comm_to_gui_callback)
        self.gui_to_coord_pub = rospy.Publisher('/gui_to_coord',
                                                String,
                                                queue_size=1000)
        self.send_num_wp_pub = rospy.Publisher('/num_wp',
                                               Int16,
                                               queue_size=1000)

        #---------------------------------------------

        #GUI Button Connections---------------------------->
        self._widget.initialize_robot_btn.clicked[bool].connect(
            self.intialize_robots)
        self._widget.load_wp_btn.clicked[bool].connect(self.load_wp)

        ## Robot1--------------->
        self._widget.approach_pick_wrkpiece_rob1_btn.clicked[bool].connect(
            self.approach_pick_wrkpiece)
        self._widget.place_workpiece_btn.clicked[bool].connect(
            self.place_workpiece)

        ##Robot2---------------->
        self._widget.pick_workpiece_rob2_btn.clicked[bool].connect(
            self.pick_workpiece_rob2)
        self._widget.segregate_workpiece_btn.clicked[bool].connect(
            self.segregate_workpiece)

        ##CNN------------------->
        self._widget.train_model_btn.clicked[bool].connect(self.train_model)
        self._widget.classify_btn.clicked[bool].connect(self.classify_image)

        ##Robot3---------------->
        self._widget.load_milling_btn.clicked[bool].connect(
            self.load_milling_plan)
        self._widget.execute_miling_btn.clicked[bool].connect(
            self.execute_milling)

        #------------------------------------------------

        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        context.add_widget(self._widget)

    ##Utility funcitons for User Interface
    def publish_msg_to_coord(self, msg):
        msg_to_publish = msg
        self.gui_to_coord_pub.publish(msg_to_publish)

    def intialize_robots(self):
        self.publish_msg_to_coord('initialize_robots')
        pass

    def load_wp(self):
        num_wp = self._widget.num_wp_txtbx.text()
        msg = int(num_wp)
        self.send_num_wp_pub.publish(msg)

    ###Robot 1----->
    def approach_pick_wrkpiece(self):
        self.publish_msg_to_coord('approach_and_pick_workpiece_rob1')
        pass

    # def pick_workpiece_rob1(self):
    #     self.publish_msg_to_coord('pick_workpiece_rob1')
    #     pass

    def place_workpiece(self):
        self.publish_msg_to_coord('place_workpiece_rob1')
        pass

    ###Robot 2------>
    def pick_workpiece_rob2(self):
        self.publish_msg_to_coord('pick_workpiece_rob2')
        pass

    def segregate_workpiece(self):
        self.publish_msg_to_coord('segregate_workpiece_rob2')
        pass

    ###Robot 3------>
    def load_milling_plan(self):
        self.publish_msg_to_coord('load_milling_plan')
        pass

    def execute_milling(self):
        self.publish_msg_to_coord('execute_milling')
        pass

    ###Update gui status------->
    def comm_to_gui_callback(self, msg):
        status = msg.data
        self._widget.gui_status_txtbx.setText(status)
        pass

    def train_model(self):
        self.publish_msg_to_coord('train_model')
        pass

    def classify_image(self):
        self.publish_msg_to_coord('classify_image')
        pass

    ###Rqt functions
    def shutdown_plugin(self):
        #Unregister all publishers and subscribers
        self.gui_to_coord_pub.unregister()
        self.coord_to_gui_sub.unregister()
        self.send_num_wp_pub.unregister()
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #4
0
class RosGraph(Plugin):

    _deferred_fit_in_view = Signal()

    def __init__(self, context):
        super(RosGraph, self).__init__(context)
        self.initialized = False
        self.setObjectName('RosGraph')

        self._graph = None
        self._current_dotcode = None

        self._widget = QWidget()

        # factory builds generic dotcode items
        self.dotcode_factory = PydotFactory()
        # self.dotcode_factory = PygraphvizFactory()
        # generator builds rosgraph
        self.dotcode_generator = RosGraphDotcodeGenerator()
        # dot_to_qt transforms into Qt elements using dot layout
        self.dot_to_qt = DotToQtGenerator()

        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_graph'), 'resource',
                               'RosGraph.ui')
        loadUi(ui_file, self._widget,
               {'InteractiveGraphicsView': InteractiveGraphicsView})
        self._widget.setObjectName('RosGraphUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        self._scene = QGraphicsScene()
        self._scene.setBackgroundBrush(Qt.white)
        self._widget.graphics_view.setScene(self._scene)

        self._widget.graph_type_combo_box.insertItem(0, self.tr('Nodes only'),
                                                     NODE_NODE_GRAPH)
        self._widget.graph_type_combo_box.insertItem(
            1, self.tr('Nodes/Topics (active)'), NODE_TOPIC_GRAPH)
        self._widget.graph_type_combo_box.insertItem(
            2, self.tr('Nodes/Topics (all)'), NODE_TOPIC_ALL_GRAPH)
        self._widget.graph_type_combo_box.setCurrentIndex(0)
        self._widget.graph_type_combo_box.currentIndexChanged.connect(
            self._refresh_rosgraph)

        self.node_completionmodel = NamespaceCompletionModel(
            self._widget.filter_line_edit, False)
        completer = RepeatedWordCompleter(self.node_completionmodel, self)
        completer.setCompletionMode(QCompleter.PopupCompletion)
        completer.setWrapAround(True)
        completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._widget.filter_line_edit.editingFinished.connect(
            self._refresh_rosgraph)
        self._widget.filter_line_edit.setCompleter(completer)

        self.topic_completionmodel = NamespaceCompletionModel(
            self._widget.topic_filter_line_edit, False)
        topic_completer = RepeatedWordCompleter(self.topic_completionmodel,
                                                self)
        topic_completer.setCompletionMode(QCompleter.PopupCompletion)
        topic_completer.setWrapAround(True)
        topic_completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._widget.topic_filter_line_edit.editingFinished.connect(
            self._refresh_rosgraph)
        self._widget.topic_filter_line_edit.setCompleter(topic_completer)

        self._widget.namespace_cluster_check_box.clicked.connect(
            self._refresh_rosgraph)
        self._widget.actionlib_check_box.clicked.connect(
            self._refresh_rosgraph)
        self._widget.dead_sinks_check_box.clicked.connect(
            self._refresh_rosgraph)
        self._widget.leaf_topics_check_box.clicked.connect(
            self._refresh_rosgraph)
        self._widget.quiet_check_box.clicked.connect(self._refresh_rosgraph)

        self._widget.refresh_graph_push_button.setIcon(
            QIcon.fromTheme('view-refresh'))
        self._widget.refresh_graph_push_button.pressed.connect(
            self._update_rosgraph)

        self._widget.highlight_connections_check_box.toggled.connect(
            self._redraw_graph_view)
        self._widget.auto_fit_graph_check_box.toggled.connect(
            self._redraw_graph_view)
        self._widget.fit_in_view_push_button.setIcon(
            QIcon.fromTheme('zoom-original'))
        self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)

        self._widget.load_dot_push_button.setIcon(
            QIcon.fromTheme('document-open'))
        self._widget.load_dot_push_button.pressed.connect(self._load_dot)
        self._widget.save_dot_push_button.setIcon(
            QIcon.fromTheme('document-save-as'))
        self._widget.save_dot_push_button.pressed.connect(self._save_dot)
        self._widget.save_as_svg_push_button.setIcon(
            QIcon.fromTheme('document-save-as'))
        self._widget.save_as_svg_push_button.pressed.connect(self._save_svg)
        self._widget.save_as_image_push_button.setIcon(
            QIcon.fromTheme('image'))
        self._widget.save_as_image_push_button.pressed.connect(
            self._save_image)

        self._update_rosgraph()
        self._deferred_fit_in_view.connect(self._fit_in_view,
                                           Qt.QueuedConnection)
        self._deferred_fit_in_view.emit()

        context.add_widget(self._widget)

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value(
            'graph_type_combo_box_index',
            self._widget.graph_type_combo_box.currentIndex())
        instance_settings.set_value('filter_line_edit_text',
                                    self._widget.filter_line_edit.text())
        instance_settings.set_value('topic_filter_line_edit_text',
                                    self._widget.topic_filter_line_edit.text())
        instance_settings.set_value(
            'namespace_cluster_check_box_state',
            self._widget.namespace_cluster_check_box.isChecked())
        instance_settings.set_value(
            'actionlib_check_box_state',
            self._widget.actionlib_check_box.isChecked())
        instance_settings.set_value(
            'dead_sinks_check_box_state',
            self._widget.dead_sinks_check_box.isChecked())
        instance_settings.set_value(
            'leaf_topics_check_box_state',
            self._widget.leaf_topics_check_box.isChecked())
        instance_settings.set_value('quiet_check_box_state',
                                    self._widget.quiet_check_box.isChecked())
        instance_settings.set_value(
            'auto_fit_graph_check_box_state',
            self._widget.auto_fit_graph_check_box.isChecked())
        instance_settings.set_value(
            'highlight_connections_check_box_state',
            self._widget.highlight_connections_check_box.isChecked())

    def restore_settings(self, plugin_settings, instance_settings):
        self._widget.graph_type_combo_box.setCurrentIndex(
            int(instance_settings.value('graph_type_combo_box_index', 0)))
        self._widget.filter_line_edit.setText(
            instance_settings.value('filter_line_edit_text', '/'))
        self._widget.topic_filter_line_edit.setText(
            instance_settings.value('topic_filter_line_edit_text', '/'))
        self._widget.namespace_cluster_check_box.setChecked(
            instance_settings.value('namespace_cluster_check_box_state', True)
            in [True, 'true'])
        self._widget.actionlib_check_box.setChecked(
            instance_settings.value('actionlib_check_box_state', True) in
            [True, 'true'])
        self._widget.dead_sinks_check_box.setChecked(
            instance_settings.value('dead_sinks_check_box_state', True) in
            [True, 'true'])
        self._widget.leaf_topics_check_box.setChecked(
            instance_settings.value('leaf_topics_check_box_state', True) in
            [True, 'true'])
        self._widget.quiet_check_box.setChecked(
            instance_settings.value('quiet_check_box_state', True) in
            [True, 'true'])
        self._widget.auto_fit_graph_check_box.setChecked(
            instance_settings.value('auto_fit_graph_check_box_state', True) in
            [True, 'true'])
        self._widget.highlight_connections_check_box.setChecked(
            instance_settings.value('highlight_connections_check_box_state',
                                    True) in [True, 'true'])
        self.initialized = True
        self._refresh_rosgraph()

    def _update_rosgraph(self):
        # re-enable controls customizing fetched ROS graph
        self._widget.graph_type_combo_box.setEnabled(True)
        self._widget.filter_line_edit.setEnabled(True)
        self._widget.topic_filter_line_edit.setEnabled(True)
        self._widget.namespace_cluster_check_box.setEnabled(True)
        self._widget.actionlib_check_box.setEnabled(True)
        self._widget.dead_sinks_check_box.setEnabled(True)
        self._widget.leaf_topics_check_box.setEnabled(True)
        self._widget.quiet_check_box.setEnabled(True)

        self._graph = rosgraph.impl.graph.Graph()
        self._graph.set_master_stale(5.0)
        self._graph.set_node_stale(5.0)
        self._graph.update()
        self.node_completionmodel.refresh(self._graph.nn_nodes)
        self.topic_completionmodel.refresh(self._graph.nt_nodes)
        self._refresh_rosgraph()

    def _refresh_rosgraph(self):
        if not self.initialized:
            return
        self._update_graph_view(self._generate_dotcode())

    def _generate_dotcode(self):
        ns_filter = self._widget.filter_line_edit.text()
        topic_filter = self._widget.topic_filter_line_edit.text()
        graph_mode = self._widget.graph_type_combo_box.itemData(
            self._widget.graph_type_combo_box.currentIndex())
        orientation = 'LR'
        if self._widget.namespace_cluster_check_box.isChecked():
            namespace_cluster = 1
        else:
            namespace_cluster = 0
        accumulate_actions = self._widget.actionlib_check_box.isChecked()
        hide_dead_end_topics = self._widget.dead_sinks_check_box.isChecked()
        hide_single_connection_topics = self._widget.leaf_topics_check_box.isChecked(
        )
        quiet = self._widget.quiet_check_box.isChecked()

        return self.dotcode_generator.generate_dotcode(
            rosgraphinst=self._graph,
            ns_filter=ns_filter,
            topic_filter=topic_filter,
            graph_mode=graph_mode,
            hide_single_connection_topics=hide_single_connection_topics,
            hide_dead_end_topics=hide_dead_end_topics,
            cluster_namespaces_level=namespace_cluster,
            accumulate_actions=accumulate_actions,
            dotcode_factory=self.dotcode_factory,
            orientation=orientation,
            quiet=quiet)

    def _update_graph_view(self, dotcode):
        if dotcode == self._current_dotcode:
            return
        self._current_dotcode = dotcode
        self._redraw_graph_view()

    def _generate_tool_tip(self, url):
        if url is not None and ':' in url:
            item_type, item_path = url.split(':', 1)
            if item_type == 'node':
                tool_tip = 'Node:\n  %s' % (item_path)
                service_names = rosservice.get_service_list(node=item_path)
                if service_names:
                    tool_tip += '\nServices:'
                    for service_name in service_names:
                        try:
                            service_type = rosservice.get_service_type(
                                service_name)
                            tool_tip += '\n  %s [%s]' % (service_name,
                                                         service_type)
                        except rosservice.ROSServiceIOException as e:
                            tool_tip += '\n  %s' % (e)
                return tool_tip
            elif item_type == 'topic':
                topic_type, topic_name, _ = rostopic.get_topic_type(item_path)
                return 'Topic:\n  %s\nType:\n  %s' % (topic_name, topic_type)
        return url

    def _redraw_graph_view(self):
        self._scene.clear()

        if self._widget.highlight_connections_check_box.isChecked():
            highlight_level = 3
        else:
            highlight_level = 1

        # layout graph and create qt items
        (nodes, edges) = self.dot_to_qt.dotcode_to_qt_items(
            self._current_dotcode,
            highlight_level=highlight_level,
            same_label_siblings=True)

        for node_item in nodes.values():
            self._scene.addItem(node_item)
        for edge_items in edges.values():
            for edge_item in edge_items:
                edge_item.add_to_scene(self._scene)

        self._scene.setSceneRect(self._scene.itemsBoundingRect())
        if self._widget.auto_fit_graph_check_box.isChecked():
            self._fit_in_view()

    def _load_dot(self, file_name=None):
        if file_name is None:
            file_name, _ = QFileDialog.getOpenFileName(
                self._widget, self.tr('Open graph from file'), None,
                self.tr('DOT graph (*.dot)'))
            if file_name is None or file_name == '':
                return

        try:
            fh = open(file_name, 'rb')
            dotcode = fh.read()
            fh.close()
        except IOError:
            return

        # disable controls customizing fetched ROS graph
        self._widget.graph_type_combo_box.setEnabled(False)
        self._widget.filter_line_edit.setEnabled(False)
        self._widget.topic_filter_line_edit.setEnabled(False)
        self._widget.namespace_cluster_check_box.setEnabled(False)
        self._widget.actionlib_check_box.setEnabled(False)
        self._widget.dead_sinks_check_box.setEnabled(False)
        self._widget.leaf_topics_check_box.setEnabled(False)
        self._widget.quiet_check_box.setEnabled(False)

        self._update_graph_view(dotcode)

    def _fit_in_view(self):
        self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(),
                                             Qt.KeepAspectRatio)

    def _save_dot(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget, self.tr('Save as DOT'), 'rosgraph.dot',
            self.tr('DOT graph (*.dot)'))
        if file_name is None or file_name == '':
            return

        handle = QFile(file_name)
        if not handle.open(QIODevice.WriteOnly | QIODevice.Text):
            return

        handle.write(self._current_dotcode)
        handle.close()

    def _save_svg(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget, self.tr('Save as SVG'), 'rosgraph.svg',
            self.tr('Scalable Vector Graphic (*.svg)'))
        if file_name is None or file_name == '':
            return

        generator = QSvgGenerator()
        generator.setFileName(file_name)
        generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())

        painter = QPainter(generator)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()

    def _save_image(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget, self.tr('Save as image'), 'rosgraph.png',
            self.tr('Image (*.bmp *.jpg *.png *.tiff)'))
        if file_name is None or file_name == '':
            return

        img = QImage((self._scene.sceneRect().size() * 2.0).toSize(),
                     QImage.Format_ARGB32_Premultiplied)
        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()
        img.save(file_name)
コード例 #5
0
ファイル: gauss_module.py プロジェクト: yxw027/gauss
class GaussPlugin(Plugin):
    pop_up = Signal()

    def __init__(self, context):
        super(GaussPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('GaussPlugin')
        rp = rospkg.RosPack()
        rospy.loginfo('[RQt] Started Gauss RQt node!')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print '[RQt] arguments: ', args
            print '[RQt] unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which is a sibling of this file
        # in this example the .ui and .py file are in the same folder
        ui_file = os.path.join(rp.get_path('rqt_gauss'), 'resource', 'GaussPlugin.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('GaussPluginUi')

        # Do connections and stuff here. For complex plugins, consider
        # creating custom helper classes instead of QWidget
        self.status_pub = rospy.Publisher('/gauss/flight', RPSChangeFlightStatus, queue_size=1)
        self.acceptance_pub = rospy.Publisher('/gauss/flightacceptance', RPSFlightPlanAccept, queue_size=1)
        rospy.Subscriber('/gauss/alternative_flight_plan', UTMAlternativeFlightPlan, self.alternative_flight_plan_callback)
        self.read_icao_service = rospy.ServiceProxy('/gauss/read_icao', ReadIcao)

        self._widget.refresh_button.clicked.connect(self.handle_refresh_button_clicked)
        self._widget.start_button.clicked.connect(self.handle_start_button_clicked)
        self._widget.stop_button.clicked.connect(self.handle_stop_button_clicked)
        self.pop_up.connect(self.show_pop_up)
        self.alternative_flight_plan = None

        self.handle_refresh_button_clicked()  # Try to refresh here

        # Show _widget.windowTitle on left-top of each plugin (when 
        # it's set in _widget). This is useful when you open multiple 
        # plugins at once. Also if you open multiple instances of your 
        # plugin at once, these lines add number to make it easy to 
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

    def handle_refresh_button_clicked(self):
        try:
            read_icao_response = self.read_icao_service.call(ReadIcaoRequest())

            self._widget.icao_list.clear()
            for icao in read_icao_response.icao_address:
                self._widget.icao_list.addItem(icao)

        except rospy.ServiceException as e:
            print("[RQt] Service call failed: %s"%e)

    def change_flight_status(self, status):
        selected_icao = self._widget.icao_list.currentItem()
        if selected_icao is None:
            rospy.logerr('[RQt] Select an icao address')
            return
        icao = selected_icao.text()

        change_flight_msg = RPSChangeFlightStatus()
        change_flight_msg.icao = int(icao)
        # change_flight_msg.flight_plan_id  # TODO
        change_flight_msg.status = status
        # change_flight_msg.timestamp  # TODO

        rospy.loginfo('[RQt] RPA[{}]: mission {}'.format(icao, status))
        self.status_pub.publish(change_flight_msg)

    def handle_start_button_clicked(self):
        self.change_flight_status('start')

    def handle_stop_button_clicked(self):
        self.change_flight_status('stop')

    def alternative_flight_plan_callback(self, data):
        if self.alternative_flight_plan is None:
            self.alternative_flight_plan = data
            rospy.loginfo('[RQt] Received: [{}]'.format(data))
            self.pop_up.emit()
        else:
            rospy.loginfo('[RQt] Waiting for pilot response...')

    def show_pop_up(self):
        msg = QMessageBox()
        msg.setWindowTitle('Alternative flight plan received')
        msg.setText('Accept alternative flight plan?')
        msg.setIcon(QMessageBox.Question)
        msg.setStandardButtons(QMessageBox.Yes|QMessageBox.No)
        msg.setDefaultButton(QMessageBox.No)
        # msg.setInformativeText('Accept new plan?')
        msg.setDetailedText('{}'.format(self.alternative_flight_plan))
        # msg.buttonClicked.connect(self.handle_message_box)  # TODO: do handling here?
        pop_up_response = msg.exec_()
        ros_response = RPSFlightPlanAccept()
        ros_response.icao = self.alternative_flight_plan.icao
        ros_response.flight_plan_id = self.alternative_flight_plan.flight_plan_id

        if pop_up_response == QMessageBox.Yes:
            ros_response.accept = True
            # Try to call light_sim service to change flight plan
            try:
                light_sim_change_flight_plan = rospy.ServiceProxy('/gauss_light_sim/change_flight_plan', ChangeFlightPlan)
                light_sim_change_flight_plan(ChangeFlightPlanRequest(alternative=copy.deepcopy(self.alternative_flight_plan)))
            except rospy.ServiceException as e:
                print("[RQt] Service call failed: %s"%e)

        if pop_up_response == QMessageBox.No:
            ros_response.accept = False
        self.acceptance_pub.publish(ros_response)
        self.alternative_flight_plan = None

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #6
0
class Top(Plugin):

    NODE_FIELDS   = [             'pid', 'get_cpu_percent', 'get_memory_percent', 'get_num_threads']
    OUT_FIELDS    = ['node_name', 'pid', 'cpu_percent',     'memory_percent',     'num_threads'    ]
    FORMAT_STRS   = ['%s',        '%s',  '%0.2f',           '%0.2f',              '%s'             ]
    NODE_LABELS   = ['Node',      'PID', 'CPU %',           'Mem %',              'Num Threads'    ]
    SORT_TYPE     = [str,         str,   float,             float,                float            ]
    TOOLTIPS      = {
        0: ('cmdline', lambda x: '\n'.join(textwrap.wrap(' '.join(x)))),
        3: ('memory_info', lambda x: ('Resident: %0.2f MiB, Virtual: %0.2f MiB' % (x[0]/2**20, x[1]/2**20)))
    }

    _node_info = NodeInfo()

    name_filter = re.compile('')

    def __init__(self, context):
        super(Top, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('Top')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        # if not args.quiet:
        #     print 'arguments: ', args
        #     print 'unknowns: ', unknowns

        self._selected_node = ''
        self._selected_node_lock = RLock()

        # Setup the toolbar
        self._toolbar = QToolBar()
        self._filter_box = QLineEdit()
        self._regex_box  = QCheckBox()
        self._regex_box.setText('regex')
        self._toolbar.addWidget(QLabel('Filter'))
        self._toolbar.addWidget(self._filter_box)
        self._toolbar.addWidget(self._regex_box)

        self._filter_box.returnPressed.connect(self.update_filter)
        self._regex_box.stateChanged.connect(self.update_filter)

        # Create a container widget and give it a layout
        self._container = QWidget()
        self._container.setWindowTitle('Process Monitor')
        self._layout    = QVBoxLayout()
        self._container.setLayout(self._layout)

        self._layout.addWidget(self._toolbar)

        # Create the table widget
        self._table_widget = QTreeWidget()
        self._table_widget.setObjectName('TopTable')
        self._table_widget.setColumnCount(len(self.NODE_LABELS))
        self._table_widget.setHeaderLabels(self.NODE_LABELS)
        self._table_widget.itemClicked.connect(self._tableItemClicked)
        self._table_widget.setSortingEnabled(True)
        self._table_widget.setAlternatingRowColors(True)

        self._layout.addWidget(self._table_widget)
        context.add_widget(self._container)

        # Add a button for killing nodes
        self._kill_button = QPushButton('Kill Node')
        self._layout.addWidget(self._kill_button)
        self._kill_button.clicked.connect(self._kill_node)

        # Update twice since the first cpu% lookup will always return 0
        self.update_table()
        self.update_table()

        self._table_widget.resizeColumnToContents(0)

        # Start a timer to trigger updates
        self._update_timer = QTimer()
        self._update_timer.setInterval(1000)
        self._update_timer.timeout.connect(self.update_table)
        self._update_timer.start()

    def _tableItemClicked(self, item, column):
        with self._selected_node_lock:
            self._selected_node = item.text(0)

    def update_filter(self, *args):
        if self._regex_box.isChecked():
            expr = self._filter_box.text()
        else:
            expr = re.escape(self._filter_box.text())
        self.name_filter = re.compile(expr)
        self.update_table()

    def _kill_node(self):
        self._node_info.kill_node(self._selected_node)

    def update_one_item(self, row, info):
        twi = TopWidgetItem()
        for col, field in enumerate(self.OUT_FIELDS):
            val = info[field]
            twi.setText(col, self.FORMAT_STRS[col] % val)
        self._table_widget.insertTopLevelItem(row, twi)

        for col, (key, func) in self.TOOLTIPS.items():
            twi.setToolTip(col, func(info[key]))

        with self._selected_node_lock:
            if twi.text(0) == self._selected_node:
                twi.setSelected(True)

        twi.setHidden(len(self.name_filter.findall(info['node_name'])) == 0)

    def update_table(self):
        self._table_widget.clear()
        infos = self._node_info.get_all_node_fields(self.NODE_FIELDS)
        for nx, info in enumerate(infos):
            self.update_one_item(nx, info)

    def shutdown_plugin(self):
        self._update_timer.stop()

    def save_settings(self, plugin_settings, instance_settings):        
        instance_settings.set_value('filter_text', self._filter_box.text())
        instance_settings.set_value('is_regex', int(self._regex_box.checkState()))

    def restore_settings(self, plugin_settings, instance_settings):
        self._filter_box.setText(instance_settings.value('filter_text'))
        is_regex_int = instance_settings.value('is_regex')
        if is_regex_int:
            self._regex_box.setCheckState(Qt.CheckState(is_regex_int))
        else:
            self._regex_box.setCheckState(Qt.CheckState(0))
        self.update_filter()
コード例 #7
0
ファイル: plugins.py プロジェクト: utexas-bwi/bwi_common
class LevelSelectorPlugin(Plugin):

    button_signal = pyqtSignal()
    button_status_signal = pyqtSignal()

    def __init__(self, context):
        super(LevelSelectorPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('LevelSelectorPlugin')

        # Create QWidget
        self._widget = QWidget()
        self._widget.setFont(QFont("Times", 15, QFont.Bold))
        self._button_layout = QVBoxLayout(self._widget)

        self.buttons = []
        self.text_label = QLabel("Waiting for MultiLevelMapData...", self._widget)
        self._button_layout.addWidget(self.text_label)

        self._widget.setObjectName('LevelSelectorPluginUI')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle()
                                        + (' (%d)' % context.serial_number()))
        context.add_widget(self._widget)

        self.button_signal.connect(self.update_buttons)
        self.button_status_signal.connect(self.update_button_status)

        # Subscribe to the multi level map data to get information about all the maps.
        self.multimap_subscriber = rospy.Subscriber("map_metadata", MultiLevelMapData,
                                                    self.process_multimap)
        self.levels = []
        self.current_level = None
        rospy.loginfo('level selector: subscribed to maps')

        # Subscribe to the current level we are on.
        self.status_subscriber = None

        # Create a service proxy to change the current level.
        self.level_selector_proxy = rospy.ServiceProxy("level_mux/change_current_level",
                                                       ChangeCurrentLevel)
        self.level_selector_proxy.wait_for_service()
        rospy.loginfo('level selector: found "level_mux/change_current_level" service')

    def process_multimap(self, msg):
        """ Map metadata topic callback. """
        rospy.loginfo('level selector: map metadata received.')
        self.levels = msg.levels
        # update level buttons in the selection window
        self.button_signal.emit()

    def update_buttons(self):
        """ Update buttons in Qt window. """
        rospy.loginfo('level selector: update_buttons called')
        self.clean()
        for index, level in enumerate(self.levels):
            button = QPushButton(level.level_id, self._widget)
            button.clicked[bool].connect(self.handle_button)
            button.setCheckable(True)
            self._button_layout.addWidget(button)
            self.buttons.append(button)

        # Subscribe to the current level we are on.
        if self.status_subscriber is None:
            self.status_subscriber = rospy.Subscriber("level_mux/current_level",
                                                      LevelMetaData,
                                                      self.process_level_status)

    def update_button_status(self):
        """ Update button status Qt push button callback. """
        rospy.loginfo('level selector: update_button_status')
        if not self.buttons or not self.current_level:
            rospy.logwarn('level selector: current level not available')
            return
        rospy.logdebug('buttons: ' + str(self.buttons))
        for index, level in enumerate(self.levels):
            rospy.logdebug('level[' + str(index) + ']: ' + str(level.level_id))
            if self.current_level == level.level_id:
                self.buttons[index].setChecked(True)
            else:
                self.buttons[index].setChecked(False)

    def process_level_status(self, msg):
        """ level_mux/current_level topic callback. """
        rospy.loginfo('level selector: current_level topic message received')
        level_found = False
        for level in self.levels:
            if msg.level_id == level.level_id:
                self.current_level = level.level_id
                level_found = True
                break
        if not level_found:
            self.current_level = None
        self.button_status_signal.emit()

    def handle_button(self):
        source = self.sender()

        if source.text() == self.current_level:
            source.setChecked(True)
            return

        # Construct a identity pose. The level selector cannot be used
        # to choose the initialpose, as it does not have the interface
        # for specifying the position. The position should be
        # specified via rviz.
        origin_pose = PoseWithCovarianceStamped()
        origin_pose.header.frame_id = frameIdFromLevelId(source.text())
        origin_pose.pose.pose.orientation.w = 1    # Makes the origin quaternion valid.
        origin_pose.pose.covariance[0] = 1.0
        origin_pose.pose.covariance[7] = 1.0
        origin_pose.pose.covariance[14] = 1.0
        origin_pose.pose.covariance[21] = 1.0
        origin_pose.pose.covariance[28] = 1.0
        origin_pose.pose.covariance[35] = 1.0

        # Don't actually publish the initial pose via the level
        # selector. It doesn't know any better.
        self.level_selector_proxy(source.text(), False, origin_pose)

    def clean(self):
        while self._button_layout.count():
            item = self._button_layout.takeAt(0)
            item.widget().deleteLater()

    def save_settings(self, plugin_settings, instance_settings):
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        pass
コード例 #8
0
class RqtLgsvlSimulatorConfiguratorPlugin(Plugin):

    def __init__(self, context):
        super(RqtLgsvlSimulatorConfiguratorPlugin, self).__init__(context)
        rospack = rospkg.RosPack()
        self.settings = QSettings(rospack.get_path('lgsvl_simulator_bridge')+"/config/setting.dat",QSettings.IniFormat)
        # Give QObjects reasonable names
        self.setObjectName('RqtLgsvlSimulatorConfiguratorPlugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser
        parser.add_argument("-q", "--quiet", action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('lgsvl_simulator_bridge'), 'resource', 'LgsvlSimulatorConfigratorPlugin.ui')
        # Extend the widget with all atrributes and children from UI File
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('RqtLgsvlSimulatorConfiguratorPluginUi')
        # Show _widget.windowTitle on left-top of each plugin(when it's set in _widget).
        # This is useful when you open multiple plugins aat once. Also if you open multiple
        # instances of your plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' %d' % context.serial_number()))
        # Add widget to the user interface
        self.settings.beginGroup("simulator_params")
        context.add_widget(self._widget)
        ip = self.settings.value('ip')
        if ip != None:
            self._widget.ip_box.setText(ip)
        port = self.settings.value('port')
        if port != None:
            self._widget.port_box.setText(port)
        config_path = self.settings.value('config_path')
        if config_path != None:
            self._widget.configpath_box.setText(config_path)
        self.settings.endGroup()
        self.json_dict = {}
        self.instance_id = ""
        self.is_remote = False
        self._widget.button_config_ref.clicked[bool].connect(self._handle_config_ref_button_clicked)
        self._widget.button_config.clicked[bool].connect(self._handle_config_button_clicked)
        self._widget.launch_button.clicked[bool].connect(self._handle_launch_button_clicked)
        self._widget.terminate_button.clicked[bool].connect(self._handle_terminate_button_clicked)

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.get_value(k, v)
        pass

    def restore_settings(self, pluign_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

    def _handle_launch_button_clicked(self):
        if str(self._widget.ip_box.text()) == "localhost" or str(self._widget.ip_box.text()) == "0.0.0.0" or str(self._widget.ip_box.text()) == "127.0.0.1":
            cmd = ["roslaunch","lgsvl_simulator_bridge","lgsvl_simulator.launch"]
            self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
            self._widget.launch_button.setStyleSheet("background-color: #8fb8e0")
            self._widget.terminate_button.setStyleSheet("background-color: #FFFFFF")
            self.is_remote = False
            return
        else:
            cmd = ["roslaunch","lgsvl_simulator_bridge","bridge.launch"]
            self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)            
        address = "http://" + self._widget.ip_box.text() + ":" + self._widget.port_box.text()+"/simulator/launch"
        self.settings.beginGroup("simulator_params")
        self.settings.setValue("ip", self._widget.ip_box.text())
        self.settings.setValue("port", self._widget.port_box.text())
        self.settings.endGroup()
        try:
            response = requests.post(address,json=self.json_dict)
            self.instance_id = response.json()[u'instance_id']
        except:
            self._widget.launch_button.setStyleSheet("background-color: #F5A9A9")
            return
        self.is_remote = False
        self._widget.launch_button.setStyleSheet("background-color: #8fb8e0")
        self._widget.terminate_button.setStyleSheet("background-color: #FFFFFF")

    def _handle_config_ref_button_clicked(self):
        rospack = rospkg.RosPack()
        fname = QFileDialog.getOpenFileName(self._widget, 'Open file', rospack.get_path('lgsvl_simulator_bridge')+'/config')
        self._widget.configpath_box.setText(fname[0])

    def _handle_config_button_clicked(self):
        path = self._widget.configpath_box.text()
        try:
            f = open(path, "r+")
            self.json_dict = json.load(f)
            self.settings.beginGroup("simulator_params")
            self.settings.setValue("config_path", path)
            self.settings.endGroup()
        except:
            self._widget.button_config.setStyleSheet("background-color: #F5A9A9")
            return
        self._widget.button_config.setStyleSheet("background-color: #8fb8e0")

    def _handle_terminate_button_clicked(self):
        address = "http://" + self._widget.ip_box.text() + ":" + self._widget.port_box.text()+"/simulator/terminate"
        self.settings.beginGroup("simulator_params")
        self.settings.setValue("ip", self._widget.ip_box.text())
        self.settings.setValue("port", self._widget.port_box.text())
        self.settings.endGroup()
        if self.instance_id == "" and self.is_remote == True:
            return
        if self.is_remote == True:
            try:
                response = requests.post(address,json={u'instance_id':self.instance_id})    
            except:
                self._widget.terminate_button.setStyleSheet("background-color: #F5A9A9")
                return
        try:
            self.proc.terminate()
        except:
            self._widget.terminate_button.setStyleSheet("background-color: #F5A9A9")
            return            
        self._widget.terminate_button.setStyleSheet("background-color: #8fb8e0")
        self._widget.launch_button.setStyleSheet("background-color: #FFFFFF")
コード例 #9
0
class MyPlugin(Plugin):
    def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('MyPlugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(
            rospkg.RosPack().get_path('pressure_controller_setup'), 'resource',
            'MyPlugin.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('MyPluginUi')

        self.settings = self.get_settings()

        self.init_sliders()
        #self.init_config()

        self._client = actionlib.SimpleActionClient(
            'pressure_control', pressure_controller_ros.msg.CommandAction)
        self._client_connected = self._client.wait_for_server(
            timeout=rospy.rostime.Duration(1))

        if not self._client_connected:
            print("No command server avaiable... changes will not be sent")

        self.set_graph_state(True)
        self.send_channel_state(0, self.settings['channel_states'][0])

        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

    # def init_config(self):
    #     self._plot_type_index = 0
    #     self.plot_types = [
    #         {
    #             'title': 'PyQtGraph',
    #             'widget_class': PyQtGraphDataPlot,
    #             'description': 'Based on PyQtGraph\n- installer: http://luke.campagnola.me/code/pyqtgraph',
    #             'enabled': True,#PyQtGraphDataPlot is not None,
    #         },
    #         {
    #             'title': 'MatPlot',
    #             'widget_class': MatDataPlot,
    #             'description': 'Based on MatPlotLib\n- needs most CPU\n- needs matplotlib >= 1.1.0\n- if using PySide: PySide > 1.1.0',
    #             'enabled': True,#MatDataPlot is not None,
    #         },
    #         {
    #             'title': 'QwtPlot',
    #             'widget_class': QwtDataPlot,
    #             'description': 'Based on QwtPlot\n- does not use timestamps\n- uses least CPU\n- needs Python Qwt bindings',
    #             'enabled': True,#QwtDataPlot is not None,
    #         },
    #     ]

    def init_sliders(self):
        sliderbox = self._widget.findChild(QLayout, 'Sliders')

        graph_button = QPushButton()
        graph_button.setCheckable(True)
        graph_button.setText("Graph Off")
        graph_button.toggle()
        graph_button.clicked.connect(self.set_graph_state)
        self.graph_button = graph_button

        firstCol = QVBoxLayout()
        firstCol.addWidget(graph_button)

        sliderbox.addLayout(firstCol)

        self.sliders = []

        all_rows_layout = QVBoxLayout()
        chan_idx = 0
        for num_channels_row in self.settings['num_channels']:
            row_layout = QHBoxLayout()
            for i in range(num_channels_row):
                idx = chan_idx * 1

                slider_group = {
                    'slider_p': None,
                    'number_p': None,
                    'slider_i': None,
                    'number_i': None,
                    'slider_d': None,
                    'number_d': None,
                    'on_off': None
                }

                layout_cluster = QVBoxLayout()
                slider_cluster = QHBoxLayout()
                label = QLabel()
                label.setText("Chan. %d" % (idx + 1))
                label.setAlignment(Qt.AlignCenter)
                layout_cluster.addWidget(label)
                for j in range(3):
                    layout = QVBoxLayout()
                    layout.setAlignment(Qt.AlignHCenter)

                    if j == 0:
                        maxrange = 1.0
                    elif j == 1:
                        maxrange = 10
                    elif j == 2:
                        maxrange = 10

                    slider = QSlider(Qt.Vertical)
                    slider.setMinimum(0)
                    slider.setMaximum(maxrange * 100)
                    slider.setValue(self.settings['pid_gains'][chan_idx][j] *
                                    100)
                    slider.setTickPosition(QSlider.TicksRight)
                    slider.setTickInterval(maxrange / 100.0)

                    spinbox = QDoubleSpinBox()
                    spinbox.setMinimum(0)
                    spinbox.setMaximum(maxrange)
                    spinbox.setValue(self.settings['pid_gains'][chan_idx][j])
                    spinbox.setDecimals(2)
                    spinbox.setSingleStep(maxrange / 100.0)

                    cb_function_number = lambda value, idx=idx, gain_idx=j, slider=False: self.send_slider_value(
                        idx, gain_idx, value, slider)
                    cb_function_slider = lambda value, idx=idx, gain_idx=j, slider=True: self.send_slider_value(
                        idx, gain_idx, value, slider)

                    slider.valueChanged.connect(cb_function_slider)
                    spinbox.valueChanged.connect(cb_function_number)

                    label = QLabel()
                    label.setAlignment(Qt.AlignCenter)

                    if j == 0:
                        slider_group['slider_p'] = slider
                        slider_group['number_p'] = spinbox
                        label.setText("P")
                    elif j == 1:
                        slider_group['slider_i'] = slider
                        slider_group['number_i'] = spinbox
                        label.setText("I")
                    elif j == 2:
                        slider_group['slider_d'] = slider
                        slider_group['number_d'] = spinbox
                        label.setText("D")

                    labelmax = QLabel()
                    labelmax.setAlignment(Qt.AlignCenter)
                    labelmax.setText("%0.1f" % (maxrange))

                    labelmin = QLabel()
                    labelmin.setAlignment(Qt.AlignCenter)
                    labelmin.setText("0")

                    layout.addWidget(label)
                    layout.addWidget(labelmax)
                    layout.addWidget(slider, Qt.AlignHCenter)
                    layout.addWidget(labelmin)
                    layout.addWidget(spinbox, Qt.AlignHCenter)
                    layout.setAlignment(slider, Qt.AlignHCenter)
                    layout.setAlignment(spinbox, Qt.AlignHCenter)

                    slider_cluster.addLayout(layout)

                on_button = QPushButton()
                on_button.setCheckable(True)
                on_button.setText("Off")

                if self.settings['channel_states'][chan_idx]:
                    on_button.toggle()
                    on_button.setText("On")

                on_button.clicked.connect(
                    lambda state, idx=idx: self.send_channel_state(idx, state))

                slider_group['on_off'] = on_button

                layout_cluster.addLayout(slider_cluster)
                layout_cluster.addWidget(on_button)

                row_layout.addLayout(layout_cluster)
                row_layout.addSpacing(20)

                self.sliders.append(slider_group)
                chan_idx += 1

            all_rows_layout.addLayout(row_layout)
        sliderbox.addLayout(all_rows_layout)

        #self._widget.setLayout(sliderbox)

    def get_settings(self):
        settings = {}
        settings['channel_states'] = rospy.get_param(
            '/config_node/channels/states', [1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
        settings['num_channels'] = rospy.get_param(
            '/pressure_control/num_channels', [])
        settings['num_channels_tot'] = sum(settings['num_channels'])
        settings['pid_gains'] = rospy.get_param('/config_node/PID/values',
                                                [[0, 0, 0]] *
                                                settings['num_channels_tot'])

        if isinstance(settings['pid_gains'][0], list):
            settings['pid_gains'] = settings['pid_gains']
        else:
            settings['pid_gains'] = [settings['pid_gains']
                                     ] * settings['num_channels_tot']

        print(settings['pid_gains'])

        return settings

    def send_slider_value(self, idx, gain_idx, value, slider):

        if gain_idx == 0:
            slider_str = 'slider_p'
            number_str = 'number_p'
        elif gain_idx == 1:
            slider_str = 'slider_i'
            number_str = 'number_i'
        elif gain_idx == 2:
            slider_str = 'slider_d'
            number_str = 'number_d'

        if slider:
            cur_val = self.sliders[idx][slider_str].value() / 100.0
            self.sliders[idx][number_str].setValue(cur_val)
        else:
            cur_val = self.sliders[idx][number_str].value()
            self.sliders[idx][slider_str].setValue(cur_val * 100.0)

        self.settings['pid_gains'][idx][gain_idx] = cur_val

        self.send_command(command='pid',
                          args=[idx] + self.settings['pid_gains'][idx])

    def send_channel_state(self, idx, state):
        if state:
            self.settings['channel_states'][idx] = 1
            self.sliders[idx]['on_off'].setText("On")
        else:
            self.settings['channel_states'][idx] = 0
            self.sliders[idx]['on_off'].setText("Off")

        self.send_command(command='chan', args=self.settings['channel_states'])

        rospy.set_param('/config_node/channels/states',
                        self.settings['channel_states'])

    def set_graph_state(self, value):

        if value:
            on_off_str = 'on'
            self.graph_button.setText("Graph ON")
        else:
            on_off_str = 'off'
            self.graph_button.setText("Graph OFF")

        self.send_command(command=on_off_str, args=[])

    def send_command(self, command, args, wait_for_ack=False):
        if self._client_connected:
            # Send commands to the command server and wait for things to be taken care of
            goal = pressure_controller_ros.msg.CommandGoal(command=command,
                                                           args=args,
                                                           wait_for_ack=False)
            self._client.send_goal(goal)
            self._client.wait_for_result()

            if not self._client.get_result():
                raise ('Something went wrong and a setting was not validated')
                pass
            else:
                pass
        else:
            print(command, args)

    def shutdown_sliders(self):
        rospy.set_param('/config_node/PID/values', self.settings['pid_gains'])
        print("")
        print("Final PID Gains:")
        print(self.settings['pid_gains'])
        print("")
        print("These settings were saved on the pressure controller")
        print("")
        print(
            "To ensure these parameters are always recalled, for now you need to:"
        )
        print(
            "    - Copy these settings into 'pid/values' in all your control config files"
        )

        self.set_graph_state(False)

        for slider_group in self.sliders:
            for widget in slider_group:
                if 'on_off' in widget:
                    slider_group[widget].clicked.disconnect()
                else:
                    slider_group[widget].valueChanged.disconnect()

    def save_mcu_settings(self):
        self.send_command(command='save', args=[])

    def shutdown_plugin(self):
        self._client.cancel_all_goals()
        self.shutdown_sliders()
        self.save_mcu_settings()

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #10
0
class MyPlugin(Plugin):
    sig_sysmsg = Signal(str)
    l_location=pyqtSignal(float,float)
    r_location=pyqtSignal(float,float)
    l_rssi = pyqtSignal(int)
    r_rssi = pyqtSignal(int)
    l_com = pyqtSignal(float)
    r_com = pyqtSignal(float)



    def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('MyPlugin')
        rp = rospkg.RosPack()

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

##        def goCoords():
##            def resetError():
##               # coordsEdit.setStyleSheet('')
##
##            try:
##		print("work")
##                #latitude, longitude = coordsEdit.text().split(",")
##		
##            except ValueError:
##                #coordsEdit.setStyleSheet("color: red;")
##                QTimer.singleShot(500, resetError)
##            else:
##                map.centerAt(latitude, longitude)
##                # map.moveMarker("MyDragableMark", latitude, longitude)

        def onMarkerMoved(key, latitude, longitude):
            print("Moved!!", key, latitude, longitude)
            #coordsEdit.setText("{}, {}".format(latitude, longitude))
	        #self.map.moveMarker("local GPS",latitude+0.01, longitude+0.01)
        def onMarkerRClick(key):
            print("RClick on ", key)
            # map.setMarkerOptions(key, draggable=False)

        def onMarkerLClick(key):
            print("LClick on ", key)

        def onMarkerDClick(key):
            print("DClick on ", key)
            # map.setMarkerOptions(key, draggable=True)

        def onMapMoved(latitude, longitude):
            print("Moved to ", latitude, longitude)

        def onMapRClick(latitude, longitude):
            print("RClick on ", latitude, longitude)

        def onMapLClick(latitude, longitude):
            print("LClick on ", latitude, longitude)

        def onMapDClick(latitude, longitude):
            print("DClick on ", latitude, longitude)

        def move_l_mark(lat_l,lon_l):

            #onMarkerMoved("local gps",lat_,lon_)
            #print("get location",lat_,lon_)
            self.map.moveMarker("local GPS",lat_l,lon_l)
            l_coordsEdit.setText("{0:.8}, {1:.8}".format(lat_l,lon_l))
        def local_callback(llocation):
            llat=llocation.data[0]
            llon=llocation.data[1]
            #move_mark(self,lat,lon)
            self.l_location.emit(llat,llon)
            
               
        def move_r_mark(lat_r,lon_r):

            #onMarkerMoved("local gps",lat_,lon_)
            #print("get location",lat_,lon_)
            self.map.moveMarker("remote GPS",lat_r,lon_r)
            r_coordsEdit.setText("{0:.8}, {1:.8}".format(lat_r,lon_r))
           
        def remote_callback(rlocation):
            rlat=rlocation.data[0]
            rlon=rlocation.data[1]
            #move_mark(self,lat,lon)
            self.r_location.emit(rlat,rlon)


        def change_l_rssi(rssi):
            l_rssi_text.setText(str(rssi))

        def change_r_rssi(rssi):
            r_rssi_text.setText(str(rssi))

        def l_rssi_callback(rssi):
            self.l_rssi.emit(rssi.data)
            #l_rssi_text.setText(str(rssi.data))


        def r_rssi_callback(rssi):
            self.r_rssi.emit(rssi.data)
            #r_rssi_text.setText(str(rssi.data))


        def move_l_compass(headidng):
            l_com_text.setText("%.1f" % headidng)
            time.sleep(0.01)
            l_com_widget.setAngle(headidng)
            time.sleep(0.01)

        def move_r_compass(headidng):
            r_com_text.setText("%.1f" % headidng)
            time.sleep(0.01)
            r_com_widget.setAngle(headidng)
            time.sleep(0.01)

        def l_com_callback(heading):

            self.l_com.emit(heading.data)
            #l_com_widget.setAngle(heading.data)


        def r_com_callback(heading):

            self.r_com.emit(heading.data)
            #r_com_widget.setAngle(heading.data)

            
        

        # Create QWidget
        self._widget = QWidget()
        gcv=QVBoxLayout(self._widget)



        h = QVBoxLayout()
        #h = QVBoxLayout(w)
        v = QHBoxLayout()
        # l = QFormLayout()
        # v.addLayout(l)
        l_coordsEdit = QLineEdit()
        r_coordsEdit = QLineEdit()
        lgps_label = QLabel('LGPS:')
        rgps_label = QLabel('RGPS:')
        v.addWidget(lgps_label)
        v.addWidget(l_coordsEdit)
        v.addWidget(rgps_label)
        v.addWidget(r_coordsEdit)
        h.addLayout(v)


	
        l_com_widget=CompassWidget()
        r_com_widget = CompassWidget()
        l_rssi_lable=QLabel('local_rssi')
        r_rssi_lable=QLabel('remote_rssi')
        l_rssi_text=QLineEdit()
        l_rssi_text.setMaximumWidth(70)
        r_rssi_text=QLineEdit()
        r_rssi_text.setMaximumWidth(70)

        l_com_label=QLabel('LCOM')
        r_com_label=QLabel('RCOM')
        l_com_text=QLineEdit()
        l_com_text.setMaximumWidth(80)
        r_com_text=QLineEdit()
        r_com_text.setMaximumWidth(80)
        l_h_layout=QHBoxLayout()
        r_h_layout = QHBoxLayout()

        l_h_layout.addStretch(1)
        l_h_layout.addWidget(l_rssi_lable)
        l_h_layout.addWidget(l_rssi_text)
        l_h_layout.addWidget(l_com_label)
        l_h_layout.addWidget(l_com_text)
        l_h_layout.addStretch(1)

        r_h_layout.addStretch(1)
        r_h_layout.addWidget(r_rssi_lable)
        r_h_layout.addWidget(r_rssi_text)
        r_h_layout.addWidget(r_com_label)
        r_h_layout.addWidget(r_com_text)
        r_h_layout.addStretch(1)

        mid_layout=QHBoxLayout()
        #mid_layout.setSpacing(20)

        mid_layout.addLayout(l_h_layout)
        mid_layout.addLayout(r_h_layout)



        c_h_l=QHBoxLayout()
        c_h_l.addWidget(l_com_widget)
        c_h_l.addWidget(r_com_widget)
        #c_h_l.SetMinimumSize(240)
        gcv.addLayout(c_h_l)
        gcv.addLayout(mid_layout)
        gcv.addLayout(h)
        gcv.setStretchFactor(c_h_l,3)
        gcv.setStretchFactor(h,5)



	

        # l.addRow('Coords:', coordsEdit)
        # l.addRow('lcoords:',r_coordsEdit)
        #h.addLayout(v)
        #l = QFormLayout()
        #h.addLayout(l)
        #coordsEdit = QLineEdit()
        #l.addRow('Coords:', coordsEdit)
        #coordsEdit.editingFinished.connect(goCoords)
        self.map = QOSM(self._widget)
        self.map.mapMoved.connect(onMapMoved)
        #self.map.markerMoved.connect(onMarkerMoved)
        self.map.mapClicked.connect(onMapLClick)
        self.map.mapDoubleClicked.connect(onMapDClick)
        self.map.mapRightClicked.connect(onMapRClick)
        self.map.markerClicked.connect(onMarkerLClick)
        self.map.markerDoubleClicked.connect(onMarkerDClick)
        self.map.markerRightClicked.connect(onMarkerRClick)
        self.l_location.connect(move_l_mark)
	self.r_location.connect(move_r_mark)	
        h.addWidget(self.map)
        self.map.setSizePolicy(
            QSizePolicy.MinimumExpanding,
            QSizePolicy.MinimumExpanding)

        self.l_com.connect(move_l_compass)
        self.r_com.connect(move_r_compass)
        self.l_rssi.connect(change_l_rssi)
        self.r_rssi.connect(change_r_rssi)








        # Get path to UI file which is a sibling of this file
        # in this example the .ui and .py file are in the same folder
        ui_file = os.path.join(rp.get_path('gps_com_node'), 'resource', 'MyPlugin.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('MyPluginUi')
        # Show _widget.windowTitle on left-top of each plugin (when 
        # it's set in _widget). This is useful when you open multiple 
        # plugins at once. Also if you open multiple instances of your 
        # plugin at once, these lines add number to make it easy to 
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)
        self.map.waitUntilReady()

        self.map.centerAt(32.7471012, -96.883642)
        self.map.setZoom(12)
        # Many icons at: https://sites.google.com/site/gmapsdevelopment/
        coords = self.map.center()
        self.map.addMarker("local GPS", *coords, **dict(
            icon="http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_gray.png",
            draggable=True,
            title="locat GPS marker!"
        ))

        coords = coords[0] + 0.1, coords[1] + 0.1
        self.map.addMarker("remote GPS", *coords, **dict(
            icon="http://maps.gstatic.com/mapfiles/ridefinder-images/mm_20_red.png",
            draggable=True,
            title="remote GPS marker"
        ))
	sub1 = rospy.Subscriber("/local_gps", Float64MultiArray, local_callback)
        sub2 = rospy.Subscriber("/remote_gps", Float64MultiArray, remote_callback)
        sub3=rospy.Subscriber("/local_rssi",Int32,l_rssi_callback)
        sub4=rospy.Subscriber("/remote_rssi",Int32,r_rssi_callback)
        sub5=rospy.Subscriber("/local_com",Float64,l_com_callback)
        sub6=rospy.Subscriber("/remote_com",Float64,r_com_callback)






	#time.sleep(10)

	

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #11
0
class Top(Plugin):

    NODE_FIELDS = [
        'pid', 'get_cpu_percent', 'get_memory_percent', 'get_num_threads'
    ]
    OUT_FIELDS = [
        'node_name', 'pid', 'cpu_percent', 'memory_percent', 'num_threads'
    ]
    FORMAT_STRS = ['%s', '%s', '%0.2f', '%0.2f', '%s']
    NODE_LABELS = ['Node', 'PID', 'CPU %', 'Mem %', 'Num Threads']
    SORT_TYPE = [str, str, float, float, float]
    TOOLTIPS = {
        0: ('cmdline', lambda x: '\n'.join(textwrap.wrap(' '.join(x)))),
        3: ('memory_info', lambda x:
            ('Resident: %0.2f MiB, Virtual: %0.2f MiB' %
             (x[0] / 2**20, x[1] / 2**20)))
    }

    _node_info = NodeInfo()

    name_filter = re.compile('')

    def __init__(self, context):
        super(Top, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('Top')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        # if not args.quiet:
        #     print 'arguments: ', args
        #     print 'unknowns: ', unknowns

        self._selected_node = ''
        self._selected_node_lock = RLock()

        # Setup the toolbar
        self._toolbar = QToolBar()
        self._filter_box = QLineEdit()
        self._regex_box = QCheckBox()
        self._regex_box.setText('regex')
        self._toolbar.addWidget(QLabel('Filter'))
        self._toolbar.addWidget(self._filter_box)
        self._toolbar.addWidget(self._regex_box)

        self._filter_box.returnPressed.connect(self.update_filter)
        self._regex_box.stateChanged.connect(self.update_filter)

        # Create a container widget and give it a layout
        self._container = QWidget()
        self._container.setWindowTitle('Process Monitor')
        self._layout = QVBoxLayout()
        self._container.setLayout(self._layout)

        self._layout.addWidget(self._toolbar)

        # Create the table widget
        self._table_widget = QTreeWidget()
        self._table_widget.setObjectName('TopTable')
        self._table_widget.setColumnCount(len(self.NODE_LABELS))
        self._table_widget.setHeaderLabels(self.NODE_LABELS)
        self._table_widget.itemClicked.connect(self._tableItemClicked)
        self._table_widget.setSortingEnabled(True)
        self._table_widget.setAlternatingRowColors(True)

        self._layout.addWidget(self._table_widget)
        context.add_widget(self._container)

        # Add a button for killing nodes
        self._kill_button = QPushButton('Kill Node')
        self._layout.addWidget(self._kill_button)
        self._kill_button.clicked.connect(self._kill_node)

        # Update twice since the first cpu% lookup will always return 0
        self.update_table()
        self.update_table()

        self._table_widget.resizeColumnToContents(0)

        # Start a timer to trigger updates
        self._update_timer = QTimer()
        self._update_timer.setInterval(1000)
        self._update_timer.timeout.connect(self.update_table)
        self._update_timer.start()

    def _tableItemClicked(self, item, column):
        with self._selected_node_lock:
            self._selected_node = item.text(0)

    def update_filter(self, *args):
        if self._regex_box.isChecked():
            expr = self._filter_box.text()
        else:
            expr = re.escape(self._filter_box.text())
        self.name_filter = re.compile(expr)
        self.update_table()

    def _kill_node(self):
        self._node_info.kill_node(self._selected_node)

    def update_one_item(self, row, info):
        twi = TopWidgetItem()
        for col, field in enumerate(self.OUT_FIELDS):
            val = info[field]
            twi.setText(col, self.FORMAT_STRS[col] % val)
        self._table_widget.insertTopLevelItem(row, twi)

        for col, (key, func) in self.TOOLTIPS.iteritems():
            twi.setToolTip(col, func(info[key]))

        with self._selected_node_lock:
            if twi.text(0) == self._selected_node:
                twi.setSelected(True)

        twi.setHidden(len(self.name_filter.findall(info['node_name'])) == 0)

    def update_table(self):
        self._table_widget.clear()
        infos = self._node_info.get_all_node_fields(self.NODE_FIELDS)
        for nx, info in enumerate(infos):
            self.update_one_item(nx, info)

    def shutdown_plugin(self):
        self._update_timer.stop()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('filter_text', self._filter_box.text())
        instance_settings.set_value('is_regex',
                                    int(self._regex_box.checkState()))

    def restore_settings(self, plugin_settings, instance_settings):
        self._filter_box.setText(instance_settings.value('filter_text'))
        is_regex_int = instance_settings.value('is_regex')
        if is_regex_int:
            self._regex_box.setCheckState(Qt.CheckState(is_regex_int))
        else:
            self._regex_box.setCheckState(Qt.CheckState(0))
        self.update_filter()
コード例 #12
0
class SchunkPlugin(Plugin):
    def __init__(self, context):
        super(SchunkPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('SchunkPlugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print('arguments: ', args)
            print('unknowns: ', unknowns)

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('schunk_rqt_gui'),
                               'resource', 'ui', 'SchunkJoints.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('SchunkPluginUI')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        # Connect to ROS
        # action clients
        self.action_client = actionlib.SimpleActionClient(
            '/gripper/sdh_controller/follow_joint_trajectory',
            FollowJointTrajectoryAction)

        # subscribers
        self.sub_temp = rospy.Subscriber("/gripper/sdh_controller/temperature",
                                         TemperatureArray, self.on_temp)

        # Connect to UI
        # service buttons
        self._widget.button_init.clicked.connect(
            lambda: self.call_service("init"))
        self._widget.button_disconnect.clicked.connect(
            lambda: self.call_service("shutdown"))
        self._widget.button_estop.clicked.connect(
            lambda: self.call_service("emergency_stop"))
        self._widget.button_motor_on.clicked.connect(
            lambda: self.call_service("motor_on"))
        self._widget.button_motor_off.clicked.connect(
            lambda: self.call_service("motor_off"))

        # status text
        self.status_message = self._widget.status_message

        # joint sliders
        self._widget.proximal_slider.valueChanged.connect(
            lambda value: self.on_slider_update(self._widget.proximal_spinbox,
                                                value))
        self._widget.distal_slider.valueChanged.connect(
            lambda value: self.on_slider_update(self._widget.distal_spinbox,
                                                value))
        # joint spinners
        self._widget.proximal_spinbox.valueChanged.connect(
            lambda value: self.on_spinner_update(self._widget.proximal_slider,
                                                 value))
        self._widget.distal_spinbox.valueChanged.connect(
            lambda value: self.on_spinner_update(self._widget.distal_slider,
                                                 value))

        # set spinner boxes by default sliders values
        self._widget.proximal_spinbox.setValue(
            self._widget.proximal_slider.value() / 1000.0)
        self._widget.distal_spinbox.setValue(
            self._widget.distal_slider.value() / 1000.0)

        # map temperature names to spinner boxes
        self.tempspinners = dict()
        self.tempspinners["root"] = self._widget.spin_root
        self.tempspinners["controller"] = self._widget.spin_ctrl
        self.tempspinners["pcb"] = self._widget.spin_pcb

        self.is_initialised = False
        self.is_motor_on = False
        self.has_new_data = False

        # start working thread
        self.running = True
        self.thread = threading.Thread(target=self.loop, args=())
        self.thread.start()

    def on_slider_update(self, spinner, value):
        # just set spinner value, do not forward signal back to slider
        spinner.blockSignals(True)
        spinner.setValue(value / 1000.0)
        spinner.blockSignals(False)
        self.has_new_data = True

    def on_spinner_update(self, slider, value):
        # just set slider value, do not forward signal back to spinner
        slider.blockSignals(True)
        slider.setValue(value * 1000)
        slider.blockSignals(False)
        self.has_new_data = True

    def call_service(self, name):
        service_name = '/gripper/sdh_controller/' + name

        try:
            rospy.wait_for_service(service_name, timeout=0.5)
        except rospy.exceptions.ROSException:
            rospy.logerr("service '" + str(name) + "' is not available")
            self.status_message.setText("service '" + str(name) +
                                        "' is not available")
            return False

        service = rospy.ServiceProxy(service_name, Trigger)
        try:
            resp = service()
        except rospy.service.ServiceException as e:
            msg = "service call '" + str(name) + "' failed"
            rospy.logerr(msg)
            self.status_message.setText(msg)
            return False

        print("Called service:", name)
        print("Response:")
        print(resp)
        self.status_message.setText(resp.message)

        if name == "init":
            self.is_initialised = resp.success
            self.is_motor_on = resp.success
        elif name == "shutdown":
            self.is_initialised = not resp.success
            self.is_motor_on = not resp.success

        if name == "motor_on":
            self.is_motor_on = resp.success
        elif name == "motor_off":
            self.is_motor_on = not resp.success

        if resp.success and (name in ["init", "motor_on"]):
            self.has_new_data = True

        return resp.success

    def loop(self):
        self.running = True
        while self.running:
            if self.is_initialised and self.is_motor_on and self.has_new_data:
                self.send_grasp_joint_positions()
                self.has_new_data = False
            time.sleep(0.1)

    def send_grasp_joint_positions(self):
        # values in range 0 ... 1
        proximal_value = self._widget.proximal_spinbox.value()
        distal_value = self._widget.distal_spinbox.value()

        # define sets of joints
        proximal_joints = [
            "sdh_thumb_2_joint", "sdh_finger_12_joint", "sdh_finger_22_joint"
        ]
        distal_joints = [
            "sdh_thumb_3_joint", "sdh_finger_13_joint", "sdh_finger_23_joint"
        ]
        static_joints = ["sdh_knuckle_joint"]
        all_joints = static_joints + proximal_joints + distal_joints

        # map joint ranges from [0..1] to individual set ranges
        # proximal range: [-pi/2 .. 0]
        # distal range: [0 .. pi/2]

        proximal_range = [-math.pi / 2.0, 0.0]
        distal_range = [0.0, math.pi / 2.0]
        proximal_jpos = proximal_range[0] + proximal_value * (
            proximal_range[1] - proximal_range[0])
        distal_jpos = distal_range[0] + distal_value * (distal_range[1] -
                                                        distal_range[0])

        trajectory_goal = FollowJointTrajectoryGoal()

        # add single single joint point to trajectory
        trajectory_goal.trajectory.points.append(JointTrajectoryPoint())
        for jname in all_joints:
            trajectory_goal.trajectory.joint_names.append(jname)
            # select joint position from set
            if jname in static_joints:
                trajectory_goal.trajectory.points[0].positions.append(0)
            elif jname in proximal_joints:
                trajectory_goal.trajectory.points[0].positions.append(
                    proximal_jpos)
            elif jname in distal_joints:
                trajectory_goal.trajectory.points[0].positions.append(
                    distal_jpos)
            else:
                raise Exception("joint not in set")

        # send trajectory and wait for response
        self.action_client.send_goal(trajectory_goal)

        if self.action_client.wait_for_result(timeout=rospy.Duration(5.0)):
            trajectory_result = self.action_client.get_result()
            print("set joints to " +
                  ('%s' % trajectory_goal.trajectory.points[0].positions))
            self.status_message.setText("set joints")
            return trajectory_result.error_code == FollowJointTrajectoryResult.SUCCESSFUL
        else:
            rospy.logerr("timeout while waiting for response from grasp goal")
            self.status_message.setText(
                "timeout while waiting for response from grasp goal")
            return False

    def on_temp(self, temp_msg):
        if self.is_initialised:
            temps = dict(zip(temp_msg.name, temp_msg.temperature))
            for name, spinner in self.tempspinners.iteritems():
                try:
                    spinner.setValue(temps[name])
                except KeyError:
                    rospy.logerr("temperature", name,
                                 "is not provided by SDH driver node")

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        self.sub_temp.unregister()

        self.action_client.cancel_all_goals()
        self.running = False
        self.thread.join()

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #13
0
class MyPlugin(Plugin):

    def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('MyPlugin')
        rospy.loginfo("Remora: ECU Console")

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which is a sibling of this file
        # in this example the .ui and .py file are in the same folder
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ecu_console.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('MyPluginUi')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        self._widget.setWindowTitle('ECU')
        if context.serial_number() > 1:
          self._widget.setWindowTitle(self._widget.windowTitle()+(' (%d)'% context.serial_number()))

        # Add widget to the user interface
        context.add_widget(self._widget)

        self.firstStatusMsg = False

        # Register all subsribers here
        self.intTempSub_  = rospy.Subscriber('/sunfish/ecu/int_temp', Temperature, self.callback_IntTemp)
        self.extTempSub_  = rospy.Subscriber('/sunfish/ecu/ext_temp', Temperature, self.callback_ExtTemp)
        self.depthSub_    = rospy.Subscriber('/sunfish/ecu/depth',    Depth,       self.callback_Depth)
        self.insSub_      = rospy.Subscriber('/sunfish/ecu/INS',      INS,         self.callback_INS)
        self.magFieldSub_ = rospy.Subscriber('/sunfish/ecu/MST',      MagField,    self.callback_MagField)
        self.statusSub_   = rospy.Subscriber('/sunfish/ecu/status',   Status,      self.callback_Status)
        return

    def shutdown_plugin(self):
        self.intTempSub_.unregister()
        self.extTempSub_.unregister()
        self.depthSub_.unregister()
        self.insSub_.unregister()
        self.magFieldSub_.unregister()
        self.statusSub_.unregister()
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

    #def trigger_configuration(self):
        # Comment in to signal that the plugin has a way to configure
        # This will enable a setting button (gear icon) in each dock widget title bar
        # Usually used to open a modal configuration dialog

    def formatFloat(self, f):
        return ("%2.2f" % f)

    def callback_IntTemp(self, value):
        self._widget.intTempData.setText(self.formatFloat(value.temperature))
        return

    def callback_ExtTemp(self, value):
        self._widget.extTempData.setText(self.formatFloat(value.temperature))
        return

    def callback_Depth(self, value):
        self._widget.pressureData.setText(self.formatFloat(value.Pressure))
        self._widget.tempData.setText(self.formatFloat(value.Temperature))
        return

    def callback_INS(self, value):
        self._widget.ab_data_0.setText(self.formatFloat(value.AbsOrientation[0]))
        self._widget.ab_data_1.setText(self.formatFloat(value.AbsOrientation[1]))
        self._widget.ab_data_2.setText(self.formatFloat(value.AbsOrientation[2]))

        self._widget.av_Data_0.setText(self.formatFloat(value.AngularVelocity[0]))
        self._widget.av_Data_1.setText(self.formatFloat(value.AngularVelocity[1]))
        self._widget.av_Data_2.setText(self.formatFloat(value.AngularVelocity[2]))

        self._widget.af_data_0.setText(self.formatFloat(value.AccelerationVector[0]))
        self._widget.af_data_1.setText(self.formatFloat(value.AccelerationVector[1]))
        self._widget.af_data_2.setText(self.formatFloat(value.AccelerationVector[2]))

        self._widget.la_Data_0.setText(self.formatFloat(value.LinearAcceleration[0]))
        self._widget.la_Data_1.setText(self.formatFloat(value.LinearAcceleration[1]))
        self._widget.la_Data_2.setText(self.formatFloat(value.LinearAcceleration[2]))

        self._widget.gv_data_0.setText(self.formatFloat(value.GravityVector[0]))
        self._widget.gv_data_1.setText(self.formatFloat(value.GravityVector[1]))
        self._widget.gv_data_2.setText(self.formatFloat(value.GravityVector[2]))
        return

    def callback_MagField(self, value):
        self._widget.mf_data_0.setText(self.formatFloat(value.MagneticField[0]))
        self._widget.mf_data_1.setText(self.formatFloat(value.MagneticField[1]))
        self._widget.mf_data_2.setText(self.formatFloat(value.MagneticField[2]))
        return

    def callback_Status(self, value):
        if self.firstStatusMsg == False:
          self._widget.hwdData.setText("Hwd: " + value.Hardware)
          self._widget.firmwareData.setText("Firmware:" + value.Firmware)
          self.firstStatusMsg = True

        self._widget.voltData.setText(("%2.1f" % value.Voltage))
        self._widget.currentData.setText(("%2.2f" % value.Current))
        self._widget.pwm_lcd_0.display(10)
        self._widget.pwm_lcd_1.display(10)
        self._widget.pwm_lcd_2.display(10)
        self._widget.pwm_lcd_3.display(10)

        self._widget.pwm_lcd_4.display(50)
        self._widget.pwm_lcd_5.display(50)
        self._widget.pwm_lcd_6.display(50)
        self._widget.pwm_lcd_7.display(50)

        self._widget.pwm_lcd_8.display(100)
        self._widget.pwm_lcd_9.display(100)
        self._widget.pwm_lcd_10.display(100)
        self._widget.pwm_lcd_11.display(100)
        return
コード例 #14
0
class Overview(Plugin):
    def __init__(self, context):
        super(Overview, self).__init__(context)
        self.__tfListener = tf.TransformListener()

        self.__copterFrame = "base_link"
        self.__worldFrame = "world"

        # Give QObjects reasonable names
        self.setObjectName('simple_position_control_gui')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-c", "--copterFrame", dest="copterFrame", help="Specify the copter frame")
        parser.add_argument("-w", "--worldFrame", dest="worldFrame", help="Specify the world frame")

        args, unknowns = parser.parse_known_args(context.argv())
        if args.copterFrame:
            self.__copterFrame = args.copterFrame
            rospy.loginfo("using %s as copter frame", self.__copterFrame)
            
        if args.worldFrame:
            self.__worldFrame = args.worldFrame
            rospy.loginfo("using %s as world frame", self.__worldFrame)
        
        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this node
        ui_file = os.path.join(rospkg.RosPack().get_path('position_controller'), 'src', 'rqt_control_gui', 'resource', 'overview.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('OverviewUI')
        # Show _widget.windowTitle on left-top of each plugin (when 
        # it's set in _widget). This is useful when you open multiple 
        # plugins at once. Also if you open multiple instances of your 
        # plugin at once, these lines add number to make it easy to 
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)
        self._widget.forwardButton.clicked.connect(self.goForward)
        self._widget.backwardButton.clicked.connect(self.goBackward)
        self._widget.leftButton.clicked.connect(self.goLeft)
        self._widget.rightButton.clicked.connect(self.goRight)
        self._widget.rotateButton.clicked.connect(self.rotate)
        self._widget.landButton.clicked.connect(self.land)
        self._widget.startButton.clicked.connect(self.start)
        self._widget.absoluteControlButton.clicked.connect(self.absoluteControl)
        self._widget.tfButton.clicked.connect(self.goToTF)
        self.__statusLabel = self._widget.statusLabel
    
    def setRelativePose(self, x_delta, y_delta, yaw_delta):
        """
        set new target position relative to current position
            :param: x_delta: change in x
            :param: y_delta: change in y
            :param: yaw_delta: change in yaw
        """
        try:
            (trans, rot) = self.__tfListener.lookupTransform(self.__worldFrame, self.__copterFrame, rospy.Time(0))
                        
            quad_delta = tf.transformations.quaternion_from_euler(0, 0, yaw_delta)
                        
            translation_delta = tf.transformations.translation_matrix((x_delta, y_delta, 0))
                        
            m_current = tf.transformations.translation_matrix(trans).dot(tf.transformations.quaternion_matrix(rot))
                      
            m_delta =  translation_delta.dot(tf.transformations.quaternion_matrix(quad_delta))
                        
            m_target = m_current.dot(m_delta)
                        
            _, _, target_yaw = tf.transformations.euler_from_matrix(m_target)
            
            target_x, target_y, _  = tf.transformations.translation_from_matrix(m_target)
                                    
            self.__statusLabel.setText("going to x: {0} y: {1} yaw: {2}".format(target_x, target_y, target_yaw))
            rospy.wait_for_service('/position_controller/set_target_pos', 1)
            setPose = rospy.ServiceProxy('/position_controller/set_target_pos', SetPos)
            setPose(target_x, target_y, target_yaw)
        except Exception as e:
            self.__statusLabel.setText(str(e))
        
    def goForward(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(distance, 0, 0)
            
    def goBackward(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(-distance, 0, 0)
        
    def goLeft(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(0, distance, 0)
    
    def goRight(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(0, -distance, 0)
    
    def rotate(self):
        angle = math.pi * float(self._widget.rotationEdit.text()) / 180.0
        self.setRelativePose(0, 0, angle)
                    
    def land(self):
        try:
            self.setAltitude(0.0)
        except ValueError:
            pass
        
    def start(self):
        try:
            altitude = float(self._widget.zEdit.text())
            self.setAltitude(altitude)
            self.setAltitude(altitude)
            statusMessage = "starting to altitude {0}".format(altitude)
            self.__statusLabel.setText(statusMessage)
        except ValueError:
            pass
            
    def setAltitude(self, altitude):
        rospy.wait_for_service('/position_controller/set_altitude', 1)
        setAltitudeService = rospy.ServiceProxy('/position_controller/set_altitude', SetAltitude)
        setAltitudeService(altitude)
    
    def absoluteControl(self):
        statusMessage = ""
        try:
            ((x, y, _z), quaternion) = self.__tfListener.lookupTransform(self.__worldFrame, self.__copterFrame, rospy.Time(0))
            (_roll, _pitch, yaw) = euler_from_quaternion(quaternion)
            try:
                yaw = math.pi * float(self._widget.angleEdit.text()) / 180.0
            except ValueError:
                pass                
            try:
                x = float(self._widget.xEdit.text())
            except ValueError:
                pass
            try:
                y = float(self._widget.yEdit.text())
            except ValueError:
                pass
            statusMessage += "going to x: {0} y: {1} yaw: {2} ".format(x, y, yaw)
            rospy.wait_for_service('/position_controller/set_target_pos', 1)
            setPose = rospy.ServiceProxy('/position_controller/set_target_pos', SetPos)
            setPose(x, y, yaw)
            self.__statusLabel.setText(statusMessage)
            try:
                altitude = float(self._widget.zEdit.text())
                statusMessage += "setting altitude to {0}".format(altitude)
                self.setAltitude(altitude)
                self.__statusLabel.setText(statusMessage)
            except ValueError:
                pass
        except Exception as e:
            self.__statusLabel.setText(str(e))
    
    def goToTF(self):
        pass

    def shutdown_plugin(self):
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        rospy.loginfo("saving simple position controller gui setting")
        instance_settings.set_value("worldFrame", self.__worldFrame)
        instance_settings.set_value("copterFrame", self.__copterFrame)

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        rospy.loginfo("restoring simple position controller gui setting")
        storedWorldFrame = instance_settings.value("worldFrame")
        if type(storedWorldFrame) == unicode:
            storedWorldFrame = storedWorldFrame.encode('ascii', 'ignore')
        if storedWorldFrame:
            self.__worldFrame = storedWorldFrame
        storedCopterFrame = instance_settings.value("copterFrame")
        if type(storedCopterFrame) == unicode:
            storedCopterFrame = storedCopterFrame.encode('ascii', 'ignore')
        if storedCopterFrame:
            self.__copterFrame = storedCopterFrame
コード例 #15
0
class HandEyeCalibration(Plugin):
    PLUGIN_TITLE = ' Intel OTC Robotics: Hand-Eye Calibration'

    def __init__(self, context):
        super(HandEyeCalibration, self).__init__(context)
        self.context = context
        self.node = context.node
        self.widget = QWidget()
        self.widget.setObjectName(self.PLUGIN_TITLE)
        self.widget.setWindowTitle(self.PLUGIN_TITLE)

        # Data
        self.Tsamples = []

        # Toolbar
        _, path_pkg = get_resource('packages', 'handeye_dashboard')
        print("{}".format(path_pkg))

        self.snapshot_action = QAction(QIcon.fromTheme('camera-photo'),
                                       'Take a snapshot', self.widget)
        path = path_pkg + '/share/handeye_dashboard/images/capture.png'
        self.calibrate_action = QAction(QIcon(QPixmap.fromImage(QImage(path))),
                                        'Get the camera/robot transform',
                                        self.widget)
        self.clear_action = QAction(QIcon.fromTheme('edit-clear'),
                                    'Clear the record data.', self.widget)
        path = path_pkg + '/share/handeye_dashboard/images/UR5.png'
        self.execut_action = QAction(QIcon(QPixmap.fromImage(QImage(path))),
                                     'EStart the publishing the TF.',
                                     self.widget)
        self.toolbar = QToolBar()
        self.toolbar.addAction(self.snapshot_action)
        self.toolbar.addAction(self.calibrate_action)
        self.toolbar.addAction(self.clear_action)
        self.toolbar.addAction(self.execut_action)

        # Toolbar0
        self.l0 = QLabel(self.widget)
        self.l0.setText("Camera-Mount-Type: ")
        self.l0.setFixedWidth(150)
        self.l0.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.combobox = QComboBox(self.widget)
        self.combobox.addItem('attached on robot')
        self.combobox.addItem('fixed beside robot')
        self.toolbar0 = QToolBar()
        self.toolbar0.addWidget(self.l0)
        self.toolbar0.addWidget(self.combobox)

        # Toolbar1
        self.l1 = QLabel(self.widget)
        self.l1.setText("Camera-Frame: ")
        self.l1.setFixedWidth(150)
        self.l1.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.camera_frame = QLineEdit(self.widget)
        self.camera_frame.setText("camera_link")
        self.toolbar1 = QToolBar()
        self.toolbar1.addWidget(self.l1)
        self.toolbar1.addWidget(self.camera_frame)

        # Toolbar2
        self.l2 = QLabel(self.widget)
        self.l2.setText("Object-Frame: ")
        self.l2.setFixedWidth(150)
        self.l2.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.object_frame = QLineEdit(self.widget)
        self.object_frame.setText("calib_board")
        self.toolbar2 = QToolBar()
        self.toolbar2.addWidget(self.l2)
        self.toolbar2.addWidget(self.object_frame)

        # Toolbar3
        self.l3 = QLabel(self.widget)
        self.l3.setText("Robot-Base-Frame: ")
        self.l3.setFixedWidth(150)
        self.l3.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.base_frame = QLineEdit(self.widget)
        self.base_frame.setText("base")
        self.toolbar3 = QToolBar()
        self.toolbar3.addWidget(self.l3)
        self.toolbar3.addWidget(self.base_frame)

        # Toolbar4
        self.l4 = QLabel(self.widget)
        self.l4.setText("End-Effector-Frame: ")
        self.l4.setFixedWidth(150)
        self.l4.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.endeffector_frame = QLineEdit(self.widget)
        self.endeffector_frame.setText("tool0")
        self.toolbar4 = QToolBar()
        self.toolbar4.addWidget(self.l4)
        self.toolbar4.addWidget(self.endeffector_frame)

        # Toolbar5
        self.l5 = QLabel(self.widget)
        self.l5.setText("Sample-Number: ")
        self.l5.setFixedWidth(150)
        self.l5.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
        self.le5 = QLineEdit(self.widget)
        self.le5.setValidator(QIntValidator())
        self.le5.setText('10')
        self.le5.setReadOnly(True)
        self.toolbar5 = QToolBar()
        self.toolbar5.addWidget(self.l5)
        self.toolbar5.addWidget(self.le5)

        # TreeView
        self.treeview = QTreeView()
        self.treeview.setAlternatingRowColors(True)
        self.model = QStandardItemModel(self.treeview)
        self.treeview.setModel(self.model)
        self.treeview.setHeaderHidden(True)

        # TextEdit
        self.textedit = QTextEdit(self.widget)
        self.textedit.setReadOnly(True)

        # Layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.toolbar0)
        self.layout.addWidget(self.toolbar1)
        self.layout.addWidget(self.toolbar2)
        self.layout.addWidget(self.toolbar3)
        self.layout.addWidget(self.toolbar4)
        self.layout.addWidget(self.toolbar5)
        self.layout.addWidget(self.toolbar)
        self.layoutH = QHBoxLayout()
        self.layoutH.addWidget(self.treeview)
        self.layoutH.addWidget(self.textedit)
        self.layout.addLayout(self.layoutH)
        self.widget.setLayout(self.layout)
        # Add the widget to the user interface
        if context.serial_number() > 1:
            self.widget.setWindowTitle(self.widget.windowTitle() +
                                       (' (%d)' % context.serial_number()))
        context.add_widget(self.widget)
        # Make the connections
        self.snapshot_action.triggered.connect(self.take_snapshot)
        self.calibrate_action.triggered.connect(self.calibration)
        self.clear_action.triggered.connect(self.clear)
        self.execut_action.triggered.connect(self.execution)

        # Package path
        self.path_pkg = path_pkg

        # Set up TF
        self.cli = self.node.create_client(HandeyeTF, 'handeye_tf_service')
        while not self.cli.wait_for_service(timeout_sec=1.0):
            self.node.get_logger().info(
                'service not available, waiting again...')
        self.req = HandeyeTF.Request()

    def clear(self):
        # >>> Clear the recorded samples
        self.textedit.append('Clearing the recorded data ...')
        self.textedit.clear()
        self.Tsamples = []
        self.model.clear()

    def get_tf_transform(self, frame_id, child_frame_id):
        self.req.transform.header.frame_id = frame_id
        self.req.transform.child_frame_id = child_frame_id
        self.req.publish.data = False

        future = self.cli.call_async(self.req)
        rclpy.spin_until_future_complete(self.node, future)

        transform = TransformStamped()

        try:
            result = future.result()
        except Exception as e:
            self.node.get_logger().info('Service call failed %r' % (e, ))
        else:
            transform = result.tf_lookup_result

        return transform

    def publish_tf_transform(self, transform_to_publish):
        self.req.publish.data = True
        self.req.transform = transform_to_publish

        future = self.cli.call_async(self.req)
        rclpy.spin_until_future_complete(self.node, future)

        try:
            future.result()
        except Exception as e:
            self.node.get_logger().info('Service call failed %r' % (e, ))
        else:
            self.node.get_logger().info(
                'Send the camera-robot transform :\n\tfrom `{}` to `{}`.'.
                format(self.req.transform.header.frame_id,
                       self.req.transform.child_frame_id))

    def take_snapshot(self):
        # >>> Take the snapshot
        self.textedit.append('Taking snapshot ...')

        # Get the transform from `tool0` to `base_link`
        T = self.get_tf_transform(self.base_frame.text(),
                                  self.endeffector_frame.text())
        bTe = np.zeros((4, 4))
        q = [
            T.transform.rotation.w, T.transform.rotation.x,
            T.transform.rotation.y, T.transform.rotation.z
        ]
        bTe = br.quaternion.to_transform(q)
        bTe[:3, 3] = np.array([
            T.transform.translation.x, T.transform.translation.y,
            T.transform.translation.z
        ])
        self.textedit.append('Lookup transform: from `{}` to `{}`.'.format(
            self.base_frame.text(), self.endeffector_frame.text()))
        self.node.get_logger().info(bcolors.OKGREEN + 'bTe:' + bcolors.ENDC +
                                    '\n{}'.format(bTe))

        # Get the transform from `calib_board` to `camera_link`
        T = self.get_tf_transform(self.camera_frame.text(),
                                  self.object_frame.text())
        cTo = np.zeros((4, 4))
        q = [
            T.transform.rotation.w, T.transform.rotation.x,
            T.transform.rotation.y, T.transform.rotation.z
        ]
        cTo = br.quaternion.to_transform(q)
        cTo[:3, 3] = np.array([
            T.transform.translation.x, T.transform.translation.y,
            T.transform.translation.z
        ])
        self.textedit.append('Lookup transform: from `{}` to `{}`.'.format(
            self.camera_frame.text(), self.object_frame.text()))
        self.node.get_logger().info(bcolors.OKGREEN + 'cTo:' + bcolors.ENDC +
                                    '\n{}'.format(cTo))

        parent = QStandardItem('Snapshot {}'.format(len(self.Tsamples)))
        child_1 = QStandardItem('bTe:\n{}\n{}\n{}\n{}'.format(
            bTe[0, :], bTe[1, :], bTe[2, :], bTe[3, :]))
        child_2 = QStandardItem('cTo:\n{}\n{}\n{}\n{}'.format(
            cTo[0, :], cTo[1, :], cTo[2, :], cTo[3, :]))
        parent.appendRow(child_1)
        parent.appendRow(child_2)
        self.model.appendRow(parent)
        self.Tsamples.append((bTe, cTo))
        self.le5.setText(str(len(self.Tsamples)))

    def calibration(self):
        # >>> Compute the calibration
        self.textedit.append('Making the calibration ...')
        if len(self.Tsamples) == 0:
            self.textedit.append(
                'No transform recorded, please take snapshots.')
            return
        # save samples to `dataset.json` file
        save_samples_to_file(self.Tsamples)
        import handeye
        if self.combobox.currentIndex() == 0:
            solver_cri = handeye.calibrator.HandEyeCalibrator(setup='Moving')
        if self.combobox.currentIndex() == 1:
            solver_cri = handeye.calibrator.HandEyeCalibrator(setup='Fixed')
        for sample in self.Tsamples:
            solver_cri.add_sample(sample[0], sample[1])
        try:
            bTc = solver_cri.solve(method=handeye.solver.Daniilidis1999)
            # save the calibration result to 'camera-robot.json' file
            file_output = '/tmp/' + 'camera-robot.json'
            with open(file_output, 'w') as f:
                json.dump(bTc.tolist(), f)
        except Exception:
            self.textedit.append("Failed to solve the hand-eye calibration.")

    def execution(self):
        # >>> Publish the camera-robot transform
        self.textedit.append('Publishing the camera TF ...')
        file_input = '/tmp/' + 'camera-robot.json'
        with open(file_input, 'r') as f:
            datastore = json.load(f)

        to_frame = self.camera_frame.text()
        if self.combobox.currentIndex() == 0:
            from_frame = self.endeffector_frame.text()
        if self.combobox.currentIndex() == 1:
            from_frame = self.base_frame.text()

        bTc = np.array(datastore)
        static_transformStamped = TransformStamped()
        static_transformStamped.header.stamp = ROSClock().now().to_msg()
        static_transformStamped.header.frame_id = from_frame
        static_transformStamped.child_frame_id = to_frame

        static_transformStamped.transform.translation.x = bTc[0, 3]
        static_transformStamped.transform.translation.y = bTc[1, 3]
        static_transformStamped.transform.translation.z = bTc[2, 3]

        q = br.transform.to_quaternion(bTc)
        static_transformStamped.transform.rotation.x = q[1]
        static_transformStamped.transform.rotation.y = q[2]
        static_transformStamped.transform.rotation.z = q[3]
        static_transformStamped.transform.rotation.w = q[0]

        self.publish_tf_transform(static_transformStamped)

        output_string = "camera-robot pose:\n"
        output_string += "  Translation: [{}, {}, {}]\n".format(
            bTc[0, 3], bTc[1, 3], bTc[2, 3])
        output_string += "  Rotation: in Quaternion [{}, {}, {}, {}]".format(
            q[0], q[1], q[2], q[3])
        file_path = '/tmp/' + 'camera-robot.txt'
        with open(file_path, 'w') as f:
            f.write(output_string)

    def shutdown_plugin(self):
        """
    Unregister subscribers when the plugin shutdown
    """
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # Nothing to be done here
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # Nothing to be done here
        pass
コード例 #16
0
class PidTuner(Plugin):
    def __init__(self, context):
        super(PidTuner, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('PidTuner')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_pid_tuner'),
                               'resource', 'pid_tuner.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('PidTunerUI')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.

        self.vals = {
            Tuner.Pid1: {
                PIDValue.Pl: 0.0,
                PIDValue.Il: 0.0,
                PIDValue.Dl: 0.0,
                PIDValue.Sl: 0.0,
                PIDValue.Pa: 0.0,
                PIDValue.Ia: 0.0,
                PIDValue.Da: 0.0,
                PIDValue.Sa: 0.0,
                "service": "",
                "topic": ""
            },
            Tuner.Pid2: {
                PIDValue.Pl: 0.0,
                PIDValue.Il: 0.0,
                PIDValue.Dl: 0.0,
                PIDValue.Sl: 0.0,
                PIDValue.Pa: 0.0,
                PIDValue.Ia: 0.0,
                PIDValue.Da: 0.0,
                PIDValue.Sa: 0.0,
                "service": "",
                "topic": ""
            },
            Tuner.Inertial: {
                InertialValue.Mass: 0.0,
                InertialValue.Buoyancy: 0.0,
                InertialValue.Ixx: 0.0,
                InertialValue.Iyy: 0.0,
                InertialValue.Izz: 0.0,
                "service": "",
                "topic": ""
            }
        }

        self.cachedVals = {
            Tuner.Pid1: {
                PIDValue.Pl: 0.0,
                PIDValue.Il: 0.0,
                PIDValue.Dl: 0.0,
                PIDValue.Sl: 0.0,
                PIDValue.Pa: 0.0,
                PIDValue.Ia: 0.0,
                PIDValue.Da: 0.0,
                PIDValue.Sa: 0.0
            },
            Tuner.Pid2: {
                PIDValue.Pl: 0.0,
                PIDValue.Il: 0.0,
                PIDValue.Dl: 0.0,
                PIDValue.Sl: 0.0,
                PIDValue.Pa: 0.0,
                PIDValue.Ia: 0.0,
                PIDValue.Da: 0.0,
                PIDValue.Sa: 0.0
            },
            Tuner.Inertial: {
                InertialValue.Mass: 0.0,
                InertialValue.Buoyancy: 0.0,
                InertialValue.Ixx: 0.0,
                InertialValue.Iyy: 0.0,
                InertialValue.Izz: 0.0
            }
        }

        self.sub_t1 = None
        self.sub_t2 = None
        self.sub_t3 = None

        self._widget.t1_lp.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Pl))
        self._widget.t1_li.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Il))
        self._widget.t1_ld.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Dl))
        self._widget.t1_ls.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Sl))
        self._widget.t1_ap.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Pa))
        self._widget.t1_ai.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Ia))
        self._widget.t1_ad.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Da))
        self._widget.t1_as.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid1, PIDValue.Sa))
        self._widget.t1_srv.editingFinished.connect(
            lambda: self.on_srv_name_changed(self._widget.t1_srv, Tuner.Pid1))
        self._widget.t1_tpc.editingFinished.connect(
            lambda: self.on_topic_name_changed(self._widget.t1_tpc, Tuner.Pid1
                                               ))
        self._widget.t1_read.pressed.connect(
            lambda: self.on_reload_values(Tuner.Pid1))
        self._widget.t1_send.pressed.connect(
            lambda: self.on_send_values(Tuner.Pid1))

        self._widget.t2_lp.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Pl))
        self._widget.t2_li.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Il))
        self._widget.t2_ld.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Dl))
        self._widget.t2_ls.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Sl))
        self._widget.t2_ap.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Pa))
        self._widget.t2_ai.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Ia))
        self._widget.t2_ad.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Da))
        self._widget.t2_as.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Pid2, PIDValue.Sa))
        self._widget.t2_srv.editingFinished.connect(
            lambda: self.on_srv_name_changed(self._widget.t2_srv, Tuner.Pid2))
        self._widget.t2_tpc.editingFinished.connect(
            lambda: self.on_topic_name_changed(self._widget.t2_tpc, Tuner.Pid2
                                               ))
        self._widget.t2_read.pressed.connect(
            lambda: self.on_reload_values(Tuner.Pid2))
        self._widget.t2_send.pressed.connect(
            lambda: self.on_send_values(Tuner.Pid2))

        self._widget.t3_m.textEdited.connect(lambda t: self.on_pid_val_changed(
            t, Tuner.Inertial, InertialValue.Mass))
        self._widget.t3_b.textEdited.connect(lambda t: self.on_pid_val_changed(
            t, Tuner.Inertial, InertialValue.Buoyancy))
        self._widget.t3_ixx.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Inertial, InertialValue.
                                              Ixx))
        self._widget.t3_iyy.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Inertial, InertialValue.
                                              Iyy))
        self._widget.t3_izz.textEdited.connect(
            lambda t: self.on_pid_val_changed(t, Tuner.Inertial, InertialValue.
                                              Izz))
        self._widget.t3_srv.editingFinished.connect(
            lambda: self.on_srv_name_changed(self._widget.t3_srv, Tuner.
                                             Inertial))
        self._widget.t3_tpc.editingFinished.connect(
            lambda: self.on_topic_name_changed(self._widget.t3_tpc, Tuner.
                                               Inertial))
        self._widget.t3_read.pressed.connect(
            lambda: self.on_reload_values(Tuner.Inertial))
        self._widget.t3_send.pressed.connect(
            lambda: self.on_send_values(Tuner.Inertial))

        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

    def on_pid_val_changed(self, text, tuner, pidvaluetype):
        try:
            self.vals[tuner][pidvaluetype] = float(text)
        except ValueError as ve:
            pass
            self.update_text()

    def on_srv_name_changed(self, source, tuner):
        self.vals[tuner]["service"] = source.text()

    def on_topic_name_changed(self, source, tuner):
        self.vals[tuner]["topic"] = source.text()
        self.resub()

    def update_text(self):
        self._widget.t1_lp.setText(str(self.vals[Tuner.Pid1][PIDValue.Pl]))
        self._widget.t1_li.setText(str(self.vals[Tuner.Pid1][PIDValue.Il]))
        self._widget.t1_ld.setText(str(self.vals[Tuner.Pid1][PIDValue.Dl]))
        self._widget.t1_ls.setText(str(self.vals[Tuner.Pid1][PIDValue.Sl]))
        self._widget.t1_ap.setText(str(self.vals[Tuner.Pid1][PIDValue.Pa]))
        self._widget.t1_ai.setText(str(self.vals[Tuner.Pid1][PIDValue.Ia]))
        self._widget.t1_ad.setText(str(self.vals[Tuner.Pid1][PIDValue.Da]))
        self._widget.t1_as.setText(str(self.vals[Tuner.Pid1][PIDValue.Sa]))
        self._widget.t1_srv.setText(str(self.vals[Tuner.Pid1]["service"]))
        self._widget.t1_tpc.setText(str(self.vals[Tuner.Pid1]["topic"]))
        self._widget.t2_lp.setText(str(self.vals[Tuner.Pid2][PIDValue.Pl]))
        self._widget.t2_li.setText(str(self.vals[Tuner.Pid2][PIDValue.Il]))
        self._widget.t2_ld.setText(str(self.vals[Tuner.Pid2][PIDValue.Dl]))
        self._widget.t2_ls.setText(str(self.vals[Tuner.Pid2][PIDValue.Sl]))
        self._widget.t2_ap.setText(str(self.vals[Tuner.Pid2][PIDValue.Pa]))
        self._widget.t2_ai.setText(str(self.vals[Tuner.Pid2][PIDValue.Ia]))
        self._widget.t2_ad.setText(str(self.vals[Tuner.Pid2][PIDValue.Da]))
        self._widget.t2_as.setText(str(self.vals[Tuner.Pid2][PIDValue.Sa]))
        self._widget.t2_srv.setText(str(self.vals[Tuner.Pid2]["service"]))
        self._widget.t2_tpc.setText(str(self.vals[Tuner.Pid2]["topic"]))
        self._widget.t3_m.setText(
            str(self.vals[Tuner.Inertial][InertialValue.Mass]))
        self._widget.t3_b.setText(
            str(self.vals[Tuner.Inertial][InertialValue.Buoyancy]))
        self._widget.t3_ixx.setText(
            str(self.vals[Tuner.Inertial][InertialValue.Ixx]))
        self._widget.t3_iyy.setText(
            str(self.vals[Tuner.Inertial][InertialValue.Iyy]))
        self._widget.t3_izz.setText(
            str(self.vals[Tuner.Inertial][InertialValue.Izz]))
        self._widget.t3_srv.setText(str(self.vals[Tuner.Inertial]["service"]))
        self._widget.t3_tpc.setText(str(self.vals[Tuner.Inertial]["topic"]))

    def on_reload_values(self, tuner):
        if tuner == Tuner.Pid1:
            self.on_topic_name_changed(self._widget.t1_tpc, tuner)
        if tuner == Tuner.Pid2:
            self.on_topic_name_changed(self._widget.t2_tpc, tuner)
        if tuner == Tuner.Inertial:
            self.on_topic_name_changed(self._widget.t3_tpc, tuner)

        if tuner != Tuner.Inertial:
            self.vals[tuner][PIDValue.Pl] = self.cachedVals[tuner][PIDValue.Pl]
            self.vals[tuner][PIDValue.Il] = self.cachedVals[tuner][PIDValue.Il]
            self.vals[tuner][PIDValue.Dl] = self.cachedVals[tuner][PIDValue.Dl]
            self.vals[tuner][PIDValue.Sl] = self.cachedVals[tuner][PIDValue.Sl]
            self.vals[tuner][PIDValue.Pa] = self.cachedVals[tuner][PIDValue.Pa]
            self.vals[tuner][PIDValue.Ia] = self.cachedVals[tuner][PIDValue.Ia]
            self.vals[tuner][PIDValue.Da] = self.cachedVals[tuner][PIDValue.Da]
            self.vals[tuner][PIDValue.Sa] = self.cachedVals[tuner][PIDValue.Sa]
        else:
            self.vals[tuner][InertialValue.Mass] = self.cachedVals[tuner][
                InertialValue.Mass]
            self.vals[tuner][InertialValue.Buoyancy] = self.cachedVals[tuner][
                InertialValue.Buoyancy]
            self.vals[tuner][InertialValue.Iyy] = self.cachedVals[tuner][
                InertialValue.Ixx]
            self.vals[tuner][InertialValue.Ixx] = self.cachedVals[tuner][
                InertialValue.Iyy]
            self.vals[tuner][InertialValue.Izz] = self.cachedVals[tuner][
                InertialValue.Izz]
        self.update_text()

    def on_send_values(self, tuner):
        if tuner == Tuner.Pid1:
            self.on_srv_name_changed(self._widget.t1_srv, tuner)
        if tuner == Tuner.Pid2:
            self.on_srv_name_changed(self._widget.t2_srv, tuner)
        if tuner == Tuner.Inertial:
            self.on_srv_name_changed(self._widget.t3_srv, tuner)

        if tuner != Tuner.Inertial:
            try:
                tunesrv = rospy.ServiceProxy(self.vals[tuner]["service"],
                                             TunePid)
                tp = TunePid()
                tp.l_p = self.vals[tuner][PIDValue.Pl]
                tp.l_i = self.vals[tuner][PIDValue.Il]
                tp.l_d = self.vals[tuner][PIDValue.Dl]
                tp.l_sat = self.vals[tuner][PIDValue.Sl]
                tp.a_p = self.vals[tuner][PIDValue.Pa]
                tp.a_i = self.vals[tuner][PIDValue.Ia]
                tp.a_d = self.vals[tuner][PIDValue.Da]
                tp.a_sat = self.vals[tuner][PIDValue.Sa]
                tunesrv(tp)
            except rospy.ServiceException, e:
                print "Service call failed: %s" % e
        else:
コード例 #17
0
class CarlaControlPlugin(Plugin):
    """
    RQT Plugin to control CARLA
    """
    def __init__(self, context):
        """
        Constructor
        """
        super(CarlaControlPlugin, self).__init__(context)
        self.setObjectName('CARLA Control')

        self._widget = QWidget()

        self._node = CompatibleNode('rqt_carla_control_node')

        package_share_dir = roscomp.get_package_share_directory(
            'rqt_carla_control')
        ui_file = os.path.join(package_share_dir, 'resource',
                               'CarlaControl.ui')

        loadUi(ui_file, self._widget)
        self._widget.setObjectName('CarlaControl')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        self.pause_icon = QIcon(
            QPixmap(os.path.join(package_share_dir, 'resource', 'pause.png')))
        self.play_icon = QIcon(
            QPixmap(os.path.join(package_share_dir, 'resource', 'play.png')))
        self._widget.pushButtonStepOnce.setIcon(
            QIcon(
                QPixmap(
                    os.path.join(package_share_dir, 'resource',
                                 'step_once.png'))))

        self.carla_status = None
        self.carla_status_subscriber = self._node.new_subscription(
            CarlaStatus,
            "/carla/status",
            self.carla_status_changed,
            qos_profile=10)

        self.carla_control_publisher = self._node.new_publisher(
            CarlaControl, "/carla/control", qos_profile=10)

        self._widget.pushButtonPlayPause.setDisabled(True)
        self._widget.pushButtonStepOnce.setDisabled(True)
        self._widget.pushButtonPlayPause.setIcon(self.play_icon)
        self._widget.pushButtonPlayPause.clicked.connect(
            self.toggle_play_pause)
        self._widget.pushButtonStepOnce.clicked.connect(self.step_once)

        context.add_widget(self._widget)

        if roscomp.get_ros_version() == 2:
            spin_thread = threading.Thread(target=self._node.spin, daemon=True)
            spin_thread.start()

    def toggle_play_pause(self):
        """
        toggle play/pause
        """
        if self.carla_status.synchronous_mode:
            if self.carla_status.synchronous_mode_running:
                self.carla_control_publisher.publish(
                    CarlaControl(command=CarlaControl.PAUSE))
            else:
                self.carla_control_publisher.publish(
                    CarlaControl(command=CarlaControl.PLAY))

    def step_once(self):
        """
        execute one step
        """
        self.carla_control_publisher.publish(
            CarlaControl(command=CarlaControl.STEP_ONCE))

    def carla_status_changed(self, status):
        """
        callback whenever carla status changes
        """
        self.carla_status = status
        if status.synchronous_mode:
            self._widget.pushButtonPlayPause.setDisabled(False)
            self._widget.pushButtonStepOnce.setDisabled(False)
            if status.synchronous_mode_running:
                self._widget.pushButtonPlayPause.setIcon(self.pause_icon)
            else:
                self._widget.pushButtonPlayPause.setIcon(self.play_icon)
        else:
            self._widget.pushButtonPlayPause.setDisabled(True)
            self._widget.pushButtonStepOnce.setDisabled(True)

    def shutdown_plugin(self):
        """
        shutdown plugin
        """
        self._node.destroy_subscription(self.carla_control_publisher)
コード例 #18
0
class RosPackGraph(Plugin):

    _deferred_fit_in_view = Signal()

    def __init__(self, context):
        super(RosPackGraph, self).__init__(context)
        self.initialized = False
        self._current_dotcode = None
        self._update_thread = WorkerThread(self._update_thread_run, self._update_finished)
        self._nodes = {}
        self._edges = {}
        self._options = {}
        self._options_serialized = ''

        self.setObjectName('RosPackGraph')

        rospack = rospkg.RosPack()
        rosstack = rospkg.RosStack()

        # factory builds generic dotcode items
        self.dotcode_factory = PydotFactory()
        # self.dotcode_factory = PygraphvizFactory()
        # generator builds rosgraph
        self.dotcode_generator = RosPackageGraphDotcodeGenerator(rospack, rosstack)
        # dot_to_qt transforms into Qt elements using dot layout
        self.dot_to_qt = DotToQtGenerator()

        self._widget = QWidget()
        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_dep'), 'resource', 'RosPackGraph.ui')
        loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView})
        self._widget.setObjectName('RosPackGraphUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))

        self._scene = QGraphicsScene()
        self._scene.setBackgroundBrush(Qt.white)
        self._widget.graphics_view.setScene(self._scene)

        self._widget.depth_combo_box.insertItem(0, self.tr('infinite'), -1)
        self._widget.depth_combo_box.insertItem(1, self.tr('1'), 2)
        self._widget.depth_combo_box.insertItem(2, self.tr('2'), 3)
        self._widget.depth_combo_box.insertItem(3, self.tr('3'), 4)
        self._widget.depth_combo_box.insertItem(4, self.tr('4'), 5)
        self._widget.depth_combo_box.currentIndexChanged.connect(self._refresh_rospackgraph)

        self._widget.directions_combo_box.insertItem(0, self.tr('depends'), 0)
        self._widget.directions_combo_box.insertItem(1, self.tr('depends_on'), 1)
        self._widget.directions_combo_box.insertItem(2, self.tr('both'), 2)
        self._widget.directions_combo_box.currentIndexChanged.connect(self._refresh_rospackgraph)

        self._widget.package_type_combo_box.insertItem(0, self.tr('wet & dry'), 3)
        self._widget.package_type_combo_box.insertItem(1, self.tr('wet only'), 2)
        self._widget.package_type_combo_box.insertItem(2, self.tr('dry only'), 1)
        self._widget.package_type_combo_box.currentIndexChanged.connect(self._refresh_rospackgraph)

        completionmodel = StackageCompletionModel(self._widget.filter_line_edit, rospack, rosstack)
        completer = RepeatedWordCompleter(completionmodel, self)
        completer.setCompletionMode(QCompleter.PopupCompletion)
        completer.setWrapAround(True)

        completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._widget.filter_line_edit.editingFinished.connect(self._refresh_rospackgraph)
        self._widget.filter_line_edit.setCompleter(completer)
        self._widget.filter_line_edit.selectionChanged.connect(self._clear_filter)
        
        self._widget.with_stacks_check_box.clicked.connect(self._refresh_rospackgraph)
        self._widget.mark_check_box.clicked.connect(self._refresh_rospackgraph)
        self._widget.colorize_check_box.clicked.connect(self._refresh_rospackgraph)
        self._widget.hide_transitives_check_box.clicked.connect(self._refresh_rospackgraph)

        self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('view-refresh'))
        self._widget.refresh_graph_push_button.pressed.connect(self._update_rospackgraph)

        self._widget.highlight_connections_check_box.toggled.connect(self._refresh_rospackgraph)
        self._widget.auto_fit_graph_check_box.toggled.connect(self._refresh_rospackgraph)
        self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme('zoom-original'))
        self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)

        self._widget.load_dot_push_button.setIcon(QIcon.fromTheme('document-open'))
        self._widget.load_dot_push_button.pressed.connect(self._load_dot)
        self._widget.save_dot_push_button.setIcon(QIcon.fromTheme('document-save-as'))
        self._widget.save_dot_push_button.pressed.connect(self._save_dot)
        self._widget.save_as_svg_push_button.setIcon(QIcon.fromTheme('document-save-as'))
        self._widget.save_as_svg_push_button.pressed.connect(self._save_svg)
        self._widget.save_as_image_push_button.setIcon(QIcon.fromTheme('image'))
        self._widget.save_as_image_push_button.pressed.connect(self._save_image)

        self._deferred_fit_in_view.connect(self._fit_in_view, Qt.QueuedConnection)
        self._deferred_fit_in_view.emit()

        context.add_widget(self._widget)
        
        # If in either of following case, this turnes True
        # - 1st filtering key is already input by user
        # - filtering key is restored
        self._filtering_started = False

    def shutdown_plugin(self):
        self._update_thread.kill()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('depth_combo_box_index', self._widget.depth_combo_box.currentIndex())
        instance_settings.set_value('directions_combo_box_index', self._widget.directions_combo_box.currentIndex())
        instance_settings.set_value('package_type_combo_box', self._widget.package_type_combo_box.currentIndex())
        instance_settings.set_value('filter_line_edit_text', self._widget.filter_line_edit.text())
        instance_settings.set_value('with_stacks_state', self._widget.with_stacks_check_box.isChecked())
        instance_settings.set_value('hide_transitives_state', self._widget.hide_transitives_check_box.isChecked())
        instance_settings.set_value('mark_state', self._widget.mark_check_box.isChecked())
        instance_settings.set_value('colorize_state', self._widget.colorize_check_box.isChecked())
        instance_settings.set_value('auto_fit_graph_check_box_state', self._widget.auto_fit_graph_check_box.isChecked())
        instance_settings.set_value('highlight_connections_check_box_state', self._widget.highlight_connections_check_box.isChecked())

    def restore_settings(self, plugin_settings, instance_settings):
        _str_filter = instance_settings.value('filter_line_edit_text', '')
        if (_str_filter == None or _str_filter == '') and \
           not self._filtering_started:
            _str_filter = '(Separate pkgs by comma)'
        else:
            self._filtering_started = True
        
        self._widget.depth_combo_box.setCurrentIndex(int(instance_settings.value('depth_combo_box_index', 0)))
        self._widget.directions_combo_box.setCurrentIndex(int(instance_settings.value('directions_combo_box_index', 0)))
        self._widget.package_type_combo_box.setCurrentIndex(int(instance_settings.value('package_type_combo_box', 0)))
        self._widget.filter_line_edit.setText(_str_filter)
        self._widget.with_stacks_check_box.setChecked(instance_settings.value('with_stacks_state', True) in [True, 'true'])
        self._widget.mark_check_box.setChecked(instance_settings.value('mark_state', True) in [True, 'true'])
        self._widget.colorize_check_box.setChecked(instance_settings.value('colorize_state', False) in [True, 'true'])
        self._widget.hide_transitives_check_box.setChecked(instance_settings.value('hide_transitives_state', False) in [True, 'true'])
        self._widget.auto_fit_graph_check_box.setChecked(instance_settings.value('auto_fit_graph_check_box_state', True) in [True, 'true'])
        self._widget.highlight_connections_check_box.setChecked(instance_settings.value('highlight_connections_check_box_state', True) in [True, 'true'])
        self.initialized = True
        self._refresh_rospackgraph()

    def _update_rospackgraph(self):
        # re-enable controls customizing fetched ROS graph
        self._widget.depth_combo_box.setEnabled(True)
        self._widget.directions_combo_box.setEnabled(True)
        self._widget.package_type_combo_box.setEnabled(True)
        self._widget.filter_line_edit.setEnabled(True)
        self._widget.with_stacks_check_box.setEnabled(True)
        self._widget.mark_check_box.setEnabled(True)
        self._widget.colorize_check_box.setEnabled(True)
        self._widget.hide_transitives_check_box.setEnabled(True)

        self._refresh_rospackgraph(force_update=True)

    def _update_options(self):
        self._options['depth'] = self._widget.depth_combo_box.itemData(self._widget.depth_combo_box.currentIndex())
        self._options['directions'] = self._widget.directions_combo_box.itemData(self._widget.directions_combo_box.currentIndex())
        self._options['package_types'] = self._widget.package_type_combo_box.itemData(self._widget.package_type_combo_box.currentIndex())
        self._options['with_stacks'] = self._widget.with_stacks_check_box.isChecked()
        self._options['mark_selected'] = self._widget.mark_check_box.isChecked()
        self._options['hide_transitives'] = self._widget.hide_transitives_check_box.isChecked()
        # TODO: Allow different color themes
        self._options['colortheme'] = True if self._widget.colorize_check_box.isChecked() else None
        self._options['names'] = self._widget.filter_line_edit.text().split(',')
        if self._options['names'] == [u'None']:
            self._options['names'] = []
        self._options['highlight_level'] = 3 if self._widget.highlight_connections_check_box.isChecked() else 1
        self._options['auto_fit'] = self._widget.auto_fit_graph_check_box.isChecked()

    def _refresh_rospackgraph(self, force_update=False):
        if not self.initialized:
            return

        self._update_thread.kill()

        self._update_options()

        # avoid update if options did not change and force_update is not set
        new_options_serialized = pickle.dumps(self._options)
        if new_options_serialized == self._options_serialized and not force_update:
            return
        self._options_serialized = pickle.dumps(self._options)

        self._scene.setBackgroundBrush(Qt.lightGray)

        self._update_thread.start()

    # this runs in a non-gui thread, so don't access widgets here directly
    def _update_thread_run(self):
        self._update_graph(self._generate_dotcode())

    @Slot()
    def _update_finished(self):
        self._scene.setBackgroundBrush(Qt.white)
        self._redraw_graph_scene()

    # this runs in a non-gui thread, so don't access widgets here directly
    def _generate_dotcode(self):
        includes = []
        excludes = []
        for name in self._options['names']:
            if name.strip().startswith('-'):
                excludes.append(name.strip()[1:])
            else:
                includes.append(name.strip())
        # orientation = 'LR'
        descendants = True
        ancestors = True
        if self._options['directions'] == 1:
            descendants = False
        if self._options['directions'] == 0:
            ancestors = False
        return self.dotcode_generator.generate_dotcode(dotcode_factory=self.dotcode_factory,
                                                       selected_names=includes,
                                                       excludes=excludes,
                                                       depth=self._options['depth'],
                                                       with_stacks=self._options['with_stacks'],
                                                       descendants=descendants,
                                                       ancestors=ancestors,
                                                       mark_selected=self._options['mark_selected'],
                                                       colortheme=self._options['colortheme'],
                                                       hide_transitives=self._options['hide_transitives'],
                                                       hide_wet=self._options['package_types'] == 1,
                                                       hide_dry=self._options['package_types'] == 2)

    # this runs in a non-gui thread, so don't access widgets here directly
    def _update_graph(self, dotcode):
        self._current_dotcode = dotcode
        self._nodes, self._edges = self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode, self._options['highlight_level'])

    def _generate_tool_tip(self, url):
        if url is not None and ':' in url:
            item_type, item_path = url.split(':', 1)
            if item_type == 'node':
                tool_tip = 'Node:\n  %s' % (item_path)
                service_names = rosservice.get_service_list(node=item_path)
                if service_names:
                    tool_tip += '\nServices:'
                    for service_name in service_names:
                        try:
                            service_type = rosservice.get_service_type(service_name)
                            tool_tip += '\n  %s [%s]' % (service_name, service_type)
                        except rosservice.ROSServiceIOException as e:
                            tool_tip += '\n  %s' % (e)
                return tool_tip
            elif item_type == 'topic':
                topic_type, topic_name, _ = rostopic.get_topic_type(item_path)
                return 'Topic:\n  %s\nType:\n  %s' % (topic_name, topic_type)
        return url

    def _redraw_graph_scene(self):
        # remove items in order to not garbage nodes which will be continued to be used
        for item in self._scene.items():
            self._scene.removeItem(item)
        self._scene.clear()
        for node_item in self._nodes.values():
            self._scene.addItem(node_item)
        for edge_items in self._edges.values():
            for edge_item in edge_items:
                edge_item.add_to_scene(self._scene)

        self._scene.setSceneRect(self._scene.itemsBoundingRect())
        if self._options['auto_fit']:
            self._fit_in_view()

    def _load_dot(self, file_name=None):
        if file_name is None:
            file_name, _ = QFileDialog.getOpenFileName(self._widget, self.tr('Open graph from file'), None, self.tr('DOT graph (*.dot)'))
            if file_name is None or file_name == '':
                return

        try:
            fh = open(file_name, 'rb')
            dotcode = fh.read()
            fh.close()
        except IOError:
            return

        # disable controls customizing fetched ROS graph
        self._widget.depth_combo_box.setEnabled(False)
        self._widget.directions_combo_box.setEnabled(False)
        self._widget.package_type_combo_box.setEnabled(False)
        self._widget.filter_line_edit.setEnabled(False)
        self._widget.with_stacks_check_box.setEnabled(False)
        self._widget.mark_check_box.setEnabled(False)
        self._widget.colorize_check_box.setEnabled(False)
        self._widget.hide_transitives_check_box.setEnabled(False)

        self._update_graph(dotcode)
        self._redraw_graph_scene()

    @Slot()
    def _fit_in_view(self):
        self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio)

    def _save_dot(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as DOT'), 'rospackgraph.dot', self.tr('DOT graph (*.dot)'))
        if file_name is None or file_name == '':
            return

        handle = QFile(file_name)
        if not handle.open(QIODevice.WriteOnly | QIODevice.Text):
            return

        handle.write(self._current_dotcode)
        handle.close()

    def _save_svg(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as SVG'), 'rospackgraph.svg', self.tr('Scalable Vector Graphic (*.svg)'))
        if file_name is None or file_name == '':
            return

        generator = QSvgGenerator()
        generator.setFileName(file_name)
        generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())

        painter = QPainter(generator)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()

    def _save_image(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as image'), 'rospackgraph.png', self.tr('Image (*.bmp *.jpg *.png *.tiff)'))
        if file_name is None or file_name == '':
            return

        img = QImage((self._scene.sceneRect().size() * 2.0).toSize(), QImage.Format_ARGB32_Premultiplied)
        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()
        img.save(file_name)
    
    def _clear_filter(self):
        if not self._filtering_started:
            self._widget.filter_line_edit.setText('')
            self._filtering_started = True
コード例 #19
0
class QuaternionView(Plugin):
	_draw = Signal()

	def __init__(self, context):
		super(QuaternionView, self).__init__(context)
		# Give QObjects reasonable names
		self.setObjectName('QuaternionView')
		rp = rospkg.RosPack()

		# Process standalone plugin command-line arguments
		#from argparse import ArgumentParser
		#parser = ArgumentParser()
		# Add argument(s) to the parser.
		#parser.add_argument("-q", "--quiet", action="store_true",
		#              dest="quiet",
		#              help="Put plugin in silent mode")
		#args, unknowns = parser.parse_known_args(context.argv())
		#if not args.quiet:
		#    print 'arguments: ', args
		#    print 'unknowns: ', unknowns

		# Create QWidget
		self._widget = QWidget()
		# Get path to UI file which is a sibling of this file
		# in this example the .ui and .py file are in the same folder
		ui_file = os.path.join(rp.get_path('rqt_quaternion_view'), 'resource', 'QuaternionView.ui')
		# Extend the widget with all attributes and children from UI file
		loadUi(ui_file, self._widget)
		# Give QObjects reasonable names
		self._widget.setObjectName('QuaternionView')
		# Show _widget.windowTitle on left-top of each plugin (when
		# it's set in _widget). This is useful when you open multiple
		# plugins at once. Also if you open multiple instances of your
		# plugin at once, these lines add number to make it easy to
		# tell from pane to pane.
		if context.serial_number() > 1:
			self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
		# Add widget to the user interface
		context.add_widget(self._widget)

		self.sub = None
		self.topic_name = ""
		self.topic_type = ""
		self.topic_content = ""
		self.refresh_rate = 5.0
		self.refresh_period = rospy.Duration(0)
		self.time_last = rospy.Time(0)
		self.manual_mode = True
		self.val = Quaternion(0.0,0.0,0.0,1.0)

		self.update_refresh_period()
		self.set_manual_mode(self.manual_mode)

		self.plot_3d_figure = Figure()
		self.plot_3d_figure.patch.set_facecolor('white')
		self.plot_3d_canvas = FigureCanvas(self.plot_3d_figure)
		self.plot_3d_toolbar = NavigationToolbar(self.plot_3d_canvas, self._widget.widget_plot_3d)
		self.plot_3d_ax = self.plot_3d_figure.add_subplot(1,1,1, projection='3d')
		self.set_plot_layout(self._widget.widget_plot_3d, self.plot_3d_toolbar, self.plot_3d_canvas)
		self.plot_3d_ax.set_title(self.topic_name)
		self.plot_3d_ax.set_xlabel('X')
		self.plot_3d_ax.set_ylabel('Y')
		self.plot_3d_ax.set_zlabel('Z')

		self._draw.connect(self.update_display)
		self.update_display()

	def shutdown_plugin(self):
		if self.sub is not None:
			self.sub.unregister()

	def save_settings(self, plugin_settings, instance_settings):
		instance_settings.set_value("topic_name", self.topic_name)
		instance_settings.set_value("topic_type", self.topic_type)
		instance_settings.set_value("topic_content", self.topic_content)
		instance_settings.set_value("refresh_rate", self.refresh_rate)
		instance_settings.set_value("manual_mode", str(self.manual_mode))

	def restore_settings(self, plugin_settings, instance_settings):
		topic_name = instance_settings.value("topic_name")
		topic_type = instance_settings.value("topic_type")
		topic_content = instance_settings.value("topic_content")
		manual_mode = instance_settings.value("manual_mode")
		refresh_rate = instance_settings.value("refresh_rate")

		if (topic_name is not None) and (topic_type is not None) and (topic_content is not None) and (manual_mode is not None) and (refresh_rate is not None):

			self.topic_name = str(topic_name)
			self.topic_type = str(topic_type)
			self.topic_content = str(topic_content)
			self.manual_mode = (str(manual_mode).lower() == "true")
			self.refresh_rate = self.parse_float(refresh_rate, default=5.0)

			self.update_refresh_period()

			self.set_manual_mode(self.manual_mode)

			if (not self.manual_mode) and self.topic_name and self.topic_type and self.topic_content:
				self.sub = rospy.Subscriber(self.topic_name, self.get_topic_class_from_type(self.topic_type), self.sub_callback)

	def trigger_configuration(self):
		self.open_settings_dialog()

	def set_plot_layout(self, widget, toolbar, canvas):
		layout = QVBoxLayout()
		layout.addWidget(toolbar)
		layout.addWidget(canvas)
		widget.setLayout(layout)

	def clear_plot(self):
		# Discards the old graph data
		artists = self.plot_3d_ax.lines + self.plot_3d_ax.collections

		for artist in artists:
			artist.remove()

	def getKey(self,item):
		return item[0]

	def get_topic_class_from_type(self, msg_type):
		connection_header = msg_type.split("/")
		ros_pkg = connection_header[0] + ".msg"
		msg_type = connection_header[1]

		msg_class = getattr(import_module(ros_pkg), msg_type)

		return msg_class

	def get_topic_type(self, name):
		topics = sorted(rospy.get_published_topics(), key=self.getKey)
		topic_names, topic_types = zip(*topics)
		topic_type = topic_types[topic_names.index(name)]

		msg_class = self.get_topic_class_from_type(topic_type)

		return topic_type, msg_class

	def recursive_topic_content(self, msg_in, content):
		attr = None
		subcontent = content.split('/',1)

		if len(subcontent) > 1:
			attr = self.recursive_topic_content(getattr(msg_in, subcontent[0]), subcontent[1])
		else:
			attr = getattr(msg_in, content)

		return attr

	def sub_callback(self, msg_in):
		now = rospy.Time.now()

		if now > self.time_last:
			# Rate-limit refreshes
			if now - self.time_last > self.refresh_period:
				self.val = Quaternion(0.0,0.0,0.0,1.0)

				try:
					new_val = self.recursive_topic_content(msg_in, self.topic_content)
					if type(new_val) is Quaternion:
						val = new_val
					else:
						raise TypeError
				except AttributeError as e:
					rospy.logwarn("AttributeError: " + str(e))
					self.sub.unregister()
				except TypeError as e:
					rospy.logwarn("Unable to display " + str(getattr(msg_in, self.topic_content).__class__.__name__) + " as a quaternion")
					self.sub.unregister()

				self.val = val

				self.time_last = now
				self._draw.emit()
		else:
			# Timestep backwards, reset
			self.time_last = rospy.Time(0)

	def normalize_tf_quaternion(self,q):
		d = math.sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3])

		if d > 0.0:
			q[0] = q[0]/d
			q[1] = q[1]/d
			q[2] = q[2]/d
			q[3] = q[3]/d
		else:
			#Invalid quaternion, reset
			q[0] = 0.0
			q[1] = 0.0
			q[2] = 0.0
			q[3] = 1.0

		return q

	def update_display(self):
		self.clear_plot()

		x = [1.0,0.0,0.0,0.0]
		x2 = [0.8,0.0,0.0,0.0]
		z = [0.8,0.0,0.1,0.0]
		q = self.normalize_tf_quaternion([self.val.x, self.val.y, self.val.z,self.val.w])

		xr = tft.quaternion_multiply(tft.quaternion_multiply(q,x),tft.quaternion_inverse(q))
		x2r = tft.quaternion_multiply(tft.quaternion_multiply(q,x2),tft.quaternion_inverse(q))
		zr = tft.quaternion_multiply(tft.quaternion_multiply(q,z),tft.quaternion_inverse(q))

		self.plot_3d_ax.plot([0.0,0.5], [0.0,0.0], [0.0,0.0], 'r-')
		self.plot_3d_ax.plot([0.0,0.0], [0.0,0.5], [0.0,0.0], 'g-')
		self.plot_3d_ax.plot([0.0,0.0], [0.0,0.0], [0.0,0.5], 'b-')
		self.plot_3d_ax.plot([0.0,xr[0]], [0.0,xr[1]], [0.0,xr[2]], 'k-', linewidth=2)
		self.plot_3d_ax.plot([zr[0],xr[0]], [zr[1],xr[1]], [zr[2],xr[2]], 'k-', linewidth=2)
		self.plot_3d_ax.plot([zr[0],x2r[0]], [zr[1],x2r[1]], [zr[2],x2r[2]], 'k-', linewidth=2)

		self.plot_3d_ax.set_aspect('equal', 'box')
		self.plot_3d_ax.set_xlim(-1.0, 1.0)
		self.plot_3d_ax.set_ylim(-1.0, 1.0)
		self.plot_3d_ax.set_zlim(-1.0, 1.0)

		self.plot_3d_canvas.draw()

		if not self.manual_mode:
			self.update_normalized_displays()

	def parse_float(self, text, default=0.0):
		val = default
		try:
			val = float(text)
		except (ValueError, TypeError):
			pass

		return val

	def manual_update(self, _=None, new_val=None, update_euler=True):
		if type(new_val) is Quaternion:
			self.val = new_val
		else:
			self.val = Quaternion(self.parse_float(self._widget.input_q_x.text()),
								  self.parse_float(self._widget.input_q_y.text()),
								  self.parse_float(self._widget.input_q_z.text()),
								  self.parse_float(self._widget.input_q_w.text()))

		if update_euler:
			q = [self.val.x, self.val.y, self.val.z,self.val.w]
			e = tft.euler_from_quaternion(q, axes='rzyx')

			self._widget.input_e_r.setText('%.5f' % e[0])
			self._widget.input_e_p.setText('%.5f' % e[0])
			self._widget.input_e_y.setText('%.5f' % e[0])
			self._widget.input_e_r_deg.setText('%.5f' % math.degrees(e[2]))
			self._widget.input_e_p_deg.setText('%.5f' % math.degrees(e[1]))
			self._widget.input_e_y_deg.setText('%.5f' % math.degrees(e[0]))

		self._draw.emit()

	def manual_update_rpy(self, _=None, new_val=None, update_euler_degrees=True):
		yaw = 0.0
		pitch = 0.0
		roll = 0.0

		if new_val is None:
			yaw = self.parse_float(self._widget.input_e_y.text())
			pitch = self.parse_float(self._widget.input_e_p.text())
			roll = self.parse_float(self._widget.input_e_r.text())
		else:
			yaw = new_val[0]
			pitch = new_val[1]
			roll = new_val[2]

		q = tft.quaternion_from_euler(yaw, pitch, roll, axes='rzyx')

		self._widget.input_q_w.setText('%.5f' % q[3])
		self._widget.input_q_x.setText('%.5f' % q[0])
		self._widget.input_q_y.setText('%.5f' % q[1])
		self._widget.input_q_z.setText('%.5f' % q[2])

		if update_euler_degrees:
			self._widget.input_e_r_deg.setText('%.5f' % math.degrees(roll))
			self._widget.input_e_p_deg.setText('%.5f' % math.degrees(pitch))
			self._widget.input_e_y_deg.setText('%.5f' % math.degrees(yaw))

		self.manual_update(new_val=Quaternion(q[0], q[1], q[2], q[3]), update_euler=False)

	def manual_update_rpy_deg(self, _=None):
		yaw = math.radians(self.parse_float(self._widget.input_e_y_deg.text()))
		pitch = math.radians(self.parse_float(self._widget.input_e_p_deg.text()))
		roll = math.radians(self.parse_float(self._widget.input_e_r_deg.text()))

		self.manual_update_rpy(new_val=[yaw,pitch,roll], update_euler_degrees=False)

	def update_normalized_displays(self):
		q = self.normalize_tf_quaternion([self.val.x, self.val.y, self.val.z,self.val.w])
		e = tft.euler_from_quaternion(q, axes='szyx')

		self._widget.input_q_w.setText('%.5f' % q[3])
		self._widget.input_q_x.setText('%.5f' % q[0])
		self._widget.input_q_y.setText('%.5f' % q[1])
		self._widget.input_q_z.setText('%.5f' % q[2])
		self._widget.input_e_r.setText('%.5f' % e[2])
		self._widget.input_e_p.setText('%.5f' % e[1])
		self._widget.input_e_y.setText('%.5f' % e[0])
		self._widget.input_e_r_deg.setText('%.5f' % math.degrees(e[2]))
		self._widget.input_e_p_deg.setText('%.5f' % math.degrees(e[1]))
		self._widget.input_e_y_deg.setText('%.5f' % math.degrees(e[0]))

	def set_manual_mode(self, manual):
		if manual:
			print("Manual mode")
			if self.sub is not None:
				self.sub.unregister()

			self._widget.input_q_w.textEdited.connect(self.manual_update)
			self._widget.input_q_x.textEdited.connect(self.manual_update)
			self._widget.input_q_y.textEdited.connect(self.manual_update)
			self._widget.input_q_z.textEdited.connect(self.manual_update)
			self._widget.input_e_r.textEdited.connect(self.manual_update_rpy)
			self._widget.input_e_p.textEdited.connect(self.manual_update_rpy)
			self._widget.input_e_y.textEdited.connect(self.manual_update_rpy)
			self._widget.input_e_r_deg.textEdited.connect(self.manual_update_rpy_deg)
			self._widget.input_e_p_deg.textEdited.connect(self.manual_update_rpy_deg)
			self._widget.input_e_y_deg.textEdited.connect(self.manual_update_rpy_deg)

			self._widget.input_q_w.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_q_x.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_q_y.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_q_z.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_e_r.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_e_p.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_e_y.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_e_r_deg.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_e_p_deg.returnPressed.connect(self.update_normalized_displays)
			self._widget.input_e_y_deg.returnPressed.connect(self.update_normalized_displays)
		else:
			print("Subscriber mode")
			try:
				self._widget.input_q_w.textEdited.disconnect()
				self._widget.input_q_x.textEdited.disconnect()
				self._widget.input_q_y.textEdited.disconnect()
				self._widget.input_q_z.textEdited.disconnect()
				self._widget.input_e_r.textEdited.disconnect()
				self._widget.input_e_p.textEdited.disconnect()
				self._widget.input_e_y.textEdited.disconnect()
				self._widget.input_e_r_deg.textEdited.disconnect()
				self._widget.input_e_p_deg.textEdited.disconnect()
				self._widget.input_e_y_deg.textEdited.disconnect()

				self._widget.input_q_w.returnPressed.disconnect()
				self._widget.input_q_x.returnPressed.disconnect()
				self._widget.input_q_y.returnPressed.disconnect()
				self._widget.input_q_z.returnPressed.disconnect()
				self._widget.input_e_r.returnPressed.disconnect()
				self._widget.input_e_p.returnPressed.disconnect()
				self._widget.input_e_y.returnPressed.disconnect()
				self._widget.input_e_r_deg.returnPressed.disconnect()
				self._widget.input_e_p_deg.returnPressed.disconnect()
				self._widget.input_e_y_deg.returnPressed.disconnect()
			except TypeError:
				pass

	def update_refresh_period(self):
		self.refresh_period = rospy.Duration( 1.0 / self.refresh_rate )

	def open_settings_dialog(self):
		"""Present the user with a dialog for choosing the topic to view,
		the data type, and other settings used to generate the HUD.
		This displays a SimpleSettingsDialog asking the user to choose
		the settings as desired.

		This method is blocking"""

		dialog = SimpleSettingsDialog(title='Quaternion View Options')
		dialog.add_topic_list("topic_list", str(self.topic_name), "Topics")
		dialog.add_combobox_empty("content_list", "Contents", str(self.topic_content))
		dialog.add_lineedit("refresh_rate", str(self.refresh_rate), "Refresh Rate")
		dialog.add_checkbox("manual_mode", bool(self.manual_mode), "Manual Mode")

		settings = dialog.get_settings();
		if settings is not None:
			for s in settings:
				if s[0] == "topic_list":
					self.topic_name = str(s[1])
				elif s[0] == "content_list":
					self.topic_content = str(s[1])
				elif s[0] == "refresh_rate":
					self.refresh_rate = self.parse_float(s[1])
				elif s[0] == "manual_mode":
					self.manual_mode = bool(s[1])

			self.update_refresh_period()

			if self.manual_mode:
				self.set_manual_mode(True)
			elif self.topic_name and self.topic_content:
				self.topic_type, msg_class = self.get_topic_type(self.topic_name)
				self.sub = rospy.Subscriber(self.topic_name, msg_class, self.sub_callback)

		self._draw.emit()
コード例 #20
0
class ControllerManager(Plugin):
    """
    Graphical frontend for managing ros_control controllers.
    """
    _cm_update_freq = 1  # Hz

    def __init__(self, context):
        super(ControllerManager, self).__init__(context)
        self.setObjectName('ControllerManager')

        # Create QWidget and extend it with all the attributes and children
        # from the UI file
        self._widget = QWidget()
        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_controller_manager'),
                               'resource',
                               'controller_manager.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('ControllerManagerUi')

        # Pop-up that displays controller information
        self._popup_widget = QWidget()
        ui_file = os.path.join(rp.get_path('rqt_controller_manager'),
                               'resource',
                               'controller_info.ui')
        loadUi(ui_file, self._popup_widget)
        self._popup_widget.setObjectName('ControllerInfoUi')

        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        # Initialize members
        self._cm_ns = []  # Namespace of the selected controller manager
        self._controllers = []  # State of each controller
        self._table_model = None
        self._controller_lister = None

        # Controller manager service proxies
        self._load_srv = None
        self._unload_srv = None
        self._switch_srv = None

        # Controller state icons
        rospack = rospkg.RosPack()
        path = rospack.get_path('rqt_controller_manager')
        self._icons = {'running': QIcon(path + '/resource/led_green.png'),
                       'stopped': QIcon(path + '/resource/led_red.png'),
                       'uninitialized': QIcon(path + '/resource/led_off.png')}

        # Controllers display
        table_view = self._widget.table_view
        table_view.setContextMenuPolicy(Qt.CustomContextMenu)
        table_view.customContextMenuRequested.connect(self._on_ctrl_menu)

        table_view.doubleClicked.connect(self._on_ctrl_info)

        header = table_view.horizontalHeader()
        header.setSectionResizeMode(QHeaderView.ResizeToContents)
        header.setContextMenuPolicy(Qt.CustomContextMenu)
        header.customContextMenuRequested.connect(self._on_header_menu)

        # Timer for controller manager updates
        self._list_cm = ControllerManagerLister()
        self._update_cm_list_timer = QTimer(self)
        self._update_cm_list_timer.setInterval(1000.0 /
                                               self._cm_update_freq)
        self._update_cm_list_timer.timeout.connect(self._update_cm_list)
        self._update_cm_list_timer.start()

        # Timer for running controller updates
        self._update_ctrl_list_timer = QTimer(self)
        self._update_ctrl_list_timer.setInterval(1000.0 /
                                                 self._cm_update_freq)
        self._update_ctrl_list_timer.timeout.connect(self._update_controllers)
        self._update_ctrl_list_timer.start()

        # Signal connections
        w = self._widget
        w.cm_combo.currentIndexChanged[str].connect(self._on_cm_change)

    def shutdown_plugin(self):
        self._update_cm_list_timer.stop()
        self._update_ctrl_list_timer.stop()
        self._popup_widget.hide()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('cm_ns', self._cm_ns)

    def restore_settings(self, plugin_settings, instance_settings):
        # Restore last session's controller_manager, if present
        self._update_cm_list()
        cm_ns = instance_settings.value('cm_ns')
        cm_combo = self._widget.cm_combo
        cm_list = [cm_combo.itemText(i) for i in range(cm_combo.count())]
        try:
            idx = cm_list.index(cm_ns)
            cm_combo.setCurrentIndex(idx)
        except (ValueError):
            pass

    # def trigger_configuration(self):
        # Comment in to signal that the plugin has a way to configure
        # This will enable a setting button (gear icon) in each dock widget
        # title bar
        # Usually used to open a modal configuration dialog

    def _update_cm_list(self):
        update_combo(self._widget.cm_combo, self._list_cm())

    def _on_cm_change(self, cm_ns):
        self._cm_ns = cm_ns

        # Setup services for communicating with the selected controller manager
        self._set_cm_services(cm_ns)

        # Controller lister for the selected controller manager
        if cm_ns:
            self._controller_lister = ControllerLister(cm_ns)
            self._update_controllers()
        else:
            self._controller_lister = None

    def _set_cm_services(self, cm_ns):
        if cm_ns:
            # NOTE: Persistent services are used for performance reasons.
            # If the controller manager dies, we detect it and disconnect from
            # it anyway
            load_srv_name = _append_ns(cm_ns, 'load_controller')
            self._load_srv = rospy.ServiceProxy(load_srv_name,
                                                LoadController,
                                                persistent=True)
            unload_srv_name = _append_ns(cm_ns, 'unload_controller')
            self._unload_srv = rospy.ServiceProxy(unload_srv_name,
                                                  UnloadController,
                                                  persistent=True)
            switch_srv_name = _append_ns(cm_ns, 'switch_controller')
            self._switch_srv = rospy.ServiceProxy(switch_srv_name,
                                                  SwitchController,
                                                  persistent=True)
        else:
            self._load_srv = None
            self._unload_srv = None
            self._switch_srv = None

    def _update_controllers(self):
        # Find controllers associated to the selected controller manager
        controllers = self._list_controllers()

        # Update controller display, if necessary
        if self._controllers != controllers:
            self._controllers = controllers
            self._show_controllers()  # NOTE: Model is recomputed from scratch

    def _list_controllers(self):
        """
        @return List of controllers associated to a controller manager
        namespace. Contains both stopped/running controllers, as returned by
        the C{list_controllers} service, plus uninitialized controllers with
        configurations loaded in the parameter server.
        @rtype [str]
        """
        if not self._cm_ns:
            return []

        # Add loaded controllers first
        controllers = self._controller_lister()

        # Append potential controller configs found in the parameter server
        all_ctrls_ns = _resolve_controllers_ns(self._cm_ns)
        for name in get_rosparam_controller_names(all_ctrls_ns):
            add_ctrl = not any(name == ctrl.name for ctrl in controllers)
            if add_ctrl:
                type_str = _rosparam_controller_type(all_ctrls_ns, name)
                uninit_ctrl = ControllerState(name=name,
                                              type=type_str,
                                              state='uninitialized')
                controllers.append(uninit_ctrl)
        return controllers

    def _show_controllers(self):
        table_view = self._widget.table_view
        self._table_model = ControllerTable(self._controllers, self._icons)
        table_view.setModel(self._table_model)

    def _on_ctrl_menu(self, pos):
        # Get data of selected controller
        row = self._widget.table_view.rowAt(pos.y())
        if row < 0:
            return  # Cursor is not under a valid item

        ctrl = self._controllers[row]

        # Show context menu
        menu = QMenu(self._widget.table_view)
        if ctrl.state == 'running':
            action_stop = menu.addAction(self._icons['stopped'], 'Stop')
            action_kill = menu.addAction(self._icons['uninitialized'],
                                         'Stop and Unload')
        elif ctrl.state == 'stopped':
            action_start = menu.addAction(self._icons['running'], 'Start')
            action_unload = menu.addAction(self._icons['uninitialized'],
                                           'Unload')
        elif ctrl.state == 'uninitialized':
            action_load = menu.addAction(self._icons['stopped'], 'Load')
            action_spawn = menu.addAction(self._icons['running'],
                                          'Load and Start')

        action = menu.exec_(self._widget.table_view.mapToGlobal(pos))

        # Evaluate user action
        if ctrl.state == 'running':
            if action is action_stop:
                self._stop_controller(ctrl.name)
            elif action is action_kill:
                self._stop_controller(ctrl.name)
                self._unload_controller(ctrl.name)
        elif ctrl.state == 'stopped':
            if action is action_start:
                self._start_controller(ctrl.name)
            elif action is action_unload:
                self._unload_controller(ctrl.name)
        elif ctrl.state == 'uninitialized':
            if action is action_load:
                self._load_controller(ctrl.name)
            if action is action_spawn:
                self._load_controller(ctrl.name)
                self._start_controller(ctrl.name)

    def _on_ctrl_info(self, index):
        popup = self._popup_widget

        ctrl = self._controllers[index.row()]
        popup.ctrl_name.setText(ctrl.name)
        popup.ctrl_type.setText(ctrl.type)

        res_model = QStandardItemModel()
        model_root = QStandardItem('Claimed Resources')
        res_model.appendRow(model_root)
        for hw_res in ctrl.claimed_resources:
            hw_iface_item = QStandardItem(hw_res.hardware_interface)
            model_root.appendRow(hw_iface_item)
            for res in hw_res.resources:
                res_item = QStandardItem(res)
                hw_iface_item.appendRow(res_item)

        popup.resource_tree.setModel(res_model)
        popup.resource_tree.setItemDelegate(FontDelegate(popup.resource_tree))
        popup.resource_tree.expandAll()
        popup.move(QCursor.pos())
        popup.show()

    def _on_header_menu(self, pos):
        header = self._widget.table_view.horizontalHeader()

        # Show context menu
        menu = QMenu(self._widget.table_view)
        action_toggle_auto_resize = menu.addAction('Toggle Auto-Resize')
        action = menu.exec_(header.mapToGlobal(pos))

        # Evaluate user action
        if action is action_toggle_auto_resize:
            if header.resizeMode(0) == QHeaderView.ResizeToContents:
                header.setSectionResizeMode(QHeaderView.Interactive)
            else:
                header.setSectionResizeMode(QHeaderView.ResizeToContents)

    def _load_controller(self, name):
        self._load_srv.call(LoadControllerRequest(name=name))

    def _unload_controller(self, name):
        self._unload_srv.call(UnloadControllerRequest(name=name))

    def _start_controller(self, name):
        strict = SwitchControllerRequest.STRICT
        req = SwitchControllerRequest(start_controllers=[name],
                                      stop_controllers=[],
                                      strictness=strict)
        self._switch_srv.call(req)

    def _stop_controller(self, name):
        strict = SwitchControllerRequest.STRICT
        req = SwitchControllerRequest(start_controllers=[],
                                      stop_controllers=[name],
                                      strictness=strict)
        self._switch_srv.call(req)
コード例 #21
0
ファイル: rqt_smach.py プロジェクト: utahrobotics/AMEE
class Smach(Plugin):
    update_graph_sig = Signal(str)

    def __init__(self, context):
        super(Smach, self).__init__(context)

        self.initialized = 0

        self._dotcode_sub = None
        self._topic_dict = {}
        self._update_thread = WorkerThread(self._update_thread_run,
                                           self._update_finished)

        # Give QObjects reasonable names
        self.setObjectName('Smach')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()

        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()

        # Get path to UI file which is a sibling of this file
        # in this example the .ui and .py file are in the same folder
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                               'rqt_smach.ui')

        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)

        # Give QObjects reasonable names
        self._widget.ns_refresh_button.setIcon(QIcon.fromTheme('view-refresh'))
        self._widget.setObjectName('SmachPluginUi')

        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        # Add widget to the user interface
        context.add_widget(self._widget)
        palette = QPalette()
        palette.setColor(QPalette.Background, Qt.white)
        self._widget.setPalette(palette)

        #Connect widgets with corresponding methods
        self._widget.namespace_input.currentIndexChanged.connect(
            self._handle_ns_changed)
        self._widget.ns_refresh_button.clicked.connect(self.refresh_combo_box)
        self._widget.restrict_ns.clicked.connect(self.refresh_combo_box)
        self._widget.ud_path_input.currentIndexChanged.connect(
            self._handle_ud_path)
        self._widget.ud_set_initial.clicked.connect(self._handle_ud_set_path)
        self._widget.ud_text_browser.setReadOnly(1)
        self._widget.show_implicit_button.clicked.connect(
            self._handle_show_implicit)
        self._widget.help_button.setIcon(QIcon.fromTheme('help-contents'))
        self._widget.help_button.clicked.connect(self._handle_help)
        self._widget.tree.clicked.connect(self._handle_tree_clicked)

        #Depth and width spinners:
        self._widget.depth_input.setRange(-1, 1337)
        self._widget.depth_input.setValue(-1)
        self._widget.depth_input.valueChanged.connect(self._set_depth)

        self._widget.label_width_input.setRange(1, 1337)
        self._widget.label_width_input.setValue(40)
        self._widget.label_width_input.valueChanged.connect(self._set_width)

        self._widget.tree.setColumnCount(1)
        self._widget.tree.setHeaderLabels(["Containers"])
        self._widget.tree.show()

        self._ns = ""
        self.refresh_combo_box()

        # Bind path list
        self._widget.path_input.currentIndexChanged.connect(
            self._handle_path_changed)

        #Keep Combo Boxes sorted
        self._widget.namespace_input.setInsertPolicy(6)
        self._widget.path_input.setInsertPolicy(6)
        self._widget.ud_path_input.setInsertPolicy(6)

        #Set up mouse actions for xdot widget
        self._widget.xdot_widget.register_select_callback(self.select_cb)

        # Create graph data structures
        # Containers is a map of (container path) -> container proxy
        self._containers = {}
        self._top_containers = {}

        # smach introspection client
        self._client = smach_ros.IntrospectionClient()
        self._selected_paths = []

        # Message subscribers
        self._structure_subs = {}
        self._status_subs = {}

        # Initialize xdot display state
        self._path = '/'
        self._needs_zoom = True
        self._structure_changed = True
        self._max_depth = -1
        self._show_all_transitions = False
        self._label_wrapper = textwrap.TextWrapper(40, break_long_words=True)
        self._graph_needs_refresh = True
        self._tree_needs_refresh = True

        self._keep_running = True

        self._update_server_list()
        self._update_graph()
        self._update_tree()

        # Start a timer to update the server list
        self._server_timer = QTimer(self)
        self._server_timer.timeout.connect(self._update_server_list)
        self._server_timer.start(1000)

        # Start a timer to update the graph display
        self._graph_timer = QTimer(self)
        self._graph_timer.timeout.connect(self._update_graph)
        self._graph_timer.start(1093)

        # Start a timer to update the._widget.tree display
        self._tree_timer = QTimer(self)
        self._tree_timer.timeout.connect(self._update_tree)
        self._tree_timer.start(1217)

    def _handle_tree_clicked(self):
        selected = self._widget.tree.selectedItems()[0]
        path = "/" + str(selected.text(0))
        parent = selected.parent()
        while parent:
            path = "/" + str(parent.text(0)) + path
            parent = parent.parent()
        self._widget.ud_path_input.setCurrentIndex(
            self._widget.ud_path_input.findText(path))

    def _handle_help(self):
        """Event: Help button pressed"""
        helpMsg = QMessageBox()
        helpMsg.setWindowTitle("Keyboard Controls")
        helpMsg.setIcon(QMessageBox.Information)
        helpMsg.setText(
            "Pan: Arrow Keys\nZoom: PageUp / PageDown\nZoom To Fit: F\nRefresh: R"
        )
        ret = helpMsg.exec_()

    def select_cb(self, event):
        """Event: Click to select a graph node to display user data and update the graph."""
        # Only set string status
        x = event.x()
        y = event.y()
        url = self._widget.xdot_widget.get_url(x, y)

        if url is None:
            return

        item = self._widget.xdot_widget.items_by_url.get(url.url, None)

        if item:
            self._widget.status_bar.showMessage(item.url)
            # Left button-up

            if item.url not in self._containers:
                if ':' in item.url:
                    container_url = '/'.join(item.url.split(':')[:-1])
                else:
                    container_url = '/'.join(item.url.split('/')[:-1])
            else:
                container_url = item.url

            if event.button() == Qt.LeftButton:
                self._selected_paths = [item.url]
                self._widget.ud_path_input.setCurrentIndex(
                    self._widget.ud_path_input.findText(item.url))

        self._graph_needs_refresh = True

    def _handle_show_implicit(self):
        """Event: Show Implicit button checked"""
        self._show_all_transitions = not self._show_all_transitions
        self._graph_needs_refresh = True
        self._structure_changed = True
        if self._widget.zoom_checkbox.isChecked():
            self._needs_zoom = True

    def _set_depth(self):
        """Event: Depth value changed"""
        self._max_depth = self._widget.depth_input.value()
        self._graph_needs_refresh = True
        self._structure_changed = True
        if self._widget.zoom_checkbox.isChecked():
            self._needs_zoom = True

    def _set_width(self):
        """Event: Label Width value changed"""
        self._label_wrapper.width = self._widget.label_width_input.value()
        self._graph_needs_refresh = True

    def _handle_ud_set_path(self):
        """Event: Set as Initial State button pressed"""
        state_path = self._widget.ud_path_input.currentText()
        parent_path = get_parent_path(state_path)
        if len(parent_path) > 0:
            state = get_label(state_path)
            server_name = self._containers[parent_path]._server_name
            self._client.set_initial_state(server_name,
                                           parent_path, [state],
                                           timeout=rospy.Duration(60.0))
            self._structure_changed = True
            self._graph_needs_refresh = True
            if self._widget.zoom_checkbox.isChecked():
                self._needs_zoom = True

    def _handle_ud_path(self):
        """Event: User Data selection combo box changed"""
        path = self._widget.ud_path_input.currentText()
        #Check the path is non-zero length
        if len(path) > 0:
            # Get the container corresponding to this path, since userdata is
            # stored in the containers
            if path not in self._containers:
                parent_path = get_parent_path(path)
            else:
                parent_path = path

            if parent_path not in self._containers:
                parent_path = get_parent_path(parent_path)

            if parent_path in self._containers:
                # Get the container
                container = self._containers[parent_path]

                # Generate the userdata string
                ud_str = ''
                for (k, v) in container._local_data._data.iteritems():
                    ud_str += str(k) + ": "
                    vstr = str(v)
                    # Add a line break if this is a multiline value
                    if vstr.find('\n') != -1:
                        ud_str += '\n'
                    ud_str += vstr + '\n\n'
                #Display the user data
                self._widget.ud_text_browser.setPlainText(ud_str)
                self._widget.ud_text_browser.show()

    def _update_server_list(self):
        """Update the list of known SMACH introspection servers."""
        # Discover new servers
        if self._widget.restrict_ns.isChecked():
            server_names = [self._widget.namespace_input.currentText()[0:-1]]
            #self._status_subs = {}
        else:
            server_names = self._client.get_servers()

        new_server_names = [
            sn for sn in server_names if sn not in self._status_subs
        ]

        # Create subscribers for newly discovered servers
        for server_name in new_server_names:

            # Create a subscriber for the plan structure (topology) published by this server
            self._structure_subs[server_name] = rospy.Subscriber(
                server_name + smach_ros.introspection.STRUCTURE_TOPIC,
                SmachContainerStructure,
                callback=self._structure_msg_update,
                callback_args=server_name,
                queue_size=50)

            # Create a subscriber for the active states in the plan published by this server
            self._status_subs[server_name] = rospy.Subscriber(
                server_name + smach_ros.introspection.STATUS_TOPIC,
                SmachContainerStatus,
                callback=self._status_msg_update,
                queue_size=50)

    def _structure_msg_update(self, msg, server_name):
        """Update the structure of the SMACH plan (re-generate the dotcode)."""

        # Just return if we're shutting down
        if not self._keep_running:
            return

        # Get the node path
        path = msg.path
        pathsplit = path.split('/')
        parent_path = '/'.join(pathsplit[0:-1])

        rospy.logdebug("RECEIVED: " + path)
        rospy.logdebug("CONTAINERS: " + str(self._containers.keys()))

        # Initialize redraw flag
        needs_redraw = False

        # Determine if we need to update one of the proxies or create a new one
        if path in self._containers:
            rospy.logdebug("UPDATING: " + path)

            # Update the structure of this known container
            needs_redraw = self._containers[path].update_structure(msg)
        else:
            rospy.logdebug("CONSTRUCTING: " + path)

            # Create a new container
            container = ContainerNode(server_name, msg)
            self._containers[path] = container

            #Add items to ud path combo box
            if self._widget.ud_path_input.findText(path) == -1:
                self._widget.ud_path_input.addItem(path)

            for item in container._children:
                if self._widget.ud_path_input.findText(path + "/" +
                                                       item) == -1:
                    self._widget.ud_path_input.addItem(path + "/" + item)

            # Store this as a top container if it has no parent
            if parent_path == '':
                self._top_containers[path] = container

            # Append the path to the appropriate widgets
            self._append_new_path(path)

            # We need to redraw the graph if this container's parent is being viewed
            if path.find(self._widget.path_input.currentText()) == 0:
                needs_redraw = True

        if needs_redraw:
            self._structure_changed = True
            if self._widget.zoom_checkbox.isChecked():
                self._needs_zoom = True
            self._tree_needs_refresh = True
            self._graph_needs_refresh = True

    def _status_msg_update(self, msg):
        """Process status messages."""

        # Check if we're in the process of shutting down
        if not self._keep_running:
            return

        # Get the path to the updating conainer
        path = msg.path
        rospy.logdebug("STATUS MSG: " + path)

        # Check if this is a known container
        if path in self._containers:
            # Get the container and check if the status update requires regeneration
            container = self._containers[path]
            if container.update_status(msg):
                self._graph_needs_refresh = True
                self._tree_needs_refresh = True

    def _append_new_path(self, path):
        """Append a new path to the path selection widgets"""
        if ((not self._widget.restrict_ns.isChecked())
                or ((self._widget.restrict_ns.isChecked()) and
                    (self._widget.namespace_input.currentText() in path))
                or (path == self._widget.namespace_input.currentText()[0:-1])):
            self._widget.path_input.addItem(path)

    def _update_graph(self):
        """This thread continuously updates the graph when it changes.

        The graph gets updated in one of two ways:

          1: The structure of the SMACH plans has changed, or the display
          settings have been changed. In this case, the dotcode needs to be
          regenerated.

          2: The status of the SMACH plans has changed. In this case, we only
          need to change the styles of the graph.
        """
        if self._keep_running and self._graph_needs_refresh and not rospy.is_shutdown(
        ):
            # Get the containers to update
            containers_to_update = {}
            # Check if the path that's currently being viewed is in the
            # list of existing containers
            if self._path in self._containers:
                # Some non-root path
                containers_to_update = {
                    self._path: self._containers[self._path]
                }
            elif self._path == '/':
                # Root path
                containers_to_update = self._top_containers

            # Check if we need to re-generate the dotcode (if the structure changed)
            # TODO: needs_zoom is a misnomer
            if self._structure_changed or self._needs_zoom:
                dotstr = "digraph {\n\t"
                dotstr += ';'.join([
                    "compound=true",
                    "outputmode=nodesfirst",
                    "labeljust=l",
                    "nodesep=0.5",
                    "minlen=2",
                    "mclimit=5",
                    "clusterrank=local",
                    "ranksep=0.75",
                    # "remincross=true",
                    # "rank=sink",
                    "ordering=\"\"",
                ])
                dotstr += ";\n"

                # Generate the rest of the graph
                # TODO: Only re-generate dotcode for containers that have changed
                for path, container in containers_to_update.items():
                    dotstr += container.get_dotcode(self._selected_paths, [],
                                                    0, self._max_depth,
                                                    self._containers,
                                                    self._show_all_transitions,
                                                    self._label_wrapper)

                # The given path isn't available
                if len(containers_to_update) == 0:
                    dotstr += '"__empty__" [label="Path not available.", shape="plaintext"]'

                dotstr += '\n}\n'

                # Set the dotcode to the new dotcode, reset the flags
                self.set_dotcode(dotstr, zoom=False)
                self._structure_changed = False
                self._graph_needs_refresh = False

            # Update the styles for the graph if there are any updates
            for path, container in containers_to_update.items():
                container.set_styles(self._selected_paths, 0, self._max_depth,
                                     self._widget.xdot_widget.items_by_url,
                                     self._widget.xdot_widget.subgraph_shapes,
                                     self._containers)
            self._widget.xdot_widget.repaint()

    def set_dotcode(self, dotcode, zoom=True):
        """Set the xdot view's dotcode and refresh the display."""
        # Set the new dotcode
        if self._widget.xdot_widget.set_dotcode(dotcode, False):
            # Re-zoom if necessary
            if zoom or self._needs_zoom:
                self._widget.xdot_widget.zoom_to_fit()
                self._needs_zoom = False

    def _update_tree(self):
        """Update the tree view."""
        if self._keep_running and self._tree_needs_refresh and not rospy.is_shutdown(
        ):
            self._tree_nodes = {}
            self._widget.tree.clear()
            for path, tc in self._top_containers.iteritems():
                if ((not self._widget.restrict_ns.isChecked()) or
                    ((self._widget.restrict_ns.isChecked()) and
                     (self._widget.namespace_input.currentText() in path)) or
                    (path
                     == self._widget.namespace_input.currentText()[0:-1])):
                    self.add_to_tree(path, None)
            self._tree_needs_refresh = False
            self._widget.tree.sortItems(0, 0)

    def add_to_tree(self, path, parent):
        """Add a path to the tree view."""
        if parent is None:
            container = QTreeWidgetItem()
            container.setText(0, self._containers[path]._label)
            self._widget.tree.addTopLevelItem(container)
        else:
            container = QTreeWidgetItem(parent)
            container.setText(0, self._containers[path]._label)

        # Add children to_tree
        for label in self._containers[path]._children:
            child_path = '/'.join([path, label])
            if child_path in self._containers.keys():
                self.add_to_tree(child_path, container)
            else:
                child = QTreeWidgetItem(container)
                child.setText(0, label)

    def append_tree(self, container, parent=None):
        """Append an item to the tree view."""
        if not parent:
            node = QTreeWidgetItem()
            node.setText(0, container._label)
            self._widget.tree.addTopLevelItem(node)
            for child_label in container._children:
                child = QTreeWidgetItem(node)
                child.setText(0, child_label)

    def _update_thread_run(self):
        """Update the list of namespaces."""
        _, _, topic_types = rospy.get_master().getTopicTypes()
        self._topic_dict = dict(topic_types)
        keys = list(self._topic_dict.keys())
        namespaces = list()
        for i in keys:
            print i
            if i.endswith("smach/container_status"):
                namespaces.append(i[0:i.index("smach/container_status")])
        self._widget.namespace_input.setItems.emit(namespaces)

    @Slot()
    def _update_finished(self):
        """Enable namespace combo box."""
        self._widget.namespace_input.setEnabled(True)

    def _handle_ns_changed(self):
        """If namespace selection is changed then reinitialize everything."""
        ns = self._widget.namespace_input.currentText()
        if len(ns) > 0:
            if self._ns != ns:
                self._actions_connected = False
                self._ns = ns
                self.enable_widgets(False)
                rospy.loginfo("Creating action clients on namespace '%s'..." %
                              ns)
                rospy.loginfo("Action clients created.")
                self._actions_connected = True
                self.enable_widgets(True)

                self._containers = {}
                self._top_containers = {}
                self._selected_paths = []

                self._structure_subs = {}
                self._status_subs = {}

                self._needs_zoom = True
                self._structure_changed = True
                self._max_depth = -1
                self._show_all_transitions = False
                self._graph_needs_refresh = True
                self._tree_needs_refresh = True
                #self._widget.namespace_input.clear()
                self._widget.path_input.clear()
                self._widget.ud_path_input.clear()
                self._widget.tree.clear()

    @Slot()
    def refresh_combo_box(self):
        """Refresh namespace combo box."""
        self._update_thread.kill()
        self._containers = {}
        self._top_containers = {}
        self._selected_paths = []

        self._structure_subs = {}
        self._status_subs = {}

        self._structure_changed = True
        self._tree_needs_refresh = True
        #self._graph_needs_refresh = True

        #self._widget.namespace_input.clear()
        self._widget.path_input.clear()
        self._widget.ud_path_input.clear()
        self._widget.tree.clear()
        if self._widget.restrict_ns.isChecked():
            self._widget.namespace_input.setEnabled(False)
            self._widget.namespace_input.setEditText('updating...')
            self._widget.ns_refresh_button.setEnabled(True)
            self._update_thread.start()
        else:
            self._widget.namespace_input.setEnabled(False)
            self._widget.namespace_input.setEditText('Unrestricted')
            self._widget.ns_refresh_button.setEnabled(False)
            self._graph_needs_refresh = True
            self._tree_needs_refresh = True
            self._widget.path_input.addItem('/')

    def _handle_path_changed(self, checked):
        """If the path input is changed, update graph."""
        self._path = self._widget.path_input.currentText()
        self._graph_needs_refresh = True
        self._structure_changed = True
        if self._widget.zoom_checkbox.isChecked():
            self._needs_zoom = True

    def enable_widgets(self, enable):
        """Enable all widgets."""
        self._widget.xdot_widget.setEnabled(enable)
        self._widget.path_input.setEnabled(enable)
        self._widget.depth_input.setEnabled(enable)
        self._widget.label_width_input.setEnabled(enable)
        self._widget.ud_path_input.setEnabled(enable)
        self._widget.ud_text_browser.setEnabled(enable)
コード例 #22
0
class RqtInjerrobot(Plugin):
    def __init__(self, context):
        super(RqtInjerrobot, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('RqtInjerrobot')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_injerrobot'),
                               'resource', 'RqtInjerrobot.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('RqtInjerrobotUi')

        # add signals/slots
        self._widget.selectYamlPushButton.pressed.connect(
            self.press_select_yaml)
        self._widget.loadYamlPushButton.pressed.connect(self.press_load_yaml)
        self._widget.saveYamlPushButton.pressed.connect(self.press_save_yaml)

        self._widget.armBox.currentIndexChanged.connect(self.arm_selected)
        self._widget.armBox.highlighted.connect(self.arm_activated)

        self._widget.editStepPushButton.pressed.connect(self.press_edit_step)

        self._widget.actionNSEdit.editingFinished.connect(self.edited_actionns)
        self._widget.groupNameEdit.editingFinished.connect(
            self.edited_groupname)
        self._widget.baseLinkEdit.editingFinished.connect(self.edited_baselink)
        self._widget.endEffectorEdit.editingFinished.connect(
            self.edited_endeffector)

        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        self._yaml_file = ""
        self._params = dict()
        self._name = "RqtInjerrobot"

        self._keys_not_steps = [
            'arm_ip', 'arm_port', 'joint_names', 'group_name', 'action_ns'
        ]

        self._goto_dialog = RqtGoto()

# controller

    def edited_actionns(self):
        self.set_current_actionns(self._widget.actionNSEdit.text())

    def edited_groupname(self):
        self.set_current_movegroup_name(self._widget.groupNameEdit.text())

    def edited_baselink(self):
        self.set_current_base_link(self._widget.baseLinkEdit.text())

    def edited_endeffector(self):
        self.set_current_end_effector(self._widget.endEffectorEdit.text())

    def arm_activated(self, val):
        print 'arm_activated', val

    def get_arm_names(self):
        return self._params.keys()

    def get_current_arm_name(self):
        current_arm_name = self._widget.armBox.currentText()

        if current_arm_name in self.get_arm_names():
            return current_arm_name
        return None

    def get_steps(self):
        steps = None
        try:
            steps = [
                k for k in self._params[self.get_current_arm_name()].keys()
                if k not in self._keys_not_steps
            ]
        except KeyError, e:
            rospy.logerr('%s::%s: : cannot get steps for %s. %s' %
                         (self._name, self.get_function_name(),
                          self.get_current_arm_name(), e))
        return steps
コード例 #23
0
class RobotSteering(Plugin):

    slider_factor = 1000.0

    def __init__(self, context):
        super(RobotSteering, self).__init__(context)
        self.setObjectName('RobotSteering')

        self._publisher = None

        self._widget = QWidget()
        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_robot_steering'), 'resource',
                               'RobotSteering.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('RobotSteeringUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        context.add_widget(self._widget)

        self._widget.topic_line_edit.textChanged.connect(
            self._on_topic_changed)
        self._widget.stop_push_button.pressed.connect(self._on_stop_pressed)

        self._widget.x_linear_slider.valueChanged.connect(
            self._on_x_linear_slider_changed)
        self._widget.z_angular_slider.valueChanged.connect(
            self._on_z_angular_slider_changed)

        self._widget.increase_x_linear_push_button.pressed.connect(
            self._on_strong_increase_x_linear_pressed)
        self._widget.reset_x_linear_push_button.pressed.connect(
            self._on_reset_x_linear_pressed)
        self._widget.decrease_x_linear_push_button.pressed.connect(
            self._on_strong_decrease_x_linear_pressed)
        self._widget.increase_z_angular_push_button.pressed.connect(
            self._on_strong_increase_z_angular_pressed)
        self._widget.reset_z_angular_push_button.pressed.connect(
            self._on_reset_z_angular_pressed)
        self._widget.decrease_z_angular_push_button.pressed.connect(
            self._on_strong_decrease_z_angular_pressed)

        self._widget.max_x_linear_double_spin_box.valueChanged.connect(
            self._on_max_x_linear_changed)
        self._widget.min_x_linear_double_spin_box.valueChanged.connect(
            self._on_min_x_linear_changed)
        self._widget.max_z_angular_double_spin_box.valueChanged.connect(
            self._on_max_z_angular_changed)
        self._widget.min_z_angular_double_spin_box.valueChanged.connect(
            self._on_min_z_angular_changed)

        self.shortcut_w = QShortcut(QKeySequence(Qt.Key_W), self._widget)
        self.shortcut_w.setContext(Qt.ApplicationShortcut)
        self.shortcut_w.activated.connect(self._on_increase_x_linear_pressed)
        self.shortcut_x = QShortcut(QKeySequence(Qt.Key_X), self._widget)
        self.shortcut_x.setContext(Qt.ApplicationShortcut)
        self.shortcut_x.activated.connect(self._on_reset_x_linear_pressed)
        self.shortcut_s = QShortcut(QKeySequence(Qt.Key_S), self._widget)
        self.shortcut_s.setContext(Qt.ApplicationShortcut)
        self.shortcut_s.activated.connect(self._on_decrease_x_linear_pressed)
        self.shortcut_a = QShortcut(QKeySequence(Qt.Key_A), self._widget)
        self.shortcut_a.setContext(Qt.ApplicationShortcut)
        self.shortcut_a.activated.connect(self._on_increase_z_angular_pressed)
        self.shortcut_z = QShortcut(QKeySequence(Qt.Key_Z), self._widget)
        self.shortcut_z.setContext(Qt.ApplicationShortcut)
        self.shortcut_z.activated.connect(self._on_reset_z_angular_pressed)
        self.shortcut_d = QShortcut(QKeySequence(Qt.Key_D), self._widget)
        self.shortcut_d.setContext(Qt.ApplicationShortcut)
        self.shortcut_d.activated.connect(self._on_decrease_z_angular_pressed)

        self.shortcut_shift_w = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_W),
                                          self._widget)
        self.shortcut_shift_w.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_w.activated.connect(
            self._on_strong_increase_x_linear_pressed)
        self.shortcut_shift_x = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_X),
                                          self._widget)
        self.shortcut_shift_x.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_x.activated.connect(
            self._on_reset_x_linear_pressed)
        self.shortcut_shift_s = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_S),
                                          self._widget)
        self.shortcut_shift_s.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_s.activated.connect(
            self._on_strong_decrease_x_linear_pressed)
        self.shortcut_shift_a = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_A),
                                          self._widget)
        self.shortcut_shift_a.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_a.activated.connect(
            self._on_strong_increase_z_angular_pressed)
        self.shortcut_shift_z = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_Z),
                                          self._widget)
        self.shortcut_shift_z.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_z.activated.connect(
            self._on_reset_z_angular_pressed)
        self.shortcut_shift_d = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_D),
                                          self._widget)
        self.shortcut_shift_d.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_d.activated.connect(
            self._on_strong_decrease_z_angular_pressed)

        self.shortcut_space = QShortcut(QKeySequence(Qt.Key_Space),
                                        self._widget)
        self.shortcut_space.setContext(Qt.ApplicationShortcut)
        self.shortcut_space.activated.connect(self._on_stop_pressed)
        self.shortcut_space = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_Space),
                                        self._widget)
        self.shortcut_space.setContext(Qt.ApplicationShortcut)
        self.shortcut_space.activated.connect(self._on_stop_pressed)

        self._widget.stop_push_button.setToolTip(
            self._widget.stop_push_button.toolTip() + ' ' +
            self.tr('([Shift +] Space)'))
        self._widget.increase_x_linear_push_button.setToolTip(
            self._widget.increase_x_linear_push_button.toolTip() + ' ' +
            self.tr('([Shift +] W)'))
        self._widget.reset_x_linear_push_button.setToolTip(
            self._widget.reset_x_linear_push_button.toolTip() + ' ' +
            self.tr('([Shift +] X)'))
        self._widget.decrease_x_linear_push_button.setToolTip(
            self._widget.decrease_x_linear_push_button.toolTip() + ' ' +
            self.tr('([Shift +] S)'))
        self._widget.increase_z_angular_push_button.setToolTip(
            self._widget.increase_z_angular_push_button.toolTip() + ' ' +
            self.tr('([Shift +] A)'))
        self._widget.reset_z_angular_push_button.setToolTip(
            self._widget.reset_z_angular_push_button.toolTip() + ' ' +
            self.tr('([Shift +] Z)'))
        self._widget.decrease_z_angular_push_button.setToolTip(
            self._widget.decrease_z_angular_push_button.toolTip() + ' ' +
            self.tr('([Shift +] D)'))

        # timer to consecutively send twist messages
        self._update_parameter_timer = QTimer(self)
        self._update_parameter_timer.timeout.connect(
            self._on_parameter_changed)
        self._update_parameter_timer.start(100)
        self.zero_cmd_sent = False

    @Slot(str)
    def _on_topic_changed(self, topic):
        topic = str(topic)
        self._unregister_publisher()
        if topic == '':
            return
        try:
            self._publisher = rospy.Publisher(topic, Twist, queue_size=10)
        except TypeError:
            self._publisher = rospy.Publisher(topic, Twist)

    def _on_stop_pressed(self):
        # If the current value of sliders is zero directly send stop twist msg
        if self._widget.x_linear_slider.value() == 0 and \
                self._widget.z_angular_slider.value() == 0:
            self.zero_cmd_sent = False
            self._on_parameter_changed()
        else:
            self._widget.x_linear_slider.setValue(0)
            self._widget.z_angular_slider.setValue(0)

    def _on_x_linear_slider_changed(self):
        self._widget.current_x_linear_label.setText(
            '%0.2f m/s' % (self._widget.x_linear_slider.value() /
                           RobotSteering.slider_factor))
        self._on_parameter_changed()

    def _on_z_angular_slider_changed(self):
        self._widget.current_z_angular_label.setText(
            '%0.2f rad/s' % (self._widget.z_angular_slider.value() /
                             RobotSteering.slider_factor))
        self._on_parameter_changed()

    def _on_increase_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(
            self._widget.x_linear_slider.value() +
            self._widget.x_linear_slider.singleStep())

    def _on_reset_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(0)

    def _on_decrease_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(
            self._widget.x_linear_slider.value() -
            self._widget.x_linear_slider.singleStep())

    def _on_increase_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(
            self._widget.z_angular_slider.value() +
            self._widget.z_angular_slider.singleStep())

    def _on_reset_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(0)

    def _on_decrease_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(
            self._widget.z_angular_slider.value() -
            self._widget.z_angular_slider.singleStep())

    def _on_max_x_linear_changed(self, value):
        self._widget.x_linear_slider.setMaximum(value *
                                                RobotSteering.slider_factor)

    def _on_min_x_linear_changed(self, value):
        self._widget.x_linear_slider.setMinimum(value *
                                                RobotSteering.slider_factor)

    def _on_max_z_angular_changed(self, value):
        self._widget.z_angular_slider.setMaximum(value *
                                                 RobotSteering.slider_factor)

    def _on_min_z_angular_changed(self, value):
        self._widget.z_angular_slider.setMinimum(value *
                                                 RobotSteering.slider_factor)

    def _on_strong_increase_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(
            self._widget.x_linear_slider.value() +
            self._widget.x_linear_slider.pageStep())

    def _on_strong_decrease_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(
            self._widget.x_linear_slider.value() -
            self._widget.x_linear_slider.pageStep())

    def _on_strong_increase_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(
            self._widget.z_angular_slider.value() +
            self._widget.z_angular_slider.pageStep())

    def _on_strong_decrease_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(
            self._widget.z_angular_slider.value() -
            self._widget.z_angular_slider.pageStep())

    def _on_parameter_changed(self):
        self._send_twist(
            self._widget.x_linear_slider.value() / RobotSteering.slider_factor,
            self._widget.z_angular_slider.value() /
            RobotSteering.slider_factor)

    def _send_twist(self, x_linear, z_angular):
        if self._publisher is None:
            return
        twist = Twist()
        twist.linear.x = x_linear
        twist.linear.y = 0
        twist.linear.z = 0
        twist.angular.x = 0
        twist.angular.y = 0
        twist.angular.z = z_angular

        # Only send the zero command once so other devices can take control
        if x_linear == 0 and z_angular == 0:
            if not self.zero_cmd_sent:
                self.zero_cmd_sent = True
                self._publisher.publish(twist)
        else:
            self.zero_cmd_sent = False
            self._publisher.publish(twist)

    def _unregister_publisher(self):
        if self._publisher is not None:
            self._publisher.unregister()
            self._publisher = None

    def shutdown_plugin(self):
        self._update_parameter_timer.stop()
        self._unregister_publisher()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('topic',
                                    self._widget.topic_line_edit.text())
        instance_settings.set_value(
            'vx_max', self._widget.max_x_linear_double_spin_box.value())
        instance_settings.set_value(
            'vx_min', self._widget.min_x_linear_double_spin_box.value())
        instance_settings.set_value(
            'vw_max', self._widget.max_z_angular_double_spin_box.value())
        instance_settings.set_value(
            'vw_min', self._widget.min_z_angular_double_spin_box.value())

    def restore_settings(self, plugin_settings, instance_settings):
        value = instance_settings.value('topic', '/cmd_vel')
        value = rospy.get_param('~default_topic', value)
        self._widget.topic_line_edit.setText(value)

        value = self._widget.max_x_linear_double_spin_box.value()
        value = instance_settings.value('vx_max', value)
        value = rospy.get_param('~default_vx_max', value)
        self._widget.max_x_linear_double_spin_box.setValue(float(value))

        value = self._widget.min_x_linear_double_spin_box.value()
        value = instance_settings.value('vx_min', value)
        value = rospy.get_param('~default_vx_min', value)
        self._widget.min_x_linear_double_spin_box.setValue(float(value))

        value = self._widget.max_z_angular_double_spin_box.value()
        value = instance_settings.value('vw_max', value)
        value = rospy.get_param('~default_vw_max', value)
        self._widget.max_z_angular_double_spin_box.setValue(float(value))

        value = self._widget.min_z_angular_double_spin_box.value()
        value = instance_settings.value('vw_min', value)
        value = rospy.get_param('~default_vw_min', value)
        self._widget.min_z_angular_double_spin_box.setValue(float(value))
コード例 #24
0
class MyPlugin(Plugin):
    def __init__(self, context):
        self.cmd_pub = rospy.Publisher("/welder/cmd", String, queue_size=1)
        self.welder_status_sub = rospy.Subscriber('/welder/status',
                                                  String,
                                                  self.status_callback,
                                                  queue_size=10)
        self.welder_time_sub = rospy.Subscriber('/welder/time',
                                                String,
                                                self.time_callback,
                                                queue_size=10)
        self.welder_progress_sub = rospy.Subscriber('/welder/progress',
                                                    String,
                                                    self.progress_callback,
                                                    queue_size=10)
        self.image_sub = rospy.Subscriber("/laser_ctrl/img_feed/compressed",
                                          CompressedImage,
                                          self.image_callback,
                                          queue_size=10)
        self.laser_state_sub = rospy.Subscriber('/laser_ctrl/cmd',
                                                String,
                                                self.laser_state_clbk,
                                                queue_size=10)

        super(MyPlugin, self).__init__(context)

        # Give QObjects reasonable names
        self.setObjectName('welder_node_gui')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print(f"arguments: {args}")
            print(f"unknowns: {unknowns}")

        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('welder_node_gui'),
                               'resource', 'gui.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('Welder Robot GUI')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        context.add_widget(self._widget)
        self._widget.startButton.clicked.connect(self.start_cnc)
        self._widget.stopButton.clicked.connect(self.stop_cnc)
        self._widget.timeLabel.setText('None')

    def start_cnc(self):
        self.cmd_pub.publish('1')

    def progress_slot(self, data):
        blade_count = int(
            ros_data.data)  # this will not work, ros_data not defined
        progress = int(float(blade_count) / 256 * 100)
        grids = int((blade_count / 16))
        self._widget.progressBar.setValue(progress)
        self._widget.bladeNumber.display(blade_count)
        self._widget.gridNumber.display(grids)

    def stop_cnc(self):
        self.cmd_pub.publish('0')
        self._widget.timeLabel.setText('None')

    def image_callback(self, ros_data):
        np_arr = np.fromstring(ros_data.data, np.uint8)
        image = cv.imdecode(np_arr, cv.IMREAD_COLOR)
        height, width, _ = image.shape
        bytes_per_line = 3 * width
        qImage = QImage(image.data, width, height, bytes_per_line,
                        QImage.Format_RGB888).rgbSwapped()
        pix = QPixmap(qImage)
        h_label = self._widget.videoDisplay.height()
        w_label = self._widget.videoDisplay.width()
        self._widget.videoDisplay.setPixmap(
            pix.scaled(w_label, h_label, Qt.KeepAspectRatio))

    def time_callback(self, ros_data):
        time_text = ros_data.data + ' s'
        self._widget.timeLabel.setText(time_text)

    def progress_callback(self, ros_data):
        self.emit(Signal("changeUI(PyQt_PyObject)"), ros_data.data)

    def laser_state_clbk(self, ros_data):
        if ros_data.data == 's':
            self._widget.laserStatusLabel.setText('OFF')
        elif ros_data.data == 'f':
            self._widget.laserStatusLabel.setText('ON')

    def status_callback(self, ros_data):
        if ros_data.data != '0':
            self._widget.startButton.setEnabled(False)
            self._widget.stopButton.setEnabled(True)
        else:
            self._widget.startButton.setEnabled(True)
            self._widget.stopButton.setEnabled(False)
        self._widget.statusLabel.setText(WELDER_STATES[int(ros_data.data)])

    def shutdown_plugin(self):
        # TODO (Pablo): unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO (Pablo): save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO (Pablo): restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #25
0
class Overview(Plugin):
    def __init__(self, context):
        super(Overview, self).__init__(context)
        self.__tfListener = tf.TransformListener()

        self.__copterFrame = "base_link"
        self.__worldFrame = "world"

        # Give QObjects reasonable names
        self.setObjectName('simple_position_control_gui')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-c",
                            "--copterFrame",
                            dest="copterFrame",
                            help="Specify the copter frame")
        parser.add_argument("-w",
                            "--worldFrame",
                            dest="worldFrame",
                            help="Specify the world frame")

        args, unknowns = parser.parse_known_args(context.argv())
        if args.copterFrame:
            self.__copterFrame = args.copterFrame
            rospy.loginfo("using %s as copter frame", self.__copterFrame)

        if args.worldFrame:
            self.__worldFrame = args.worldFrame
            rospy.loginfo("using %s as world frame", self.__worldFrame)

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this node
        ui_file = os.path.join(
            rospkg.RosPack().get_path('position_controller'), 'src',
            'rqt_control_gui', 'resource', 'overview.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('OverviewUI')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)
        self._widget.forwardButton.clicked.connect(self.goForward)
        self._widget.backwardButton.clicked.connect(self.goBackward)
        self._widget.leftButton.clicked.connect(self.goLeft)
        self._widget.rightButton.clicked.connect(self.goRight)
        self._widget.rotateButton.clicked.connect(self.rotate)
        self._widget.landButton.clicked.connect(self.land)
        self._widget.startButton.clicked.connect(self.start)
        self._widget.absoluteControlButton.clicked.connect(
            self.absoluteControl)
        self._widget.tfButton.clicked.connect(self.goToTF)
        self.__statusLabel = self._widget.statusLabel

    def setRelativePose(self, x_delta, y_delta, yaw_delta):
        """
        set new target position relative to current position
            :param: x_delta: change in x
            :param: y_delta: change in y
            :param: yaw_delta: change in yaw
        """
        try:
            (trans,
             rot) = self.__tfListener.lookupTransform(self.__worldFrame,
                                                      self.__copterFrame,
                                                      rospy.Time(0))

            quad_delta = tf.transformations.quaternion_from_euler(
                0, 0, yaw_delta)

            translation_delta = tf.transformations.translation_matrix(
                (x_delta, y_delta, 0))

            m_current = tf.transformations.translation_matrix(trans).dot(
                tf.transformations.quaternion_matrix(rot))

            m_delta = translation_delta.dot(
                tf.transformations.quaternion_matrix(quad_delta))

            m_target = m_current.dot(m_delta)

            _, _, target_yaw = tf.transformations.euler_from_matrix(m_target)

            target_x, target_y, _ = tf.transformations.translation_from_matrix(
                m_target)

            self.__statusLabel.setText(
                "going to x: {0} y: {1} yaw: {2}".format(
                    target_x, target_y, target_yaw))
            rospy.wait_for_service('/position_controller/set_target_pos', 1)
            setPose = rospy.ServiceProxy('/position_controller/set_target_pos',
                                         SetPos)
            setPose(target_x, target_y, target_yaw)
        except Exception as e:
            self.__statusLabel.setText(str(e))

    def goForward(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(distance, 0, 0)

    def goBackward(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(-distance, 0, 0)

    def goLeft(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(0, distance, 0)

    def goRight(self):
        distance = float(self._widget.distanceEdit.text())
        self.setRelativePose(0, -distance, 0)

    def rotate(self):
        angle = math.pi * float(self._widget.rotationEdit.text()) / 180.0
        self.setRelativePose(0, 0, angle)

    def land(self):
        try:
            self.setAltitude(0.0)
        except ValueError:
            pass

    def start(self):
        try:
            altitude = float(self._widget.zEdit.text())
            self.setAltitude(altitude)
            self.setAltitude(altitude)
            statusMessage = "starting to altitude {0}".format(altitude)
            self.__statusLabel.setText(statusMessage)
        except ValueError:
            pass

    def setAltitude(self, altitude):
        rospy.wait_for_service('/position_controller/set_altitude', 1)
        setAltitudeService = rospy.ServiceProxy(
            '/position_controller/set_altitude', SetAltitude)
        setAltitudeService(altitude)

    def absoluteControl(self):
        statusMessage = ""
        try:
            ((x, y, _z), quaternion) = self.__tfListener.lookupTransform(
                self.__worldFrame, self.__copterFrame, rospy.Time(0))
            (_roll, _pitch, yaw) = euler_from_quaternion(quaternion)
            try:
                yaw = math.pi * float(self._widget.angleEdit.text()) / 180.0
            except ValueError:
                pass
            try:
                x = float(self._widget.xEdit.text())
            except ValueError:
                pass
            try:
                y = float(self._widget.yEdit.text())
            except ValueError:
                pass
            statusMessage += "going to x: {0} y: {1} yaw: {2} ".format(
                x, y, yaw)
            rospy.wait_for_service('/position_controller/set_target_pos', 1)
            setPose = rospy.ServiceProxy('/position_controller/set_target_pos',
                                         SetPos)
            setPose(x, y, yaw)
            self.__statusLabel.setText(statusMessage)
            try:
                altitude = float(self._widget.zEdit.text())
                statusMessage += "setting altitude to {0}".format(altitude)
                self.setAltitude(altitude)
                self.__statusLabel.setText(statusMessage)
            except ValueError:
                pass
        except Exception as e:
            self.__statusLabel.setText(str(e))

    def goToTF(self):
        pass

    def shutdown_plugin(self):
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        rospy.loginfo("saving simple position controller gui setting")
        instance_settings.set_value("worldFrame", self.__worldFrame)
        instance_settings.set_value("copterFrame", self.__copterFrame)

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        rospy.loginfo("restoring simple position controller gui setting")
        storedWorldFrame = instance_settings.value("worldFrame")
        if type(storedWorldFrame) == unicode:
            storedWorldFrame = storedWorldFrame.encode('ascii', 'ignore')
        if storedWorldFrame:
            self.__worldFrame = storedWorldFrame
        storedCopterFrame = instance_settings.value("copterFrame")
        if type(storedCopterFrame) == unicode:
            storedCopterFrame = storedCopterFrame.encode('ascii', 'ignore')
        if storedCopterFrame:
            self.__copterFrame = storedCopterFrame
コード例 #26
0
class CarlaControlPlugin(Plugin):
    """
    RQT Plugin to control CARLA
    """
    def __init__(self, context):
        """
        Constructor
        """
        super(CarlaControlPlugin, self).__init__(context)
        self.setObjectName('CARLA Control')

        self._widget = QWidget()
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_carla_control'),
                               'resource', 'CarlaControl.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('CarlaControl')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        self.pause_icon = QIcon(
            QPixmap(
                os.path.join(rospkg.RosPack().get_path('rqt_carla_control'),
                             'resource', 'pause.png')))
        self.play_icon = QIcon(
            QPixmap(
                os.path.join(rospkg.RosPack().get_path('rqt_carla_control'),
                             'resource', 'play.png')))
        self._widget.pushButtonStepOnce.setIcon(
            QIcon(
                QPixmap(
                    os.path.join(
                        rospkg.RosPack().get_path('rqt_carla_control'),
                        'resource', 'step_once.png'))))

        self.carla_status = None
        self.carla_status_subscriber = rospy.Subscriber(
            "/carla/status", CarlaStatus, self.carla_status_changed)
        self.carla_control_publisher = rospy.Publisher("/carla/control",
                                                       CarlaControl,
                                                       queue_size=10)

        self._widget.pushButtonPlayPause.setDisabled(True)
        self._widget.pushButtonStepOnce.setDisabled(True)
        self._widget.pushButtonPlayPause.setIcon(self.play_icon)
        self._widget.pushButtonPlayPause.clicked.connect(
            self.toggle_play_pause)
        self._widget.pushButtonStepOnce.clicked.connect(self.step_once)

        context.add_widget(self._widget)

    def toggle_play_pause(self):
        """
        toggle play/pause
        """
        if self.carla_status.synchronous_mode:
            if self.carla_status.synchronous_mode_running:
                self.carla_control_publisher.publish(
                    CarlaControl(command=CarlaControl.PAUSE))
            else:
                self.carla_control_publisher.publish(
                    CarlaControl(command=CarlaControl.PLAY))

    def step_once(self):
        """
        execute one step
        """
        self.carla_control_publisher.publish(
            CarlaControl(command=CarlaControl.STEP_ONCE))

    def carla_status_changed(self, status):
        """
        callback whenever carla status changes
        """
        self.carla_status = status
        if status.synchronous_mode:
            self._widget.pushButtonPlayPause.setDisabled(False)
            self._widget.pushButtonStepOnce.setDisabled(False)
            if status.synchronous_mode_running:
                self._widget.pushButtonPlayPause.setIcon(self.pause_icon)
            else:
                self._widget.pushButtonPlayPause.setIcon(self.play_icon)
        else:
            self._widget.pushButtonPlayPause.setDisabled(True)
            self._widget.pushButtonStepOnce.setDisabled(True)

    def shutdown_plugin(self):
        """
        shutdown plugin
        """
        self.carla_control_publisher.unregister()
コード例 #27
0
class MyPlugin(Plugin):
    def __init__(self, context):
        super(MyPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('MyPlugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument(action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_mypkg'),
                               'resource', 'MyPlugin.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('MyPluginUi')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        # These directions are matched to the Arduino code
        self.directions = {
            0: "Forward",
            1: "Back",
            2: "Left",
            3: "Right",
            4: "ForwardLeft",
            5: "BackRight",
            6: "ForwardRight",
            7: "BackLeft",
            8: "CW",
            9: "CCW"
        }

        self.directionsinv = {
            "Forward": 0,
            "Back": 1,
            "Left": 2,
            "Right": 3,
            "ForwardLeft": 4,
            "BackRight": 5,
            "ForwardRight": 6,
            "BackLeft": 7,
            "CW": 8,
            "CCW": 9
        }

        self.states = {
            0: "Status : Stopped",
            1: "Status : Moving",
            2: "Status : Calibration Mode",
            3: "Status : Mapping",
            4: "Status : Measuring"
        }
        self.robotstate = 1

        self._widget.Forward.pressed.connect(self.on_direction_press)
        self._widget.Back.pressed.connect(self.on_direction_press)
        self._widget.Left.pressed.connect(self.on_direction_press)
        self._widget.Right.pressed.connect(self.on_direction_press)
        self._widget.ForwardLeft.pressed.connect(self.on_direction_press)
        self._widget.ForwardRight.pressed.connect(self.on_direction_press)
        self._widget.BackLeft.pressed.connect(self.on_direction_press)
        self._widget.BackRight.pressed.connect(self.on_direction_press)
        self._widget.CW.pressed.connect(self.on_direction_press)
        self._widget.CCW.pressed.connect(self.on_direction_press)

        self._widget.Go.pressed.connect(self.on_go_pressed)
        self._widget.Stop.pressed.connect(self.on_stop_pressed)

        #
        # self._widget.EncoderLinearSpinBox.valueChanged.connect(self.on_encoder_linear_spinbox_pressed)
        # self._widget.EncoderLinearSlider.valueChanged.connect(self.on_encoder_linear_slider_pressed)
        #
        # self.on_encoder_linear_spinbox_pressed()
        #
        # self._widget.EncoderRotationSpinBox.valueChanged.connect(self.on_encoder_rotary_spinbox_pressed)
        # self._widget.EncoderRotationSlider.valueChanged.connect(self.on_encoder_rotary_slider_pressed)
        #
        # self.on_encoder_rotary_spinbox_pressed()
        #

        self._widget.ServoSpinBox.valueChanged.connect(
            self.on_servo_spinbox_pressed)
        self._widget.ServoSlider.valueChanged.connect(
            self.on_servo_slider_pressed)
        self._widget.ServoButton.pressed.connect(self.on_servo_button_pressed)

        self.on_servo_spinbox_pressed()

        self._widget.MicSpinBox.valueChanged.connect(
            self.on_mic_spinbox_pressed)
        self._widget.MicSlider.valueChanged.connect(self.on_mic_slider_pressed)
        self._widget.MicButton.pressed.connect(self.on_mic_button_pressed)

        self.on_mic_spinbox_pressed()

        self._widget.MicCalibrateButton.pressed.connect(
            self.on_mic_calibrate_button_pressed)

        self._widget.MicDirectionButton.pressed.connect(
            self.on_mic_direction_button_pressed)
        self.micdirection = 0

        self._widget.CameraButton.pressed.connect(
            self.on_camera_button_pressed)
        self._widget.SweepButton.pressed.connect(self.on_sweep_button_pressed)
        self._widget.ImpulseButton.pressed.connect(
            self.on_impulse_button_pressed)
        self._widget.CalibrateEncoderButton.pressed.connect(
            self.on_calibrate_encoder_button_pressed)

        self._widget.FrequencySlider.valueChanged.connect(
            self.on_frequency_slider_pressed)
        self._widget.FrequencySpinBox.valueChanged.connect(
            self.on_frequency_spinbox_pressed)

        # Camera image selection button groups
        self.bg1 = QButtonGroup()
        self.bg2 = QButtonGroup()

        self.bg1.addButton(self._widget.LeftImageCheckBox)
        self.bg1.addButton(self._widget.RightImageCheckBox)
        self.bg1.addButton(self._widget.DisparityImageCheckBox)
        self.bg2.addButton(self._widget.NormalImageCheckBox)
        self.bg2.addButton(self._widget.EdgeImageCheckBox)
        self.bg2.addButton(self._widget.BWImageCheckBox)
        self.bg2.addButton(self._widget.KeypointsImageCheckBox)

        self._widget.LeftImageCheckBox.pressed.connect(
            self.on_camera_type_changed)
        self._widget.RightImageCheckBox.pressed.connect(
            self.on_camera_type_changed)
        self._widget.DisparityImageCheckBox.pressed.connect(
            self.on_camera_type_changed)
        self._widget.EdgeImageCheckBox.pressed.connect(
            self.on_image_type_changed)
        self._widget.BWImageCheckBox.pressed.connect(
            self.on_image_type_changed)
        self._widget.KeypointsImageCheckBox.pressed.connect(
            self.on_image_type_changed)
        self._widget.NormalImageCheckBox.pressed.connect(
            self.on_image_type_changed)
        self.bg1.buttonClicked.connect(self.on_camera_type_changed)
        self.bg2.buttonClicked.connect(self.on_image_type_changed)

        self.on_camera_type_changed()
        self.on_image_type_changed()
        self.camera_type = 2
        self.image_type = 2

        self.micHeight = 15
        self.encoderCalibMode = False

        # Connect to Arduino's subscriber to control it
        self.pub2ard = rospy.Publisher('arduinoSub',
                                       Arduinostate,
                                       queue_size=10)

        self.pub2pi = rospy.Publisher('imagetrigger', Int8, queue_size=10)
        self.pub2spkr = rospy.Publisher('speakerListener', Int8, queue_size=10)
        self.pub2mic = rospy.Publisher('audiotrigger', String, queue_size=10)
        self.r = rospy.Rate(5)
        self.arduinomsg = Arduinostate()
        #self.pimsg = Bool()
        self.pimsg = Int8()
        self.spkrmsg = Int8()
        self.micmsg = String()

        self.mapSub = rospy.Subscriber("mapPub", String, self.plotCallback)
        self.mapPub = rospy.Publisher("mapSub", String, queue_size=10)

        self.mapmsg = String()
        self.mapUtility = mapping.Map()
        self.mapUtility.z = 5

        self.freqPlot = pg.plot(title="Frequency Spectrum")
        self.distFreqPlot = pg.plot(title="Frequency vs. Distance")
        self.contourPlot = pg.ImageView(name="2D Power - Single Frequency")

        S = audiopath + "AAsaw.wav"
        fs, audio = read(S)
        self.freqPlot.plot(abs(fft(audio)))

        self.lastaudio = ""
        self.audiofiles = []

    def plotCallback(self, data):
        S = data.data
        if (S != self.lastaudio):
            fs, audio = read(audiopath + S + ".wav")
            self.audiofiles.append(abs(fft(audio)))
            self.freqPlot.clear()
            self.freqPlot.plot(abs(fft(audio)))
            self.lastaudio = S
            print("Showing plot of " + self.lastaudio)

            x = linspace(-100, 100, 201)
            xx, yy = meshgrid(x, x)
            # Go through audio files at the given height
            # Get power at the given frequency for all x,y

            #self.contourPlot.setImage(z)

    # Need separate function for Freq, amplitude distance in 1D and 2D
    # Might want one for updating files and another for updating frequency
    def distancePlot(self, axis):
        if (axis == 0):  # x
            # Search for all files in path that have different x values
            g = os.listdir(audiopath)
            A = []
            x = []
            for k in range(len(g)):
                if "wav" in g:
                    fs, audio = read(g[k])
                    x.append(int(g[0]))
                    A.append(20 * log10(abs(fft(audio))))

        self.distancePlot.plot(x, A)

    def talker(self, msgtype):
        if (msgtype == 0):
            self.arduinomsg.toggleVelocity = True
            self.arduinomsg.toggleServo = False
            self.arduinomsg.toggleFunction = False
            self.arduinomsg.vel_x = 0
            self.arduinomsg.vel_y = 0
            self.arduinomsg.vel_theta = 0
        elif (msgtype == 1):  # Go in a direction for some time
            self.arduinomsg.toggleFunction = True
            self.arduinomsg.toggleVelocity = False
            self.arduinomsg.toggleServo = False
            direction = self.directionsinv[
                self._widget.DirectionTextBox.text()]
            time = float(self._widget.TimeLineEdit.text())
            self.arduinomsg.functionSelect = 1
            self.arduinomsg.functionIntParam = direction
            self.arduinomsg.functionFloatParam = time
            self.mapUtility.updatePosition(direction, time)
            self.mapmsg = "%d,%d,%d,%d,0" % (
                self.mapUtility.x, self.mapUtility.y, self.mapUtility.t,
                self.mapUtility.z)
            self.mapPub.publish(self.mapmsg)
            # Need additional part of message to tell map to make mic marker
            self.r.sleep()
            S = self.mapmsg.split(",")
            self.arduinomsg.x = int(S[0])
            self.arduinomsg.y = int(S[1])
            self.arduinomsg.theta = int(S[2])
            self.arduinomsg.micPosition = int(S[3])
            # For mapping, need to update coordinates here+
        elif (msgtype == 2):  # Velocity control
            self.arduinomsg.toggleVelocity = True
            self.arduinomsg.toggleFunction = False
            self.arduinomsg.toggleServo = False
        elif (msgtype == 3):  # Servo
            self.arduinomsg.toggleServo = True
            self.arduinomsg.toggleVelocity = False
            self.arduinomsg.toggleFunction = False
            self.arduinomsg.servoangle = int8(
                self._widget.ServoSpinBox.value())
        elif (msgtype == 4):  # Mic
            self.arduinomsg.toggleFunction = True
            self.arduinomsg.toggleServo = False
            self.arduinomsg.toggleVelocity = False
            self.arduinomsg.functionSelect = 3
            self.arduinomsg.functionIntParam = int8(self.micdirection)
            self.arduinomsg.functionFloatParam = self._widget.MicSpinBox.value(
            )
            self.mapUtility.z += (-1)**(self.micdirection)
            self.mapmsg = "%d,%d,%d,%d,0" % (
                self.mapUtility.x, self.mapUtility.y, self.mapUtility.t,
                self.mapUtility.z)
            self.mapPub.publish(self.mapmsg)
            self.r.sleep()
            S = self.mapmsg.split(",")
            self.arduinomsg.x = int(S[0])
            self.arduinomsg.y = int(S[1])
            self.arduinomsg.theta = int(S[2])
            self.arduinomsg.micPosition = int(S[3])

        elif (msgtype == 5):  # Mic calibrate
            self.arduinomsg.toggleFunction = True
            self.arduinomsg.toggleServo = False
            self.arduinomsg.toggleVelocity = False
            self.arduinomsg.functionSelect = 4
            self.arduinomsg.functionIntParam = int8(
                self._widget.MicCalibrateSpinBox.value())

        elif (msgtype == 6):  # Measure with mic
            pass
            # Get position of robot and mic height
            # Send trigger then start recording
            # Place marker on the map at the location and save WAV file
        elif (msgtype == 7):  # Encoder calibrate
            self.arduinomsg.toggleFunction = True
            self.arduinomsg.toggleServo = False
            self.arduinomsg.toggleVelocity = False
            direction = self.directionsinv[
                self._widget.DirectionTextBox.text()]
            time = float(self._widget.TimeLineEdit.text())
            self.arduinomsg.functionSelect = 2
            self.arduinomsg.functionIntParam = direction
            self.arduinomsg.functionFloatParam = time
            numTrials = 10
            for k in range(numTrials):
                x1 = int16(
                    subprocess.check_output("rostopic echo /arduinoPub/x -n1",
                                            shell=True).split('\n')[0])
                y1 = int16(
                    subprocess.check_output("rostopic echo /arduinoPub/y -n1",
                                            shell=True).split('\n')[0])
                z1 = int16(
                    subprocess.check_output(
                        "rostopic echo /arduinoPub/theta -n1",
                        shell=True).split('\n')[0])
                self.pub2ard.publish(self.arduinomsg)
                x2 = int16(
                    subprocess.check_output("rostopic echo /arduinoPub/x -n1",
                                            shell=True).split('\n')[0])
                y2 = int16(
                    subprocess.check_output("rostopic echo /arduinoPub/y -n1",
                                            shell=True).split('\n')[0])
                z2 = int16(
                    subprocess.check_output(
                        "rostopic echo /arduinoPub/theta -n1",
                        shell=True).split('\n')[0])
                dx = abs(x2) - abs(x1)
                dy = abs(y2) - abs(y1)
                dz = abs(z2) - abs(z1)
                # Error is larger with long times, not reading output correctly
                #print("Trial %d : %d " %(k,dy - dz))
                # Reverse to return to original position
                if (direction == 0):
                    print("Trial %d: Error : %d , Total y: %d, theta: %d" %
                          (k, dy - dz, dy, dz))
                    self.arduinomsg.functionIntParam = 1
                    self.pub2ard.publish(self.arduinomsg)
                    #time.sleep(time)
                    self.arduinomsg.functionIntParam = 0

                    # Right back minus left back
                    # Positive error means increase left wheel speed or decrease right wheel speed
                if (direction == 1):
                    print("Trial %d: Error : %d , Total y: %d, theta: %d" %
                          (k, dy - dz, dy, dz))
                    self.arduinomsg.functionIntParam = 0
                    self.pub2ard.publish(self.arduinomsg)
                    #time.sleep(time)
                    self.arduinomsg.functionIntParam = 1

                # Left and right might be tricky...

                if (direction == 8 or direction == 9):
                    print("Trial %d : %d, %d, %d " % (k, dx, dy, dz))

        rospy.loginfo(self.arduinomsg)  # Logging sent message, no received!
        self.pub2ard.publish(self.arduinomsg)
        self.r.sleep()

    def shutdown_plugin(self):
        sys.exit()

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

    def updateState(self, newstate):
        self.robotstate = self.states[newstate]
        self._widget.StatusTextBox.setText(self.robotstate)

    def on_direction_press(self):
        self._widget.DirectionTextBox.setText(self.sender().objectName())

    def on_stop_pressed(self):
        self.updateState(0)
        self.talker(0)

    def on_go_pressed(self):
        self.updateState(1)
        self.talker(1)

    def on_frequency_spinbox_pressed(self):
        self._widget.FrequencySlider.setValue(
            self._widget.FrequencySpinBox.value())

    def on_frequency_slider_pressed(self):
        self._widget.FrequencySpinBox.setValue(
            self._widget.FrequencySlider.value())

    def on_servo_spinbox_pressed(self):
        self._widget.ServoSlider.setValue(self._widget.ServoSpinBox.value())

    def on_servo_slider_pressed(self):
        self._widget.ServoSpinBox.setValue(self._widget.ServoSlider.value())

    def on_mic_spinbox_pressed(self):
        self._widget.MicSlider.setValue(self._widget.MicSpinBox.value())

    def on_mic_slider_pressed(self):
        self._widget.MicSpinBox.setValue(self._widget.MicSlider.value())

    def on_mic_button_pressed(self):
        self.talker(4)

    def on_mic_calibrate_button_pressed(self):
        self._widget.MicCalibrateButton.setText(
            "Calibrate %i" % self._widget.MicCalibrateSpinBox.value())
        self.talker(5)

    def on_mic_direction_button_pressed(self):
        if (self.micdirection):  # If true, set to down
            self._widget.MicDirectionButton.setText("Mic Up")
            self.micdirection = 0
        else:
            self._widget.MicDirectionButton.setText("Mic Down")
            self.micdirection = 1

    def on_servo_button_pressed(self):
        self.talker(3)

    # Loop it here instead of in arduino
    def on_calibrate_encoder_button_pressed(self):
        if (self.encoderCalibMode):
            self.encoderCalibMode = False
            self._widget.CalibrateMicButton.setText("Calibrate: Off")
        else:
            self.encoderCalibMode = True
            self.talker(6)
            self._widget.CalibrateEncoderButton.setText("Calibrate: On")

    def on_camera_button_pressed(self):
        if (self.camera_type == 2):
            if (self.image_type == 2):
                self.pimsg = 1
            elif (self.image_type == 3):
                self.pimsg = 2
            elif (self.image_type == 4):
                self.pimsg = 3
            elif (self.image_type == 5):
                self.pimsg = 4

        elif (self.camera_type == 3):  # Right
            if (self.image_type == 2):
                self.pimsg = 5
            elif (self.image_type == 3):
                self.pimsg = 6
            elif (self.image_type == 4):
                self.pimsg = 7
            elif (self.image_type == 5):
                self.pimsg = 8

        elif (self.camera_type == 4):
            self.pimsg = 7  # Disparity map

        self.pub2pi.publish(self.pimsg)

    def on_camera_type_changed(self):
        self.camera_type = -1 * self.bg1.checkedId()
        #self._widget.CameraButton.setText("Camera %i" % self.camera_type )

    def on_image_type_changed(self):
        self.image_type = -1 * self.bg2.checkedId()
        #self._widget.CameraButton.setText("Camera %i" % self.image_type )

    '''
    When measurement button is triggered, get the current position to save as a marker
    '''

    def on_sweep_button_pressed(self):
        self.spkrmsg = 0
        self.mapmsg = "%d,%d,%d,%d,1" % (self.mapUtility.x, self.mapUtility.y,
                                         self.mapUtility.t, self.mapUtility.z)
        self.micmsg = self.mapmsg
        self.pub2mic.publish(self.micmsg)
        self.pub2spkr.publish(self.spkrmsg)
        self.r.sleep()  # Wait until recording is done to add marker
        self.mapPub.publish(self.mapmsg)
        S = self.mapmsg.split(",")
        self.arduinomsg.x = int(S[0])
        self.arduinomsg.y = int(S[1])
        self.arduinomsg.theta = int(S[2])
        self.arduinomsg.micPosition = int(S[3])

    def on_impulse_button_pressed(self):
        self.spkrmsg = 2
        self.pub2spkr.publish(self.spkrmsg)

    # Calibrate cameras individually and in stereo to get camera parameters
    def calibrateCameras(self):
        self.updateState(2)

    def mappingMode(self):
        self.updateState(3)

    def measurementMode(self):
        self.updateState(4)
コード例 #28
0
class Packml(Plugin):

    def __init__(self, context):
        super(Packml, self).__init__(context)
        self.setObjectName('Packml')

        from argparse import ArgumentParser
        parser = ArgumentParser()

        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        ui_file = os.path.join(rospkg.RosPack().get_path('packml_gui'), 'resource', 'packml.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('Packml')

        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))

        context.add_widget(self._widget)

        # Custom code begins here
        self._widget.reset_button.clicked[bool].connect(self.__handle_reset_clicked)
        self._widget.start_button.clicked[bool].connect(self.__handle_start_clicked)
        self._widget.stop_button.clicked[bool].connect(self.__handle_stop_clicked)
        self._widget.clear_button.clicked[bool].connect(self.__handle_clear_clicked)
        self._widget.hold_button.clicked[bool].connect(self.__handle_hold_clicked)
        self._widget.unhold_button.clicked[bool].connect(self.__handle_unhold_clicked)
        self._widget.suspend_button.clicked[bool].connect(self.__handle_suspend_clicked)
        self._widget.unsuspend_button.clicked[bool].connect(self.__handle_unsuspend_clicked)
        self._widget.abort_button.clicked[bool].connect(self.__handle_abort_clicked)

        self._service_thread = Thread(target=self.wait_for_services, args=())
        self._service_thread.start()

        self._status_sub = rospy.Subscriber('packml/status', Status, self.status_callback)

        self.threadpool = QThreadPool()

    def disable_all_buttons(self):
        self._widget.clear_button.setEnabled(False)
        self._widget.reset_button.setEnabled(False)
        self._widget.start_button.setEnabled(False)
        self._widget.stop_button.setEnabled(False)
        self._widget.hold_button.setEnabled(False)
        self._widget.suspend_button.setEnabled(False)
        self._widget.unhold_button.setEnabled(False)
        self._widget.unsuspend_button.setEnabled(False)
        self._widget.abort_button.setEnabled(False)

    def set_message_text(self, text):
        self._widget.message_box.setText("Message: " + text)

    def status_callback(self, msg):
        self.update_button_states(msg.state.val)
        self.update_status_fields(msg)


    def update_button_states(self, state):
        self.disable_all_buttons()
        if state == State.ABORTED:
            self._widget.clear_button.setEnabled(True)
        elif state == State.STOPPED:
            self._widget.reset_button.setEnabled(True)
        elif state == State.IDLE:
            self._widget.start_button.setEnabled(True)
        elif state == State.EXECUTE:
            self._widget.hold_button.setEnabled(True)
            self._widget.suspend_button.setEnabled(True)
        elif state == State.HELD:
            self._widget.unhold_button.setEnabled(True)
        elif state == State.SUSPENDED:
            self._widget.unsuspend_button.setEnabled(True)
        elif state == State.COMPLETE:
            self._widget.reset_button.setEnabled(True)

        if state != State.STOPPED and \
        state != State.STOPPING and \
        state != State.ABORTED and \
        state != State.ABORTING and \
        state != State.CLEARING:
            self._widget.stop_button.setEnabled(True)


        if state != State.ABORTED and \
        state != State.ABORTING:
            self._widget.abort_button.setEnabled(True)

    def update_status_fields(self, msg):
        self.update_state_field(msg.state.val)
        self._widget.substate.setText(str(msg.sub_state))
        self.update_mode_field(msg.mode.val)
        self._widget.error_code.setText(str(msg.error))
        self._widget.suberror_code.setText(str(msg.sub_error))


    def update_state_field(self, state):
        if state == State.UNDEFINED:
            self._widget.state_name.setText("UNDEFINED")
        elif state == State.OFF:
            self._widget.state_name.setText("OFF")
        elif state == State.STOPPED:
            self._widget.state_name.setText("STOPPED")
        elif state == State.STARTING:
            self._widget.state_name.setText("STARTING")
        elif state == State.IDLE:
            self._widget.state_name.setText("IDLE")
        elif state == State.SUSPENDED:
            self._widget.state_name.setText("SUSPENDED")
        elif state == State.EXECUTE:
            self._widget.state_name.setText("EXECUTE")
        elif state == State.STOPPING:
            self._widget.state_name.setText("STOPPING")
        elif state == State.ABORTING:
            self._widget.state_name.setText("ABORTING")
        elif state == State.ABORTED:
            self._widget.state_name.setText("ABORTED")
        elif state == State.HOLDING:
            self._widget.state_name.setText("HOLDING")
        elif state == State.HELD:
            self._widget.state_name.setText("HELD")
        elif state == State.RESETTING:
            self._widget.state_name.setText("RESETTING")
        elif state == State.SUSPENDING:
            self._widget.state_name.setText("SUSPENDING")
        elif state == State.UNSUSPENDING:
            self._widget.state_name.setText("UNSUSPENDING")
        elif state == State.CLEARING:
            self._widget.state_name.setText("CLEARING")
        elif state == State.UNHOLDING:
            self._widget.state_name.setText("UNHOLDING")
        elif state == State.COMPLETING:
            self._widget.state_name.setText("COMPLETING")
        elif state == State.COMPLETE:
            self._widget.state_name.setText("COMPLETE")
        else:
            self._widget.state_name.setTest("UNKNOWN")



    def update_mode_field(self, mode):
        if mode == Mode.UNDEFINED:
            self._widget.mode_name.setText("UNDEFINED")
        elif mode == Mode.AUTOMATIC:
            self._widget.mode_name.setText("AUTOMATIC")
        elif mode == Mode.SEMI_AUTOMATIC:
            self._widget.mode_name.setText("SEMI-AUTOMATIC")
        elif mode == Mode.MANUAL:
            self._widget.mode_name.setText("MANUAL")
        elif mode == Mode.IDLE:
            self._widget.mode_name.setText("IDLE")
        elif mode == Mode.SETUP:
            self._widget.mode_name.setText("SETUP")
        else:
            self._widget.mode_name.setText("UNKNOWN")



    def wait_for_services(self):
        self._widget.setEnabled(False)
        transition_service_name = 'packml/transition'
        rospy.wait_for_service(transition_service_name, 30)
        self.transition_service = rospy.ServiceProxy(transition_service_name, Transition)
        self._widget.setEnabled(True)

    def shutdown_plugin(self):
        self._status_sub.unregister()
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass


    def handle_click_thread(self, request):
        try:
            service_thread = WorkerThread(self.transition_service, request, self.set_message_text)
            if self.threadpool.activeThreadCount() >= 1:
                return
            else:
                self.threadpool.start(service_thread)
        except rospy.ServiceException as exc:
            rospy.logerror("Service did not process request: " + str(exc))

    def __handle_start_clicked(self, checked):
        rospy.loginfo("Start button press")
        self.handle_click_thread(TransitionRequest.START)

    def __handle_stop_clicked(self, checked):
        rospy.loginfo("Stop button press")
        self.handle_click_thread(TransitionRequest.STOP)

    def __handle_reset_clicked(self, checked):
        rospy.loginfo("Reset button press")
        self.handle_click_thread(TransitionRequest.RESET)

    def __handle_clear_clicked(self, checked):
        rospy.loginfo("Clear button press")
        self.handle_click_thread(TransitionRequest.CLEAR)

    def __handle_hold_clicked(self, checked):
        rospy.loginfo("Hold button press")
        self.handle_click_thread(TransitionRequest.HOLD)

    def __handle_unhold_clicked(self, checked):
        rospy.loginfo("Unhold button press")
        self.handle_click_thread(TransitionRequest.UNHOLD)

    def __handle_suspend_clicked(self, checked):
        rospy.loginfo("Suspend button press")
        self.handle_click_thread(TransitionRequest.SUSPEND)

    def __handle_unsuspend_clicked(self, checked):
        rospy.loginfo("Unsuspend button press")
        self.handle_click_thread(TransitionRequest.UNSUSPEND)

    def __handle_abort_clicked(self, checked):
        rospy.loginfo("Abort button press")
        self.handle_click_thread(TransitionRequest.ABORT)

    @staticmethod
    def add_arguments(parser):
        rospy.loginfo("Add arguments callback")
        group = parser.add_argument_group('Options for PackML plugin')
        group.add_argument('--arg1', action='store_true', help='arg1 help')
コード例 #29
0
class SchunkPlugin(Plugin):
    def __init__(self, context):
        super(SchunkPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('SchunkPlugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print('arguments: ', args)
            print('unknowns: ', unknowns)

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('schunk_ui'),
                               'resource', 'ui', 'SchunkJoints.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('SchunkPluginUI')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        # Connect to ROS
        # action clients
        self.action_client = actionlib.SimpleActionClient(
            '/gripper/sdh_controller/follow_joint_trajectory',
            FollowJointTrajectoryAction)

        # subscribers
        self.sub_temp = rospy.Subscriber("/gripper/sdh_controller/temperature",
                                         TemperatureArray, self.on_temp)
        self.sub_tactile = rospy.Subscriber("/gripper/sdh_controller/pressure",
                                            PressureArrayList, self.on_tactile)

        # maximum measurable pressure
        # 4096 * calib_pressure / calib_voltage
        # 2^12 * 0.000473 / 592.1 = 0.0032720959297415976 (unit: N/(mm*mm) )
        self.max_pressure = 2**12 * 0.000473 / 592.1

        self.max_pressure_readings = 0

        # Connect to UI
        # service buttons
        self._widget.button_init.clicked.connect(
            lambda: self.call_service("init"))
        self._widget.button_disconnect.clicked.connect(
            lambda: self.call_service("shutdown"))
        self._widget.button_estop.clicked.connect(
            lambda: self.call_service("emergency_stop"))
        self._widget.button_motor_on.clicked.connect(
            lambda: self.call_service("motor_on"))
        self._widget.button_motor_off.clicked.connect(
            lambda: self.call_service("motor_off"))

        self._widget.button_reset_max_pressure.clicked.connect(
            lambda: self.reset_max_pressure_readings())

        # status text
        self.status_message = self._widget.status_message

        # joint sliders
        self._widget.proximal_slider.valueChanged.connect(
            lambda value: self.on_slider_update(self._widget.proximal_spinbox,
                                                value))
        self._widget.distal_slider.valueChanged.connect(
            lambda value: self.on_slider_update(self._widget.distal_spinbox,
                                                value))
        # joint spinners
        self._widget.proximal_spinbox.valueChanged.connect(
            lambda value: self.on_spinner_update(self._widget.proximal_slider,
                                                 value))
        self._widget.distal_spinbox.valueChanged.connect(
            lambda value: self.on_spinner_update(self._widget.distal_slider,
                                                 value))

        # set spinner boxes by default sliders values
        self._widget.proximal_spinbox.setValue(
            self._widget.proximal_slider.value() / 1000.0)
        self._widget.distal_spinbox.setValue(
            self._widget.distal_slider.value() / 1000.0)

        # map temperature names to spinner boxes
        self.tempspinners = dict()
        self.tempspinners["root"] = self._widget.spin_root
        self.tempspinners["controller"] = self._widget.spin_ctrl
        self.tempspinners["pcb"] = self._widget.spin_pcb

        self.is_initialised = False
        self.is_motor_on = False
        self.has_new_data = False

        # start working thread
        self.running = True
        self.thread = threading.Thread(target=self.loop, args=())
        self.thread.start()

    def on_slider_update(self, spinner, value):
        # just set spinner value, do not forward signal back to slider
        spinner.blockSignals(True)
        spinner.setValue(value / 1000.0)
        spinner.blockSignals(False)
        self.has_new_data = True

    def on_spinner_update(self, slider, value):
        # just set slider value, do not forward signal back to spinner
        slider.blockSignals(True)
        slider.setValue(value * 1000)
        slider.blockSignals(False)
        self.has_new_data = True

    def call_service(self, name):
        service_name = '/gripper/sdh_controller/' + name

        try:
            rospy.wait_for_service(service_name, timeout=0.5)
        except rospy.exceptions.ROSException:
            rospy.logerr("service '" + str(name) + "' is not available")
            self.status_message.setText("service '" + str(name) +
                                        "' is not available")
            return False

        service = rospy.ServiceProxy(service_name, Trigger)
        resp = service()

        print("Called service:", name)
        print("Response:")
        print(resp)
        self.status_message.setText(resp.message)

        if name == "init":
            self.is_initialised = resp.success
            self.is_motor_on = resp.success
        elif name == "shutdown":
            self.is_initialised = not resp.success
            self.is_motor_on = not resp.success

        if name == "motor_on":
            self.is_motor_on = resp.success
        elif name == "motor_off":
            self.is_motor_on = not resp.success

        if resp.success and (name in ["init", "motor_on"]):
            self.has_new_data = True

        return resp.success

    def loop(self):
        self.running = True
        while self.running:
            if self.is_initialised and self.is_motor_on and self.has_new_data:
                self.send_grasp_joint_positions()
                self.has_new_data = False
            time.sleep(0.1)

    def send_grasp_joint_positions(self):
        # values in range 0 ... 1
        proximal_value = self._widget.proximal_spinbox.value()
        distal_value = self._widget.distal_spinbox.value()

        # define sets of joints
        proximal_joints = [
            "sdh_thumb_2_joint", "sdh_finger_12_joint", "sdh_finger_22_joint"
        ]
        distal_joints = [
            "sdh_thumb_3_joint", "sdh_finger_13_joint", "sdh_finger_23_joint"
        ]
        static_joints = ["sdh_knuckle_joint"]
        all_joints = static_joints + proximal_joints + distal_joints

        # map joint ranges from [0..1] to individual set ranges
        # proximal range: [-pi/2 .. 0]
        # distal range: [0 .. pi/2]

        proximal_range = [-math.pi / 2.0, 0.0]
        distal_range = [0.0, math.pi / 2.0]
        proximal_jpos = proximal_range[0] + proximal_value * (
            proximal_range[1] - proximal_range[0])
        distal_jpos = distal_range[0] + distal_value * (distal_range[1] -
                                                        distal_range[0])

        trajectory_goal = FollowJointTrajectoryGoal()

        # add single single joint point to trajectory
        trajectory_goal.trajectory.points.append(JointTrajectoryPoint())
        for jname in all_joints:
            trajectory_goal.trajectory.joint_names.append(jname)
            # select joint position from set
            if jname in static_joints:
                trajectory_goal.trajectory.points[0].positions.append(0)
            elif jname in proximal_joints:
                trajectory_goal.trajectory.points[0].positions.append(
                    proximal_jpos)
            elif jname in distal_joints:
                trajectory_goal.trajectory.points[0].positions.append(
                    distal_jpos)
            else:
                raise Exception("joint not in set")

        # send trajectory and wait for response
        self.action_client.send_goal(trajectory_goal)

        if self.action_client.wait_for_result(timeout=rospy.Duration(5.0)):
            trajectory_result = self.action_client.get_result()
            print("set joints to " +
                  ('%s' % trajectory_goal.trajectory.points[0].positions))
            self.status_message.setText("set joints")
            return trajectory_result.error_code == FollowJointTrajectoryResult.SUCCESSFUL
        else:
            rospy.logerr("timeout while waiting for response from grasp goal")
            self.status_message.setText(
                "timeout while waiting for response from grasp goal")
            return False

    def on_temp(self, temp_msg):
        if self.is_initialised:
            temps = dict(zip(temp_msg.name, temp_msg.temperature))
            for name, spinner in self.tempspinners.iteritems():
                try:
                    spinner.setValue(temps[name])
                except KeyError:
                    rospy.logerr("temperature", name,
                                 "is not provided by SDH driver node")

    @staticmethod
    def jet(m):
        # clip values to range [0,1]
        m = np.clip(m, 0.0, 1.0)
        r = np.clip(np.minimum(4 * m - 1.5, -4 * m + 4.5), 0.0, 1.0)
        g = np.clip(np.minimum(4 * m - 0.5, -4 * m + 3.5), 0.0, 1.0)
        b = np.clip(np.minimum(4 * m + 0.5, -4 * m + 2.5), 0.0, 1.0)
        return (np.dstack((r, g, b)) * 255).astype(np.uint8)

    def on_tactile(self, msg_tactile):
        for m in msg_tactile.pressure_list:
            p = np.array(m.pressure, dtype=np.float64).reshape(
                (m.cells_y, m.cells_x))
            # apply colour map to scaled values
            im = SchunkPlugin.jet(p / self.max_pressure)

            getattr(self._widget,
                    "lbl_" + m.sensor_name).setText(m.sensor_name + ":")
            getattr(self._widget,
                    "max_" + m.sensor_name).setText("{:.9f}".format(np.max(p)))

            if np.max(p) > self.max_pressure_readings:
                self.max_pressure_readings = np.max(p)
                self._widget.max_pressure.setText(
                    str(self.max_pressure_readings))

            # label for tactile content
            lbl = getattr(self._widget, "tactile_" + m.sensor_name)
            qimg = QImage(im.data, im.shape[1], im.shape[0], 3 * im.shape[1],
                          QImage.Format_RGB888)
            T = QTransform()
            T.rotate(90)
            if self._widget.checkBox_show_img.isChecked():
                qpix = QPixmap.fromImage(qimg).transformed(T)
                qpix = qpix.scaledToWidth(qpix.width() * 20)
                lbl.setPixmap(qpix)
            else:
                lbl.setText("image placeholder")

    def reset_max_pressure_readings(self):
        self.max_pressure_readings = 0
        self._widget.max_pressure.setText(str(self.max_pressure_readings))

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        self.sub_temp.unregister()
        self.sub_tactile.unregister()

        self.action_client.cancel_all_goals()
        self.running = False
        self.thread.join()

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #30
0
class Planner(Plugin):
	def __init__(self, context):
		super(Planner, self).__init__(context)
		# Give QObjects reasonable names
		self.setObjectName('ContrailPlanner')
		rp = rospkg.RosPack()

		# Process standalone plugin command-line arguments
		#from argparse import ArgumentParser
		#parser = ArgumentParser()
		# Add argument(s) to the parser.
		#parser.add_argument("-q", "--quiet", action="store_true",
		#              dest="quiet",
		#              help="Put plugin in silent mode")
		#args, unknowns = parser.parse_known_args(context.argv())
		#if not args.quiet:
		#    print 'arguments: ', args
		#    print 'unknowns: ', unknowns

		# Create QWidget
		self._widget = QWidget()
		# Get path to UI file which is a sibling of this file
		# in this example the .ui and .py file are in the same folder
		ui_file = os.path.join(rp.get_path('rqt_contrail_planner'), 'resource', 'ContrailPlanner.ui')
		# Extend the widget with all attributes and children from UI file
		loadUi(ui_file, self._widget)
		# Give QObjects reasonable names
		self._widget.setObjectName('ContrailPlannerUi')
		# Show _widget.windowTitle on left-top of each plugin (when
		# it's set in _widget). This is useful when you open multiple
		# plugins at once. Also if you open multiple instances of your
		# plugin at once, these lines add number to make it easy to
		# tell from pane to pane.
		if context.serial_number() > 1:
			self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
		# Add widget to the user interface
		context.add_widget(self._widget)

		# Interface Functionality

		# Plot Tabs
		self._widget.tabgroup_plots.currentChanged.connect(self.draw_focused_plot)

		# Management
		self._widget.button_load.clicked.connect(self.button_load_pressed)
		self._widget.button_save.clicked.connect(self.button_save_pressed)
		self._widget.button_save_as.clicked.connect(self.button_save_as_pressed)
		self._widget.button_reset.clicked.connect(self.button_reset_pressed)
		self._widget.button_yaw_smooth.clicked.connect(self.button_yaw_smooth_pressed)

		# Flight Plan
		self._widget.combobox_mode.currentIndexChanged.connect(self.mode_changed)
		self._widget.input_duration.textEdited.connect(self.set_flight_plan_duration)
		self._widget.input_nom_vel.textEdited.connect(self.set_flight_plan_nom_vel)
		self._widget.input_nom_rate.textEdited.connect(self.set_flight_plan_nom_rate)

		# Waypoint
		self._widget.table_waypoints.currentItemChanged.connect(self.list_item_changed)
		#self._widget.table_waypoints.itemChanged.connect(self.waypoint_item_changed)
		self._widget.input_x.returnPressed.connect(self.button_overwrite_pressed)
		self._widget.input_y.returnPressed.connect(self.button_overwrite_pressed)
		self._widget.input_z.returnPressed.connect(self.button_overwrite_pressed)
		self._widget.input_psi.returnPressed.connect(self.button_overwrite_pressed)

		self._widget.button_insert.clicked.connect(self.button_insert_pressed)
		self._widget.button_append.clicked.connect(self.button_append_pressed)
		self._widget.button_overwrite.clicked.connect(self.button_overwrite_pressed)
		self._widget.button_move_up.clicked.connect(self.button_move_up_pressed)
		self._widget.button_move_down.clicked.connect(self.button_move_down_pressed)
		self._widget.button_remove.clicked.connect(self.button_remove_pressed)

		# Class Variales
		self.loaded_movement = Movement()
		self.num_interp = 25

		#Create plot figures
		self.plot_3d_figure = Figure()
		self.plot_3d_figure.patch.set_facecolor('white')
		self.plot_3d_canvas = FigureCanvas(self.plot_3d_figure)
		self.plot_3d_toolbar = NavigationToolbar(self.plot_3d_canvas, self._widget.widget_plot_3d)
		self.plot_3d_ax = self.plot_3d_figure.add_subplot(1,1,1, projection='3d')
		self.set_plot_layout(self._widget.widget_plot_3d, self.plot_3d_toolbar, self.plot_3d_canvas)
		self.plot_3d_ax.set_title('3D Projection')
		self.plot_3d_ax.set_xlabel('Position (X)')
		self.plot_3d_ax.set_ylabel('Position (Y)')
		self.plot_3d_ax.set_zlabel('Position (Z)')
		self.plot_3d_ax.set_aspect('equal', 'box')

		self.plot_x_figure = Figure()
		self.plot_x_figure.patch.set_facecolor('white')
		self.plot_x_canvas = FigureCanvas(self.plot_x_figure)
		self.plot_x_toolbar = NavigationToolbar(self.plot_x_canvas, self._widget.widget_plot_x)
		(self.plot_x_ax_pos, self.plot_x_ax_vel, self.plot_x_ax_acc) = self.set_subplots(self.plot_x_figure, 'X', 'm')
		self.set_plot_layout(self._widget.widget_plot_x, self.plot_x_toolbar, self.plot_x_canvas)

		self.plot_y_figure = Figure()
		self.plot_y_figure.patch.set_facecolor('white')
		self.plot_y_canvas = FigureCanvas(self.plot_y_figure)
		self.plot_y_toolbar = NavigationToolbar(self.plot_y_canvas, self._widget.widget_plot_y)
		(self.plot_y_ax_pos, self.plot_y_ax_vel, self.plot_y_ax_acc) = self.set_subplots(self.plot_y_figure, 'Y', 'm')
		self.set_plot_layout(self._widget.widget_plot_y, self.plot_y_toolbar, self.plot_y_canvas)

		self.plot_z_figure = Figure()
		self.plot_z_figure.patch.set_facecolor('white')
		self.plot_z_canvas = FigureCanvas(self.plot_z_figure)
		self.plot_z_toolbar = NavigationToolbar(self.plot_z_canvas, self._widget.widget_plot_z)
		(self.plot_z_ax_pos, self.plot_z_ax_vel, self.plot_z_ax_acc) = self.set_subplots(self.plot_z_figure, 'Z', 'm')
		self.set_plot_layout(self._widget.widget_plot_z, self.plot_z_toolbar, self.plot_z_canvas)

		self.plot_psi_figure = Figure()
		self.plot_psi_figure.patch.set_facecolor('white')
		self.plot_psi_canvas = FigureCanvas(self.plot_psi_figure)
		self.plot_psi_toolbar = NavigationToolbar(self.plot_psi_canvas, self._widget.widget_plot_psi)
		(self.plot_psi_ax_pos, self.plot_psi_ax_vel, self.plot_psi_ax_acc) = self.set_subplots(self.plot_psi_figure, 'Yaw', 'rad')
		self.set_plot_layout(self._widget.widget_plot_psi, self.plot_psi_toolbar, self.plot_psi_canvas)

		self.reset_flight_plan()

	def set_subplots(self, figure, title, unit):
		ax_pos = figure.add_subplot(3,1,1)
		ax_vel = figure.add_subplot(3,1,2)
		ax_acc = figure.add_subplot(3,1,3)

		ax_pos.grid(b=True)
		ax_vel.grid(b=True)
		ax_acc.grid(b=True)

		ax_pos.set_title(title + ' Axis')
		ax_pos.set_ylabel('Pos. (' + unit + ')')
		ax_vel.set_ylabel('Vel. (' + unit + '/s)')
		ax_acc.set_ylabel('Acc. (' + unit + '/s/s)')
		ax_acc.set_xlabel('Time (s)')

		return (ax_pos, ax_vel, ax_acc)

	def set_plot_layout(self, widget, toolbar, canvas):
		layout = QVBoxLayout()
		layout.addWidget(toolbar)
		layout.addWidget(canvas)
		widget.setLayout(layout)

	def shutdown_plugin(self):
		pass

	def save_settings(self, plugin_settings, instance_settings):
		instance_settings.set_value("num_interp", self.num_interp)

	def restore_settings(self, plugin_settings, instance_settings):
		try:
			self.num_interp = int( instance_settings.value("num_interp") )
		except (AttributeError,TypeError) as e:
			self.num_interp = 25

	#def trigger_configuration(self):
		#TODO: allow user to set 'self.num_interp'

	def button_save_pressed(self):
		if not self.loaded_movement.filename:
			self.button_save_as_pressed()

		if self.loaded_movement.filename:
			self.set_flight_plan()

			yaml_move = self.loaded_movement.encode_yaml()

			with open(self.loaded_movement.filename, 'w') as outfile:
				yaml.dump(yaml_move, outfile,default_flow_style = False)

			rospy.loginfo("Movements saved: %s" % self.loaded_movement.filename)
		else:
			rospy.logerr("Can't save, no filename set")

	def button_save_as_pressed(self):
		(name,filt) = QFileDialog.getSaveFileName(caption='Save Movement',filter="YAML (*.yaml)")
		self.loaded_movement.filename = name

		if self.loaded_movement.filename:
			self.button_save_pressed()
		else:
			rospy.logerr("No filename specified")

	def button_load_pressed(self):
		(name,filt) = QFileDialog.getOpenFileName(caption='Open Movement')

		if name:
			stream = open(name, 'r')

			try:
				with stream:
					self.loaded_movement = Movement(filename=name, movement=yaml.safe_load(stream))
			except yaml.YAMLError as e:
				rospy.logerr(e)

		self.update_display()

	def button_reset_pressed(self):
		self.reset_flight_plan()


	def make_yaw_continuous(self, psi_c, psi_p):
		while math.fabs(psi_c - psi_p) > math.pi:
			if psi_c > psi_p:
				psi_c -= 2*math.pi
			else:
				psi_c += 2*math.pi

		return psi_c

	def button_yaw_smooth_pressed(self):
		for i in xrange(len(self.loaded_movement.waypoints)):
			if i > 1:
				self.loaded_movement.waypoints[i].yaw = self.make_yaw_continuous(self.loaded_movement.waypoints[i].yaw,self.loaded_movement.waypoints[i-1].yaw)

		self.update_display()

	def reset_flight_plan(self):
		self.plot_3d_ax.view_init(azim=-135)

		self.loaded_movement = Movement()

		self.update_display()

	def update_display(self):
		self.clear_display()

		self.update_flight_plan()

		for i in xrange(len(self.loaded_movement.waypoints)):
			self._widget.table_waypoints.insertRow(i)
			item_x = QTableWidgetItem(str(self.loaded_movement.waypoints[i].x))
			item_y = QTableWidgetItem(str(self.loaded_movement.waypoints[i].y))
			item_z = QTableWidgetItem(str(self.loaded_movement.waypoints[i].z))
			item_yaw = QTableWidgetItem(str(self.loaded_movement.waypoints[i].yaw))
			item_x.setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
			item_y.setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
			item_z.setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
			item_yaw.setTextAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)
			self._widget.table_waypoints.setItem(i, 0, item_x)
			self._widget.table_waypoints.setItem(i, 1, item_y)
			self._widget.table_waypoints.setItem(i, 2, item_z)
			self._widget.table_waypoints.setItem(i, 3, item_yaw)

		self.update_plot()

	def quiver_dir_from_yaw(self,psi):
		n = len(psi)
		u = [0.0]*n
		v = [0.0]*n
		w = [0.0]*n

		for i in xrange(n):
			u[i] = math.cos(psi[i])
			v[i] = math.sin(psi[i])
			#w[i] = 0.0
		return (u,v,w)

	def update_plot(self):
		print("plot update")
		self.clear_plot()

		if len(self.loaded_movement.waypoints):
			plot_data = [[],[],[],[]]
			for i in xrange(len(self.loaded_movement.waypoints)):
				plot_data[0].append(self.loaded_movement.waypoints[i].x)
				plot_data[1].append(self.loaded_movement.waypoints[i].y)
				plot_data[2].append(self.loaded_movement.waypoints[i].z)
				plot_data[3].append(self.loaded_movement.waypoints[i].yaw)

			d = float(self.loaded_movement.duration)
			d2 = d*d

			# Plot data
			n = len(plot_data[0])
			ni = n
			t = [0.0]*n
			x = plot_data[0]
			xd = [0.0]*n
			xdd = [0.0]*n
			y = plot_data[1]
			yd = [0.0]*n
			ydd = [0.0]*n
			z = plot_data[2]
			zd = [0.0]*n
			zdd = [0.0]*n
			psi = plot_data[3]
			psid = [0.0]*n
			psidd = [0.0]*n
			ti = [0.0]*ni
			xi = x
			xdi = [0.0]*ni
			xddi = [0.0]*ni
			yi = y
			ydi = [0.0]*ni
			yddi = [0.0]*ni
			zi = z
			zdi = [0.0]*ni
			zddi = [0.0]*ni
			psii = psi
			psidi = [0.0]*ni
			psiddi = [0.0]*ni

			if (n > 1) and (d > 0.0) and not self.loaded_movement.is_discrete:
				dt = d / (n-1)
				t = [dt * i for i in range(n)]

				# Plot spline display if in contiuous mode
				iqs_x = InterpolatedQuinticSpline()
				iqs_y = InterpolatedQuinticSpline()
				iqs_z = InterpolatedQuinticSpline()
				iqs_psi = InterpolatedQuinticSpline()

				if iqs_x.interpolate(x) and iqs_y.interpolate(y) and iqs_z.interpolate(z) and iqs_psi.interpolate(psi):
					xd = [i/d for i in iqs_x.get_dvias()]
					xdd = [i/d2 for i in iqs_x.get_ddvias()]
					yd = [i/d for i in iqs_y.get_dvias()]
					ydd = [i/d2 for i in iqs_y.get_ddvias()]
					zd = [i/d for i in iqs_z.get_dvias()]
					zdd = [i/d2 for i in iqs_z.get_ddvias()]
					psid = [i/d for i in iqs_psi.get_dvias()]
					psidd = [i/d2 for i in iqs_psi.get_ddvias()]

					ni = n*self.num_interp
					ui = [float(i) / (ni-1) for i in range(ni)]
					xvi = [iqs_x.lookup(u) for u in ui]
					yvi = [iqs_y.lookup(u) for u in ui]
					zvi = [iqs_z.lookup(u) for u in ui]
					psivi = [iqs_psi.lookup(u) for u in ui]

					xi = [i[0] for i in xvi]
					xdi = [i[1]/d for i in xvi]
					xddi = [i[2]/d2 for i in xvi]

					yi = [i[0] for i in yvi]
					ydi = [i[1]/d for i in yvi]
					yddi = [i[2]/d2 for i in yvi]

					zi = [i[0] for i in zvi]
					zdi = [i[1]/d for i in zvi]
					zddi = [i[2]/d2 for i in zvi]

					psii = [i[0] for i in psivi]
					psidi = [i[1]/d for i in psivi]
					psiddi = [i[2]/d2 for i in psivi]

					dti = d / (ni-1)
					ti = [dti * i for i in range(ni)]
				else:
					rospy.logerr("Could not interpolate spline!")

				self.plot_3d_ax.plot(x, y, z, 'b--')
				self.plot_3d_ax.plot(xi, yi, zi, 'g-')
				self.plot_x_ax_pos.plot(ti, xi, 'g-')
				self.plot_x_ax_vel.plot(ti, xdi, 'g-')
				self.plot_x_ax_acc.plot(ti, xddi, 'g-')
				self.plot_y_ax_pos.plot(ti, yi, 'g-')
				self.plot_y_ax_vel.plot(ti, ydi, 'g-')
				self.plot_y_ax_acc.plot(ti, yddi, 'g-')
				self.plot_z_ax_pos.plot(ti, zi, 'g-')
				self.plot_z_ax_vel.plot(ti, zdi, 'g-')
				self.plot_z_ax_acc.plot(ti, zddi, 'g-')
				self.plot_psi_ax_pos.plot(ti, psii, 'g-')
				self.plot_psi_ax_vel.plot(ti, psidi, 'g-')
				self.plot_psi_ax_acc.plot(ti, psiddi, 'g-')
			# Visual waypoint data
			elif self.loaded_movement.is_discrete:
				# Generate fake t data for some form of display
				t = xrange(len(x))
				self.plot_3d_ax.plot(x, y, z, 'g-')
				self.plot_x_ax_pos.plot(t, x, 'g-')
				self.plot_x_ax_vel.plot(t, xd, 'g-')
				self.plot_x_ax_acc.plot(t, xdd, 'g-')
				self.plot_y_ax_pos.plot(t, y, 'g-')
				self.plot_y_ax_vel.plot(t, yd, 'g-')
				self.plot_y_ax_acc.plot(t, ydd, 'g-')
				self.plot_z_ax_pos.plot(t, z, 'g-')
				self.plot_z_ax_vel.plot(t, zd, 'g-')
				self.plot_z_ax_acc.plot(t, zdd, 'g-')
				self.plot_psi_ax_pos.plot(t, psi, 'g-')
				self.plot_psi_ax_vel.plot(t, psid, 'g-')
				self.plot_psi_ax_acc.plot(t, psidd, 'g-')

			self.plot_3d_ax.plot(x, y, z, 'bo')
			(qu,qv,qw) = self.quiver_dir_from_yaw(psi)
			self.plot_3d_ax.quiver(x, y, z, qu, qv, qw, length=0.25, pivot='tail')#, cmap='Reds', lw=2)
			self.plot_x_ax_pos.plot(t, x, 'bo')
			self.plot_x_ax_vel.plot(t, xd, 'bo')
			self.plot_x_ax_acc.plot(t, xdd, 'bo')
			self.plot_y_ax_pos.plot(t, y, 'bo')
			self.plot_y_ax_vel.plot(t, yd, 'bo')
			self.plot_y_ax_acc.plot(t, ydd, 'bo')
			self.plot_z_ax_pos.plot(t, z, 'bo')
			self.plot_z_ax_vel.plot(t, zd, 'bo')
			self.plot_z_ax_acc.plot(t, zdd, 'bo')
			self.plot_psi_ax_pos.plot(t, psi, 'bo')
			self.plot_psi_ax_vel.plot(t, psid, 'bo')
			self.plot_psi_ax_acc.plot(t, psidd, 'bo')

			sel_ind = self._widget.table_waypoints.currentRow()
			if sel_ind >= 0:
				self.plot_3d_ax.plot([x[sel_ind]], [y[sel_ind]], [z[sel_ind]], 'ro')
				self.plot_3d_ax.quiver([x[sel_ind]], [y[sel_ind]], [z[sel_ind]], [qu[sel_ind]], [qv[sel_ind]], [qw[sel_ind]], length=0.25, pivot='tail', colors=[[1.0,0.0,0.0]], lw=3)

				self.plot_x_ax_pos.plot([t[sel_ind]], [x[sel_ind]], 'ro')
				self.plot_x_ax_vel.plot([t[sel_ind]], [xd[sel_ind]], 'ro')
				self.plot_x_ax_acc.plot([t[sel_ind]], [xdd[sel_ind]], 'ro')
				self.plot_y_ax_pos.plot([t[sel_ind]], [y[sel_ind]], 'ro')
				self.plot_y_ax_vel.plot([t[sel_ind]], [yd[sel_ind]], 'ro')
				self.plot_y_ax_acc.plot([t[sel_ind]], [ydd[sel_ind]], 'ro')
				self.plot_z_ax_pos.plot([t[sel_ind]], [z[sel_ind]], 'ro')
				self.plot_z_ax_vel.plot([t[sel_ind]], [zd[sel_ind]], 'ro')
				self.plot_z_ax_acc.plot([t[sel_ind]], [zdd[sel_ind]], 'ro')
				self.plot_psi_ax_pos.plot([t[sel_ind]], [psi[sel_ind]], 'ro')
				self.plot_psi_ax_vel.plot([t[sel_ind]], [psid[sel_ind]], 'ro')
				self.plot_psi_ax_acc.plot([t[sel_ind]], [psidd[sel_ind]], 'ro')


			# Calculate nice limits for 3D
			minx = min([min(x),min(xi)])
			miny = min([min(y),min(yi)])
			minz = min([min(z),min(zi)])
			maxx = max([max(x),max(xi)])
			maxy = max([max(y),max(yi)])
			maxz = max([max(z),max(zi)])

			max_range = 1.25*max([maxx-minx, maxy-miny, maxz-minz]) / 2.0

			mid_x = (maxx+minx) / 2.0
			mid_y = (maxy+miny) / 2.0
			mid_z = (maxz+minz) / 2.0

			if max_range:
				self.plot_3d_ax.set_xlim(mid_x - max_range, mid_x + max_range)
				self.plot_3d_ax.set_ylim(mid_y - max_range, mid_y + max_range)
				self.plot_3d_ax.set_zlim(mid_z - max_range, mid_z + max_range)

			self.set_nice_limits_2d(self.plot_x_ax_pos, 0, t[-1], min([min(x),min(xi)]), max([max(x),max(xi)]))
			self.set_nice_limits_2d(self.plot_x_ax_vel, 0, t[-1], min([min(xd),min(xdi)]), max([max(xd),max(xdi)]))
			self.set_nice_limits_2d(self.plot_x_ax_acc, 0, t[-1], min([min(xdd),min(xddi)]), max([max(xdd),max(xddi)]))
			self.set_nice_limits_2d(self.plot_y_ax_pos, 0, t[-1], min([min(y),min(yi)]), max([max(y),max(yi)]))
			self.set_nice_limits_2d(self.plot_y_ax_vel, 0, t[-1], min([min(yd),min(ydi)]), max([max(yd),max(ydi)]))
			self.set_nice_limits_2d(self.plot_y_ax_acc, 0, t[-1], min([min(ydd),min(yddi)]), max([max(ydd),max(yddi)]))
			self.set_nice_limits_2d(self.plot_z_ax_pos, 0, t[-1], min([min(z),min(zi)]), max([max(z),max(zi)]))
			self.set_nice_limits_2d(self.plot_z_ax_vel, 0, t[-1], min([min(zd),min(zdi)]), max([max(zd),max(zdi)]))
			self.set_nice_limits_2d(self.plot_z_ax_acc, 0, t[-1], min([min(zdd),min(zddi)]), max([max(zdd),max(zddi)]))
			self.set_nice_limits_2d(self.plot_psi_ax_pos, 0, t[-1], min([min(psi),min(psii)]), max([max(psi),max(psii)]))
			self.set_nice_limits_2d(self.plot_psi_ax_vel, 0, t[-1], min([min(psid),min(psidi)]), max([max(psid),max(psidi)]))
			self.set_nice_limits_2d(self.plot_psi_ax_acc, 0, t[-1], min([min(psidd),min(psiddi)]), max([max(psidd),max(psiddi)]))

		# Refresh current canvas
		self.draw_focused_plot(self._widget.tabgroup_plots.currentIndex())

	def set_nice_limits_2d(self, ax, x_min, x_max, y_min, y_max):
		mr = 1.25*(y_max-y_min) / 2.0
		y_mid = (y_max+y_min) / 2.0
		ax.set_xlim(x_min, x_max)
		ax.set_ylim(y_mid - mr, y_mid + mr)

	def draw_focused_plot(self,index):
		if(index == 0):
			self.plot_3d_canvas.draw()
		elif(index == 1):
			self.plot_x_ax_pos.relim()
			self.plot_x_ax_vel.relim()
			self.plot_x_ax_acc.relim()
			self.plot_x_canvas.draw()
		elif(index == 2):
			self.plot_y_ax_pos.relim()
			self.plot_y_ax_vel.relim()
			self.plot_y_ax_acc.relim()
			self.plot_y_canvas.draw()
		elif(index == 3):
			self.plot_z_ax_pos.relim()
			self.plot_z_ax_vel.relim()
			self.plot_z_ax_acc.relim()
			self.plot_z_canvas.draw()
		elif(index == 4):
			self.plot_psi_ax_pos.relim()
			self.plot_psi_ax_vel.relim()
			self.plot_psi_ax_acc.relim()
			self.plot_psi_canvas.draw()

	def clear_plot(self):
		# Discards the old graph data
		artists = self.plot_3d_ax.lines + self.plot_3d_ax.collections \
				+ self.plot_x_ax_pos.lines + self.plot_x_ax_pos.collections \
				+ self.plot_x_ax_vel.lines + self.plot_x_ax_vel.collections \
				+ self.plot_x_ax_acc.lines + self.plot_x_ax_acc.collections \
				+ self.plot_y_ax_pos.lines + self.plot_y_ax_pos.collections \
				+ self.plot_y_ax_vel.lines + self.plot_y_ax_vel.collections \
				+ self.plot_y_ax_acc.lines + self.plot_y_ax_acc.collections \
				+ self.plot_z_ax_pos.lines + self.plot_z_ax_pos.collections \
				+ self.plot_z_ax_vel.lines + self.plot_z_ax_vel.collections \
				+ self.plot_z_ax_acc.lines + self.plot_z_ax_acc.collections \
				+ self.plot_psi_ax_pos.lines + self.plot_psi_ax_pos.collections \
				+ self.plot_psi_ax_vel.lines + self.plot_psi_ax_vel.collections \
				+ self.plot_psi_ax_acc.lines + self.plot_psi_ax_acc.collections

		for artist in artists:
			artist.remove()

	def clear_inputs(self):
		self._widget.input_x.setText("0.0")
		self._widget.input_y.setText("0.0")
		self._widget.input_z.setText("0.0")
		self._widget.input_psi.setText("0.0")

	def clear_display(self):
		self._widget.table_waypoints.setRowCount(0)
		self.clear_inputs()
		self.clear_plot()

	def mode_changed(self):
		mode = self._widget.combobox_mode.currentText()

		if mode == 'Continuous':
			self.loaded_movement.is_discrete = False

			self._widget.label_duration.setEnabled(True)
			self._widget.input_duration.setEnabled(True)
			self._widget.label_nom_vel.setEnabled(False)
			self._widget.input_nom_vel.setEnabled(False)
			self._widget.label_nom_rate.setEnabled(False)
			self._widget.input_nom_rate.setEnabled(False)
		else: # Discrete
			self.loaded_movement.is_discrete = True

			self._widget.label_duration.setEnabled(False)
			self._widget.input_duration.setEnabled(False)
			self._widget.label_nom_vel.setEnabled(True)
			self._widget.input_nom_vel.setEnabled(True)
			self._widget.label_nom_rate.setEnabled(True)
			self._widget.input_nom_rate.setEnabled(True)

		self.update_plot()

	#def waypoint_item_changed(self, item):
	#	if item:
	#		ind = item.row()
	#		dind = item.column()
	#		data = float(item.text())
	#		if dind == 0:
	#			self.loaded_movement.waypoints[ind].x = data
	#		elif dind == 1:
	#			self.loaded_movement.waypoints[ind].y = data
	#		elif dind == 2:
	#			self.loaded_movement.waypoints[ind].z = data
	#		elif dind == 3:
	#			self.loaded_movement.waypoints[ind].yaw = data
	#
	#		self.list_item_changed()
	#		self.update_plot()

	def list_item_changed(self):
		ind = self._widget.table_waypoints.currentRow()
		if ind >= 0:
			wp = self.loaded_movement.waypoints[ind]

			self._widget.input_x.setText(str(wp.x))
			self._widget.input_y.setText(str(wp.y))
			self._widget.input_z.setText(str(wp.z))
			self._widget.input_psi.setText(str(wp.yaw))
		else:
			self.clear_inputs()

		self.update_plot()

	def set_flight_plan(self):
		mode = self._widget.combobox_mode.currentText()

		if mode == 'Discrete':
			self.loaded_movement.is_discrete = True
		else:
			self.loaded_movement.is_discrete = False

		self.loaded_movement.duration = float(self._widget.input_duration.text())
		self.loaded_movement.nom_vel = float(self._widget.input_nom_vel.text())
		self.loaded_movement.nom_rate = float(self._widget.input_nom_rate.text())

	def set_flight_plan_duration(self, text):
		self.loaded_movement.duration = float(text)
		self.update_plot()

	def set_flight_plan_nom_vel(self, text):
		self.loaded_movement.nom_vel = float(text)
		self.update_plot()

	def set_flight_plan_nom_rate(self, text):
		self.loaded_movement.nom_rate = float(text)
		self.update_plot()

	def update_flight_plan(self):
		self._widget.combobox_mode.currentText()

		if self.loaded_movement.is_discrete:
			self._widget.combobox_mode.setCurrentIndex(1)
		else:
			self._widget.combobox_mode.setCurrentIndex(0)

		self._widget.input_duration.setText(str(self.loaded_movement.duration))
		self._widget.input_nom_vel.setText(str(self.loaded_movement.nom_vel))
		self._widget.input_nom_rate.setText(str(self.loaded_movement.nom_rate))

	def prepare_waypoint(self):
		return Waypoint(x=self._widget.input_x.text(),
						y=self._widget.input_y.text(),
						z=self._widget.input_z.text(),
						yaw=self._widget.input_psi.text())

	def button_insert_pressed(self):
		ind = self._widget.table_waypoints.currentRow()
		self.loaded_movement.waypoints.insert(ind, self.prepare_waypoint())
		self.update_display()
		self._widget.table_waypoints.selectRow(ind)

	def button_append_pressed(self):
		ind = self._widget.table_waypoints.currentRow() + 1
		self.loaded_movement.waypoints.insert(ind, self.prepare_waypoint())
		self.update_display()
		self._widget.table_waypoints.selectRow(ind)

	def button_overwrite_pressed(self):
		ind = self._widget.table_waypoints.currentRow()
		if (len(self.loaded_movement.waypoints) > ind) and (ind >= 0):
			self.loaded_movement.waypoints[ind] = self.prepare_waypoint()
			self.update_display()
			self._widget.table_waypoints.selectRow(ind)

	def button_move_up_pressed(self):
		ind = self._widget.table_waypoints.currentRow()
		if (len(self.loaded_movement.waypoints) > ind) and (ind > 0):
			self.loaded_movement.waypoints.insert(ind-1, self.loaded_movement.waypoints.pop(ind))
			self.update_display()
			self._widget.table_waypoints.selectRow(ind-1)

	def button_move_down_pressed(self):
		ind = self._widget.table_waypoints.currentRow()
		if (len(self.loaded_movement.waypoints)-1 > ind) and (ind >= 0):
			self.loaded_movement.waypoints.insert(ind+1, self.loaded_movement.waypoints.pop(ind))
			self.update_display()
			self._widget.table_waypoints.selectRow(ind+1)

	def button_remove_pressed(self):
		ind = self._widget.table_waypoints.currentRow()
		if (len(self.loaded_movement.waypoints) > ind) and (ind >= 0):
			self.loaded_movement.waypoints.pop(ind)
			self.update_display()
			self._widget.table_waypoints.selectRow(ind-1)
コード例 #31
0
ファイル: gui.py プロジェクト: dariomihelcic/HDR
class TrackAnnotationPlugin(Plugin):
    def __init__(self, context):
        super(TrackAnnotationPlugin, self).__init__(context)
        self.setObjectName('TrackAnnotationPlugin')
        self.shutdownRequested = False

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()

        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true", dest="quiet", help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self.widget = QWidget()
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'gui.ui')
        loadUi(ui_file, self.widget)

        # Give QObjects reasonable names
        self.widget.setObjectName('TrackAnnotationPluginUi')

        if context.serial_number() > 1:
            self.widget.setWindowTitle(self.widget.windowTitle() + (' (%d)' % context.serial_number()))

        # Setup backend components
        self.setupBackend()

        # Populate inputs
        self.widget.genderCombo.addItem("")
        self.widget.genderCombo.addItem(CategoricalAttribute.GENDER_MALE)
        self.widget.genderCombo.addItem(CategoricalAttribute.GENDER_FEMALE)

        self.widget.ageGroupCombo.addItem("")
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_0_TO_2)
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_3_TO_7)
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_8_TO_12)
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_13_TO_19)
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_20_TO_36)
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_37_TO_65)
        self.widget.ageGroupCombo.addItem(CategoricalAttribute.AGE_GROUP_66_TO_99)

        # Register event handlers
        uiActions = UiActions(self.widget, self.database, self.editor, self.simulator, self.visualizer)
        self.uiActions = uiActions

        self.widget.loadDatabaseButton.clicked[bool].connect(uiActions.loadDatabase)
        self.widget.saveDatabaseButton.clicked[bool].connect(uiActions.saveDatabase)
        self.widget.saveDatabaseAsButton.clicked[bool].connect(uiActions.saveDatabaseAs)

        self.widget.newTrackButton.clicked[bool].connect(uiActions.newTrack)
        self.widget.previousTrackButton.clicked[bool].connect(uiActions.previousTrack)
        self.widget.trackIdText.returnPressed.connect(uiActions.trackIdTextChanged)
        self.widget.nextTrackButton.clicked[bool].connect(uiActions.nextTrack)
        self.widget.deleteTrackButton.clicked[bool].connect(uiActions.deleteTrack)
        self.widget.mergeTracksButton.clicked[bool].connect(uiActions.mergeTracks)

        self.widget.waypointFrameCombo.editTextChanged[str].connect(uiActions.waypointFrameChanged)
        self.widget.previousWaypointButton.clicked[bool].connect(uiActions.previousWaypoint)
        self.widget.waypointIndexText.returnPressed.connect(uiActions.waypointIndexTextChanged)
        self.widget.nextWaypointButton.clicked[bool].connect(uiActions.nextWaypoint)
        self.widget.deleteWaypointButton.clicked[bool].connect(uiActions.deleteWaypoint)

        self.widget.genderCombo.editTextChanged[str].connect(uiActions.genderChanged)
        self.widget.ageGroupCombo.editTextChanged[str].connect(uiActions.ageGroupChanged)

        self.widget.syncTracksWithDetectionsCheckbox.stateChanged[int].connect(uiActions.syncTracksWithDetectionsChanged)
        self.widget.publishClockCheckbox.stateChanged[int].connect(uiActions.publishClockChanged)
        self.widget.hideInactiveTrajectoriesCheckbox.stateChanged[int].connect(uiActions.hideInactiveTrajectoriesChanged)
        self.widget.pauseCheckbox.stateChanged[int].connect(uiActions.pauseChanged)

        self.widget.simulationLengthInput.valueChanged[float].connect(uiActions.simulationLengthChanged)
        self.widget.simulationRateInput.valueChanged[float].connect(uiActions.simulationRateChanged)

        # Set default values
        self.widget.syncTracksWithDetectionsCheckbox.setCheckState(0)
        self.widget.hideInactiveTrajectoriesCheckbox.setCheckState(0)
        self.widget.waypointFrameCombo.setEditText("laser_front_link")
        self.widget.publishClockCheckbox.setCheckState(0)

        # Add widget to the user interface
        context.add_widget(self.widget)

        # Initialization done, start update thread
        rospy.loginfo("Track annotation editor has finished loading!")
        self.startUpdateThread()

        # Load database if specified
        databaseToLoad = str(rospy.get_param("~filename", ""))
        if databaseToLoad:
            uiActions.loadGivenDatabase(databaseToLoad)


    def setupBackend(self):
        # Create backend components
        self.transformer = track_annotation_tool.Transformer()
        self.database = track_annotation_tool.Database()
        self.editor = track_annotation_tool.Editor(self.database, self.transformer)
        self.simulator = track_annotation_tool.Simulator(self.database, self.editor, self.transformer)
        self.visualizer = track_annotation_tool.Visualizer(self.database, self.editor, self.transformer)

        # Create ROS subscribers for communication with RViz plugin
        self.waypointSubscriber = WaypointSubscriber(self.editor)

        # Start clock generator (must happen after all calls to rospy.get_param() due to thread synchronization issues)
        self.simulator.clockGenerator.start()

    def startUpdateThread(self):
        # Start update loop
        self.thread = threading.Thread(target=self.updateLoop)
        self.thread.start()

    def updateLoop(self):
        # Run simulation
        lastTime = rospy.Time.now()
        rate = track_annotation_tool.WallRate(30)  # in hertz

        while not self.shutdownRequested:
            currentTime = rospy.Time.now()
            if currentTime.to_sec() < lastTime.to_sec():
                rospy.logwarn("*** TIME MOVED BACKWARDS! RESETTING TRACK POSITIONS! ***")
            lastTime = currentTime

            self.simulator.update()
            self.visualizer.update()

            self.updateAvailableFrames()

            try:
                rate.sleep()
            except rospy.exceptions.ROSInterruptException as e:
                rospy.logwarn(e)

    def updateAvailableFrames(self):
        selectedFrame = self.editor.activeFrameId
        existingFrames = [self.widget.waypointFrameCombo.itemText(i) for i in range(self.widget.waypointFrameCombo.count())]
        for frameId in sorted(self.simulator.transformer.tfListener.getFrameStrings()):
            if not frameId in existingFrames:
                self.widget.waypointFrameCombo.addItem(frameId)
                #rospy.loginfo("New TF frame available: " + frameId)
        self.widget.waypointFrameCombo.setEditText(selectedFrame)

    def shutdown_plugin(self):
        self.shutdownRequested = True
        if self.database.hasUnsavedChanges:
            reply = QMessageBox.warning(self.widget, "Close track annotation tool", "You have unsaved changes. Do you want to save these changes before closing?", QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.uiActions.saveDatabase()

    def save_settings(self, plugin_settings, instance_settings):
        plugin_settings.set_value('default_waypoint_frame', self.editor.activeFrameId)
        plugin_settings.set_value('hide_inactive_trajectories', self.visualizer.hideInactiveTrajectories)

    def restore_settings(self, plugin_settings, instance_settings):
        self.widget.hideInactiveTrajectoriesCheckbox.setCheckState(2 if plugin_settings.value('hide_inactive_trajectories') else 0)
        defaultWaypointFrame = plugin_settings.value('default_waypoint_frame')
        if defaultWaypointFrame:
            self.widget.waypointFrameCombo.setEditText(defaultWaypointFrame)
コード例 #32
0
class RobotSteering(Plugin):

    slider_factor = 1000.0

    def __init__(self, context):
        super(RobotSteering, self).__init__(context)
        self.setObjectName('RobotSteering')

        self._publisher = None

        self._widget = QWidget()
        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_robot_steering'), 'resource', 'RobotSteering.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('RobotSteeringUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        context.add_widget(self._widget)

        self._widget.topic_line_edit.textChanged.connect(self._on_topic_changed)
        self._widget.stop_push_button.pressed.connect(self._on_stop_pressed)

        self._widget.x_linear_slider.valueChanged.connect(self._on_x_linear_slider_changed)
        self._widget.z_angular_slider.valueChanged.connect(self._on_z_angular_slider_changed)

        self._widget.increase_x_linear_push_button.pressed.connect(self._on_strong_increase_x_linear_pressed)
        self._widget.reset_x_linear_push_button.pressed.connect(self._on_reset_x_linear_pressed)
        self._widget.decrease_x_linear_push_button.pressed.connect(self._on_strong_decrease_x_linear_pressed)
        self._widget.increase_z_angular_push_button.pressed.connect(self._on_strong_increase_z_angular_pressed)
        self._widget.reset_z_angular_push_button.pressed.connect(self._on_reset_z_angular_pressed)
        self._widget.decrease_z_angular_push_button.pressed.connect(self._on_strong_decrease_z_angular_pressed)

        self._widget.max_x_linear_double_spin_box.valueChanged.connect(self._on_max_x_linear_changed)
        self._widget.min_x_linear_double_spin_box.valueChanged.connect(self._on_min_x_linear_changed)
        self._widget.max_z_angular_double_spin_box.valueChanged.connect(self._on_max_z_angular_changed)
        self._widget.min_z_angular_double_spin_box.valueChanged.connect(self._on_min_z_angular_changed)

        self.shortcut_w = QShortcut(QKeySequence(Qt.Key_W), self._widget)
        self.shortcut_w.setContext(Qt.ApplicationShortcut)
        self.shortcut_w.activated.connect(self._on_increase_x_linear_pressed)
        self.shortcut_x = QShortcut(QKeySequence(Qt.Key_X), self._widget)
        self.shortcut_x.setContext(Qt.ApplicationShortcut)
        self.shortcut_x.activated.connect(self._on_reset_x_linear_pressed)
        self.shortcut_s = QShortcut(QKeySequence(Qt.Key_S), self._widget)
        self.shortcut_s.setContext(Qt.ApplicationShortcut)
        self.shortcut_s.activated.connect(self._on_decrease_x_linear_pressed)
        self.shortcut_a = QShortcut(QKeySequence(Qt.Key_A), self._widget)
        self.shortcut_a.setContext(Qt.ApplicationShortcut)
        self.shortcut_a.activated.connect(self._on_increase_z_angular_pressed)
        self.shortcut_z = QShortcut(QKeySequence(Qt.Key_Z), self._widget)
        self.shortcut_z.setContext(Qt.ApplicationShortcut)
        self.shortcut_z.activated.connect(self._on_reset_z_angular_pressed)
        self.shortcut_d = QShortcut(QKeySequence(Qt.Key_D), self._widget)
        self.shortcut_d.setContext(Qt.ApplicationShortcut)
        self.shortcut_d.activated.connect(self._on_decrease_z_angular_pressed)

        self.shortcut_shift_w = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_W), self._widget)
        self.shortcut_shift_w.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_w.activated.connect(self._on_strong_increase_x_linear_pressed)
        self.shortcut_shift_x = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_X), self._widget)
        self.shortcut_shift_x.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_x.activated.connect(self._on_reset_x_linear_pressed)
        self.shortcut_shift_s = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_S), self._widget)
        self.shortcut_shift_s.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_s.activated.connect(self._on_strong_decrease_x_linear_pressed)
        self.shortcut_shift_a = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_A), self._widget)
        self.shortcut_shift_a.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_a.activated.connect(self._on_strong_increase_z_angular_pressed)
        self.shortcut_shift_z = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_Z), self._widget)
        self.shortcut_shift_z.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_z.activated.connect(self._on_reset_z_angular_pressed)
        self.shortcut_shift_d = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_D), self._widget)
        self.shortcut_shift_d.setContext(Qt.ApplicationShortcut)
        self.shortcut_shift_d.activated.connect(self._on_strong_decrease_z_angular_pressed)

        self.shortcut_space = QShortcut(QKeySequence(Qt.Key_Space), self._widget)
        self.shortcut_space.setContext(Qt.ApplicationShortcut)
        self.shortcut_space.activated.connect(self._on_stop_pressed)
        self.shortcut_space = QShortcut(QKeySequence(Qt.SHIFT + Qt.Key_Space), self._widget)
        self.shortcut_space.setContext(Qt.ApplicationShortcut)
        self.shortcut_space.activated.connect(self._on_stop_pressed)

        self._widget.stop_push_button.setToolTip(self._widget.stop_push_button.toolTip() + ' ' + self.tr('([Shift +] Space)'))
        self._widget.increase_x_linear_push_button.setToolTip(self._widget.increase_x_linear_push_button.toolTip() + ' ' + self.tr('([Shift +] W)'))
        self._widget.reset_x_linear_push_button.setToolTip(self._widget.reset_x_linear_push_button.toolTip() + ' ' + self.tr('([Shift +] X)'))
        self._widget.decrease_x_linear_push_button.setToolTip(self._widget.decrease_x_linear_push_button.toolTip() + ' ' + self.tr('([Shift +] S)'))
        self._widget.increase_z_angular_push_button.setToolTip(self._widget.increase_z_angular_push_button.toolTip() + ' ' + self.tr('([Shift +] A)'))
        self._widget.reset_z_angular_push_button.setToolTip(self._widget.reset_z_angular_push_button.toolTip() + ' ' + self.tr('([Shift +] Z)'))
        self._widget.decrease_z_angular_push_button.setToolTip(self._widget.decrease_z_angular_push_button.toolTip() + ' ' + self.tr('([Shift +] D)'))

        # timer to consecutively send twist messages
        self._update_parameter_timer = QTimer(self)
        self._update_parameter_timer.timeout.connect(self._on_parameter_changed)
        self._update_parameter_timer.start(100)
        self.zero_cmd_sent = False

    @Slot(str)
    def _on_topic_changed(self, topic):
        topic = str(topic)
        self._unregister_publisher()
        try:
            self._publisher = rospy.Publisher(topic, Twist, queue_size=10)
        except TypeError:
            self._publisher = rospy.Publisher(topic, Twist)

    def _on_stop_pressed(self):
        self._widget.x_linear_slider.setValue(0)
        self._widget.z_angular_slider.setValue(0)

    def _on_x_linear_slider_changed(self):
        self._widget.current_x_linear_label.setText('%0.2f m/s' % (self._widget.x_linear_slider.value() / RobotSteering.slider_factor))
        self._on_parameter_changed()

    def _on_z_angular_slider_changed(self):
        self._widget.current_z_angular_label.setText('%0.2f rad/s' % (self._widget.z_angular_slider.value() / RobotSteering.slider_factor))
        self._on_parameter_changed()

    def _on_increase_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(self._widget.x_linear_slider.value() + self._widget.x_linear_slider.singleStep())

    def _on_reset_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(0)

    def _on_decrease_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(self._widget.x_linear_slider.value() - self._widget.x_linear_slider.singleStep())

    def _on_increase_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(self._widget.z_angular_slider.value() + self._widget.z_angular_slider.singleStep())

    def _on_reset_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(0)

    def _on_decrease_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(self._widget.z_angular_slider.value() - self._widget.z_angular_slider.singleStep())

    def _on_max_x_linear_changed(self, value):
        self._widget.x_linear_slider.setMaximum(value * RobotSteering.slider_factor)

    def _on_min_x_linear_changed(self, value):
        self._widget.x_linear_slider.setMinimum(value * RobotSteering.slider_factor)

    def _on_max_z_angular_changed(self, value):
        self._widget.z_angular_slider.setMaximum(value * RobotSteering.slider_factor)

    def _on_min_z_angular_changed(self, value):
        self._widget.z_angular_slider.setMinimum(value * RobotSteering.slider_factor)

    def _on_strong_increase_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(self._widget.x_linear_slider.value() + self._widget.x_linear_slider.pageStep())

    def _on_strong_decrease_x_linear_pressed(self):
        self._widget.x_linear_slider.setValue(self._widget.x_linear_slider.value() - self._widget.x_linear_slider.pageStep())

    def _on_strong_increase_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(self._widget.z_angular_slider.value() + self._widget.z_angular_slider.pageStep())

    def _on_strong_decrease_z_angular_pressed(self):
        self._widget.z_angular_slider.setValue(self._widget.z_angular_slider.value() - self._widget.z_angular_slider.pageStep())

    def _on_parameter_changed(self):
        self._send_twist(self._widget.x_linear_slider.value() / RobotSteering.slider_factor, self._widget.z_angular_slider.value() / RobotSteering.slider_factor)

    def _send_twist(self, x_linear, z_angular):
        if self._publisher is None:
            return
        twist = Twist()
        twist.linear.x = x_linear
        twist.linear.y = 0
        twist.linear.z = 0
        twist.angular.x = 0
        twist.angular.y = 0
        twist.angular.z = z_angular

        # Only send the zero command once so other devices can take control
        if x_linear == 0 and z_angular == 0:
            if not self.zero_cmd_sent:
                self.zero_cmd_sent = True
                self._publisher.publish(twist)
        else:
            self.zero_cmd_sent = False
            self._publisher.publish(twist)

    def _unregister_publisher(self):
        if self._publisher is not None:
            self._publisher.unregister()
            self._publisher = None

    def shutdown_plugin(self):
        self._update_parameter_timer.stop()
        self._unregister_publisher()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('topic' , self._widget.topic_line_edit.text())
        instance_settings.set_value('vx_max', self._widget.max_x_linear_double_spin_box.value())
        instance_settings.set_value('vx_min', self._widget.min_x_linear_double_spin_box.value()) 
        instance_settings.set_value('vw_max', self._widget.max_z_angular_double_spin_box.value())
        instance_settings.set_value('vw_min', self._widget.min_z_angular_double_spin_box.value())
        
    def restore_settings(self, plugin_settings, instance_settings):
                     
        value = instance_settings.value('topic', "/cmd_vel")
        value = rospy.get_param("~default_topic", value)           
        self._widget.topic_line_edit.setText(value)
        
        value = self._widget.max_x_linear_double_spin_box.value()
        value = instance_settings.value( 'vx_max', value)
        value = rospy.get_param("~default_vx_max", value)           
        self._widget.max_x_linear_double_spin_box.setValue(float(value))
        
        value = self._widget.min_x_linear_double_spin_box.value()
        value = instance_settings.value( 'vx_min', value)
        value = rospy.get_param("~default_vx_min", value)    
        self._widget.min_x_linear_double_spin_box.setValue(float(value))
        
        value = self._widget.max_z_angular_double_spin_box.value()
        value = instance_settings.value( 'vw_max', value)
        value = rospy.get_param("~default_vw_max", value)     
        self._widget.max_z_angular_double_spin_box.setValue(float(value))
        
        value = self._widget.min_z_angular_double_spin_box.value()
        value = instance_settings.value( 'vw_min', value)
        value = rospy.get_param("~default_vw_min", value) 
        self._widget.min_z_angular_double_spin_box.setValue(float(value))
コード例 #33
0
class Dashboard(Plugin):
    autoStateSignal = QtCore.pyqtSignal(int)
    nBallsSignal = QtCore.pyqtSignal(int)
    shooterInRangeSignal = QtCore.pyqtSignal(bool)
    turretInRangeSignal = QtCore.pyqtSignal(bool)

    msg_data = "default"
    def __init__(self, context):
        #Start client -rosbridge
        self.client = roslibpy.Ros(host='localhost', port=5803)
        self.client.run()
        super(Dashboard, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('Dashboard')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_dashboard'), 'resource', 'Dashboard.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('DashboardUi')


        # Set up signal-slot connections
        self._widget.set_imu_angle_button.clicked.connect(self.setImuAngle)
        self._widget.imu_angle.valueChanged.connect(self.imuAngleChanged)
        
        self._widget.auto_wall_dist_button.clicked.connect(self.setAutoWallDist)
        self._widget.auto_wall_dist.valueChanged.connect(self.autoWallDistChanged)
        
        self._widget.ball_reset_button.clicked.connect(self.resetBallCount)
        self._widget.ball_reset_count.valueChanged.connect(self.resetBallChanged)
        
        # Add buttons for auto modes
        v_layout = self._widget.auto_mode_v_layout #vertical layout storing the buttons
        self.auto_mode_button_group = QButtonGroup(self._widget) # needs to be a member variable so the publisher can access it to see which auto mode was selected
        # Search for auto_mode config items
        for i in range(1,100): # loop will exit when can't find the next auto mode, so really only a while loop needed, but exiting at 100 will prevent infinite looping
            if rospy.has_param("/auto/auto_mode_" + str(i)):
                auto_sequence = rospy.get_param("/auto/auto_mode_" + str(i))
               
                new_auto_mode = QWidget()
                new_h_layout = QHBoxLayout()
                new_h_layout.setContentsMargins(0,0,0,0)

                new_button = QRadioButton("Mode " + str(i))
                new_button.setStyleSheet("font-weight: bold") 
                self.auto_mode_button_group.addButton(new_button, i) #second arg is the button's id
                new_h_layout.addWidget( new_button )
                
                new_h_layout.addWidget( QLabel(", ".join(auto_sequence)) )

                hSpacer = QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Minimum)
                new_h_layout.addItem(hSpacer)

                new_auto_mode.setLayout( new_h_layout )
                v_layout.addWidget(new_auto_mode)
            else:
                print(str(i-1) + " auto modes found.")
                # if no auto modes found, inform the user with a label
                if (i-1) == 0:
                    v_layout.addWidget( QLabel("No auto modes found") )
                break #break out of for loop searching for auto modes
            
        # auto state stuff
        self.autoState = 0
        self.displayAutoState() #display initial auto state

        # publish thread
        publish_thread = Thread(target=self.publish_thread) #args=(self,))
        publish_thread.start()

        # number balls display
        self.zero_balls = QPixmap(":/images/0_balls.png")
        self.one_ball = QPixmap(":/images/1_ball.png")
        self.two_balls = QPixmap(":/images/2_balls.png")
        self.three_balls = QPixmap(":/images/3_balls.png")
        self.four_balls = QPixmap(":/images/4_balls.png")
        self.five_balls = QPixmap(":/images/5_balls.png")
        self.more_than_five_balls = QPixmap(":/images/more_than_5_balls.png")
        
        self.n_balls = -1 #don't know n balls at first 

        #in range stuff
        self.shooter_in_range = False
        self.turret_in_range = False
        self.in_range_pixmap = QPixmap(":/images/GreenTarget.png")
        self.not_in_range_pixmap = QPixmap(":/images/RedTarget.png")
        self._widget.in_range_display.setPixmap(self.not_in_range_pixmap)

        # Show _widget.windowTitle on left-top of each plugin (when 
        # it's set in _widget). This is useful when you open multiple 
        # plugins at once. Also if you open multiple instances of your 
        # plugin at once, these lines add number to make it easy to 
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        #initialize subscribers last, so that any callbacks they execute won't interfere with init
        auto_state_listener = roslibpy.Topic(self.client, '/auto/auto_state', 'behavior_actions/AutoState')
        self.auto_state_sub = auto_state_listener.subscribe(self.autoStateCallback)

        n_balls_listener = roslibpy.Topic(self.client,'/num_powercells','std_msgs/UInt8')
        self.n_balls_sub = n_balls_listener.subscribe(self.nBallsCallback)

        shooter_in_range_listener = roslibpy.Topic(self.client, '/shooter/shooter_in_range', std_msgs.msg.Bool)
        self.shooter_in_range_sub = shooter_in_range_listener.subscribe(self.shooterInRangeCallback)

        turret_in_range_listener = roslibpy.Topic(self.client, '/align_to_shoot/turret_in_range', std_msgs.msg.Bool)
        self.turret_in_range_sub = turret_in_range_listener.subscribe(self.turretInRangeCallback)

        self.autoStateSignal.connect(self.autoStateSlot)
        self.nBallsSignal.connect(self.nBallsSlot)
        self.shooterInRangeSignal.connect(self.shooterInRangeSlot)
        self.turretInRangeSignal.connect(self.turretInRangeSlot)


    def autoStateCallback(self, msg):
        self.autoStateSignal.emit(int(msg.id));

    def autoStateSlot(self, state):
        #self.lock.acquire()
        if(self.autoState != state):
            self.autoState = state
            self.displayAutoState()
    
    def displayAutoState(self):
        if self.autoState == 0:
            self._widget.auto_state_display.setText("Not ready")
            self._widget.auto_state_display.setStyleSheet("background-color:#ff5555;")
        elif self.autoState == 1: 
            self._widget.auto_state_display.setText("Ready, waiting for auto period")
            self._widget.auto_state_display.setStyleSheet("background-color:#ffffff;") 
        elif self.autoState == 2: 
            self._widget.auto_state_display.setText("Running")
            self._widget.auto_state_display.setStyleSheet("background-color:#ffff00")       
        elif self.autoState == 3: 
            self._widget.auto_state_display.setText("Finished")
            self._widget.auto_state_display.setStyleSheet("background-color:#00ff00;")
	elif self.autoState == 4:
	    self._widget.auto_state_display.setText("Error")
            self._widget.auto_state_display.setStyleSheet("background-color:#ff5555;")


    def nBallsCallback(self, msg):
        self.nBallsSignal.emit(int(msg.data))

    def nBallsSlot(self, state):
        if(self.n_balls != state):
            self.n_balls = state
            display = self._widget.n_balls_display
            
            if state == 0:
                display.setPixmap(self.zero_balls)
            elif state == 1:
                display.setPixmap(self.one_ball)
            elif state == 2:
                display.setPixmap(self.two_balls)
            elif state == 3:
                display.setPixmap(self.three_balls)
            elif state == 4:
                display.setPixmap(self.four_balls)
            elif state == 5:
                display.setPixmap(self.five_balls)
            elif state > 5:
                display.setPixmap(self.more_than_five_balls)
            else:
                display.setText("Couldn't read # balls")

    def shooterInRangeCallback(self, msg):
        self.shooterInRangeSignal.emit(bool(msg.data))

    def shooterInRangeSlot(self, in_range):
        self.shooter_in_range = in_range #set here so that it's set synchronously with the other slots

    def turretInRangeCallback(self, msg):
        self.turretInRangeSignal.emit(bool(msg.data))
        self.updateInRange()

    def turretInRangeSlot(self, in_range):
        self.turret_in_range = in_range #set here so that it's set synchronously with the other slots
        self.updateInRange()

    def updateInRange(self):
        display = self._widget.in_range_display
        if(self.shooter_in_range and self.turret_in_range):
            display.setPixmap(self.in_range_pixmap)
        else:
            display.setPixmap(self.not_in_range_pixmap)


    def setImuAngle(self):
        print("setting imu")
        #self.lock.acquire()
        angle = self._widget.imu_angle.value() # imu_angle is the text field (doublespinbox) that the user can edit to change the navx angle, defaulting to zero
        
        # call the service
        try:
            service = roslibpy.Service(self.client,'/imu/set_imu_zero',ImuZeroAngle)
           # rospy.wait_for_service("/imu/set_imu_zero", 1) # timeout in sec, TODO maybe put in config file?
            #TODO remove print
            #Service Request-rosbridge
            request = roslibpy.ServiceRequest()
            result = service.call(request)
            #result(angle)
            # change button to green color to indicate that the service call went through
            self._widget.set_imu_angle_button.setStyleSheet("background-color:#5eff00;")

        except (rospy.ServiceException, rospy.ROSException) as e: # the second exception happens if the wait for service times out
            self.errorPopup("Imu Set Angle Error", e)
        #self.lock.release()
        print("finished setting imu")

    def imuAngleChanged(self):
        #self.lock.acquire()
        # change button to red color if someone fiddled with the angle input, to indicate that input wasn't set yet
        self._widget.set_imu_angle_button
        self._widget.set_imu_angle_button.setStyleSheet("background-color:#ff0000;")
        #self.lock.release()


    def setAutoWallDist(self):
        print("setting auto wall distance")
        #self.lock.acquire()
        distance = self._widget.auto_wall_dist.value()
        
        self._widget.auto_wall_dist_button.setStyleSheet("background-color:#5eff00;")

        print("finished setting auto wall distance")

    def autoWallDistChanged(self):
        self._widget.auto_wall_dist_button.setStyleSheet("background-color:#ff0000;")
    
    def resetBallCount(self):
        print("manually reset ball count")
        #self.lock.acquire()
        nballs = self._widget.ball_reset_count.value() 
        
        # call the service
        try:
            rospy.wait_for_service("/reset_ball", 1) #timeout in sec, TODO maybe put in config file?
            caller = rospy.ServiceProxy("/reset_ball", resetBallSrv)
            caller(nballs)
            # change button to green color to indicate that the service call went through
            self._widget.set_imu_angle_button.setStyleSheet("background-color:##5eff00;")

        except (rospy.ServiceException, rospy.ROSException) as e: # the second exception happens if the wait for service times out
            self.errorPopup("Reset ball count Error", e)
        #self.lock.release()
        print("finished resetting ball count")

    def resetBallChanged(self):
        self._widget.ball_reset_button.setStyleSheet("background-color:#ff0000;")


    def errorPopup(self, title, e):
        msg_box = QMessageBox()
        msg_box.setWindowTitle(title)
        msg_box.setIcon(QMessageBox.Warning)
        msg_box.setText("%s"%e)
        msg_box.exec_()

    #Publisher -> fake Auto States
    def publish_thread(self):

        pub = roslibpy.Topic(self.client, '/auto/auto_mode', 'behavior_actions/AutoMode')
        r = rospy.Rate(10) # 10hz
        pub.advertise()
        while self.client.is_connected:

            pub.publish(roslibpy.Message(
                {
                    'auto_mode': self.auto_mode_button_group.checkedId()
                }
            ))
            r.sleep()

    def shutdown_plugin(self):
        self.auto_state_sub.unregister()
        self.n_balls_sub.unregister()
        self.shooter_in_range_sub.unregister()
        self.turret_in_range_sub.unregister()
        self.client.close()


    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
    

    def trigger_configuration(self):
        # Comment in to signal that the plugin has a way to configure
        # This will enable a setting button (gear icon) in each dock widget title bar
        # Usually used to open a modal configuration dialog
        pass
コード例 #34
0
class CameraViewer(Plugin):
    def __init__(self, context):
        super(CameraViewer, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('CameraViewer')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        # args, unknowns = parser.parse_known_args(context.argv())
        # if not args.quiet:
        # 	print 'arguments: ', args
        # 	print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_drone_teleop'),
                               'resource', 'CamViewer.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('CamViewerUi')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        # Add widget to the user interface
        context.add_widget(self._widget)

        self.bridge = CvBridge()

        # Add Subscibers
        cam_frontal_topic = rospy.get_param('cam_frontal_topic',
                                            '/iris/cam_frontal/image_raw')
        cam_ventral_topic = rospy.get_param('cam_ventral_topic',
                                            '/iris/cam_ventral/image_raw')
        rospy.Subscriber(cam_frontal_topic, Image, self.cam_frontal_cb)
        rospy.Subscriber(cam_ventral_topic, Image, self.cam_ventral_cb)
        rospy.Subscriber('interface/filtered_img', Image, self.filtered_img_cb)
        rospy.Subscriber('interface/threshed_img', Image, self.threshed_img_cb)

    def msg_to_pixmap(self, msg):
        cv_img = self.bridge.imgmsg_to_cv2(msg)
        shape = cv_img.shape
        if len(shape) == 3 and shape[2] == 3:  # RGB888
            h, w, _ = shape
            bytesPerLine = 3 * w
            img_format = QImage.Format_RGB888
        else:  # Grayscale8
            h, w = shape[0], shape[1]
            bytesPerLine = 1 * w
            img_format = QImage.Format_Grayscale8
        q_img = QImage(cv_img.data, w, h, bytesPerLine, img_format)
        return QPixmap.fromImage(q_img).scaled(320, 240)

    def cam_frontal_cb(self, msg):
        self._widget.img_frontal.setPixmap(self.msg_to_pixmap(msg))

    def cam_ventral_cb(self, msg):
        self._widget.img_ventral.setPixmap(self.msg_to_pixmap(msg))

    def threshed_img_cb(self, msg):
        self._widget.img_threshed.setPixmap(self.msg_to_pixmap(msg))

    def filtered_img_cb(self, msg):
        self._widget.img_filtered.setPixmap(self.msg_to_pixmap(msg))

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

    # def trigger_configuration(self):
    # Comment in to signal that the plugin has a way to configure
    # This will enable a setting button (gear icon) in each dock widget title bar
    # Usually used to open a modal configuration dialog
コード例 #35
0
class RosTfTree(QObject):

    _deferred_fit_in_view = Signal()

    def __init__(self, context):
        super(RosTfTree, self).__init__(context)
        self.initialized = False

        self.setObjectName('RosTfTree')

        self._current_dotcode = None

        self._widget = QWidget()

        # factory builds generic dotcode items
        self.dotcode_factory = PydotFactory()
        # self.dotcode_factory = PygraphvizFactory()
        # generator builds rosgraph
        self.dotcode_generator = RosTfTreeDotcodeGenerator()
        self.tf2_buffer_ = tf2_ros.Buffer()
        self.tf2_listener_ = tf2_ros.TransformListener(self.tf2_buffer_)

        # dot_to_qt transforms into Qt elements using dot layout
        self.dot_to_qt = DotToQtGenerator()

        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_tf_tree'), 'resource',
                               'RosTfTree.ui')
        loadUi(ui_file, self._widget,
               {'InteractiveGraphicsView': InteractiveGraphicsView})
        self._widget.setObjectName('RosTfTreeUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        self._scene = QGraphicsScene()
        self._scene.setBackgroundBrush(Qt.white)
        self._widget.graphics_view.setScene(self._scene)

        self._widget.clear_buffer_push_button.setIcon(
            QIcon.fromTheme('edit-delete'))
        self._widget.clear_buffer_push_button.pressed.connect(
            self._clear_buffer)

        self._widget.refresh_graph_push_button.setIcon(
            QIcon.fromTheme('view-refresh'))
        self._widget.refresh_graph_push_button.pressed.connect(
            self._update_tf_graph)

        self._widget.highlight_connections_check_box.toggled.connect(
            self._redraw_graph_view)
        self._widget.auto_fit_graph_check_box.toggled.connect(
            self._redraw_graph_view)
        self._widget.fit_in_view_push_button.setIcon(
            QIcon.fromTheme('zoom-original'))
        self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)

        self._widget.load_dot_push_button.setIcon(
            QIcon.fromTheme('document-open'))
        self._widget.load_dot_push_button.pressed.connect(self._load_dot)
        self._widget.save_dot_push_button.setIcon(
            QIcon.fromTheme('document-save-as'))
        self._widget.save_dot_push_button.pressed.connect(self._save_dot)
        self._widget.save_as_svg_push_button.setIcon(
            QIcon.fromTheme('document-save-as'))
        self._widget.save_as_svg_push_button.pressed.connect(self._save_svg)
        self._widget.save_as_image_push_button.setIcon(
            QIcon.fromTheme('image-x-generic'))
        self._widget.save_as_image_push_button.pressed.connect(
            self._save_image)

        self._deferred_fit_in_view.connect(self._fit_in_view,
                                           Qt.QueuedConnection)
        self._deferred_fit_in_view.emit()

        context.add_widget(self._widget)

        self._force_refresh = False

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value(
            'auto_fit_graph_check_box_state',
            self._widget.auto_fit_graph_check_box.isChecked())
        instance_settings.set_value(
            'highlight_connections_check_box_state',
            self._widget.highlight_connections_check_box.isChecked())

    def restore_settings(self, plugin_settings, instance_settings):
        self._widget.auto_fit_graph_check_box.setChecked(
            instance_settings.value('auto_fit_graph_check_box_state', True) in
            [True, 'true'])
        self._widget.highlight_connections_check_box.setChecked(
            instance_settings.value('highlight_connections_check_box_state',
                                    True) in [True, 'true'])
        self.initialized = True
        self._refresh_tf_graph()

    def _clear_buffer(self):
        self.tf2_buffer_.clear()

    def _update_tf_graph(self):
        self._force_refresh = True
        self._refresh_tf_graph()

    def _refresh_tf_graph(self):
        if not self.initialized:
            return
        self._update_graph_view(self._generate_dotcode())

    def _generate_dotcode(self):
        force_refresh = self._force_refresh
        self._force_refresh = False
        rospy.wait_for_service('~tf2_frames')
        tf2_frame_srv = rospy.ServiceProxy('~tf2_frames', FrameGraph)
        return self.dotcode_generator.generate_dotcode(
            dotcode_factory=self.dotcode_factory,
            tf2_frame_srv=tf2_frame_srv,
            force_refresh=force_refresh)

    def _update_graph_view(self, dotcode):
        if dotcode == self._current_dotcode:
            return
        self._current_dotcode = dotcode
        self._redraw_graph_view()

    def _generate_tool_tip(self, url):
        return url

    def _redraw_graph_view(self):
        self._scene.clear()

        if self._widget.highlight_connections_check_box.isChecked():
            highlight_level = 3
        else:
            highlight_level = 1

        (nodes,
         edges) = self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode,
                                                     highlight_level)

        for node_item in nodes.values():
            self._scene.addItem(node_item)
        for edge_items in edges.values():
            for edge_item in edge_items:
                edge_item.add_to_scene(self._scene)

        self._scene.setSceneRect(self._scene.itemsBoundingRect())
        if self._widget.auto_fit_graph_check_box.isChecked():
            self._fit_in_view()

    def _load_dot(self, file_name=None):
        if file_name is None:
            file_name, _ = QFileDialog.getOpenFileName(
                self._widget, self.tr('Open graph from file'), None,
                self.tr('DOT graph (*.dot)'))
            if file_name is None or file_name == '':
                return

        try:
            fhandle = open(file_name, 'rb')
            dotcode = fhandle.read()
            fhandle.close()
        except IOError:
            return

        self._update_graph_view(dotcode)

    def _fit_in_view(self):
        self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(),
                                             Qt.KeepAspectRatio)

    def _save_dot(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget, self.tr('Save as DOT'), 'frames.dot',
            self.tr('DOT graph (*.dot)'))
        if file_name is None or file_name == '':
            return

        file = QFile(file_name)
        if not file.open(QIODevice.WriteOnly | QIODevice.Text):
            return

        file.write(self._current_dotcode)
        file.close()

    def _save_svg(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget, self.tr('Save as SVG'), 'frames.svg',
            self.tr('Scalable Vector Graphic (*.svg)'))
        if file_name is None or file_name == '':
            return

        generator = QSvgGenerator()
        generator.setFileName(file_name)
        generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())

        painter = QPainter(generator)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()

    def _save_image(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget, self.tr('Save as image'), 'frames.png',
            self.tr('Image (*.bmp *.jpg *.png *.tiff)'))
        if file_name is None or file_name == '':
            return

        img = QImage((self._scene.sceneRect().size() * 2.0).toSize(),
                     QImage.Format_ARGB32_Premultiplied)
        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()
        img.save(file_name)
コード例 #36
0
class JointTrajectoryController(Plugin):
    """
    Graphical frontend for a C{JointTrajectoryController}.

    There are two modes for interacting with a controller:
        1. B{Monitor mode} Joint displays are updated with the state reported
          by the controller. This is a read-only mode and does I{not} send
          control commands. Every time a new controller is selected, it starts
          in monitor mode for safety reasons.

        2. B{Control mode} Joint displays update the control command that is
        sent to the controller. Commands are sent periodically evan if the
        displays are not being updated by the user.

    To control the aggressiveness of the motions, the maximum speed of the
    sent commands can be scaled down using the C{Speed scaling control}

    This plugin is able to detect and keep track of all active controller
    managers, as well as the JointTrajectoryControllers that are I{running}
    in each controller manager.
    For a controller to be compatible with this plugin, it must comply with
    the following requisites:
        - The controller type contains the C{JointTrajectoryController}
        substring, e.g., C{position_controllers/JointTrajectoryController}
        - The controller exposes the C{command} and C{state} topics in its
        ROS interface.

    Additionally, there must be a URDF loaded with a valid joint limit
    specification, namely position (when applicable) and velocity limits.

    A reference implementation of the C{JointTrajectoryController} is
    available in the C{joint_trajectory_controller} ROS package.
    """
    _cmd_pub_freq = 10.0  # Hz
    _widget_update_freq = 30.0  # Hz
    _ctrlrs_update_freq = 1  # Hz
    _min_traj_dur = 5.0 / _cmd_pub_freq  # Minimum trajectory duration

    jointStateChanged = Signal([dict])

    def __init__(self, context):
        super(JointTrajectoryController, self).__init__(context)
        self.setObjectName('JointTrajectoryController')

        # Create QWidget and extend it with all the attributes and children
        # from the UI file
        self._widget = QWidget()
        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_joint_trajectory_controller'),
                               'resource', 'joint_trajectory_controller.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('JointTrajectoryControllerUi')
        ns = rospy.get_namespace()[1:-1]
        self._widget.controller_group.setTitle('ns: ' + ns)

        # Setup speed scaler
        speed_scaling = DoubleEditor(1.0, 100.0)
        speed_scaling.spin_box.setSuffix('%')
        speed_scaling.spin_box.setValue(50.0)
        speed_scaling.spin_box.setDecimals(0)
        speed_scaling.setEnabled(False)
        self._widget.speed_scaling_layout.addWidget(speed_scaling)
        self._speed_scaling_widget = speed_scaling
        speed_scaling.valueChanged.connect(self._on_speed_scaling_change)
        self._on_speed_scaling_change(speed_scaling.value())

        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        # Initialize members
        self._jtc_name = []  # Name of selected joint trajectory controller
        self._cm_ns = []  # Namespace of the selected controller manager
        self._joint_pos = {}  # name->pos map for joints of selected controller
        self._joint_names = []  # Ordered list of selected controller joints
        self._robot_joint_limits = {}  # Lazily evaluated on first use

        # Timer for sending commands to active controller
        self._update_cmd_timer = QTimer(self)
        self._update_cmd_timer.setInterval(int(1000.0 / self._cmd_pub_freq))
        self._update_cmd_timer.timeout.connect(self._update_cmd_cb)

        # Timer for updating the joint widgets from the controller state
        self._update_act_pos_timer = QTimer(self)
        self._update_act_pos_timer.setInterval(
            int(1000.0 / self._widget_update_freq))
        self._update_act_pos_timer.timeout.connect(self._update_joint_widgets)

        # Timer for controller manager updates
        self._list_cm = ControllerManagerLister()
        self._update_cm_list_timer = QTimer(self)
        self._update_cm_list_timer.setInterval(
            int(1000.0 / self._ctrlrs_update_freq))
        self._update_cm_list_timer.timeout.connect(self._update_cm_list)
        self._update_cm_list_timer.start()

        # Timer for running controller updates
        self._update_jtc_list_timer = QTimer(self)
        self._update_jtc_list_timer.setInterval(
            int(1000.0 / self._ctrlrs_update_freq))
        self._update_jtc_list_timer.timeout.connect(self._update_jtc_list)
        self._update_jtc_list_timer.start()

        # Signal connections
        w = self._widget
        w.enable_button.toggled.connect(self._on_jtc_enabled)
        w.jtc_combo.currentIndexChanged[str].connect(self._on_jtc_change)
        w.cm_combo.currentIndexChanged[str].connect(self._on_cm_change)

        self._cmd_pub = None  # Controller command publisher
        self._state_sub = None  # Controller state subscriber

        self._list_controllers = None

    def shutdown_plugin(self):
        self._update_cmd_timer.stop()
        self._update_act_pos_timer.stop()
        self._update_cm_list_timer.stop()
        self._update_jtc_list_timer.stop()
        self._unregister_state_sub()
        self._unregister_cmd_pub()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('cm_ns', self._cm_ns)
        instance_settings.set_value('jtc_name', self._jtc_name)

    def restore_settings(self, plugin_settings, instance_settings):
        # Restore last session's controller_manager, if present
        self._update_cm_list()
        cm_ns = instance_settings.value('cm_ns')
        cm_combo = self._widget.cm_combo
        cm_list = [cm_combo.itemText(i) for i in range(cm_combo.count())]
        try:
            idx = cm_list.index(cm_ns)
            cm_combo.setCurrentIndex(idx)
            # Resore last session's controller, if running
            self._update_jtc_list()
            jtc_name = instance_settings.value('jtc_name')
            jtc_combo = self._widget.jtc_combo
            jtc_list = [
                jtc_combo.itemText(i) for i in range(jtc_combo.count())
            ]
            try:
                idx = jtc_list.index(jtc_name)
                jtc_combo.setCurrentIndex(idx)
            except (ValueError):
                pass
        except (ValueError):
            pass

    # def trigger_configuration(self):
    # Comment in to signal that the plugin has a way to configure
    # This will enable a setting button (gear icon) in each dock widget
    # title bar
    # Usually used to open a modal configuration dialog

    def _update_cm_list(self):
        update_combo(self._widget.cm_combo, self._list_cm())

    def _update_jtc_list(self):
        # Clear controller list if no controller information is available
        if not self._list_controllers:
            self._widget.jtc_combo.clear()
            return

        # List of running controllers with a valid joint limits specification
        # for _all_ their joints
        running_jtc = self._running_jtc_info()
        if running_jtc and not self._robot_joint_limits:
            self._robot_joint_limits = get_joint_limits()  # Lazy evaluation
        valid_jtc = []
        for jtc_info in running_jtc:
            has_limits = all(name in self._robot_joint_limits
                             for name in _jtc_joint_names(jtc_info))
            if has_limits:
                valid_jtc.append(jtc_info)
        valid_jtc_names = [data.name for data in valid_jtc]

        # Update widget
        update_combo(self._widget.jtc_combo, sorted(valid_jtc_names))

    def _on_speed_scaling_change(self, val):
        self._speed_scale = val / self._speed_scaling_widget.slider.maximum()

    def _on_joint_state_change(self, actual_pos):
        assert (len(actual_pos) == len(self._joint_pos))
        for name in actual_pos.keys():
            self._joint_pos[name]['position'] = actual_pos[name]

    def _on_cm_change(self, cm_ns):
        self._cm_ns = cm_ns
        if cm_ns:
            self._list_controllers = ControllerLister(cm_ns)
            # NOTE: Clear below is important, as different controller managers
            # might have controllers with the same name but different
            # configurations. Clearing forces controller re-discovery
            self._widget.jtc_combo.clear()
            self._update_jtc_list()
        else:
            self._list_controllers = None

    def _on_jtc_change(self, jtc_name):
        self._unload_jtc()
        self._jtc_name = jtc_name
        if self._jtc_name:
            self._load_jtc()

    def _on_jtc_enabled(self, val):
        # Don't allow enabling if there are no controllers selected
        if not self._jtc_name:
            self._widget.enable_button.setChecked(False)
            return

        # Enable/disable joint displays
        for joint_widget in self._joint_widgets():
            joint_widget.setEnabled(val)

        # Enable/disable speed scaling
        self._speed_scaling_widget.setEnabled(val)

        if val:
            # Widgets send desired position commands to controller
            self._update_act_pos_timer.stop()
            self._update_cmd_timer.start()
        else:
            # Controller updates widgets with actual position
            self._update_cmd_timer.stop()
            self._update_act_pos_timer.start()

    def _load_jtc(self):
        # Initialize joint data corresponding to selected controller
        running_jtc = self._running_jtc_info()
        self._joint_names = next(
            _jtc_joint_names(x) for x in running_jtc
            if x.name == self._jtc_name)
        for name in self._joint_names:
            self._joint_pos[name] = {}

        # Update joint display
        try:
            layout = self._widget.joint_group.layout()
            for name in self._joint_names:
                limits = self._robot_joint_limits[name]
                joint_widget = DoubleEditor(limits['min_position'],
                                            limits['max_position'])
                layout.addRow(name, joint_widget)
                # NOTE: Using partial instead of a lambda because lambdas
                # "will not evaluate/look up the argument values before it is
                # effectively called, breaking situations like using a loop
                # variable inside it"
                from functools import partial
                par = partial(self._update_single_cmd_cb, name=name)
                joint_widget.valueChanged.connect(par)
        except:
            # TODO: Can we do better than swallow the exception?
            from sys import exc_info
            print('Unexpected error:', exc_info()[0])

        # Enter monitor mode (sending commands disabled)
        self._on_jtc_enabled(False)

        # Setup ROS interfaces
        jtc_ns = _resolve_controller_ns(self._cm_ns, self._jtc_name)
        state_topic = jtc_ns + '/state'
        cmd_topic = jtc_ns + '/command'
        self._state_sub = rospy.Subscriber(state_topic,
                                           JointTrajectoryControllerState,
                                           self._state_cb,
                                           queue_size=1)
        self._cmd_pub = rospy.Publisher(cmd_topic,
                                        JointTrajectory,
                                        queue_size=1)

        # Start updating the joint positions
        self.jointStateChanged.connect(self._on_joint_state_change)

    def _unload_jtc(self):
        # Stop updating the joint positions
        try:
            self.jointStateChanged.disconnect(self._on_joint_state_change)
        except:
            pass

        # Reset ROS interfaces
        self._unregister_state_sub()
        self._unregister_cmd_pub()

        # Clear joint widgets
        # NOTE: Implementation is a workaround for:
        # https://bugreports.qt-project.org/browse/QTBUG-15990 :(

        layout = self._widget.joint_group.layout()
        if layout is not None:
            while layout.count():
                layout.takeAt(0).widget().deleteLater()
            # Delete existing layout by reparenting to temporary
            QWidget().setLayout(layout)
        self._widget.joint_group.setLayout(QFormLayout())

        # Reset joint data
        self._joint_names = []
        self._joint_pos = {}

        # Enforce monitor mode (sending commands disabled)
        self._widget.enable_button.setChecked(False)

    def _running_jtc_info(self):
        from controller_manager_msgs.utils\
            import filter_by_type, filter_by_state

        controller_list = self._list_controllers()
        jtc_list = filter_by_type(controller_list,
                                  'JointTrajectoryController',
                                  match_substring=True)
        running_jtc_list = filter_by_state(jtc_list, 'running')
        return running_jtc_list

    def _unregister_cmd_pub(self):
        if self._cmd_pub is not None:
            self._cmd_pub.unregister()
            self._cmd_pub = None

    def _unregister_state_sub(self):
        if self._state_sub is not None:
            self._state_sub.unregister()
            self._state_sub = None

    def _state_cb(self, msg):
        actual_pos = {}
        for i in range(len(msg.joint_names)):
            joint_name = msg.joint_names[i]
            joint_pos = msg.actual.positions[i]
            actual_pos[joint_name] = joint_pos
        self.jointStateChanged.emit(actual_pos)

    def _update_single_cmd_cb(self, val, name):
        self._joint_pos[name]['command'] = val

    def _update_cmd_cb(self):
        dur = []
        traj = JointTrajectory()
        traj.joint_names = self._joint_names
        point = JointTrajectoryPoint()
        for name in traj.joint_names:
            pos = self._joint_pos[name]['position']
            cmd = pos
            try:
                cmd = self._joint_pos[name]['command']
            except (KeyError):
                pass
            max_vel = self._robot_joint_limits[name]['max_velocity']
            dur.append(max(abs(cmd - pos) / max_vel, self._min_traj_dur))
            point.positions.append(cmd)
        point.time_from_start = rospy.Duration(max(dur) / self._speed_scale)
        traj.points.append(point)

        self._cmd_pub.publish(traj)

    def _update_joint_widgets(self):
        joint_widgets = self._joint_widgets()
        for id in range(len(joint_widgets)):
            joint_name = self._joint_names[id]
            try:
                joint_pos = self._joint_pos[joint_name]['position']
                joint_widgets[id].setValue(joint_pos)
            except (KeyError):
                pass  # Can happen when first connected to controller

    def _joint_widgets(self):  # TODO: Cache instead of compute every time?
        widgets = []
        layout = self._widget.joint_group.layout()
        for row_id in range(layout.rowCount()):
            widgets.append(
                layout.itemAt(row_id, QFormLayout.FieldRole).widget())
        return widgets
コード例 #37
0
class Camera_is1500_Widget(Plugin):
    def __init__(self, context):
        super(Camera_is1500_Widget, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('Camera setting')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resources" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_camera_is1500'),
                               'resources', 'Camera_is1500_Widget.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('Camera_is1500_WidgetUI')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)
        """
        Start nodes
        """
        self.target_x = 0.0
        self.target_y = 0.0
        self.distance_x = 0.0
        """
        MAP path for is1500 package
        """
        self.map_path_name = ''
        self.map_file_name = ''
        self.destination_map_file_name = ''
        self.destination_map_config_file_name = ''
        self.cameramap = []
        self.current_wp = None
        """
        RVIZ-APPs
        """
        self.map_forRviz_file_name = self._widget.map_to_rviz_path_edit.text()
        self.utm_x_value = float(self._widget.utm_x_edit.text())
        self.utm_y_value = float(self._widget.utm_y_edit.text())
        self.utm_phi_value = float(self._widget.utm_phi_edit.text())
        self.joao_origin_utm_x_value = float(
            self._widget.joao_origin_utm_x_edit.text())
        self.joao_origin_utm_y_value = float(
            self._widget.joao_origin_utm_y_edit.text())
        self.fiducials_map = [
        ]  # contain the position of the fiducial in camera frame
        self.indoor = self._widget.indoor_checkBox_edit.isChecked()
        self.init_transf = False  # True if map already drew
        self.origin_cam_x = 0.0
        self.origin_cam_y = 0.0
        self.last_robot_x_edit = 0.0  #float(self._widget.get_pos_robot_x_edit.text())
        self.last_robot_y_edit = 0.0  #float(self._widget.get_pos_robot_y_edit.text())
        self.last_robot_yaw_edit = 0.0  #float(self._widget.get_pos_robot_yaw_edit.text())
        self.angle_btwXcam_East = 0.0
        """
        Connect stuff here
        """
        # self.model = QStandardItemModel(self._widget.arc_list)
        # self._widget.name_edit.setValidator(QDoubleValidator())
        # self._widget.path_edit.setValidator(QDoubleValidator())
        # self._widget.in_case_edit.setValidator(QDoubleValidator())
        # self._widget.y_edit.setValidator(QDoubleValidator())
        # self._widget.map_name_edit.setValidator()
        """
        Start nodes
        """
        self._widget.launch_camera_start_button.released.connect(
            self.start_camera_is1500_launchFile)
        self._widget.color_launch_camera_label.setStyleSheet(
            "background-color:#ff0000;")
        self._widget.launch_supervisor_start_button.released.connect(
            self.start_supervisor_launchFile)
        self._widget.launch_supervisor_color_label.setStyleSheet(
            "background-color:#ff0000;")
        self._widget.rviz_start_button.released.connect(self.start_rviz)
        self._widget.color_rviz_label.setStyleSheet(
            "background-color:#ff0000;")

        self._widget.target_start_button.released.connect(
            self.target_waypontPy_run)
        self._widget.color_target_label.setStyleSheet(
            "background-color:#ff0000;")
        self._widget.distance_start_button.released.connect(
            self.distance_throughDeadZone_run)
        self._widget.color_distance_label.setStyleSheet(
            "background-color:#ff0000;")

        self._widget.test_button.released.connect(self.test_button_function)
        """
        Set map
        """
        self._widget.send_map_param_button.released.connect(
            self.set_map_rosparam)
        self._widget.map_param_edit.setValidator(QDoubleValidator())
        """
        Add/modify map
        """
        self._widget.file_name_button.released.connect(self.get_file)
        self._widget.desination_file_name_button.released.connect(
            self.get_folder)
        self._widget.desination_save_file_button.released.connect(
            self.save_file)
        self._widget.config_map_file_name_button.released.connect(
            self.get_file_yaml)
        self._widget.config_map_save_file_button.released.connect(
            self.config_save_file)
        self.model = QStandardItemModel(self._widget.map_from_mapYaml_list)
        self.model.itemChanged.connect(self.on_change_mapList)

        # Screen explaination msg
        # self._widget.name_edit.setToolTip("Set the name of the folder which will contain the map ")
        # self._widget.path_edit.setToolTip("Path of th map")
        # self._widget.file_name_label.setToolTipe("Map file which be added to the map folder of the robot")
        # Doesn't work
        # self._widget.name_edit.description = "This is label name_edit"
        # self._widget.path_edit.description = "This is label path_edit"
        # self._widget.file_name_button.description = "This is the OK button"

        #
        # for widget in (self._widget.name_edit, self._widget.path_edit,
        #     self._widget.file_name_button):
        #     widget.bind("<Enter>", self.on_enter)
        #     widget.bind("<Leave>", self.on_leave)
        self._widget.map_name_edit.textChanged.connect(self.map_name_change)
        """
        Rviz part
        """
        self._widget.utm_x_edit.setValidator(QDoubleValidator())
        self._widget.utm_y_edit.setValidator(QDoubleValidator())
        self._widget.utm_phi_edit.setValidator(QDoubleValidator())
        self._widget.utm_x_edit.textChanged.connect(self.utm_x_change)
        self._widget.utm_y_edit.textChanged.connect(self.utm_y_change)
        self._widget.utm_phi_edit.textChanged.connect(self.utm_phi_change)
        self._widget.joao_origin_utm_x_edit.setValidator(QDoubleValidator())
        self._widget.joao_origin_utm_y_edit.setValidator(QDoubleValidator())
        self._widget.joao_origin_utm_x_edit.textChanged.connect(
            self.joao_origin_utm_x_change)
        self._widget.joao_origin_utm_y_edit.textChanged.connect(
            self.joao_origin_utm_y_change)
        self._widget.indoor_checkBox_edit.stateChanged.connect(
            self.indoor_isCheck)
        self._widget.reset_init_map_file_button.released.connect(
            self.reset_init_change)
        self._widget.get_position_button.released.connect(self.get_pos_change)
        self._widget.get_pos_robot_x_edit.setValidator(QDoubleValidator())
        self._widget.get_pos_robot_y_edit.setValidator(QDoubleValidator())
        self._widget.get_pos_robot_yaw_edit.setValidator(QDoubleValidator())

        # Buttons
        self._widget.map_to_rviz_send_file_button.released.connect(
            self.visualize_fiducials)

        self._widget.map_to_rviz_name_button.released.connect(
            self.get_file_map_to_rviz)
        """
        ROS
        """
        self.marker_pub = rospy.Publisher(
            '/fiducials_position',
            visualization_msgs.msg.MarkerArray,
            queue_size=10)  # publish fiducials for a chosen map
        self.currentMap_marker_pub = rospy.Publisher(
            '/fiducials_position_current',
            visualization_msgs.msg.MarkerArray,
            queue_size=10)  #publish currend used fiducials
        self.camera_pos_sub = rospy.Subscriber("/base_link_odom_camera_is1500",
                                               Odometry,
                                               self.publish_transform_pos)
        # self.camera_pos_sub = rospy.Subscriber("/position_camera_is1500", Odometry, self.publish_transform_pos)
        self.transform_cameraPos_pub = rospy.Publisher(
            '/transform_cameraPos_pub', Odometry, queue_size=1)
        self.tf_buffer = tf2_ros.Buffer()
        self.tf_listner = tf2_ros.TransformListener(self.tf_buffer)

        self.fromMetric_sub = rospy.Subscriber('/pointsFromMetric',
                                               GetPointsFromMetric,
                                               self.publish_metric_to_rviz)
        self.transform_metric_pub = rospy.Publisher(
            '/rviz_pointsFromMetric',
            visualization_msgs.msg.Marker,
            queue_size=10)
        # rospy.spin()
        # empty yet

    """
    Start nodes
    """

    def start_camera_is1500_launchFile(self):
        # os.spawnl(os.P_NOWAIT, 'sudo shutdown && 123456')
        # subprocess.call('/home/jonathan/catkin_ws_kyb/src/rqt_camera_is1500/script/camera_is1500.bash', shell=True)
        subprocess.call(
            '/home/ew/catkin_ws/src/rqt_camera_is1500/script/camera_is1500.bash',
            shell=True)
        # os.spawnl(os.P_NOWAIT, 'cd && cd /home/jonathan/catkin_ws_kyb/ && source devel/setup.bash && cd src/camera_is1500/launch/ && roslaunch camera_is1500 cameraTry.launch')
        # print 'Node: Camera_is1500 launched'
        self._widget.color_launch_camera_label.setStyleSheet(
            "background-color:#228B22;")

    def start_supervisor_launchFile(self):
        # cmd = "gnome-terminal -x sh -c 'cd && cd /home/jonathan/catkin_ws_kyb/ && source devel/setup.bash && roslaunch supervisor supervisor.launch"
        # cmd = "gnome-terminal -x sh -c 'cd && cd /home/ew/catkin_ws/ && source devel/setup.bash && roslaunch supervisor supervisor.launch'"
        # cmd = '/home/ew/catkin_ws/src/rqt_camera_is1500/script/supervisor.bash'

        subprocess.call(
            '/home/ew/catkin_ws/src/rqt_camera_is1500/script/supervisor.bash',
            shell=True)
        self._widget.launch_supervisor_color_label.setStyleSheet(
            "background-color:#228B22;")

    def start_rviz(self):
        self._widget.color_rviz_label.setStyleSheet(
            "background-color:#ff0000;")
        # subprocess.call('/home/jonathan/catkin_ws_kyb/src/rqt_camera_is1500/script/rviz.bash', shell=True)
        subprocess.call(
            '/home/ew/catkin_ws/src/rqt_camera_is1500/script/rviz.bash',
            shell=True)
        # print 'RViz launched'
        self._widget.color_rviz_label.setStyleSheet(
            "background-color:#228B22;")

    def target_waypontPy_run(self):
        self.target_x = float(self._widget.target_x_edit.text())
        self.target_y = float(self._widget.target_y_edit.text())
        # cmd = ""
        # gnome-terminal -x sh -c  'rosparam set /camera_is1500_node/mapNumber 1'
        cmd = "gnome-terminal -x sh -c  \'rosrun camera_is1500 waypoint_node.py %d %d \' " % (
            float(self.target_x), float(self.target_y))
        subprocess.call(cmd, shell=True)
        self._widget.color_target_label.setStyleSheet(
            "background-color:#228B22;")

    def distance_throughDeadZone_run(self):
        self.distance_x = float(self._widget.distance_x_edit.text())
        cmd = "gnome-terminal -x sh -c \'rosrun camera_is1500 throughDeadZone.py %d \' " % (
            float(self.distance_x))

        # cmd = "rosrun camera_is1500 throughDeadZone.py %d" % (float(self.distance_x))
        subprocess.call(str(cmd), shell=True)
        self._widget.color_distance_label.setStyleSheet(
            "background-color:#228B22;")

    def test_button_function(self):
        print('Checkbox: ', self.indoor)
        # def toggleLeftWidget(self):
        # self.map_import_layout.setHidden(not self.map_import_layout.isHidden())

    """
    Node param settings
    """

    def set_map_rosparam(self):
        cmd = "rosparam set /camera_is1500_node/mapNumber %d" % (float(
            self._widget.map_param_edit.text()))
        subprocess.call(cmd, shell=True)

    """
    Map selection function
    """

    def map_name_change(self):
        self.map_file_name = self._widget.map_name_edit.text()

    """
    """

    def read_map(self):
        self.cameramap = []
        with open(self.destination_map_config_file_name, 'r') as stream:
            map_list = yaml.load(stream)
            for map in map_list:
                new_map = CameraMap()
                new_map.from_yaml(map)
                self.cameramap.append(new_map)

        # self.populate_combobox()
        # rospy.loginfo('Read: %s'%self.cameramap)
        # print('Read: %s'%self.cameramap)
        print('Load data')
        for i in range(0, len(self.cameramap)):
            print(self.cameramap[i].name)
            print(self.cameramap[i].path)
        # self.wait_timer = rospy.Timer(rospy.Duration(2.), self.visualize_waypoints, oneshot=True)

    """
    """

    def config_save_file(self):
        map_list = []
        for map in self.cameramap:
            new_map = {}  #CameraMap()#{}
            new_map['name'] = map.name
            new_map['path'] = map.path
            map_list.append(new_map)

        new_map_to_add = {}
        new_map_to_add['name'] = self.map_file_name
        new_map_to_add['path'] = self.destination_map_config_file_name
        map_list.append(new_map_to_add)

        with open(self.destination_map_config_file_name, 'w') as stream:
            stream.write(
                yaml.safe_dump(map_list,
                               encoding='utf-8',
                               allow_unicode=False,
                               default_flow_style=False))
        print 'Saved elements in file' + self.destination_map_config_file_name
        print yaml.safe_dump(map_list,
                             encoding='utf-8',
                             allow_unicode=False,
                             default_flow_style=False)

    """
    """

    def save_file(self):
        print('Hi, you are triing to save something with the name of : ',
              map_name_edit)

    """
    """

    def get_file(self):
        file_dlg = QFileDialog()
        file_dlg.setFileMode(QFileDialog.AnyFile)

        if file_dlg.exec_():
            map_file_name = file_dlg.selectedFiles()

            rospy.loginfo('Selected files: %s' % map_file_name)
            if len(map_file_name) > 0:
                self.map_path_name = map_file_name[0]
                self._widget.file_path_edit.setText(self.map_path_name)

                # self.read_waypoints()

    """
    """

    def get_folder(self):
        file_dlg = QFileDialog()
        file_dlg.setFileMode(QFileDialog.Directory)

        if file_dlg.exec_():
            destination_map_file_name = file_dlg.selectedFiles()

            rospy.loginfo('Selected files: %s' % destination_map_file_name)
            if len(destination_map_file_name) > 0:
                self.destination_map_file_name = destination_map_file_name[0]
                self._widget.desination_file_name_edit.setText(
                    self.destination_map_file_name)

    """
    """

    def get_file_yaml(self):
        file_dlg = QFileDialog()
        file_dlg.setFileMode(QFileDialog.AnyFile)
        # file_dlg.setFilter("Yaml files (*.yaml)")
        # print('Hi, you try to get a file')
        # print(self.map_file_name)
        if file_dlg.exec_():
            destination_map_config_file_name = file_dlg.selectedFiles()

            rospy.loginfo('Selected files: %s' %
                          destination_map_config_file_name)
            if len(destination_map_config_file_name) > 0:
                self.destination_map_config_file_name = destination_map_config_file_name[
                    0]
                self._widget.config_map_file_name_edit.setText(
                    self.destination_map_config_file_name)

                self.read_map()

    """
    """

    # TODO : ........
    def on_change_mapList(self):
        print(hahaa)

    """
    RViz group
    """

    def utm_x_change(self):
        self.utm_x_value = float(self._widget.utm_x_edit.text())
        # print('utm_x: ', self.utm_x_value, ', type: ', type(self.utm_x_value))

    """
    """

    def utm_y_change(self):
        self.utm_y_value = float(self._widget.utm_y_edit.text())
        # print('utm_y: ', self.utm_y_value, ', type: ', type(self.utm_y_value))

    """
    """

    def utm_phi_change(self):
        self.utm_phi_value = float(self._widget.utm_phi_edit.text())
        # print('utm_phi: ', self.utm_phi_value, ', type: ', type(self.utm_phi_value))

    """
    """

    def joao_origin_utm_x_change(self):
        self.joao_origin_utm_x_value = float(
            self._widget.joao_origin_utm_x_edit.text())

    """
    """

    def joao_origin_utm_y_change(self):
        self.joao_origin_utm_y_value = float(
            self._widget.joao_origin_utm_y_edit.text())

    """
    """

    def get_file_map_to_rviz(self):
        file_dlg = QFileDialog()
        file_dlg.setFileMode(QFileDialog.AnyFile)
        # file_dlg.setFilter("Yaml files (*.yaml)")
        # print('Hi, you try to get a file')

        if file_dlg.exec_():
            map_file_name = file_dlg.selectedFiles()

            rospy.loginfo(
                'Selected files to extract position of the fiducials: %s' %
                map_file_name)
            if len(map_file_name) > 0:
                self.map_forRviz_file_name = map_file_name[0]
                self._widget.map_to_rviz_path_edit.setText(
                    self.map_forRviz_file_name)

    """
    """

    # init the map changement
    def reset_init_change(self):
        self.init_transf = False
        # delete all fiducial
        marker_array = visualization_msgs.msg.MarkerArray()
        new_marker = visualization_msgs.msg.Marker()
        new_marker.header.stamp = rospy.Time.now()
        # Set frame id
        new_marker.header.frame_id = 'odom'
        new_marker.ns = 'fiducials_pos'
        new_marker.action = visualization_msgs.msg.Marker.DELETEALL
        marker_array.markers.append(new_marker)
        self.currentMap_marker_pub.publish(marker_array)

    """
    """

    # Screen in RViz a selected map in chosen position when button is preshed
    def visualize_fiducials(self, event=None):
        self.generalizedDrawingMap(
            self.map_forRviz_file_name, radians(self.utm_phi_value),
            [float(self.utm_x_value),
             float(self.utm_y_value)],
            [self.joao_origin_utm_x_value, self.joao_origin_utm_y_value],
            self.marker_pub)

    """
    """

    def get_pos_change(self):
        trans = self.tf_buffer.lookup_transform('odom', 'base_link',
                                                rospy.Time())

        self._widget.get_pos_robot_x_edit.setText(
            str(trans.transform.translation.x))
        self._widget.get_pos_robot_y_edit.setText(
            str(trans.transform.translation.y))

        self.last_robot_x_edit = float(
            self._widget.get_pos_robot_x_edit.text())
        self.last_robot_y_edit = float(
            self._widget.get_pos_robot_y_edit.text())

        quat = trans.transform.rotation
        (roll, pitch, yaw) = tf.transformations.euler_from_quaternion(
            (quat.x, quat.y, quat.z, quat.w))
        self._widget.get_pos_robot_yaw_edit.setText(str(yaw / 2 / 3.14 * 360))
        self.last_robot_yaw_edit = float(
            self._widget.get_pos_robot_yaw_edit.text())

    """
    """

    # Screen in RViz a map
    # Input:    data_path: path of the map from camera is 1500
    #           angleMap: compare to the east for x-axis in RAD
    #           originMapInUTM: Origin of the camera (usually #100) in UTM coordinates
    #           originGeneralUTM: Joao's origin
    # Output: screen map on RViz
    def generalizedDrawingMap(self, data_path, angleMap, originMapInUTM,
                              originGeneralUTM, publisher):
        data = pd.read_csv(data_path,
                           header=None,
                           sep=' ',
                           skipinitialspace=True,
                           low_memory=False,
                           skiprows=3)
        fiducial_pos = data.values[:,
                                   1], data.values[:,
                                                   14], -data.values[:,
                                                                     15], data.values[:,
                                                                                      16]  #concatanate the vector
        self.fiducials_map = fiducial_pos
        # delete all fiducial
        marker_array = visualization_msgs.msg.MarkerArray()
        new_marker = visualization_msgs.msg.Marker()
        new_marker.header.stamp = rospy.Time.now()
        # Set frame id
        new_marker.header.frame_id = 'odom'
        new_marker.ns = 'fiducials_pos'
        new_marker.action = visualization_msgs.msg.Marker.DELETEALL
        marker_array.markers.append(new_marker)
        publisher.publish(marker_array)

        joao_x = originGeneralUTM[0]  #self.joao_origin_utm_x_value#468655
        joao_y = originGeneralUTM[1]  #self.joao_origin_utm_y_value#5264080
        gps_origin_map_x = originMapInUTM[
            0]  #float(self.utm_x_value)#468598.24
        gps_origin_map_y = originMapInUTM[
            1]  #float(self.utm_y_value)#5264012.01

        # Calculate the size of the fiducials matrix
        length = len(fiducial_pos[0]) - 1
        # Fiducial points to RVIZ
        marker_array = visualization_msgs.msg.MarkerArray()
        for i in range(0, length, 3):
            new_marker = visualization_msgs.msg.Marker()
            new_marker.header.stamp = rospy.Time.now()
            new_marker.header.frame_id = 'odom'
            new_marker.id = i  #int(fiducial_pos[i][0])
            new_marker.ns = 'fiducials position'
            new_marker.type = visualization_msgs.msg.Marker.SPHERE
            new_marker.action = visualization_msgs.msg.Marker.ADD
            x = fiducial_pos[1][i]
            y = fiducial_pos[2][i]
            new_marker.pose.position.x = x * np.cos(angleMap) - y * np.sin(
                angleMap) + gps_origin_map_x - joao_x
            new_marker.pose.position.y = x * np.sin(angleMap) + y * np.cos(
                angleMap) + gps_origin_map_y - joao_y
            print('( ', new_marker.pose.position.x, ', ',
                  new_marker.pose.position.y, ') ')
            new_marker.pose.position.z = -fiducial_pos[3][i]
            quat = tf.transformations.quaternion_from_euler(
                0, 0, 0)  # TODO set the angle
            new_marker.pose.orientation.x = quat[0]
            new_marker.pose.orientation.y = quat[1]
            new_marker.pose.orientation.z = quat[2]
            new_marker.pose.orientation.w = quat[3]
            new_marker.scale.x = 0.05
            new_marker.scale.y = 0.05
            new_marker.scale.z = 0.05
            new_marker.color.a = 1.0
            new_marker.color.r = 1.0
            new_marker.color.g = 0.0
            new_marker.color.b = 0.0
            marker_array.markers.append(new_marker)
        publisher.publish(marker_array)

        # Fiducial names
        marker_array = visualization_msgs.msg.MarkerArray()
        for i in range(0, length, 3):
            new_marker = visualization_msgs.msg.Marker()
            new_marker.header.stamp = rospy.Time.now()
            new_marker.header.frame_id = 'odom'
            new_marker.id = i  #int(fiducial_pos[i][0])
            new_marker.ns = 'fiducials_names'
            new_marker.type = visualization_msgs.msg.Marker.TEXT_VIEW_FACING
            new_marker.action = visualization_msgs.msg.Marker.ADD
            new_marker.text = str(fiducial_pos[0][i])
            x = fiducial_pos[1][i]
            y = fiducial_pos[2][i]
            new_marker.pose.position.x = x * np.cos(angleMap) - y * np.sin(
                angleMap) + gps_origin_map_x - joao_x
            new_marker.pose.position.y = x * np.sin(angleMap) + y * np.cos(
                angleMap) + gps_origin_map_y - joao_y
            new_marker.pose.position.z = -fiducial_pos[3][i] + 0.1
            new_marker.scale.z = 0.1
            new_marker.color.a = 1.0
            new_marker.color.r = 1.0
            new_marker.color.g = 1.0
            new_marker.color.b = 1.0
            marker_array.markers.append(new_marker)
        publisher.publish(marker_array)

    """
    """

    def transformFromLastGPS(self, positionYaw, lastUTM):
        # TODO: Check where is these angles
        # global angle_btwXcam_East = positionYaw[2] #radians(self.last_robot_yaw_edit)#radians(self.utm_phi_value)#45.0) # 0 = align with east for x and north = y
        angle_btwXcam_East = self.angle_btwXcam_East
        last_gps_yaw = radians(0.0)  #in radians
        self.distance_x = float(self._widget.distance_x_edit.text())
        wheel_odom_distance = (float(self.distance_x))  #3.0  # meter

        # if the map is already drew... just send the transfrom position
        if (self.init_transf == False):
            self.angle_btwXcam_East = positionYaw[2]
            # Use East as x-axis
            direction_vecteur = [
                float(wheel_odom_distance * np.cos(last_gps_yaw)),
                float(wheel_odom_distance * np.sin(last_gps_yaw))
            ]
            # Robot position in UTM inside the map
            first_robot_x = lastUTM[0] + direction_vecteur[0]
            first_robot_y = lastUTM[1] + direction_vecteur[1]
            # Rotation from camera frame to UTM
            rotated_x = positionYaw[0] * np.cos(angle_btwXcam_East) - (
                -1) * positionYaw[1] * np.sin(angle_btwXcam_East)
            rotated_y = positionYaw[0] * np.sin(angle_btwXcam_East) + (
                -1) * positionYaw[1] * np.cos(angle_btwXcam_East)
            self.origin_cam_x = first_robot_x - rotated_x
            self.origin_cam_y = first_robot_y - rotated_y
            # Draw the selected map
            self.generalizedDrawingMap(
                self.map_forRviz_file_name, angle_btwXcam_East,
                [self.origin_cam_x, self.origin_cam_y],
                [self.joao_origin_utm_x_value, self.joao_origin_utm_y_value],
                self.currentMap_marker_pub)
            self.init_transf = True
        # print(origin_cam_x, ', ', origin_cam_x)
        x = positionYaw[0]
        y = positionYaw[1]
        yaw = positionYaw[2]
        # Rotation from camera frame to UTM
        # Translation
        x_prim = x * np.cos(angle_btwXcam_East) - (-1) * y * np.sin(
            angle_btwXcam_East
        ) + self.origin_cam_x - self.joao_origin_utm_x_value
        # ATTENTION: ADD a minus here on y position to be on the map
        y_prim = (x * np.sin(angle_btwXcam_East) +
                  (-1) * y * np.cos(angle_btwXcam_East)
                  ) + self.origin_cam_y - self.joao_origin_utm_y_value
        yaw_prim = -yaw + angle_btwXcam_East  # Right !!!
        return x_prim, y_prim, yaw_prim

    """
    """

    def indoor_isCheck(self):
        self.indoor = self._widget.indoor_checkBox_edit.isChecked()

    """
    """

    def publish_transform_pos(self, msg):

        if self.indoor:
            # Angle in rad
            (roll, pitch,
             yaw_before) = tf.transformations.euler_from_quaternion([
                 msg.pose.pose.orientation.x, msg.pose.pose.orientation.y,
                 msg.pose.pose.orientation.z, msg.pose.pose.orientation.w
             ])

            # Transform the camera data for Rviz
            # 468655
            # 5264080
            # 468598.24
            # 5264012.01

            x, y, yaw = self.transformFromLastGPS([
                msg.pose.pose.position.x, msg.pose.pose.position.y, yaw_before
            ], [
                468655.0 + self.last_robot_x_edit,
                5264080.0 + self.last_robot_y_edit
            ])
            # x, y, yaw = self.transformFromLastGPS([msg.pose.pose.position.x,
            #     msg.pose.pose.position.y, yaw_before], [468655.0+3.0, 5264080.0-1.0])

            odom = Odometry()
            odom.header.stamp = rospy.Time.now()
            odom.header.frame_id = 'odom'
            odom.child_frame_id = 'base_link'
            odom.pose.pose.position.x = x
            odom.pose.pose.position.y = y
            odom.pose.pose.position.z = 0
            # Don't forget the "Quaternion()" and *
            odom.pose.pose.orientation = Quaternion(
                *tf.transformations.quaternion_from_euler(0, 0, yaw))
            # publisher
            self.transform_cameraPos_pub.publish(odom)

        # else:
        #     print('Outdoor')

    """
    Metric
    """

    def publish_metric_to_rviz(self, msg):
        # print ("Receive something")

        if self.indoor:
            x, y, yaw = self.transformFromLastGPS([msg.x, msg.y, 0.0], [
                468655.0 + self.last_robot_x_edit,
                5264080.0 + self.last_robot_y_edit
            ])
            # x, y, yaw = self.transformFromLastGPS([msg.pose.pose.position.x,
            #     msg.pose.pose.position.y, yaw_before], [468655.0+3.0, 5264080.0-1.0])

            new_marker = visualization_msgs.msg.Marker()
            new_marker.header.stamp = rospy.Time.now()
            new_marker.header.frame_id = 'odom'
            new_marker.ns = 'Position issue'
            new_marker.type = visualization_msgs.msg.Marker.SPHERE
            new_marker.action = visualization_msgs.msg.Marker.ADD
            new_marker.text = str(msg.cost)

            new_marker.pose.position.x = x
            new_marker.pose.position.y = y
            new_marker.pose.position.z = 0.0
            new_marker.scale.x = 0.5
            new_marker.scale.y = 0.5
            new_marker.scale.z = 0.5
            new_marker.color.a = 1.0
            new_marker.color.r = 0.0
            new_marker.color.g = 1.0
            new_marker.color.b = 0.0

            # publisher
            self.transform_metric_pub.publish(new_marker)

            new_marker = visualization_msgs.msg.Marker()
            new_marker.header.stamp = rospy.Time.now()
            new_marker.header.frame_id = 'odom'
            new_marker.ns = 'Position issue'
            new_marker.type = visualization_msgs.msg.Marker.TEXT_VIEW_FACING
            new_marker.action = visualization_msgs.msg.Marker.ADD
            new_marker.text = str(msg.cost)
            new_marker.pose.position.x = x
            new_marker.pose.position.y = y
            new_marker.pose.position.z = 0.6
            new_marker.scale.z = 0.8
            new_marker.color.a = 1.0
            new_marker.color.r = 0.0
            new_marker.color.g = 1.0
            new_marker.color.b = 0.0

            # publisher
            self.transform_metric_pub.publish(new_marker)
コード例 #38
0
class VelTeleop(Plugin):
    def __init__(self, context):
        super(VelTeleop, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('VelTeleop')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q",
                            "--quiet",
                            action="store_true",
                            dest="quiet",
                            help="Put plugin in silent mode")
        # args, unknowns = parser.parse_known_args(context.argv())
        # if not args.quiet:
        # 	print 'arguments: ', args
        # 	print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which should be in the "resource" folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('rqt_drone_teleop'),
                               'resource', 'VelocityTeleop.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('VelTeleopUi')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))

        # Add logo
        pixmap = QPixmap(
            os.path.join(rospkg.RosPack().get_path('rqt_drone_teleop'),
                         'resource', 'jderobot.png'))
        self._widget.img_logo.setPixmap(pixmap.scaled(121, 121))

        # Set Variables
        self.play_code_flag = False
        self.takeoff = False
        self.linear_velocity_scaling_factor = 1
        self.vertical_velocity_scaling_factor = 0.8
        self.angular_velocity_scaling_factor = 0.5
        self._widget.term_out.setReadOnly(True)
        self._widget.term_out.setLineWrapMode(self._widget.term_out.NoWrap)

        # Set functions for each GUI Item
        self._widget.takeoffButton.clicked.connect(self.call_takeoff_land)
        self._widget.playButton.clicked.connect(self.call_play)
        self._widget.stopButton.clicked.connect(self.stop_drone)

        # Add Publishers
        self.play_stop_pub = rospy.Publisher('gui/play_stop',
                                             Bool,
                                             queue_size=1)

        # Add global variables
        self.extended_state = ExtendedState()
        self.shared_twist_msg = Twist()
        self.current_pose = Pose()
        self.pose_frame = ''
        self.current_twist = Twist()
        self.twist_frame = ''
        self.is_running = True
        self.stop_icon = QIcon()
        self.stop_icon.addPixmap(
            QPixmap(
                os.path.join(rospkg.RosPack().get_path('rqt_drone_teleop'),
                             'resource', 'stop.png')), QIcon.Normal, QIcon.Off)

        self.play_icon = QIcon()
        self.play_icon.addPixmap(
            QPixmap(
                os.path.join(rospkg.RosPack().get_path('rqt_drone_teleop'),
                             'resource', 'play.png')), QIcon.Normal, QIcon.Off)

        self.teleop_stick_1 = TeleopWidget(self, 'set_linear_xy', 151)
        self._widget.tlLayout.addWidget(self.teleop_stick_1)
        self.teleop_stick_1.setVisible(True)

        self.teleop_stick_2 = TeleopWidget(self, 'set_alt_yawrate', 151)
        self._widget.tlLayout_2.addWidget(self.teleop_stick_2)
        self.teleop_stick_2.setVisible(True)

        self.sensors_widget = SensorsWidget(self)
        self._widget.sensorsCheck.stateChanged.connect(
            self.show_sensors_widget)

        # Add widget to the user interface
        context.add_widget(self._widget)

        # Add Subscribers
        rospy.Subscriber('drone_wrapper/local_position/pose', PoseStamped,
                         self.pose_stamped_cb)
        rospy.Subscriber('drone_wrapper/local_position/velocity_body',
                         TwistStamped, self.twist_stamped_cb)
        rospy.Subscriber('drone_wrapper/extended_state', ExtendedState,
                         self.extended_state_cb)

        # Add Timer
        self.update_status_info()

    def show_sensors_widget(self, state):
        if state == Qt.Checked:
            self.sensors_widget.show()
        else:
            self.sensors_widget.hide()

    def pose_stamped_cb(self, msg):
        self.current_pose = msg.pose
        self.pose_frame = msg.header.frame_id
        self.sensors_widget.sensorsUpdate.emit()

    def twist_stamped_cb(self, msg):
        self.current_twist = msg.twist
        self.twist_frame = msg.header.frame_id

    def update_status_info(self):
        threading.Timer(0.5, self.update_status_info).start()
        if self.is_running:
            self.set_info_pos(self.current_pose, self.pose_frame)
            self.set_info_vel(self.current_twist, self.twist_frame)

    def set_info_pos(self, pose, frame):
        self._widget.posX.setText(str(round(pose.position.x, 2)))
        self._widget.posY.setText(str(round(pose.position.y, 2)))
        self._widget.posZ.setText(str(round(pose.position.z, 2)))

        self._widget.posFrame.setText(str(frame))

    def set_info_vel(self, twist, frame):
        self._widget.velX.setText(str(round(twist.linear.x, 2)))
        self._widget.velY.setText(str(round(twist.linear.y, 2)))
        self._widget.velZ.setText(str(round(twist.linear.z, 2)))
        self._widget.velYaw.setText(str(round(twist.angular.z, 2)))

        self._widget.velFrame.setText(str(frame))

    def extended_state_cb(self, msg):
        if self.extended_state.landed_state != msg.landed_state:
            self.extended_state = msg
            if self.extended_state.landed_state == 1:  # ON GROUND
                self._widget.takeoffButton.setText("Take Off")
            elif self.extended_state.landed_state == 2:  # IN AIR
                self._widget.takeoffButton.setText("Land")

    def takeoff_drone(self):
        try:
            global drone
            drone.takeoff()
            self.takeoff = True
        finally:
            rospy.loginfo('Takeoff finished')

    def call_takeoff_land(self):
        if self.extended_state.landed_state == 0:  # UNDEFINED --> not ready
            self._widget.term_out.append('Drone not ready')
            return

        if self.takeoff == True:
            rospy.loginfo('Landing')
            self._widget.term_out.append('Landing')
            global drone
            drone.land()
            self.takeoff = False
        else:
            rospy.loginfo('Taking off')
            self._widget.term_out.append('Taking off')
            x = threading.Thread(target=self.takeoff_drone)
            x.daemon = True
            x.start()

    def call_play(self):
        if not self.play_code_flag:
            self._widget.playButton.setText('Stop Code')
            self._widget.playButton.setStyleSheet("background-color: #ec7063")
            self._widget.playButton.setIcon(self.stop_icon)
            rospy.loginfo('Executing student code')
            self._widget.term_out.append('Executing student code')
            self.play_stop_pub.publish(Bool(True))
            self.play_code_flag = True
        else:
            self._widget.playButton.setText('Play Code')
            self._widget.playButton.setStyleSheet("background-color: #7dcea0")
            self._widget.playButton.setIcon(self.play_icon)
            rospy.loginfo('Stopping student code')
            self._widget.term_out.append('Stopping student code')
            self.play_stop_pub.publish(Bool(False))
            self.play_code_flag = False

    def stop_drone(self):
        self._widget.term_out.append('Stopping Drone')
        rospy.loginfo('Stopping Drone')
        self.teleop_stick_1.stop()
        self.teleop_stick_2.stop()
        if self.play_code_flag:
            self.call_play()
        for i in range(5):
            self.shared_twist_msg = Twist()
            global drone
            drone.set_cmd_vel(self.shared_twist_msg.linear.x,
                              self.shared_twist_msg.linear.y,
                              self.shared_twist_msg.linear.z,
                              self.shared_twist_msg.angular.z)
            rospy.sleep(0.05)

    def set_linear_xy(self, u, v):
        x = -self.linear_velocity_scaling_factor * v
        y = -self.linear_velocity_scaling_factor * u
        self._widget.XValue.setText('%.2f' % x)
        self._widget.YValue.setText('%.2f' % y)
        rospy.logdebug('Stick 2 value changed to - x: %.2f y: %.2f', x, y)
        self.shared_twist_msg.linear.x = x
        self.shared_twist_msg.linear.y = y

        global drone
        drone.set_cmd_vel(self.shared_twist_msg.linear.x,
                          self.shared_twist_msg.linear.y,
                          self.shared_twist_msg.linear.z,
                          self.shared_twist_msg.angular.z)

    def set_alt_yawrate(self, u, v):
        az = -self.vertical_velocity_scaling_factor * u
        z = -self.angular_velocity_scaling_factor * v
        self._widget.rotValue.setText('%.2f' % az)
        self._widget.altdValue.setText('%.2f' % z)
        rospy.logdebug('Stick 1 value changed to - az: %.2f z: %.2f', az, z)
        self.shared_twist_msg.linear.z = z
        self.shared_twist_msg.angular.z = az

        global drone
        drone.set_cmd_vel(self.shared_twist_msg.linear.x,
                          self.shared_twist_msg.linear.y,
                          self.shared_twist_msg.linear.z,
                          self.shared_twist_msg.angular.z)

    def shutdown_plugin(self):
        # TODO unregister all publishers here
        self.is_running = False
        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass
コード例 #39
0
ファイル: gait_selection.py プロジェクト: huangshizhe/monitor
class GaitSelectionPlugin(Plugin):

    def __init__(self, context):
        super(GaitSelectionPlugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('GaitSelectionPlugin')

        # Connect to services
        try:
            rospy.wait_for_service('/march/gait_selection/get_version_map', 3)
            rospy.wait_for_service('/march/gait_selection/get_directory_structure', 3)
            rospy.wait_for_service('/march/gait_selection/set_version_map', 3)
            rospy.wait_for_service('/march/gait_selection/update_default_versions', 3)
        except rospy.ROSException:
            rospy.logerr('Shutting down march_rqt_gait_selection, could not connect to march_gait_selection.')
            rospy.signal_shutdown('Shutting down march_rqt_gait_selection, could not connect to march_gait_selection.')
            sys.exit(0)

        self.get_version_map = rospy.ServiceProxy('/march/gait_selection/get_version_map', Trigger)
        self.get_directory_structure = rospy.ServiceProxy('/march/gait_selection/get_directory_structure', Trigger)
        self.set_version_map = rospy.ServiceProxy('/march/gait_selection/set_version_map', StringTrigger)
        self.update_default_versions = rospy.ServiceProxy('/march/gait_selection/update_default_versions', Trigger)

        # Create QWidget
        self._widget = QWidget()

        # Get path to UI file which should be in the 'resource' folder of this package
        ui_file = os.path.join(rospkg.RosPack().get_path('march_rqt_gait_selection'), 'resource', 'gait_selection.ui')

        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('Gait Selection')
        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)
        self._widget.Gaits.findChild(QWidget, 'scrollArea').findChild(QWidget, 'content').setLayout(QVBoxLayout())

        self.refresh()

        self._widget.findChild(QPushButton, 'Refresh').clicked.connect(lambda: self.refresh(True))
        self._widget.findChild(QPushButton, 'Apply').clicked.connect(
            lambda: [
                self.set_gait_selection_map(True),
                self.refresh(),
            ])
        self._widget.findChild(QPushButton, 'SaveDefault').clicked.connect(
            lambda: [
                self.set_gait_selection_map(),
                self.update_defaults(True),
                self.refresh(),
            ])

        self.log('Welcome to the Gait Selection.', Color.Debug)
        self.log('Select the versions of subgaits you want to select and press Apply.', Color.Info)
        self.log('Save as default persists between launches', Color.Info)
        self.log('Any warnings or errors will be displayed in this log.', Color.Warning)
        self.log('--------------------------------------', Color.Info)

    def log(self, msg, level):
        self._widget \
            .findChild(QPlainTextEdit, 'Log') \
            .appendHtml('<p style="color:' + str(level.value) + '">' + msg + '</p>')
        scrollbar = self._widget.findChild(QPlainTextEdit, 'Log').verticalScrollBar()
        scrollbar.setValue(scrollbar.maximum())

    def refresh(self, notify=False):
        if notify:
            self.log('Refreshing gait directory', Color.Debug)
        rospy.logdebug('Refreshing ui with %s', str(self.get_directory_structure().message))

        try:
            gait_version_map = ast.literal_eval(self.get_version_map().message)
        except ValueError:
            self.log('Gait selection map is not valid'
                     + str(self.get_version_map().message), Color.Error)
            rospy.logerr('Gait selection map is not valid'
                         + str(self.get_version_map().message))
            return
        try:
            gait_directory_structure = ast.literal_eval(self.get_directory_structure().message)
        except ValueError:
            self.log('Gait directory structure is not valid '
                     + str(self.get_directory_structure().message), Color.Error)
            rospy.logerr('Gait directory structure is not valid '
                         + str(self.get_directory_structure().message))
            return

        gaits = self._widget.Gaits \
                            .findChild(QWidget, 'scrollArea') \
                            .findChild(QWidget, 'content') \
                            .findChildren(QGroupBox, 'Gait')
        for gait in gaits:
            gait.deleteLater()

        self.load_ui(gait_directory_structure, gait_version_map)

    def load_ui(self, gait_directory_structure, gait_selection_map):
        for gait_name, gait in gait_directory_structure.iteritems():
            try:
                selection_map = gait_selection_map[gait_name]
            except KeyError:
                selection_map = None
            gait_group_box = self.create_gait(gait_name, gait, selection_map)
            self._widget.Gaits \
                        .findChild(QWidget, 'scrollArea') \
                        .findChild(QWidget, 'content') \
                        .layout() \
                        .addWidget(gait_group_box)

    def create_gait(self, name, gait, selections):
        gait_group_box = QGroupBox()
        gait_group_box.setObjectName('Gait')
        gait_group_box.setLayout(QHBoxLayout())

        gait_group_box.setTitle(name)
        image = QLabel()
        image.setStyleSheet(
            'background: url(' + gait['image'] + ') no-repeat center center 100px 100px;')
        image.setFixedSize(64, 80)
        gait_group_box.layout().addWidget(image)
        for subgait_name, subgait in gait['subgaits'].iteritems():
            subgait_group_box = self.create_subgait(subgait_name, subgait, selections)
            gait_group_box.layout().addWidget(subgait_group_box)

        return gait_group_box

    def create_subgait(self, name, subgait, version_selection):
        subgait_group_box = QGroupBox()
        subgait_group_box.setLayout(QGridLayout())
        subgait_group_box.setObjectName('Subgait')
        subgait_group_box.setTitle(name)
        try:
            version_name = version_selection[name]
        except TypeError:
            version_name = None
        except KeyError:
            version_name = None
        dropdown = self.create_dropdown(subgait, version_name)

        subgait_group_box.layout().addWidget(dropdown, 0, 0)
        return subgait_group_box

    def create_dropdown(self, options, selection):
        try:
            index = options.index(selection)
        except ValueError:
            rospy.loginfo('Selection %s not found in options %s.', str(selection), str(options))
            index = -1
        dropdown = QComboBox()
        for option in options:
            dropdown.addItem(option)
        dropdown.setCurrentIndex(index)
        return dropdown

    def set_gait_selection_map(self, notify=False):
        gait_selection_map = {}
        gaits = self._widget.Gaits \
                            .findChild(QWidget, 'scrollArea') \
                            .findChild(QWidget, 'content') \
                            .findChildren(QGroupBox, 'Gait')
        for gait in gaits:
            gait_name = str(gait.title())
            gait_selection_map[gait_name] = {}

            subgaits = gait.findChildren(QGroupBox, 'Subgait')
            for subgait in subgaits:
                subgait_name = str(subgait.title())
                version = str(subgait.findChild(QComboBox).currentText())
                gait_selection_map[gait_name][subgait_name] = version

        res = self.set_version_map(str(gait_selection_map))
        if res.success:
            if notify:
                self.log('Selection applied.', Color.Debug)
            rospy.loginfo(res.message)
        else:
            self.log(res.message, Color.Error)
            self.log('Resetting to last valid selection', Color.Warning)
            rospy.logwarn(res.message)

    def update_defaults(self, notify=False):
        res = self.update_default_versions()
        if res.success:
            if notify:
                self.log('Default selection updated.', Color.Debug)
            rospy.loginfo(res.message)
        else:
            self.log(res.message, Color.Error)
            self.log('Resetting to last valid selection', Color.Warning)
            rospy.logwarn(res.message)
コード例 #40
0
class kms40_emulator_plugin(Plugin):

    def __init__(self, context):
        super(kms40_emulator_plugin, self).__init__(context)
        # Give QObjects reasonable names
        self.setObjectName('kms40_emulator_plugin')

        # Process standalone plugin command-line arguments
        from argparse import ArgumentParser
        parser = ArgumentParser()
        # Add argument(s) to the parser.
        parser.add_argument("-q", "--quiet", action="store_true",
                      dest="quiet",
                      help="Put plugin in silent mode")
        args, unknowns = parser.parse_known_args(context.argv())
        if not args.quiet:
            print 'arguments: ', args
            print 'unknowns: ', unknowns

        # Create QWidget
        self._widget = QWidget()
        # Get path to UI file which is a sibling of this file
        # in this example the .ui and .py file are in the same folder
        ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'weiss_kms40_emulator.ui')
        # Extend the widget with all attributes and children from UI file
        loadUi(ui_file, self._widget)
        # Give QObjects reasonable names
        self._widget.setObjectName('kms40_emulator_pluginUi')

        # Connect UI to slots
        self._widget.pushButton_run.clicked[bool].connect(self._handle_run_clicked)
        self._widget.pushButton_stop.clicked[bool].connect(self._handle_stop_clicked)

        self._widget.spinBox_rate.valueChanged.connect(self._handle_rate_changed)

        self._widget.doubleSpinBox_forceX.valueChanged.connect(self._handle_forceX_changed)
        self._widget.doubleSpinBox_forceY.valueChanged.connect(self._handle_forceY_changed)
        self._widget.doubleSpinBox_forceZ.valueChanged.connect(self._handle_forceZ_changed)
        self._widget.doubleSpinBox_forceA.valueChanged.connect(self._handle_forceA_changed)
        self._widget.doubleSpinBox_forceB.valueChanged.connect(self._handle_forceB_changed)
        self._widget.doubleSpinBox_forceC.valueChanged.connect(self._handle_forceC_changed)

        self._widget.horizontalSlider_forceX.valueChanged.connect(self._handle_sliderX_changed)
        self._widget.horizontalSlider_forceY.valueChanged.connect(self._handle_sliderY_changed)
        self._widget.horizontalSlider_forceZ.valueChanged.connect(self._handle_sliderZ_changed)
        self._widget.horizontalSlider_forceA.valueChanged.connect(self._handle_sliderA_changed)
        self._widget.horizontalSlider_forceB.valueChanged.connect(self._handle_sliderB_changed)
        self._widget.horizontalSlider_forceC.valueChanged.connect(self._handle_sliderC_changed)

        self._widget.lineEdit_topic.editingFinished.connect(self._handle_topic_changed)
        self._widget.lineEdit_frameId.textChanged.connect(self._handle_frameId_changed)

        # Show _widget.windowTitle on left-top of each plugin (when 
        # it's set in _widget). This is useful when you open multiple 
        # plugins at once. Also if you open multiple instances of your 
        # plugin at once, these lines add number to make it easy to 
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)
        
        # PublisherThread
        self.t = PublisherThread()
        self.t.start()
        self.updateWidgets()

    def shutdown_plugin(self):
        self.t.end()
        self.t.join()
        # TODO unregister all publishers here

        pass

    def save_settings(self, plugin_settings, instance_settings):
        # TODO save intrinsic configuration, usually using:
        # instance_settings.set_value(k, v)
        pass

    def restore_settings(self, plugin_settings, instance_settings):
        # TODO restore intrinsic configuration, usually using:
        # v = instance_settings.value(k)
        pass

    #def trigger_configuration(self):
        # Comment in to signal that the plugin has a way to configure
        # This will enable a setting button (gear icon) in each dock widget title bar
        # Usually used to open a modal configuration dialog


    def _handle_run_clicked(self):
        print "run now"
        self.t.restart()


    def _handle_stop_clicked(self):
        print "stop now"
        self.t.stop()


    def _handle_rate_changed(self):
        val = self._widget.spinBox_rate.value()
        #print "handle_rate_changed", int(val)
        self.t.setRate(val)

    #spinBoxes
    def _handle_forceX_changed(self):
        val = self._widget.doubleSpinBox_forceX.value()
        #print "_handle_forceX_changed", val
        self.t.setWrenchFX(val)
        self.updateWidgets()

    def _handle_forceY_changed(self):
        val = self._widget.doubleSpinBox_forceY.value()
        #print "_handle_forceY_changed", val
        self.t.setWrenchFY(val)
        self.updateWidgets()

    def _handle_forceZ_changed(self):
        val = self._widget.doubleSpinBox_forceZ.value()
        #print "_handle_forceZ_changed", val
        self.t.setWrenchFZ(val)
        self.updateWidgets()

    def _handle_forceA_changed(self):
        val = self._widget.doubleSpinBox_forceA.value()
        #print "_handle_forceA_changed", val
        self.t.setWrenchTZ(val)
        self.updateWidgets()

    def _handle_forceB_changed(self):
        val = self._widget.doubleSpinBox_forceB.value()
        #print "_handle_forceB_changed", val
        self.t.setWrenchTY(val)
        self.updateWidgets()

    def _handle_forceC_changed(self):
        val = self._widget.doubleSpinBox_forceC.value()
        #print "_handle_forceC_changed", val
        self.t.setWrenchTX(val)
        self.updateWidgets()

    # sliders
    def _handle_sliderX_changed(self):
        val = self._widget.horizontalSlider_forceX.value()
        #print "_handle_sliderX_changed", val
        self.t.setWrenchFX(val)
        self.updateWidgets()

    def _handle_sliderY_changed(self):
        val = self._widget.horizontalSlider_forceY.value()
        #print "_handle_sliderY_changed", val
        self.t.setWrenchFY(val)
        self.updateWidgets()

    def _handle_sliderZ_changed(self):
        val = self._widget.horizontalSlider_forceZ.value()
        #print "_handle_sliderZ_changed", val
        self.t.setWrenchFZ(val)
        self.updateWidgets()

    def _handle_sliderA_changed(self):
        val = self._widget.horizontalSlider_forceA.value()
        #print "_handle_sliderA_changed", val
        self.t.setWrenchTZ(val)
        self.updateWidgets()

    def _handle_sliderB_changed(self):
        val = self._widget.horizontalSlider_forceB.value()
        #print "_handle_sliderB_changed", val
        self.t.setWrenchTY(val)
        self.updateWidgets()

    def _handle_sliderC_changed(self):
        val = self._widget.horizontalSlider_forceC.value()
        #print "_handle_sliderC_changed", val
        self.t.setWrenchTX(val)
        self.updateWidgets()

    def _handle_frameId_changed(self):
        val = self._widget.lineEdit_frameId.text()
        #print "_handle_topic_changed", val#self._widget.lineEdit_topic.toPlainText()
        self.t.setFrameId(val)

    def _handle_topic_changed(self):
        val = self._widget.lineEdit_topic.text()
        #print "_handle_topic_changed", val#self._widget.lineEdit_topic.toPlainText()
        self.t.setTopic(val)
        self.t.stop()
        rospy.sleep(0.1)
        self.t.restart()

    def updateWidgets(self):
        wrench = self.t.getWrench()
        self._widget.doubleSpinBox_forceX.setValue(wrench.force.x)
        self._widget.doubleSpinBox_forceY.setValue(wrench.force.y)
        self._widget.doubleSpinBox_forceZ.setValue(wrench.force.z)
        self._widget.doubleSpinBox_forceA.setValue(wrench.torque.z)
        self._widget.doubleSpinBox_forceB.setValue(wrench.torque.y)
        self._widget.doubleSpinBox_forceC.setValue(wrench.torque.x)

        self._widget.horizontalSlider_forceX.setValue(wrench.force.x)
        self._widget.horizontalSlider_forceY.setValue(wrench.force.y)
        self._widget.horizontalSlider_forceZ.setValue(wrench.force.z)
        self._widget.horizontalSlider_forceA.setValue(wrench.torque.z)
        self._widget.horizontalSlider_forceB.setValue(wrench.torque.y)
        self._widget.horizontalSlider_forceC.setValue(wrench.torque.x)
コード例 #41
0
class RosGraph(Plugin):

    _deferred_fit_in_view = Signal()

    def __init__(self, context):
        super(RosGraph, self).__init__(context)
        self.initialized = False
        self.setObjectName('RosGraph')

        self._graph = None
        self._current_dotcode = None

        self._widget = QWidget()

        # factory builds generic dotcode items
        self.dotcode_factory = PydotFactory()
        # self.dotcode_factory = PygraphvizFactory()
        # generator builds rosgraph
        self.dotcode_generator = RosGraphDotcodeGenerator()
        # dot_to_qt transforms into Qt elements using dot layout
        self.dot_to_qt = DotToQtGenerator()

        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_graph'), 'resource', 'RosGraph.ui')
        loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView})
        self._widget.setObjectName('RosGraphUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))

        self._scene = QGraphicsScene()
        self._scene.setBackgroundBrush(Qt.white)
        self._widget.graphics_view.setScene(self._scene)

        self._widget.graph_type_combo_box.insertItem(0, self.tr('Nodes only'), NODE_NODE_GRAPH)
        self._widget.graph_type_combo_box.insertItem(1, self.tr('Nodes/Topics (active)'), NODE_TOPIC_GRAPH)
        self._widget.graph_type_combo_box.insertItem(2, self.tr('Nodes/Topics (all)'), NODE_TOPIC_ALL_GRAPH)
        self._widget.graph_type_combo_box.setCurrentIndex(0)
        self._widget.graph_type_combo_box.currentIndexChanged.connect(self._refresh_rosgraph)

        self.node_completionmodel = NamespaceCompletionModel(self._widget.filter_line_edit, False)
        completer = RepeatedWordCompleter(self.node_completionmodel, self)
        completer.setCompletionMode(QCompleter.PopupCompletion)
        completer.setWrapAround(True)
        completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._widget.filter_line_edit.editingFinished.connect(self._refresh_rosgraph)
        self._widget.filter_line_edit.setCompleter(completer)

        self.topic_completionmodel = NamespaceCompletionModel(self._widget.topic_filter_line_edit, False)
        topic_completer = RepeatedWordCompleter(self.topic_completionmodel, self)
        topic_completer.setCompletionMode(QCompleter.PopupCompletion)
        topic_completer.setWrapAround(True)
        topic_completer.setCaseSensitivity(Qt.CaseInsensitive)
        self._widget.topic_filter_line_edit.editingFinished.connect(self._refresh_rosgraph)
        self._widget.topic_filter_line_edit.setCompleter(topic_completer)

        self._widget.namespace_cluster_check_box.clicked.connect(self._refresh_rosgraph)
        self._widget.actionlib_check_box.clicked.connect(self._refresh_rosgraph)
        self._widget.dead_sinks_check_box.clicked.connect(self._refresh_rosgraph)
        self._widget.leaf_topics_check_box.clicked.connect(self._refresh_rosgraph)
        self._widget.quiet_check_box.clicked.connect(self._refresh_rosgraph)

        self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('view-refresh'))
        self._widget.refresh_graph_push_button.pressed.connect(self._update_rosgraph)

        self._widget.highlight_connections_check_box.toggled.connect(self._redraw_graph_view)
        self._widget.auto_fit_graph_check_box.toggled.connect(self._redraw_graph_view)
        self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme('zoom-original'))
        self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)

        self._widget.load_dot_push_button.setIcon(QIcon.fromTheme('document-open'))
        self._widget.load_dot_push_button.pressed.connect(self._load_dot)
        self._widget.save_dot_push_button.setIcon(QIcon.fromTheme('document-save-as'))
        self._widget.save_dot_push_button.pressed.connect(self._save_dot)
        self._widget.save_as_svg_push_button.setIcon(QIcon.fromTheme('document-save-as'))
        self._widget.save_as_svg_push_button.pressed.connect(self._save_svg)
        self._widget.save_as_image_push_button.setIcon(QIcon.fromTheme('image'))
        self._widget.save_as_image_push_button.pressed.connect(self._save_image)

        self._update_rosgraph()
        self._deferred_fit_in_view.connect(self._fit_in_view, Qt.QueuedConnection)
        self._deferred_fit_in_view.emit()

        context.add_widget(self._widget)

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('graph_type_combo_box_index', self._widget.graph_type_combo_box.currentIndex())
        instance_settings.set_value('filter_line_edit_text', self._widget.filter_line_edit.text())
        instance_settings.set_value('topic_filter_line_edit_text', self._widget.topic_filter_line_edit.text())
        instance_settings.set_value('namespace_cluster_check_box_state', self._widget.namespace_cluster_check_box.isChecked())
        instance_settings.set_value('actionlib_check_box_state', self._widget.actionlib_check_box.isChecked())
        instance_settings.set_value('dead_sinks_check_box_state', self._widget.dead_sinks_check_box.isChecked())
        instance_settings.set_value('leaf_topics_check_box_state', self._widget.leaf_topics_check_box.isChecked())
        instance_settings.set_value('quiet_check_box_state', self._widget.quiet_check_box.isChecked())
        instance_settings.set_value('auto_fit_graph_check_box_state', self._widget.auto_fit_graph_check_box.isChecked())
        instance_settings.set_value('highlight_connections_check_box_state', self._widget.highlight_connections_check_box.isChecked())

    def restore_settings(self, plugin_settings, instance_settings):
        self._widget.graph_type_combo_box.setCurrentIndex(int(instance_settings.value('graph_type_combo_box_index', 0)))
        self._widget.filter_line_edit.setText(instance_settings.value('filter_line_edit_text', '/'))
        self._widget.topic_filter_line_edit.setText(instance_settings.value('topic_filter_line_edit_text', '/'))
        self._widget.namespace_cluster_check_box.setChecked(instance_settings.value('namespace_cluster_check_box_state', True) in [True, 'true'])
        self._widget.actionlib_check_box.setChecked(instance_settings.value('actionlib_check_box_state', True) in [True, 'true'])
        self._widget.dead_sinks_check_box.setChecked(instance_settings.value('dead_sinks_check_box_state', True) in [True, 'true'])
        self._widget.leaf_topics_check_box.setChecked(instance_settings.value('leaf_topics_check_box_state', True) in [True, 'true'])
        self._widget.quiet_check_box.setChecked(instance_settings.value('quiet_check_box_state', True) in [True, 'true'])
        self._widget.auto_fit_graph_check_box.setChecked(instance_settings.value('auto_fit_graph_check_box_state', True) in [True, 'true'])
        self._widget.highlight_connections_check_box.setChecked(instance_settings.value('highlight_connections_check_box_state', True) in [True, 'true'])
        self.initialized = True
        self._refresh_rosgraph()

    def _update_rosgraph(self):
        # re-enable controls customizing fetched ROS graph
        self._widget.graph_type_combo_box.setEnabled(True)
        self._widget.filter_line_edit.setEnabled(True)
        self._widget.topic_filter_line_edit.setEnabled(True)
        self._widget.namespace_cluster_check_box.setEnabled(True)
        self._widget.actionlib_check_box.setEnabled(True)
        self._widget.dead_sinks_check_box.setEnabled(True)
        self._widget.leaf_topics_check_box.setEnabled(True)
        self._widget.quiet_check_box.setEnabled(True)

        self._graph = rosgraph.impl.graph.Graph()
        self._graph.set_master_stale(5.0)
        self._graph.set_node_stale(5.0)
        self._graph.update()
        self.node_completionmodel.refresh(self._graph.nn_nodes)
        self.topic_completionmodel.refresh(self._graph.nt_nodes)
        self._refresh_rosgraph()

    def _refresh_rosgraph(self):
        if not self.initialized:
            return
        self._update_graph_view(self._generate_dotcode())

    def _generate_dotcode(self):
        ns_filter = self._widget.filter_line_edit.text()
        topic_filter = self._widget.topic_filter_line_edit.text()
        graph_mode = self._widget.graph_type_combo_box.itemData(self._widget.graph_type_combo_box.currentIndex())
        orientation = 'LR'
        if self._widget.namespace_cluster_check_box.isChecked():
            namespace_cluster = 1
        else:
            namespace_cluster = 0
        accumulate_actions = self._widget.actionlib_check_box.isChecked()
        hide_dead_end_topics = self._widget.dead_sinks_check_box.isChecked()
        hide_single_connection_topics = self._widget.leaf_topics_check_box.isChecked()
        quiet = self._widget.quiet_check_box.isChecked()

        return self.dotcode_generator.generate_dotcode(
            rosgraphinst=self._graph,
            ns_filter=ns_filter,
            topic_filter=topic_filter,
            graph_mode=graph_mode,
            hide_single_connection_topics=hide_single_connection_topics,
            hide_dead_end_topics=hide_dead_end_topics,
            cluster_namespaces_level=namespace_cluster,
            accumulate_actions=accumulate_actions,
            dotcode_factory=self.dotcode_factory,
            orientation=orientation,
            quiet=quiet)

    def _update_graph_view(self, dotcode):
        if dotcode == self._current_dotcode:
            return
        self._current_dotcode = dotcode
        self._redraw_graph_view()

    def _generate_tool_tip(self, url):
        if url is not None and ':' in url:
            item_type, item_path = url.split(':', 1)
            if item_type == 'node':
                tool_tip = 'Node:\n  %s' % (item_path)
                service_names = rosservice.get_service_list(node=item_path)
                if service_names:
                    tool_tip += '\nServices:'
                    for service_name in service_names:
                        try:
                            service_type = rosservice.get_service_type(service_name)
                            tool_tip += '\n  %s [%s]' % (service_name, service_type)
                        except rosservice.ROSServiceIOException as e:
                            tool_tip += '\n  %s' % (e)
                return tool_tip
            elif item_type == 'topic':
                topic_type, topic_name, _ = rostopic.get_topic_type(item_path)
                return 'Topic:\n  %s\nType:\n  %s' % (topic_name, topic_type)
        return url

    def _redraw_graph_view(self):
        self._scene.clear()

        if self._widget.highlight_connections_check_box.isChecked():
            highlight_level = 3
        else:
            highlight_level = 1

        # layout graph and create qt items
        (nodes, edges) = self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode,
                                                            highlight_level=highlight_level,
                                                            same_label_siblings=True)

        for node_item in nodes.itervalues():
            self._scene.addItem(node_item)
        for edge_items in edges.itervalues():
            for edge_item in edge_items:
                edge_item.add_to_scene(self._scene)

        self._scene.setSceneRect(self._scene.itemsBoundingRect())
        if self._widget.auto_fit_graph_check_box.isChecked():
            self._fit_in_view()

    def _load_dot(self, file_name=None):
        if file_name is None:
            file_name, _ = QFileDialog.getOpenFileName(self._widget, self.tr('Open graph from file'), None, self.tr('DOT graph (*.dot)'))
            if file_name is None or file_name == '':
                return

        try:
            fh = open(file_name, 'rb')
            dotcode = fh.read()
            fh.close()
        except IOError:
            return

        # disable controls customizing fetched ROS graph
        self._widget.graph_type_combo_box.setEnabled(False)
        self._widget.filter_line_edit.setEnabled(False)
        self._widget.topic_filter_line_edit.setEnabled(False)
        self._widget.namespace_cluster_check_box.setEnabled(False)
        self._widget.actionlib_check_box.setEnabled(False)
        self._widget.dead_sinks_check_box.setEnabled(False)
        self._widget.leaf_topics_check_box.setEnabled(False)
        self._widget.quiet_check_box.setEnabled(False)

        self._update_graph_view(dotcode)

    def _fit_in_view(self):
        self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(), Qt.KeepAspectRatio)

    def _save_dot(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as DOT'), 'rosgraph.dot', self.tr('DOT graph (*.dot)'))
        if file_name is None or file_name == '':
            return

        handle = QFile(file_name)
        if not handle.open(QIODevice.WriteOnly | QIODevice.Text):
            return

        handle.write(self._current_dotcode)
        handle.close()

    def _save_svg(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as SVG'), 'rosgraph.svg', self.tr('Scalable Vector Graphic (*.svg)'))
        if file_name is None or file_name == '':
            return

        generator = QSvgGenerator()
        generator.setFileName(file_name)
        generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())

        painter = QPainter(generator)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()

    def _save_image(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget, self.tr('Save as image'), 'rosgraph.png', self.tr('Image (*.bmp *.jpg *.png *.tiff)'))
        if file_name is None or file_name == '':
            return

        img = QImage((self._scene.sceneRect().size() * 2.0).toSize(), QImage.Format_ARGB32_Premultiplied)
        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()
        img.save(file_name)
コード例 #42
0
class JointTrajectoryController(Plugin):
    """
    Graphical frontend for a C{JointTrajectoryController}.

    There are two modes for interacting with a controller:
        1. B{Monitor mode} Joint displays are updated with the state reported
          by the controller. This is a read-only mode and does I{not} send
          control commands. Every time a new controller is selected, it starts
          in monitor mode for safety reasons.

        2. B{Control mode} Joint displays update the control command that is
        sent to the controller. Commands are sent periodically evan if the
        displays are not being updated by the user.

    To control the aggressiveness of the motions, the maximum speed of the
    sent commands can be scaled down using the C{Speed scaling control}

    This plugin is able to detect and keep track of all active controller
    managers, as well as the JointTrajectoryControllers that are I{running}
    in each controller manager.
    For a controller to be compatible with this plugin, it must comply with
    the following requisites:
        - The controller type contains the C{JointTrajectoryController}
        substring, e.g., C{position_controllers/JointTrajectoryController}
        - The controller exposes the C{command} and C{state} topics in its
        ROS interface.

    Additionally, there must be a URDF loaded with a valid joint limit
    specification, namely position (when applicable) and velocity limits.

    A reference implementation of the C{JointTrajectoryController} is
    available in the C{joint_trajectory_controller} ROS package.
    """
    _cmd_pub_freq = 10.0  # Hz
    _widget_update_freq = 30.0  # Hz
    _ctrlrs_update_freq = 1  # Hz
    _min_traj_dur = 5.0 / _cmd_pub_freq  # Minimum trajectory duration

    jointStateChanged = Signal([dict])

    def __init__(self, context):
        super(JointTrajectoryController, self).__init__(context)
        self.setObjectName('JointTrajectoryController')

        # Create QWidget and extend it with all the attributes and children
        # from the UI file
        self._widget = QWidget()
        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_joint_trajectory_controller'),
                               'resource',
                               'joint_trajectory_controller.ui')
        loadUi(ui_file, self._widget)
        self._widget.setObjectName('JointTrajectoryControllerUi')

        # Setup speed scaler
        speed_scaling = DoubleEditor(1.0, 100.0)
        speed_scaling.spin_box.setSuffix('%')
        speed_scaling.spin_box.setValue(50.0)
        speed_scaling.spin_box.setDecimals(0)
        speed_scaling.setEnabled(False)
        self._widget.speed_scaling_layout.addWidget(speed_scaling)
        self._speed_scaling_widget = speed_scaling
        speed_scaling.valueChanged.connect(self._on_speed_scaling_change)
        self._on_speed_scaling_change(speed_scaling.value())

        # Show _widget.windowTitle on left-top of each plugin (when
        # it's set in _widget). This is useful when you open multiple
        # plugins at once. Also if you open multiple instances of your
        # plugin at once, these lines add number to make it easy to
        # tell from pane to pane.
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() +
                                        (' (%d)' % context.serial_number()))
        # Add widget to the user interface
        context.add_widget(self._widget)

        # Initialize members
        self._jtc_name = []  # Name of selected joint trajectory controller
        self._cm_ns = []  # Namespace of the selected controller manager
        self._joint_pos = {}  # name->pos map for joints of selected controller
        self._joint_names = []  # Ordered list of selected controller joints
        self._robot_joint_limits = {} # Lazily evaluated on first use

        # Timer for sending commands to active controller
        self._update_cmd_timer = QTimer(self)
        self._update_cmd_timer.setInterval(1000.0 / self._cmd_pub_freq)
        self._update_cmd_timer.timeout.connect(self._update_cmd_cb)

        # Timer for updating the joint widgets from the controller state
        self._update_act_pos_timer = QTimer(self)
        self._update_act_pos_timer.setInterval(1000.0 /
                                               self._widget_update_freq)
        self._update_act_pos_timer.timeout.connect(self._update_joint_widgets)

        # Timer for controller manager updates
        self._list_cm = ControllerManagerLister()
        self._update_cm_list_timer = QTimer(self)
        self._update_cm_list_timer.setInterval(1000.0 /
                                               self._ctrlrs_update_freq)
        self._update_cm_list_timer.timeout.connect(self._update_cm_list)
        self._update_cm_list_timer.start()

        # Timer for running controller updates
        self._update_jtc_list_timer = QTimer(self)
        self._update_jtc_list_timer.setInterval(1000.0 /
                                                self._ctrlrs_update_freq)
        self._update_jtc_list_timer.timeout.connect(self._update_jtc_list)
        self._update_jtc_list_timer.start()

        # Signal connections
        w = self._widget
        w.enable_button.toggled.connect(self._on_jtc_enabled)
        w.jtc_combo.currentIndexChanged[str].connect(self._on_jtc_change)
        w.cm_combo.currentIndexChanged[str].connect(self._on_cm_change)

        self._cmd_pub = None    # Controller command publisher
        self._state_sub = None  # Controller state subscriber

        self._list_controllers = None

    def shutdown_plugin(self):
        self._update_cmd_timer.stop()
        self._update_act_pos_timer.stop()
        self._update_cm_list_timer.stop()
        self._update_jtc_list_timer.stop()
        self._unregister_state_sub()
        self._unregister_cmd_pub()

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('cm_ns', self._cm_ns)
        instance_settings.set_value('jtc_name', self._jtc_name)

    def restore_settings(self, plugin_settings, instance_settings):
        # Restore last session's controller_manager, if present
        self._update_cm_list()
        cm_ns = instance_settings.value('cm_ns')
        cm_combo = self._widget.cm_combo
        cm_list = [cm_combo.itemText(i) for i in range(cm_combo.count())]
        try:
            idx = cm_list.index(cm_ns)
            cm_combo.setCurrentIndex(idx)
            # Resore last session's controller, if running
            self._update_jtc_list()
            jtc_name = instance_settings.value('jtc_name')
            jtc_combo = self._widget.jtc_combo
            jtc_list = [jtc_combo.itemText(i) for i in range(jtc_combo.count())]
            try:
                idx = jtc_list.index(jtc_name)
                jtc_combo.setCurrentIndex(idx)
            except (ValueError):
                pass
        except (ValueError):
            pass

    # def trigger_configuration(self):
        # Comment in to signal that the plugin has a way to configure
        # This will enable a setting button (gear icon) in each dock widget
        # title bar
        # Usually used to open a modal configuration dialog

    def _update_cm_list(self):
        update_combo(self._widget.cm_combo, self._list_cm())

    def _update_jtc_list(self):
        # Clear controller list if no controller information is available
        if not self._list_controllers:
            self._widget.jtc_combo.clear()
            return

        # List of running controllers with a valid joint limits specification
        # for _all_ their joints
        running_jtc = self._running_jtc_info()
        if running_jtc and not self._robot_joint_limits:
            self._robot_joint_limits = get_joint_limits()  # Lazy evaluation
        valid_jtc = []
        for jtc_info in running_jtc:
            has_limits = all(name in self._robot_joint_limits
                             for name in _jtc_joint_names(jtc_info))
            if has_limits:
                valid_jtc.append(jtc_info);
        valid_jtc_names = [data.name for data in valid_jtc]

        # Update widget
        update_combo(self._widget.jtc_combo, sorted(valid_jtc_names))

    def _on_speed_scaling_change(self, val):
        self._speed_scale = val / self._speed_scaling_widget.slider.maximum()

    def _on_joint_state_change(self, actual_pos):
        assert(len(actual_pos) == len(self._joint_pos))
        for name in actual_pos.keys():
            self._joint_pos[name]['position'] = actual_pos[name]

    def _on_cm_change(self, cm_ns):
        self._cm_ns = cm_ns
        if cm_ns:
            self._list_controllers = ControllerLister(cm_ns)
            # NOTE: Clear below is important, as different controller managers
            # might have controllers with the same name but different
            # configurations. Clearing forces controller re-discovery
            self._widget.jtc_combo.clear()
            self._update_jtc_list()
        else:
            self._list_controllers = None

    def _on_jtc_change(self, jtc_name):
        self._unload_jtc()
        self._jtc_name = jtc_name
        if self._jtc_name:
            self._load_jtc()

    def _on_jtc_enabled(self, val):
        # Don't allow enabling if there are no controllers selected
        if not self._jtc_name:
            self._widget.enable_button.setChecked(False)
            return

        # Enable/disable joint displays
        for joint_widget in self._joint_widgets():
            joint_widget.setEnabled(val)

        # Enable/disable speed scaling
        self._speed_scaling_widget.setEnabled(val)

        if val:
            # Widgets send desired position commands to controller
            self._update_act_pos_timer.stop()
            self._update_cmd_timer.start()
        else:
            # Controller updates widgets with actual position
            self._update_cmd_timer.stop()
            self._update_act_pos_timer.start()

    def _load_jtc(self):
        # Initialize joint data corresponding to selected controller
        running_jtc = self._running_jtc_info()
        self._joint_names = next(_jtc_joint_names(x) for x in running_jtc
                                 if x.name == self._jtc_name)
        for name in self._joint_names:
            self._joint_pos[name] = {}

        # Update joint display
        try:
            layout = self._widget.joint_group.layout()
            for name in self._joint_names:
                limits = self._robot_joint_limits[name]
                joint_widget = DoubleEditor(limits['min_position'],
                                            limits['max_position'])
                layout.addRow(name, joint_widget)
                # NOTE: Using partial instead of a lambda because lambdas
                # "will not evaluate/look up the argument values before it is
                # effectively called, breaking situations like using a loop
                # variable inside it"
                from functools import partial
                par = partial(self._update_single_cmd_cb, name=name)
                joint_widget.valueChanged.connect(par)
        except:
            # TODO: Can we do better than swallow the exception?
            from sys import exc_info
            print 'Unexpected error:', exc_info()[0]

        # Enter monitor mode (sending commands disabled)
        self._on_jtc_enabled(False)

        # Setup ROS interfaces
        jtc_ns = _resolve_controller_ns(self._cm_ns, self._jtc_name)
        state_topic = jtc_ns + '/state'
        cmd_topic = jtc_ns + '/command'
        self._state_sub = rospy.Subscriber(state_topic,
                                           JointTrajectoryControllerState,
                                           self._state_cb,
                                           queue_size=1)
        self._cmd_pub = rospy.Publisher(cmd_topic,
                                        JointTrajectory,
                                        queue_size=1)

        # Start updating the joint positions
        self.jointStateChanged.connect(self._on_joint_state_change)

    def _unload_jtc(self):
        # Stop updating the joint positions
        try:
            self.jointStateChanged.disconnect(self._on_joint_state_change)
        except:
            pass

        # Reset ROS interfaces
        self._unregister_state_sub()
        self._unregister_cmd_pub()

        # Clear joint widgets
        # NOTE: Implementation is a workaround for:
        # https://bugreports.qt-project.org/browse/QTBUG-15990 :(
        layout = self._widget.joint_group.layout()
        if layout is not None:
            while layout.count():
                layout.takeAt(0).widget().deleteLater()
            # Delete existing layout by reparenting to temporary
            QWidget().setLayout(layout)
        self._widget.joint_group.setLayout(QFormLayout())

        # Reset joint data
        self._joint_names = []
        self._joint_pos = {}

        # Enforce monitor mode (sending commands disabled)
        self._widget.enable_button.setChecked(False)

    def _running_jtc_info(self):
        from controller_manager_msgs.utils\
            import filter_by_type, filter_by_state

        controller_list = self._list_controllers()
        jtc_list = filter_by_type(controller_list,
                                  'JointTrajectoryController',
                                  match_substring=True)
        running_jtc_list = filter_by_state(jtc_list, 'running')
        return running_jtc_list

    def _unregister_cmd_pub(self):
        if self._cmd_pub is not None:
            self._cmd_pub.unregister()
            self._cmd_pub = None

    def _unregister_state_sub(self):
        if self._state_sub is not None:
            self._state_sub.unregister()
            self._state_sub = None

    def _state_cb(self, msg):
        actual_pos = {}
        for i in range(len(msg.joint_names)):
            joint_name = msg.joint_names[i]
            joint_pos = msg.actual.positions[i]
            actual_pos[joint_name] = joint_pos
        self.jointStateChanged.emit(actual_pos)

    def _update_single_cmd_cb(self, val, name):
        self._joint_pos[name]['command'] = val

    def _update_cmd_cb(self):
        dur = []
        traj = JointTrajectory()
        traj.joint_names = self._joint_names
        point = JointTrajectoryPoint()
        for name in traj.joint_names:
            pos = self._joint_pos[name]['position']
            cmd = pos
            try:
                cmd = self._joint_pos[name]['command']
            except (KeyError):
                pass
            max_vel = self._robot_joint_limits[name]['max_velocity']
            dur.append(max(abs(cmd - pos) / max_vel, self._min_traj_dur))
            point.positions.append(cmd)
        point.time_from_start = rospy.Duration(max(dur) / self._speed_scale)
        traj.points.append(point)

        self._cmd_pub.publish(traj)

    def _update_joint_widgets(self):
        joint_widgets = self._joint_widgets()
        for id in range(len(joint_widgets)):
            joint_name = self._joint_names[id]
            try:
                joint_pos = self._joint_pos[joint_name]['position']
                joint_widgets[id].setValue(joint_pos)
            except (KeyError):
                pass  # Can happen when first connected to controller

    def _joint_widgets(self):  # TODO: Cache instead of compute every time?
        widgets = []
        layout = self._widget.joint_group.layout()
        for row_id in range(layout.rowCount()):
            widgets.append(layout.itemAt(row_id,
                                         QFormLayout.FieldRole).widget())
        return widgets
コード例 #43
0
ファイル: tf_tree.py プロジェクト: mylxiaoyi/ros_distro
class RosTfTree(QObject):

    _deferred_fit_in_view = Signal()

    def __init__(self, context):
        super(RosTfTree, self).__init__(context)
        self.initialized = False

        self.setObjectName('RosTfTree')

        self._current_dotcode = None

        self._widget = QWidget()

        # factory builds generic dotcode items
        self.dotcode_factory = PydotFactory()
        # self.dotcode_factory = PygraphvizFactory()
        # generator builds rosgraph
        self.dotcode_generator = RosTfTreeDotcodeGenerator()
        self.tf2_buffer_ = tf2_ros.Buffer()
        self.tf2_listener_ = tf2_ros.TransformListener(self.tf2_buffer_)

        # dot_to_qt transforms into Qt elements using dot layout
        self.dot_to_qt = DotToQtGenerator()

        rp = rospkg.RosPack()
        ui_file = os.path.join(rp.get_path('rqt_tf_tree'), 'resource', 'RosTfTree.ui')
        loadUi(ui_file, self._widget, {'InteractiveGraphicsView': InteractiveGraphicsView})
        self._widget.setObjectName('RosTfTreeUi')
        if context.serial_number() > 1:
            self._widget.setWindowTitle(self._widget.windowTitle() + (' (%d)' % context.serial_number()))

        self._scene = QGraphicsScene()
        self._scene.setBackgroundBrush(Qt.white)
        self._widget.graphics_view.setScene(self._scene)

        self._widget.refresh_graph_push_button.setIcon(QIcon.fromTheme('view-refresh'))
        self._widget.refresh_graph_push_button.pressed.connect(self._update_tf_graph)

        self._widget.highlight_connections_check_box.toggled.connect(self._redraw_graph_view)
        self._widget.auto_fit_graph_check_box.toggled.connect(self._redraw_graph_view)
        self._widget.fit_in_view_push_button.setIcon(QIcon.fromTheme('zoom-original'))
        self._widget.fit_in_view_push_button.pressed.connect(self._fit_in_view)

        self._widget.load_dot_push_button.setIcon(QIcon.fromTheme('document-open'))
        self._widget.load_dot_push_button.pressed.connect(self._load_dot)
        self._widget.save_dot_push_button.setIcon(QIcon.fromTheme('document-save-as'))
        self._widget.save_dot_push_button.pressed.connect(self._save_dot)
        self._widget.save_as_svg_push_button.setIcon(QIcon.fromTheme('document-save-as'))
        self._widget.save_as_svg_push_button.pressed.connect(self._save_svg)
        self._widget.save_as_image_push_button.setIcon(QIcon.fromTheme('image-x-generic'))
        self._widget.save_as_image_push_button.pressed.connect(self._save_image)

        self._deferred_fit_in_view.connect(self._fit_in_view,
                                           Qt.QueuedConnection)
        self._deferred_fit_in_view.emit()

        context.add_widget(self._widget)

        self._force_refresh = False

    def save_settings(self, plugin_settings, instance_settings):
        instance_settings.set_value('auto_fit_graph_check_box_state',
                                    self._widget.auto_fit_graph_check_box.isChecked())
        instance_settings.set_value('highlight_connections_check_box_state',
                                    self._widget.highlight_connections_check_box.isChecked())

    def restore_settings(self, plugin_settings, instance_settings):
        self._widget.auto_fit_graph_check_box.setChecked(
            instance_settings.value('auto_fit_graph_check_box_state', True) in [True, 'true'])
        self._widget.highlight_connections_check_box.setChecked(
            instance_settings.value('highlight_connections_check_box_state', True) in [True, 'true'])
        self.initialized = True
        self._refresh_tf_graph()

    def _update_tf_graph(self):
        self._force_refresh = True
        self._refresh_tf_graph()

    def _refresh_tf_graph(self):
        if not self.initialized:
            return
        self._update_graph_view(self._generate_dotcode())

    def _generate_dotcode(self):
        force_refresh = self._force_refresh
        self._force_refresh = False
        rospy.wait_for_service('~tf2_frames')
        tf2_frame_srv = rospy.ServiceProxy('~tf2_frames', FrameGraph)
        return self.dotcode_generator.generate_dotcode(dotcode_factory=self.dotcode_factory,
                                                       tf2_frame_srv=tf2_frame_srv,
                                                       force_refresh=force_refresh)

    def _update_graph_view(self, dotcode):
        if dotcode == self._current_dotcode:
            return
        self._current_dotcode = dotcode
        self._redraw_graph_view()

    def _generate_tool_tip(self, url):
        return url

    def _redraw_graph_view(self):
        self._scene.clear()

        if self._widget.highlight_connections_check_box.isChecked():
            highlight_level = 3
        else:
            highlight_level = 1

        (nodes, edges) = self.dot_to_qt.dotcode_to_qt_items(self._current_dotcode,
                                                            highlight_level)

        for node_item in nodes.itervalues():
            self._scene.addItem(node_item)
        for edge_items in edges.itervalues():
            for edge_item in edge_items:
                edge_item.add_to_scene(self._scene)

        self._scene.setSceneRect(self._scene.itemsBoundingRect())
        if self._widget.auto_fit_graph_check_box.isChecked():
            self._fit_in_view()

    def _load_dot(self, file_name=None):
        if file_name is None:
            file_name, _ = QFileDialog.getOpenFileName(
                self._widget,
                self.tr('Open graph from file'),
                None,
                self.tr('DOT graph (*.dot)'))
            if file_name is None or file_name == '':
                return

        try:
            fhandle = open(file_name, 'rb')
            dotcode = fhandle.read()
            fhandle.close()
        except IOError:
            return

        self._update_graph_view(dotcode)

    def _fit_in_view(self):
        self._widget.graphics_view.fitInView(self._scene.itemsBoundingRect(),
                                             Qt.KeepAspectRatio)

    def _save_dot(self):
        file_name, _ = QFileDialog.getSaveFileName(self._widget,
                                                   self.tr('Save as DOT'),
                                                   'frames.dot',
                                                   self.tr('DOT graph (*.dot)'))
        if file_name is None or file_name == '':
            return

        file = QFile(file_name)
        if not file.open(QIODevice.WriteOnly | QIODevice.Text):
            return

        file.write(self._current_dotcode)
        file.close()

    def _save_svg(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget,
            self.tr('Save as SVG'),
            'frames.svg',
            self.tr('Scalable Vector Graphic (*.svg)'))
        if file_name is None or file_name == '':
            return

        generator = QSvgGenerator()
        generator.setFileName(file_name)
        generator.setSize((self._scene.sceneRect().size() * 2.0).toSize())

        painter = QPainter(generator)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()

    def _save_image(self):
        file_name, _ = QFileDialog.getSaveFileName(
            self._widget,
            self.tr('Save as image'),
            'frames.png',
            self.tr('Image (*.bmp *.jpg *.png *.tiff)'))
        if file_name is None or file_name == '':
            return

        img = QImage((self._scene.sceneRect().size() * 2.0).toSize(),
                     QImage.Format_ARGB32_Premultiplied)
        painter = QPainter(img)
        painter.setRenderHint(QPainter.Antialiasing)
        self._scene.render(painter)
        painter.end()
        img.save(file_name)