cfclient 2017.4__py3-none-any.whl → 2025.12.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cfclient/__init__.py +16 -11
- cfclient/configs/config.json +4 -3
- cfclient/configs/input/Generic_OS_X.json +1 -0
- cfclient/configs/input/Joystick.json +1 -0
- cfclient/configs/input/PS3_Mode_1.json +1 -0
- cfclient/configs/input/PS3_Mode_2.json +1 -0
- cfclient/configs/input/PS3_Mode_3.json +1 -0
- cfclient/configs/input/PS4_Mode_1.json +1 -0
- cfclient/configs/input/PS4_Mode_2.json +1 -0
- cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
- cfclient/configs/input/xbox360_mode1.json +1 -0
- cfclient/configs/log/PID_tuning/Attitude.json +46 -0
- cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
- cfclient/configs/log/PID_tuning/Position.json +46 -0
- cfclient/configs/log/PID_tuning/Velocity.json +46 -0
- cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
- cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
- cfclient/gui.py +44 -9
- cfclient/headless.py +3 -12
- cfclient/resources/log_param_doc.json +1 -0
- cfclient/ui/connectivity_manager.py +198 -0
- cfclient/ui/dialogs/about.py +53 -36
- cfclient/ui/dialogs/about.ui +23 -3
- cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
- cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
- cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
- cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
- cfclient/ui/dialogs/bootloader.py +448 -85
- cfclient/ui/dialogs/bootloader.ui +387 -134
- cfclient/ui/dialogs/cf2config.py +4 -4
- cfclient/ui/dialogs/cf2config.ui +3 -4
- cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
- cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
- cfclient/ui/dialogs/logconfigdialogue.py +401 -101
- cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
- cfclient/ui/icons/bl.webp +0 -0
- cfclient/ui/icons/bolt.webp +0 -0
- cfclient/ui/icons/cf21.webp +0 -0
- cfclient/ui/icons/checkmark_black.png +0 -0
- cfclient/ui/icons/checkmark_white.png +0 -0
- cfclient/ui/icons/create.png +0 -0
- cfclient/ui/icons/delete.png +0 -0
- cfclient/ui/icons/flapper.webp +0 -0
- cfclient/ui/icons/tag.webp +0 -0
- cfclient/ui/main.py +328 -258
- cfclient/ui/main.ui +184 -80
- cfclient/ui/pluginhelper.py +7 -1
- cfclient/ui/pose_logger.py +116 -0
- cfclient/ui/tab_toolbox.py +208 -0
- cfclient/ui/tabs/ColorLEDTab.py +752 -0
- cfclient/ui/tabs/ConsoleTab.py +48 -13
- cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
- cfclient/ui/tabs/ExampleTab.py +9 -16
- cfclient/ui/tabs/FlightTab.py +437 -325
- cfclient/ui/tabs/GpsTab.py +14 -20
- cfclient/ui/tabs/LEDRingTab.py +277 -0
- cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
- cfclient/ui/tabs/LogBlockTab.py +35 -35
- cfclient/ui/tabs/LogClientTab.py +85 -0
- cfclient/ui/tabs/LogTab.py +50 -27
- cfclient/ui/tabs/ParamTab.py +443 -57
- cfclient/ui/tabs/PlotTab.py +23 -25
- cfclient/ui/tabs/TuningTab.py +292 -0
- cfclient/ui/tabs/__init__.py +12 -2
- cfclient/ui/tabs/colorLEDTab.ui +624 -0
- cfclient/ui/tabs/consoleTab.ui +46 -0
- cfclient/ui/tabs/flightActionContainer.ui +103 -0
- cfclient/ui/tabs/flightTab.ui +724 -237
- cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
- cfclient/ui/tabs/lighthouse_tab.py +714 -0
- cfclient/ui/tabs/lighthouse_tab.ui +430 -0
- cfclient/ui/tabs/locopositioning_tab.py +606 -389
- cfclient/ui/tabs/locopositioning_tab.ui +370 -253
- cfclient/ui/tabs/logClientTab.ui +52 -0
- cfclient/ui/tabs/logTab.ui +1 -1
- cfclient/ui/tabs/paramTab.ui +204 -3
- cfclient/ui/tabs/tuningTab.ui +773 -0
- cfclient/ui/widgets/ai.py +37 -39
- cfclient/ui/widgets/hexspinbox.py +16 -10
- cfclient/ui/widgets/plotter.ui +39 -47
- cfclient/ui/widgets/plotwidget.py +57 -22
- cfclient/ui/widgets/super_slider.py +112 -0
- cfclient/ui/wizards/__init__.py +0 -0
- cfclient/ui/wizards/bslh_1.png +0 -0
- cfclient/ui/wizards/bslh_2.png +0 -0
- cfclient/ui/wizards/bslh_3.png +0 -0
- cfclient/ui/wizards/bslh_4.png +0 -0
- cfclient/ui/wizards/bslh_5.png +0 -0
- cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
- cfclient/utils/config_manager.py +5 -4
- cfclient/utils/input/__init__.py +77 -19
- cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
- cfclient/utils/input/inputreaderinterface.py +17 -7
- cfclient/utils/input/inputreaders/__init__.py +17 -0
- cfclient/utils/logconfigreader.py +245 -25
- cfclient/utils/logdatawriter.py +3 -1
- cfclient/utils/periodictimer.py +1 -1
- cfclient/utils/ui.py +336 -0
- cfclient/utils/zmq_led_driver.py +5 -0
- cfclient/utils/zmq_param.py +6 -0
- cfclient/version.py +34 -1
- cfclient-2025.12.1.dist-info/METADATA +70 -0
- cfclient-2025.12.1.dist-info/RECORD +152 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
- cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
- cfconfig/Makefile +51 -0
- cfconfig/configblock.py +111 -0
- cfloader/__init__.py +41 -55
- cfzmq/__init__.py +22 -14
- cfclient/ui/dialogs/cf1config.py +0 -265
- cfclient/ui/dialogs/cf1config.ui +0 -260
- cfclient/ui/tab.py +0 -96
- cfclient/ui/tabs/LEDTab.py +0 -169
- cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
- cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
- cfclient/ui/toolboxes/__init__.py +0 -45
- cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
- cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
- cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
- cfclient-2017.4.dist-info/METADATA +0 -22
- cfclient-2017.4.dist-info/RECORD +0 -104
- cfclient-2017.4.dist-info/metadata.json +0 -1
- /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
- /cfclient/ui/{toolboxes → tabs}/crtpSharkToolbox.ui +0 -0
cfclient/ui/tabs/PlotTab.py
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
|
|
8
8
|
# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
|
|
9
9
|
#
|
|
10
|
-
# Copyright (C) 2011-
|
|
10
|
+
# Copyright (C) 2011-2023 Bitcraze AB
|
|
11
11
|
#
|
|
12
12
|
# Crazyflie Nano Quadcopter Client
|
|
13
13
|
#
|
|
@@ -30,14 +30,14 @@ pre-configured.
|
|
|
30
30
|
|
|
31
31
|
import logging
|
|
32
32
|
|
|
33
|
-
from cfclient.ui.
|
|
33
|
+
from cfclient.ui.tab_toolbox import TabToolbox
|
|
34
34
|
from cfclient.ui.widgets.plotwidget import PlotWidget
|
|
35
|
-
from
|
|
36
|
-
from
|
|
37
|
-
from
|
|
38
|
-
from
|
|
39
|
-
from
|
|
40
|
-
from
|
|
35
|
+
from PyQt6 import uic
|
|
36
|
+
from PyQt6.QtCore import pyqtSignal
|
|
37
|
+
from PyQt6.QtCore import QAbstractItemModel
|
|
38
|
+
from PyQt6.QtCore import QModelIndex
|
|
39
|
+
from PyQt6.QtCore import Qt
|
|
40
|
+
from PyQt6.QtWidgets import QMessageBox
|
|
41
41
|
|
|
42
42
|
import cfclient
|
|
43
43
|
|
|
@@ -46,8 +46,7 @@ __all__ = ['PlotTab']
|
|
|
46
46
|
|
|
47
47
|
logger = logging.getLogger(__name__)
|
|
48
48
|
|
|
49
|
-
plot_tab_class = uic.loadUiType(cfclient.module_path +
|
|
50
|
-
"/ui/tabs/plotTab.ui")[0]
|
|
49
|
+
plot_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/plotTab.ui")[0]
|
|
51
50
|
|
|
52
51
|
|
|
53
52
|
class LogConfigModel(QAbstractItemModel):
|
|
@@ -60,6 +59,7 @@ class LogConfigModel(QAbstractItemModel):
|
|
|
60
59
|
def add_block(self, block):
|
|
61
60
|
self._nodes.append(block)
|
|
62
61
|
self.layoutChanged.emit()
|
|
62
|
+
self._nodes.sort(key=lambda conf: conf.name.lower())
|
|
63
63
|
|
|
64
64
|
def parent(self, index):
|
|
65
65
|
"""Re-implemented method to get the parent of the given index"""
|
|
@@ -67,7 +67,7 @@ class LogConfigModel(QAbstractItemModel):
|
|
|
67
67
|
|
|
68
68
|
def remove_block(self, block):
|
|
69
69
|
"""Remove a block from the view"""
|
|
70
|
-
|
|
70
|
+
self._nodes.remove(block)
|
|
71
71
|
|
|
72
72
|
def columnCount(self, parent):
|
|
73
73
|
"""Re-implemented method to get the number of columns"""
|
|
@@ -99,7 +99,7 @@ class LogConfigModel(QAbstractItemModel):
|
|
|
99
99
|
node = index.internalPointer() # noqa
|
|
100
100
|
if not index.isValid() or not 0 <= index.row() < len(self._nodes):
|
|
101
101
|
return None
|
|
102
|
-
if role == Qt.DisplayRole:
|
|
102
|
+
if role == Qt.ItemDataRole.DisplayRole:
|
|
103
103
|
return self._nodes[index.row()].name
|
|
104
104
|
return None
|
|
105
105
|
|
|
@@ -112,7 +112,7 @@ class LogConfigModel(QAbstractItemModel):
|
|
|
112
112
|
return self._nodes[i]
|
|
113
113
|
|
|
114
114
|
|
|
115
|
-
class PlotTab(
|
|
115
|
+
class PlotTab(TabToolbox, plot_tab_class):
|
|
116
116
|
"""Tab for plotting logging data"""
|
|
117
117
|
|
|
118
118
|
_log_data_signal = pyqtSignal(int, object, object)
|
|
@@ -132,38 +132,33 @@ class PlotTab(Tab, plot_tab_class):
|
|
|
132
132
|
(180, 60, 240), # purple
|
|
133
133
|
]
|
|
134
134
|
|
|
135
|
-
def __init__(self,
|
|
136
|
-
super(PlotTab, self).__init__(
|
|
135
|
+
def __init__(self, helper):
|
|
136
|
+
super(PlotTab, self).__init__(helper, 'Plotter')
|
|
137
137
|
self.setupUi(self)
|
|
138
138
|
|
|
139
|
-
self.tabName = "Plotter"
|
|
140
|
-
self.menuName = "Plotter"
|
|
141
|
-
|
|
142
139
|
self._log_error_signal.connect(self._logging_error)
|
|
143
140
|
|
|
144
141
|
self._plot = PlotWidget(fps=30)
|
|
145
142
|
# Check if we could find the PyQtImport. If not, then
|
|
146
143
|
# set this tab as disabled
|
|
147
|
-
|
|
144
|
+
is_enabled = self._plot.can_enable
|
|
148
145
|
|
|
149
146
|
self._model = LogConfigModel()
|
|
150
147
|
self.dataSelector.setModel(self._model)
|
|
151
148
|
self._log_data_signal.connect(self._log_data_received)
|
|
152
|
-
self.tabWidget = tabWidget
|
|
153
|
-
self.helper = helper
|
|
154
149
|
self.plotLayout.addWidget(self._plot)
|
|
155
150
|
|
|
156
151
|
# Connect external signals if we can use the tab
|
|
157
|
-
if
|
|
152
|
+
if is_enabled:
|
|
158
153
|
self._disconnected_signal.connect(self._disconnected)
|
|
159
|
-
self.
|
|
154
|
+
self._helper.cf.disconnected.add_callback(
|
|
160
155
|
self._disconnected_signal.emit)
|
|
161
156
|
|
|
162
157
|
self._connected_signal.connect(self._connected)
|
|
163
|
-
self.
|
|
158
|
+
self._helper.cf.connected.add_callback(
|
|
164
159
|
self._connected_signal.emit)
|
|
165
160
|
|
|
166
|
-
self.
|
|
161
|
+
self._helper.cf.log.block_added_cb.add_callback(self._config_added)
|
|
167
162
|
self.dataSelector.currentIndexChanged.connect(
|
|
168
163
|
self._selection_changed)
|
|
169
164
|
|
|
@@ -245,6 +240,9 @@ class PlotTab(Tab, plot_tab_class):
|
|
|
245
240
|
logger.debug("Callback for new config [%s]", logconfig.name)
|
|
246
241
|
self._model.add_block(logconfig)
|
|
247
242
|
|
|
243
|
+
def remove_config(self, logconfig):
|
|
244
|
+
self._model.remove_block(logconfig)
|
|
245
|
+
|
|
248
246
|
def _logging_error(self, log_conf, msg):
|
|
249
247
|
"""Callback from the log layer when an error occurs"""
|
|
250
248
|
QMessageBox.about(
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# ,---------, ____ _ __
|
|
4
|
+
# | ,-^-, | / __ )(_) /_______________ _____ ___
|
|
5
|
+
# | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \
|
|
6
|
+
# | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
|
|
7
|
+
# +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/
|
|
8
|
+
#
|
|
9
|
+
# Copyright (C) 2022-2023 Bitcraze AB
|
|
10
|
+
#
|
|
11
|
+
# This program is free software: you can redistribute it and/or modify
|
|
12
|
+
# it under the terms of the GNU General Public License as published by
|
|
13
|
+
# the Free Software Foundation, in version 3.
|
|
14
|
+
#
|
|
15
|
+
# This program is distributed in the hope that it will be useful,
|
|
16
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
17
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
18
|
+
# GNU General Public License for more details.
|
|
19
|
+
#
|
|
20
|
+
# You should have received a copy of the GNU General Public License
|
|
21
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
Tab for tuning PID controller, mainly for larger quads.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
from PyQt6 import QtWidgets
|
|
30
|
+
from PyQt6 import uic
|
|
31
|
+
from PyQt6.QtCore import pyqtSignal, Qt
|
|
32
|
+
import time
|
|
33
|
+
|
|
34
|
+
import cfclient
|
|
35
|
+
from cfclient.ui.tab_toolbox import TabToolbox
|
|
36
|
+
from cfclient.ui.widgets.super_slider import SuperSlider
|
|
37
|
+
from cflib.crazyflie import Crazyflie, Param
|
|
38
|
+
from cflib.utils.callbacks import Syncer
|
|
39
|
+
|
|
40
|
+
__author__ = 'Bitcraze AB'
|
|
41
|
+
__all__ = ['TuningTab']
|
|
42
|
+
|
|
43
|
+
logger = logging.getLogger(__name__)
|
|
44
|
+
|
|
45
|
+
tuning_tab_class = uic.loadUiType(cfclient.module_path + "/ui/tabs/tuningTab.ui")[0]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SliderParamMapper:
|
|
49
|
+
def __init__(self, slider: SuperSlider, group: str, name: str):
|
|
50
|
+
self.param_group = group
|
|
51
|
+
self.param_name = name
|
|
52
|
+
self.full_param_name = f'{group}.{name}'
|
|
53
|
+
|
|
54
|
+
self.slider = slider
|
|
55
|
+
self.slider.value_changed_cb.add_callback(self.slider_changed)
|
|
56
|
+
|
|
57
|
+
# Prevents param changes that comes back from the CF to set the parameter and create a feedback loop
|
|
58
|
+
self.receive_block_time = 0.0
|
|
59
|
+
|
|
60
|
+
self.linked_mapper = None
|
|
61
|
+
self.linked_checkbox: QtWidgets.QCheckBox = None
|
|
62
|
+
|
|
63
|
+
self.cf = None
|
|
64
|
+
|
|
65
|
+
def connected(self, cf: Crazyflie):
|
|
66
|
+
self.cf = cf
|
|
67
|
+
return self.param_group, self.param_name
|
|
68
|
+
|
|
69
|
+
def disconnected(self):
|
|
70
|
+
self.cf = None
|
|
71
|
+
|
|
72
|
+
def link_with(self, other_mapper: 'SliderParamMapper', checkbox: QtWidgets.QCheckBox):
|
|
73
|
+
self.linked_mapper = other_mapper
|
|
74
|
+
self.linked_checkbox = checkbox
|
|
75
|
+
|
|
76
|
+
other_mapper.linked_mapper = self
|
|
77
|
+
other_mapper.linked_checkbox = checkbox
|
|
78
|
+
|
|
79
|
+
def enable_ui(self, enabled):
|
|
80
|
+
self.slider.setEnabled(enabled)
|
|
81
|
+
|
|
82
|
+
# Called when the user has modified the value in the UI
|
|
83
|
+
def slider_changed(self, value):
|
|
84
|
+
if self.cf is not None:
|
|
85
|
+
if self.cf.is_connected():
|
|
86
|
+
self.receive_block_time = time.time() + 0.4
|
|
87
|
+
self.cf.param.set_value(self.full_param_name, value)
|
|
88
|
+
|
|
89
|
+
if self.linked_mapper is not None:
|
|
90
|
+
self.linked_mapper.linked_update(value)
|
|
91
|
+
|
|
92
|
+
# Called when a parameter in the CF has changed, this is also true if we initiated the param update
|
|
93
|
+
def param_updated_cb(self, full_param_name, value):
|
|
94
|
+
if time.time() > self.receive_block_time:
|
|
95
|
+
self.slider.set_value(float(value))
|
|
96
|
+
|
|
97
|
+
if self.linked_mapper is not None:
|
|
98
|
+
self.linked_mapper.linked_update(value)
|
|
99
|
+
|
|
100
|
+
# Called by another SliderParamMapper that is linked
|
|
101
|
+
def linked_update(self, value):
|
|
102
|
+
if self.linked_checkbox is not None and self.linked_checkbox.checkState() == Qt.CheckState.Checked:
|
|
103
|
+
self.slider.set_value(float(value))
|
|
104
|
+
if self.cf is not None and self.cf.is_connected():
|
|
105
|
+
self.receive_block_time = time.time() + 0.4
|
|
106
|
+
self.cf.param.set_value(self.full_param_name, value)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class TuningTab(TabToolbox, tuning_tab_class):
|
|
110
|
+
"""Tab for plotting logging data"""
|
|
111
|
+
|
|
112
|
+
_connected_signal = pyqtSignal(str)
|
|
113
|
+
_disconnected_signal = pyqtSignal(str)
|
|
114
|
+
|
|
115
|
+
_param_updated_signal = pyqtSignal(str, object)
|
|
116
|
+
|
|
117
|
+
def __init__(self, helper):
|
|
118
|
+
super(TuningTab, self).__init__(helper, 'Tuning')
|
|
119
|
+
self.setupUi(self)
|
|
120
|
+
|
|
121
|
+
# Always wrap callbacks from Crazyflie API though QT Signal/Slots
|
|
122
|
+
# to avoid manipulating the UI when rendering it
|
|
123
|
+
self._connected_signal.connect(self._connected)
|
|
124
|
+
self._disconnected_signal.connect(self._disconnected)
|
|
125
|
+
self._param_updated_signal.connect(self._param_updated_cb)
|
|
126
|
+
|
|
127
|
+
# Connect the Crazyflie API callbacks to the signals
|
|
128
|
+
self._helper.cf.connected.add_callback(
|
|
129
|
+
self._connected_signal.emit)
|
|
130
|
+
|
|
131
|
+
self._helper.cf.disconnected.add_callback(
|
|
132
|
+
self._disconnected_signal.emit)
|
|
133
|
+
|
|
134
|
+
# A dictionary mapping the full param name to a SliderParamMapper
|
|
135
|
+
self.mappers = self._set_up_param_mappings()
|
|
136
|
+
|
|
137
|
+
self.store_button.clicked.connect(self._store_button_clicked)
|
|
138
|
+
self.clear_stored_button.clicked.connect(self._clear_stored_button_clicked)
|
|
139
|
+
self.default_values_button.clicked.connect(self._default_values_button_clicked)
|
|
140
|
+
self._enable_ui_objects(False)
|
|
141
|
+
|
|
142
|
+
def _set_up_param_mappings(self):
|
|
143
|
+
mappers: dict[str, SliderParamMapper] = {}
|
|
144
|
+
|
|
145
|
+
# Rate PID
|
|
146
|
+
mappers.update(self._create_slider(self.grid_rate, 1, 1, 0, 1000, 'pid_rate', 'roll_kp'))
|
|
147
|
+
mappers.update(self._create_slider(self.grid_rate, 1, 2, 0, 1000, 'pid_rate', 'roll_ki'))
|
|
148
|
+
mappers.update(self._create_slider(self.grid_rate, 1, 3, 0, 10, 'pid_rate', 'roll_kd'))
|
|
149
|
+
|
|
150
|
+
mappers.update(self._create_slider(self.grid_rate, 2, 1, 0, 1000, 'pid_rate', 'pitch_kp'))
|
|
151
|
+
mappers.update(self._create_slider(self.grid_rate, 2, 2, 0, 1000, 'pid_rate', 'pitch_ki'))
|
|
152
|
+
mappers.update(self._create_slider(self.grid_rate, 2, 3, 0, 10, 'pid_rate', 'pitch_kd'))
|
|
153
|
+
|
|
154
|
+
mappers.update(self._create_slider(self.grid_rate, 3, 1, 0, 200, 'pid_rate', 'yaw_kp'))
|
|
155
|
+
mappers.update(self._create_slider(self.grid_rate, 3, 2, 0, 100, 'pid_rate', 'yaw_ki'))
|
|
156
|
+
mappers.update(self._create_slider(self.grid_rate, 3, 3, 0, 10, 'pid_rate', 'yaw_kd'))
|
|
157
|
+
|
|
158
|
+
self._link(mappers, 'pid_rate.roll_kp', 'pid_rate.pitch_kp', self.rate_link_checkbox)
|
|
159
|
+
self._link(mappers, 'pid_rate.roll_ki', 'pid_rate.pitch_ki', self.rate_link_checkbox)
|
|
160
|
+
self._link(mappers, 'pid_rate.roll_kd', 'pid_rate.pitch_kd', self.rate_link_checkbox)
|
|
161
|
+
|
|
162
|
+
# Attitude PID
|
|
163
|
+
mappers.update(self._create_slider(self.grid_attitude, 1, 1, 0, 10, 'pid_attitude', 'roll_kp'))
|
|
164
|
+
mappers.update(self._create_slider(self.grid_attitude, 1, 2, 0, 10, 'pid_attitude', 'roll_ki'))
|
|
165
|
+
mappers.update(self._create_slider(self.grid_attitude, 1, 3, 0, 10, 'pid_attitude', 'roll_kd'))
|
|
166
|
+
|
|
167
|
+
mappers.update(self._create_slider(self.grid_attitude, 2, 1, 0, 10, 'pid_attitude', 'pitch_kp'))
|
|
168
|
+
mappers.update(self._create_slider(self.grid_attitude, 2, 2, 0, 10, 'pid_attitude', 'pitch_ki'))
|
|
169
|
+
mappers.update(self._create_slider(self.grid_attitude, 2, 3, 0, 10, 'pid_attitude', 'pitch_kd'))
|
|
170
|
+
|
|
171
|
+
mappers.update(self._create_slider(self.grid_attitude, 3, 1, 0, 10, 'pid_attitude', 'yaw_kp'))
|
|
172
|
+
mappers.update(self._create_slider(self.grid_attitude, 3, 2, 0, 10, 'pid_attitude', 'yaw_ki'))
|
|
173
|
+
mappers.update(self._create_slider(self.grid_attitude, 3, 3, 0, 10, 'pid_attitude', 'yaw_kd'))
|
|
174
|
+
|
|
175
|
+
self._link(mappers, 'pid_attitude.roll_kp', 'pid_attitude.pitch_kp', self.attitude_link_checkbox)
|
|
176
|
+
self._link(mappers, 'pid_attitude.roll_ki', 'pid_attitude.pitch_ki', self.attitude_link_checkbox)
|
|
177
|
+
self._link(mappers, 'pid_attitude.roll_kd', 'pid_attitude.pitch_kd', self.attitude_link_checkbox)
|
|
178
|
+
|
|
179
|
+
# Position control PID
|
|
180
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 1, 1, 0, 10, 'posCtlPid', 'xKp'))
|
|
181
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 1, 2, 0, 10, 'posCtlPid', 'xKi'))
|
|
182
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 1, 3, 0, 10, 'posCtlPid', 'xKd'))
|
|
183
|
+
|
|
184
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 2, 1, 0, 10, 'posCtlPid', 'yKp'))
|
|
185
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 2, 2, 0, 10, 'posCtlPid', 'yKi'))
|
|
186
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 2, 3, 0, 10, 'posCtlPid', 'yKd'))
|
|
187
|
+
|
|
188
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 3, 1, 0, 10, 'posCtlPid', 'zKp'))
|
|
189
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 3, 2, 0, 10, 'posCtlPid', 'zKi'))
|
|
190
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 3, 3, 0, 10, 'posCtlPid', 'zKd'))
|
|
191
|
+
|
|
192
|
+
mappers.update(self._create_slider(self.grid_pos_ctrl, 5, 1, 0, 65536, 'posCtlPid', 'thrustBase'))
|
|
193
|
+
|
|
194
|
+
self._link(mappers, 'posCtlPid.xKp', 'posCtlPid.yKp', self.position_link_checkbox)
|
|
195
|
+
self._link(mappers, 'posCtlPid.xKi', 'posCtlPid.yKi', self.position_link_checkbox)
|
|
196
|
+
self._link(mappers, 'posCtlPid.xKd', 'posCtlPid.yKd', self.position_link_checkbox)
|
|
197
|
+
|
|
198
|
+
# Velocity control PID
|
|
199
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 1, 1, 0, 50, 'velCtlPid', 'vxKp'))
|
|
200
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 1, 2, 0, 50, 'velCtlPid', 'vxKi'))
|
|
201
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 1, 3, 0, 10, 'velCtlPid', 'vxKd'))
|
|
202
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 1, 4, 0, 10, 'velCtlPid', 'vxKFF'))
|
|
203
|
+
|
|
204
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 2, 1, 0, 50, 'velCtlPid', 'vyKp'))
|
|
205
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 2, 2, 0, 50, 'velCtlPid', 'vyKi'))
|
|
206
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 2, 3, 0, 10, 'velCtlPid', 'vyKd'))
|
|
207
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 2, 4, 0, 10, 'velCtlPid', 'vyKFF'))
|
|
208
|
+
|
|
209
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 3, 1, 0, 50, 'velCtlPid', 'vzKp'))
|
|
210
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 3, 2, 0, 50, 'velCtlPid', 'vzKi'))
|
|
211
|
+
mappers.update(self._create_slider(self.grid_vel_ctrl, 3, 3, 0, 10, 'velCtlPid', 'vzKd'))
|
|
212
|
+
|
|
213
|
+
self._link(mappers, 'velCtlPid.vxKp', 'velCtlPid.vyKp', self.velocity_link_checkbox)
|
|
214
|
+
self._link(mappers, 'velCtlPid.vxKi', 'velCtlPid.vyKi', self.velocity_link_checkbox)
|
|
215
|
+
self._link(mappers, 'velCtlPid.vxKd', 'velCtlPid.vyKd', self.velocity_link_checkbox)
|
|
216
|
+
self._link(mappers, 'velCtlPid.vxKFF', 'velCtlPid.vyKFF', self.velocity_link_checkbox)
|
|
217
|
+
|
|
218
|
+
return mappers
|
|
219
|
+
|
|
220
|
+
def _connected(self, link_uri):
|
|
221
|
+
"""Callback when the Crazyflie has been connected"""
|
|
222
|
+
for mapper in self.mappers.values():
|
|
223
|
+
param_group, param_name = mapper.connected(self._helper.cf)
|
|
224
|
+
self._helper.cf.param.add_update_callback(
|
|
225
|
+
group=param_group, name=param_name, cb=self._param_updated_signal.emit)
|
|
226
|
+
|
|
227
|
+
self._enable_ui_objects(True)
|
|
228
|
+
|
|
229
|
+
def _disconnected(self, link_uri):
|
|
230
|
+
"""Callback for when the Crazyflie has been disconnected"""
|
|
231
|
+
for mapper in self.mappers.values():
|
|
232
|
+
mapper.disconnected()
|
|
233
|
+
|
|
234
|
+
self._enable_ui_objects(False)
|
|
235
|
+
# TODO set default values?
|
|
236
|
+
|
|
237
|
+
def _create_slider(self, gridLayout, row, col, min_val, max_val, param_group: str, param_name: str):
|
|
238
|
+
initial_val = (min_val + max_val) / 2.0
|
|
239
|
+
slider = SuperSlider(min_val, max_val, initial_val)
|
|
240
|
+
gridLayout.addWidget(slider, row, col)
|
|
241
|
+
|
|
242
|
+
slider_mapper = SliderParamMapper(slider, param_group, param_name)
|
|
243
|
+
|
|
244
|
+
return {slider_mapper.full_param_name: slider_mapper}
|
|
245
|
+
|
|
246
|
+
def _param_updated_cb(self, full_param_name, value):
|
|
247
|
+
if full_param_name in self.mappers:
|
|
248
|
+
self.mappers[full_param_name].param_updated_cb(full_param_name, value)
|
|
249
|
+
|
|
250
|
+
def _link(self, mappers, first, other, checkbox):
|
|
251
|
+
first_mapper = mappers[first]
|
|
252
|
+
other_mapper = mappers[other]
|
|
253
|
+
|
|
254
|
+
first_mapper.link_with(other_mapper, checkbox)
|
|
255
|
+
|
|
256
|
+
def _store_button_clicked(self):
|
|
257
|
+
param: Param = self._helper.cf.param
|
|
258
|
+
for full_param_name in self.mappers.keys():
|
|
259
|
+
# syncer = Syncer()
|
|
260
|
+
param.persistent_store(full_param_name)
|
|
261
|
+
|
|
262
|
+
def _clear_stored_button_clicked(self):
|
|
263
|
+
param: Param = self._helper.cf.param
|
|
264
|
+
for full_param_name in self.mappers.keys():
|
|
265
|
+
param.persistent_clear(full_param_name)
|
|
266
|
+
|
|
267
|
+
def _default_values_button_clicked(self):
|
|
268
|
+
param: Param = self._helper.cf.param
|
|
269
|
+
for full_param_name in self.mappers.keys():
|
|
270
|
+
# For some reason we run into problems if we try to get all params in one go. Pace by getting them
|
|
271
|
+
# one by one.
|
|
272
|
+
syncer = Syncer()
|
|
273
|
+
param.get_default_value(full_param_name, syncer.success_cb)
|
|
274
|
+
syncer.wait()
|
|
275
|
+
self._param_updated_cb(*syncer.success_args)
|
|
276
|
+
|
|
277
|
+
def _enable_ui_objects(self, enabled):
|
|
278
|
+
objects = [
|
|
279
|
+
self.rate_link_checkbox,
|
|
280
|
+
self.attitude_link_checkbox,
|
|
281
|
+
self.position_link_checkbox,
|
|
282
|
+
self.velocity_link_checkbox,
|
|
283
|
+
self.store_button,
|
|
284
|
+
self.clear_stored_button,
|
|
285
|
+
self.default_values_button,
|
|
286
|
+
]
|
|
287
|
+
|
|
288
|
+
for item in objects:
|
|
289
|
+
item.setEnabled(enabled)
|
|
290
|
+
|
|
291
|
+
for mapper in self.mappers.values():
|
|
292
|
+
mapper.enable_ui(enabled)
|
cfclient/ui/tabs/__init__.py
CHANGED
|
@@ -30,15 +30,20 @@ Dropping a new .py file into this directory will automatically list and load
|
|
|
30
30
|
it into the UI when it is started.
|
|
31
31
|
"""
|
|
32
32
|
from .ConsoleTab import ConsoleTab
|
|
33
|
+
from .CrtpSharkToolbox import CrtpSharkToolbox
|
|
33
34
|
# from .ExampleTab import ExampleTab
|
|
34
35
|
from .FlightTab import FlightTab
|
|
35
36
|
# from .GpsTab import GpsTab
|
|
36
|
-
from .
|
|
37
|
+
from .LEDRingTab import LEDRingTab
|
|
37
38
|
from .LogBlockTab import LogBlockTab
|
|
38
39
|
from .LogTab import LogTab
|
|
39
40
|
from .ParamTab import ParamTab
|
|
40
41
|
from .PlotTab import PlotTab
|
|
41
42
|
from .locopositioning_tab import LocoPositioningTab
|
|
43
|
+
from .LogClientTab import LogClientTab
|
|
44
|
+
from .lighthouse_tab import LighthouseTab
|
|
45
|
+
from .TuningTab import TuningTab
|
|
46
|
+
from .ColorLEDTab import ColorLEDTab
|
|
42
47
|
|
|
43
48
|
__author__ = 'Bitcraze AB'
|
|
44
49
|
__all__ = []
|
|
@@ -48,10 +53,15 @@ available = [
|
|
|
48
53
|
# ExampleTab,
|
|
49
54
|
FlightTab,
|
|
50
55
|
# GpsTab,
|
|
51
|
-
|
|
56
|
+
LEDRingTab,
|
|
57
|
+
ColorLEDTab,
|
|
52
58
|
LogBlockTab,
|
|
53
59
|
LogTab,
|
|
54
60
|
ParamTab,
|
|
55
61
|
PlotTab,
|
|
56
62
|
LocoPositioningTab,
|
|
63
|
+
LighthouseTab,
|
|
64
|
+
LogClientTab,
|
|
65
|
+
TuningTab,
|
|
66
|
+
CrtpSharkToolbox,
|
|
57
67
|
]
|