new Client-Tab for controlling the Crazyflie using LH

Firmware/software/electronics/mechanics
Post Reply
Baosai
Beginner
Posts: 10
Joined: Mon Jun 08, 2020 8:49 am

new Client-Tab for controlling the Crazyflie using LH

Post by Baosai »

Hallo,
i can control the Crazyflie using Lighthouse system, but only with the python code, for example https://github.com/bitcraze/crazyflie-l ... equence.py. Now i want to write a new Client-Tab for controlling with LH. I know how to read and change the parameters in new Tab after i learned the Logging and parameter framework video https://www.youtube.com/watch?v=chWrNh73YBw. Now I want to add some features, such as logdata read and setpoint.
My problem is:
1. I tried to write the Tab based on the QualisysTab, but unfortunately, I can only complete the log reading function, but can't make the copter take off with sentpoint function.
2. After I wrote the new log reading function, all the parameters in ParamTab are locked in red and i can't modified them any more.

Does anyone have experience in this area, can you give me a simple template?

here is my code und the new Tab:

Code: Select all

import logging
from PyQt5 import uic
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtWidgets import QMessageBox
from cflib.crazyflie.log import LogConfig

import cfclient
from cfclient.ui.tab import Tab
import math

__author__ = 'Bitcraze AB'
__all__ = ['ExampleTab']

logger = logging.getLogger(__name__)

example_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/exampleTab.ui")[0]


class ExampleTab(Tab, example_tab_class):
    """Tab for plotting logging data"""

    _connected_signal = pyqtSignal(str)
    _disconnected_signal = pyqtSignal(str)
    _log_data_signal = pyqtSignal(int, object, object)
    _cmd_data_signal = pyqtSignal(int, object, object)
    _pos_data_signal = pyqtSignal(int, object, object)
    _log_error_signal = pyqtSignal(object, str)
    _param_updated_signal = pyqtSignal(str, str)

    def __init__(self, tabWidget, helper, *args):
        super(ExampleTab, self).__init__(*args)
        self.setupUi(self)

        self.tabName = "Example"
        self.menuName = "Example Tab"
        self.tabWidget = tabWidget

        self._helper = helper

        # Always wrap callbacks from Crazyflie API though QT Signal/Slots
        # to avoid manipulating the UI when rendering it
        self._connected_signal.connect(self._connected)
        self._disconnected_signal.connect(self._disconnected)
        self._cmd_data_signal.connect(self._cmd_data_received)
        self._pos_data_signal.connect(self._pos_data_received)
        self._param_updated_signal.connect(self._param_updated)

        # Connect the Crazyflie API callbacks to the signals
        self._helper.cf.connected.add_callback(
            self._connected_signal.emit)

        self._helper.cf.disconnected.add_callback(
            self._disconnected_signal.emit)

        # Connect motorPower UI signals
        self.motorPower.clicked.connect(
            lambda enable:
            self._helper.cf.param.set_value("motorPowerSet.enable", str(enable))
        )
        self._helper.cf.param.add_update_callback(group="motorPowerSet", name="enable", cb=self._param_updated_signal.emit)

        # Connect _enable_motors UI signals
        self._enable_motors.clicked.connect(
            lambda enable:
            self._helper.cf.param.set_value("mc_usr.enable_usr", str(enable))
        )
        self._helper.cf.param.add_update_callback(group="mc_usr", name="enable_usr", cb=self._param_updated_signal.emit)

        # Connect UI signals that are in this tab
        self.Target_X.valueChanged.connect(self._position_changed)
        self.Target_Y.valueChanged.connect(self._position_changed)
        self.Target_Z.valueChanged.connect(self._position_changed)
        self.Target_Yaw.valueChanged.connect(self._position_changed)

        # set original value
        self.Target_X.setValue(0)
        self.Target_Y.setValue(0)
        self.Target_Z.setValue(0)
        self.Target_Yaw.setValue(0)

        self._eingang.valueChanged.connect(self._eingang_changed)
        self._eingang.setValue(5)

    def _connected(self, link_uri):
        """Callback when the Crazyflie has been connected"""
        logger.debug("Crazyflie connected to {}".format(link_uri))
        # aktual position
        pos_conf = LogConfig('Position2', 10)
        pos_conf.add_variable('kalman.stateX')
        pos_conf.add_variable('kalman.stateY')
        pos_conf.add_variable('kalman.stateZ')

        try:
            self._helper.cf.log.add_config(pos_conf)
            pos_conf.data_received_cb.add_callback(self._pos_data_signal.emit)
            pos_conf.error_cb.add_callback(self._log_error_signal.emit)
            pos_conf.start()
        except KeyError as e:
            logger.warning(str(e))
        except AttributeError as e:
            logger.warning(str(e))

        # control Commander
        cmd_conf = LogConfig('MotorCommander', 10)  # configure the variable in Plotter
        cmd_conf.add_variable('controller.cmd_roll')  # add the variable by calling
        cmd_conf.add_variable('controller.cmd_pitch')
        cmd_conf.add_variable('controller.cmd_yaw')
        cmd_conf.add_variable('controller.cmd_thrust')

        try:
            self._helper.cf.log.add_config(cmd_conf)
            cmd_conf.data_received_cb.add_callback(self._cmd_data_signal.emit)
            cmd_conf.error_cb.add_callback(self._log_error_signal.emit)
            cmd_conf.start()
        except KeyError as e:
            logger.warning(str(e))
        except AttributeError as e:
            logger.warning(str(e))

    def _disconnected(self, link_uri):
        """Callback for when the Crazyflie has been disconnected"""
        logger.debug("Crazyflie disconnected from {}".format(link_uri))

        self._enable_motors.setEnabled(False)

    def _eingang_changed(self):
        self.x = 2 * self._eingang.value()
        self._ausgang.setText("%0.3f" % self.x)

    def _param_updated(self, name, value):
        """Callback when the registered parameter get's updated"""
        logger.debug("Updated {0} to {1}".format(name, value))

        if not self._enable_motors.isEnabled():
            self._enable_motors.setEnabled(True)

        self._enable_motors.setChecked(eval(str(value)))

    def _cmd_data_received(self, timestamp, data, log_conf):
        """Callback when the log layer receives new data"""
        self.cmd_data.setText( "roll=%0.3f  pitch=%0.3f  yaw=%0.3f  thrust=%0.3f " % (data['controller.cmd_roll'],
                                                                                      data['controller.cmd_pitch'],
                                                                                      data['controller.cmd_yaw'],
                                                                                      data['controller.cmd_thrust']))

    def _pos_data_received(self, timestamp, data, log_conf):
        self.pos_data.setText(
            "X=%0.3f  Y=%0.3f  Z=%0.3f" % (data['kalman.stateX'],
                                           data['kalman.stateY'],
                                           data['kalman.stateZ']))

    def _position_changed(self):
        #self.position_input_updated.call(self.Target_X.value(),self.Target_Y.value(),self.Target_Z.value())
        if self.sendaktiv.isChecked():
            self._cf.commander.send_position_setpoint(self.Target_X.value(),self.Target_Y.value(),
                                                         self.Target_Z.value(),self.Target_Yaw.value())

    def _logging_error(self, log_conf, msg):
        """Callback from the log layer when an error occurs"""
        QMessageBox.about(self, "Example error",
                          "Error when using log config"
                          " [{0}]: {1}".format(log_conf.name, msg))
Attachments
new Tab.png
arnaud
Bitcraze
Posts: 2300
Joined: Tue Feb 06, 2007 12:36 pm

Re: new Client-Tab for controlling the Crazyflie using LH

Post by arnaud »

Hi,

Sounds like a nice project.

For your problems:
1. I think you should look at this ticket: https://github.com/bitcraze/crazyflie-c ... issues/334. To be able to control the Crazyflie from a tab you need to disable the gamepad control, I added a function to do that in the ticket. Once the gamepad is inhibited, you should be able to control the Crazyflie form your tab the same way you are doing it in standalone python scripts.
2. I have no idea how this could happen, the log and param subsystem should be pretty independent and you should not be able to disable the params that way. Could you isolate what what triggering this effect?

Unfortunately I cannot test the tab since I do not have the .ui tab, so if you send it as well I could test it.
Baosai
Beginner
Posts: 10
Joined: Mon Jun 08, 2020 8:49 am

Re: new Client-Tab for controlling the Crazyflie using LH

Post by Baosai »

arnaud wrote: Thu Sep 10, 2020 8:54 am Hi,

Sounds like a nice project.

For your problems:
1. I think you should look at this ticket: https://github.com/bitcraze/crazyflie-c ... issues/334. To be able to control the Crazyflie from a tab you need to disable the gamepad control, I added a function to do that in the ticket. Once the gamepad is inhibited, you should be able to control the Crazyflie form your tab the same way you are doing it in standalone python scripts.
2. I have no idea how this could happen, the log and param subsystem should be pretty independent and you should not be able to disable the params that way. Could you isolate what what triggering this effect?

Unfortunately I cannot test the tab since I do not have the .ui tab, so if you send it as well I could test it.
thanks for your reply!!! now I know how to solve the problem, although i don't know why.

The LogConfig() period should not be smaller than the "ui_update_period" , which means it should not be smaller than 100 ms. Is it possible to make it smaller?
arnaud
Bitcraze
Posts: 2300
Joined: Tue Feb 06, 2007 12:36 pm

Re: new Client-Tab for controlling the Crazyflie using LH

Post by arnaud »

This is odd, you should be able to go down to 10ms. There might be a problem with the way you update the UI: I do not remember the details now, but UI and the crazyflie code runs in different threads. The UI update must pass though a signal, this way Qt will automatically put the update in a queue to be handled in the UI thread. If the UI is modified directly from a crazyflie lib callback for example, it can cause problems. You can look at how the flight tab is implemented, I think we have faster update rate in it.
Post Reply