Skip to content

Commit 29201e0

Browse files
Implement the first draft of the visualizer
1 parent 1013752 commit 29201e0

File tree

8 files changed

+729
-0
lines changed

8 files changed

+729
-0
lines changed

app.py

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# sys
2+
import sys
3+
import os
4+
5+
# GUI
6+
from ui.gui import RobotViewerMainWindow
7+
from PyQt5.QtWidgets import QApplication
8+
9+
from file_reader.signal_provider import SignalProvider
10+
11+
# Meshcat
12+
from idyntree.visualize import MeshcatVisualizer
13+
14+
15+
def get_model_path(robot_name='iCubGazeboV3'):
16+
seperbuild_prefix = os.environ["ROBOTOLOGY_SUPERBUILD_INSTALL_PREFIX"]
17+
robots_folder = os.path.join("share", "iCub", "robots", robot_name)
18+
19+
return os.path.join(seperbuild_prefix, robots_folder, "model.urdf")
20+
21+
22+
def get_joint_list():
23+
joint_list = ['neck_pitch', 'neck_roll', 'neck_yaw',
24+
'torso_pitch', 'torso_roll', 'torso_yaw',
25+
'l_shoulder_pitch', 'l_shoulder_roll', 'l_shoulder_yaw', 'l_elbow',
26+
'r_shoulder_pitch', 'r_shoulder_roll', 'r_shoulder_yaw', 'r_elbow',
27+
'l_hip_pitch', 'l_hip_roll', 'l_hip_yaw', 'l_knee', 'l_ankle_pitch', 'l_ankle_roll',
28+
'r_hip_pitch', 'r_hip_roll', 'r_hip_yaw', 'r_knee', 'r_ankle_pitch', 'r_ankle_roll']
29+
30+
return joint_list
31+
32+
33+
if __name__ == '__main__':
34+
# instantiate device_manager
35+
meshcat = MeshcatVisualizer()
36+
meshcat.set_model_from_file(get_model_path(), get_joint_list())
37+
meshcat.load_model(color=[1, 1, 1, 0.8])
38+
39+
signal_provider = SignalProvider(meshcat)
40+
41+
# instantiate a QApplication
42+
app = QApplication(sys.argv)
43+
44+
# instantiate the main window
45+
gui = RobotViewerMainWindow(meshcat=meshcat, signal_provider=signal_provider)
46+
47+
# show the main window
48+
gui.show()
49+
50+
signal_provider.start()
51+
52+
sys.exit(app.exec_())
53+
54+
55+

file_reader/__init__.py

Whitespace-only changes.

file_reader/signal_provider.py

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import time
2+
3+
import h5py
4+
import numpy as np
5+
from PyQt5.QtCore import pyqtSignal, QThread
6+
from threading import Lock
7+
8+
9+
class SignalProvider(QThread):
10+
update_index = pyqtSignal()
11+
12+
def __init__(self, meshcat_visualizer):
13+
QThread.__init__(self)
14+
15+
# set device state
16+
self._state = 'pause'
17+
self.state_lock = Lock()
18+
19+
self._last_data = None
20+
21+
self._index = 0
22+
self.index_lock = Lock()
23+
24+
self.fps = 50
25+
self.meshcat_visualizer = meshcat_visualizer
26+
27+
self.s = np.array([])
28+
29+
def open_mat_file(self, file_name: str):
30+
with h5py.File(file_name, 'r') as f:
31+
self.s = np.squeeze(np.array(f['robot_logger_device']['joints_state']['positions']['data']))
32+
self.index = 0
33+
34+
def __len__(self):
35+
return self.s.shape[0]
36+
37+
@property
38+
def state(self):
39+
self.state_lock.acquire()
40+
value = self._state
41+
self.state_lock.release()
42+
return value
43+
44+
@state.setter
45+
def state(self, new_state):
46+
self.state_lock.acquire()
47+
self._state = new_state
48+
self.state_lock.release()
49+
50+
@property
51+
def index(self):
52+
self.index_lock.acquire()
53+
value = self._index
54+
self.index_lock.release()
55+
return value
56+
57+
@index.setter
58+
def index(self, index):
59+
self.index_lock.acquire()
60+
self._index = index
61+
self.index_lock.release()
62+
63+
def register_update_index(self, slot):
64+
self.update_index.connect(slot)
65+
66+
def run(self):
67+
while True:
68+
if self.state == 'running':
69+
temp_index = min(self.index, self.s.shape[0] - 1)
70+
self._last_data = self.s[temp_index, :]
71+
self.index = temp_index + int(100/self.fps)
72+
R = np.eye(3)
73+
p = np.array([0.0, 0.0, 0.0])
74+
self.meshcat_visualizer.display(p, R, self._last_data)
75+
self.update_index.emit()
76+
77+
time.sleep(1/self.fps)
78+
79+
80+
81+
82+
83+

ui/__init__.py

Whitespace-only changes.

ui/generate_ui.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#! /bin/bash
2+
echo "Generate UI"
3+
pyuic5 -o visualizer.py visualizer.ui

ui/gui.py

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
#PyQt5
2+
from PyQt5 import QtWidgets
3+
from PyQt5.QtCore import QUrl
4+
from PyQt5.QtCore import pyqtSlot
5+
from PyQt5.QtWidgets import QFileDialog
6+
7+
import sys
8+
9+
# QtDesigner generated classes
10+
from ui.visualizer import Ui_MainWindow
11+
12+
# for logging
13+
from time import localtime, strftime
14+
15+
16+
class RobotViewerMainWindow(QtWidgets.QMainWindow):
17+
"""
18+
Main window class of EVB1000 Viewer
19+
"""
20+
def __init__(self, meshcat: str, signal_provider):
21+
# call QMainWindow constructor
22+
super().__init__()
23+
24+
# set up the user interface
25+
self.ui = Ui_MainWindow()
26+
self.ui.setupUi(self)
27+
28+
self.signal_provider = signal_provider
29+
self.signal_size = len(self.signal_provider)
30+
self.signal_provider.register_update_index(self.update_slider)
31+
32+
# instantiate the Logger
33+
self.logger = Logger(self.ui.logLabel, self.ui.logScrollArea)
34+
35+
self.slider_pressed = False
36+
37+
# connect action
38+
self.ui.actionQuit.triggered.connect(self.quit)
39+
self.ui.actionOpen.triggered.connect(self.open_mat_file)
40+
41+
self.ui.meshcatView.setUrl(QUrl(meshcat.viewer.url()))
42+
43+
self.ui.pauseButton.clicked.connect(self.pauseButton_on_click)
44+
self.ui.startButton.clicked.connect(self.startButton_on_click)
45+
self.ui.timeSlider.sliderReleased.connect(self.timeSlider_on_release)
46+
self.ui.timeSlider.sliderPressed.connect(self.timeSlider_on_pressed)
47+
48+
def timeSlider_on_pressed(self):
49+
self.slider_pressed = True
50+
51+
def timeSlider_on_release(self):
52+
index = int(self.ui.timeSlider.value() / 100 * self.signal_size)
53+
self.signal_provider.index = index
54+
self.slider_pressed = False
55+
self.logger.write_to_log("Dataset index set at " + str(index) + ".")
56+
57+
58+
def startButton_on_click(self):
59+
self.ui.startButton.setEnabled(False)
60+
self.ui.pauseButton.setEnabled(True)
61+
self.signal_provider.state = "running"
62+
63+
self.logger.write_to_log("Dataset started.")
64+
65+
def pauseButton_on_click(self):
66+
self.ui.pauseButton.setEnabled(False)
67+
self.ui.startButton.setEnabled(True)
68+
self.signal_provider.state = "pause"
69+
70+
self.logger.write_to_log("Dataset paused.")
71+
72+
@pyqtSlot()
73+
def update_slider(self):
74+
if not self.slider_pressed:
75+
index = 100 * self.signal_provider.index / self.signal_size
76+
self.ui.timeSlider.setValue(index)
77+
78+
def quit(self):
79+
"""
80+
Quit method.
81+
82+
Method called when actionQuit is triggered.
83+
"""
84+
85+
# close the window
86+
self.close()
87+
88+
def open_mat_file(self):
89+
file_name, _ = QFileDialog.getOpenFileName(self, "Open a mat file", ".", filter='*.mat')
90+
self.signal_provider.open_mat_file(file_name)
91+
self.ui.startButton.setEnabled(True)
92+
self.ui.timeSlider.setEnabled(True)
93+
self.ui.timeLabel.setEnabled(True)
94+
self.signal_size = len(self.signal_provider)
95+
self.logger.write_to_log("File '" + file_name + "' opened.")
96+
97+
98+
class Logger:
99+
"""
100+
Logger class shows events during the execution of the viewer.
101+
"""
102+
103+
def __init__(self, log_widget, scroll_area):
104+
105+
# set log widget from main window
106+
self.log_widget = log_widget
107+
108+
# set scroll area form main window
109+
self.scroll_area = scroll_area
110+
111+
# print welcome message
112+
self.write_to_log("Robot Viewer started.")
113+
114+
def write_to_log(self, text):
115+
"""
116+
Log the text "text" with a timestamp.
117+
"""
118+
119+
# extract current text from the log widget
120+
current_text = self.log_widget.text()
121+
122+
# compose new text
123+
# convert local time to string
124+
time_str = strftime(" [%H:%M:%S] ", localtime())
125+
#
126+
new_text = current_text + time_str + text + "\n"
127+
128+
# log into the widget
129+
self.log_widget.setText(new_text)
130+
131+
# scroll down text
132+
self.scroll_down()
133+
134+
def scroll_down(self):
135+
"""
136+
Scroll down the slider of the scroll area
137+
linked to this logger
138+
"""
139+
# extract scroll bar from the scroll area
140+
scroll_bar = self.scroll_area.verticalScrollBar()
141+
142+
# set maximum value of the slider, 1000 is enough
143+
scroll_bar.setMaximum(1000)
144+
scroll_bar.setValue(scroll_bar.maximum())
145+
146+
147+
if __name__ == '__main__':
148+
# construct a QApplication
149+
app = QtWidgets.QApplication(sys.argv)
150+
151+
# instantiate the main window and add the Matplotlib canvas
152+
gui = RobotViewerMainWindow()
153+
154+
# show the main window
155+
gui.show()
156+
157+
sys.exit(app.exec_())

0 commit comments

Comments
 (0)