Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Vector Collection Client to Read Real-Time Information #77

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
b46676b
Added the basics of the connect button in the menu
nicktrem Dec 26, 2023
6f1ec87
Added dummy data to test formatting and gui representation
nicktrem Dec 29, 2023
cfd0ede
Added a real-time logger button to the GUI
nicktrem Dec 29, 2023
f20c655
Added another test data set
nicktrem Dec 29, 2023
5b700c5
initial test of reading form YARP port passed
nicktrem Dec 31, 2023
f8153d4
Added more plot support for real-time data
nicktrem Jan 2, 2024
a716e59
Fixed small bug in plotting regarding mutiple plots at the same time
nicktrem Jan 2, 2024
ff33dc2
Fixed race condition with graphs and bug with plot indexes
nicktrem Jan 2, 2024
2de1dcd
Removed the need to pre-inform the logger about the data coming in
nicktrem Jan 2, 2024
6a3840d
Fixed race condition bug
nicktrem Jan 2, 2024
aa331dd
Removed hard-coded value for the length of incoming data
nicktrem Jan 2, 2024
3b86123
Can now stream all data (except mesh data) in real-time
nicktrem Jan 4, 2024
886a45b
Cleaned up the code a bit
nicktrem Jan 4, 2024
a1a0799
Added mesh support, cleaned up the code a bit
nicktrem Jan 5, 2024
e412bf4
Cleaned up the code, removed hard-coded values
nicktrem Jan 5, 2024
e19c626
Cleaned up the code some more
nicktrem Jan 9, 2024
835a17f
Added sliding window feature and icon for connection
nicktrem Jan 9, 2024
0bf0525
Fixed bug with real time logging window
nicktrem Jan 9, 2024
872b364
removed hardcoded value for fixed plot window
nicktrem Jan 9, 2024
36c9f3b
Moved intialization values outside of the conneciton loop
nicktrem Jan 9, 2024
031c401
Fixed blocking when connection closed, changed the YARP port name
nicktrem Jan 9, 2024
de5a54a
Added label to rt connection option in GUI
nicktrem Jan 9, 2024
be3d77f
Merge branch 'main' into YARP-port-client
nicktrem Jan 9, 2024
be1a667
Removed Trailing Whitespace
nicktrem Jan 9, 2024
82eb5e5
Removed whitespace
nicktrem Jan 9, 2024
83dbebc
Removed Whitespace
nicktrem Jan 9, 2024
b117936
Fixed issues with merge
nicktrem Jan 12, 2024
228e84f
Fixed freeze when connection stops
nicktrem Jan 12, 2024
4cd3a85
Initial test complete of receiving RT data from Vector Collection Server
nicktrem Jan 15, 2024
6d13f85
Got joint data streaming over the vector collection network
nicktrem Jan 16, 2024
97d2a03
Got mesh updating in realtime, cleaned up the code
nicktrem Jan 17, 2024
56c69ed
Removed unnecessary values from the tree window
nicktrem Jan 17, 2024
da33419
plot scaling now updating correctly
nicktrem Jan 17, 2024
27eb858
Flight data streaming
nicktrem Jan 17, 2024
0681e5a
Got naming corrected
nicktrem Jan 18, 2024
1631469
Fixed freezing when server is not running
nicktrem Jan 18, 2024
8d96948
Cleaned up the code a bit
nicktrem Jan 18, 2024
a8538d6
Added a grid to the plot
nicktrem Jan 18, 2024
4b203ab
Merge branch 'main' into vectorCollectionClientRT
nicktrem Jan 18, 2024
6c60288
Removed whitespace from signal_provider.py
nicktrem Jan 18, 2024
32a05b7
Changed realtime port name
nicktrem Jan 22, 2024
3604f62
Merge branch 'vectorCollectionClientRT' of https://github.com/nicktre…
nicktrem Jan 22, 2024
336db8e
Changes naming of python functions
nicktrem Feb 5, 2024
ad228f1
Edited flag for closing thread properly
nicktrem Feb 8, 2024
e10efaa
Moved enable features outside the loop
nicktrem Feb 9, 2024
bfce38b
Removed timestamps from RT view
nicktrem Feb 9, 2024
3b7e98e
Removed whitespace
nicktrem Feb 9, 2024
cadd9e0
External signals can now be added at any time
nicktrem Feb 13, 2024
0458de2
Cleaned up the code
nicktrem Feb 13, 2024
db95f5e
Removed whitespace
nicktrem Feb 13, 2024
ac36876
Fixed issue of adding mutiple exogenous signals
nicktrem Feb 13, 2024
82b5353
Removed new metadata flag from being displayed
nicktrem Feb 13, 2024
7b6f231
Cleaned up the code
nicktrem Feb 13, 2024
6056056
Might have fixed element names issue
nicktrem Feb 16, 2024
00c9011
Fixed other issue regarding element_names
nicktrem Feb 22, 2024
f633d6d
Removed old comments
nicktrem Mar 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions robot_log_visualizer/file_reader/signal_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# This software may be modified and distributed under the terms of the
# Released under the terms of the BSD 3-Clause License

import sys
import time
import math
import h5py
Expand All @@ -10,6 +11,12 @@
from robot_log_visualizer.utils.utils import PeriodicThreadState, RobotStatePath
import idyntree.swig as idyn

import bipedal_locomotion_framework.bindings as blf

# for real-time logging
import yarp
import json


class TextLoggingMsg:
def __init__(self, level, text):
Expand Down Expand Up @@ -67,7 +74,17 @@ def __init__(self, period: float):

self._current_time = 0

self.realtimeBufferReached = False
self.initMetadata = False
self.realtimeFixedPlotWindow = 20

# for networking with the real-time logger
self.realtimeNetworkInit = False
self.vectorCollectionsClient = blf.yarp_utilities.VectorsCollectionClient()
self.trajectory_span = 200
self.rtMetadataDict = {}
self.updateMetadataVal = 0
self.updateMetadata = False

def __populate_text_logging_data(self, file_object):
data = {}
Expand Down Expand Up @@ -121,8 +138,10 @@ def __populate_numerical_data(self, file_object):
if not isinstance(value, h5py._hl.group.Group):
continue
if key == "#refs#":
print("Skipping for refs")
continue
if key == "log":
print("Skipping for log")
continue
if "data" in value.keys():
data[key] = {}
Expand All @@ -145,13 +164,121 @@ def __populate_numerical_data(self, file_object):
"".join(chr(c[0]) for c in value[ref])
for ref in elements_names_ref[0]
]

else:
data[key] = self.__populate_numerical_data(file_object=value)

return data

def __populateRealtimeLoggerData(self, rawData, keys, value, recentTimestamp):
if keys[0] not in rawData:
rawData[keys[0]] = {}

if len(keys) == 1:
rawData[keys[0]]["data"] = np.append(rawData[keys[0]]["data"], value).reshape(-1, len(value))
rawData[keys[0]]["timestamps"] = np.append(rawData[keys[0]]["timestamps"], recentTimestamp)

tempInitialTime = rawData[keys[0]]["timestamps"][0]
tempEndTime = rawData[keys[0]]["timestamps"][-1]
while tempEndTime - tempInitialTime > self.realtimeFixedPlotWindow:
rawData[keys[0]]["data"] = np.delete(rawData[keys[0]]["data"], 0, axis=0)
rawData[keys[0]]["timestamps"] = np.delete(rawData[keys[0]]["timestamps"], 0)
tempInitialTime = rawData[keys[0]]["timestamps"][0]
tempEndTime = rawData[keys[0]]["timestamps"][-1]

else:
self.__populateRealtimeLoggerData(rawData[keys[0]], keys[1:], value, recentTimestamp)

def __populateRealtimeLoggerMetadata(self, rawData, keys, value):
if keys[0] == "timestamps":
return
if keys[0] not in rawData:
rawData[keys[0]] = {}

if len(keys) == 1:
if len(value) == 0:
del rawData[keys[0]]
return
if "elements_names" not in rawData[keys[0]]:
rawData[keys[0]]["elements_names"] = np.array([])
rawData[keys[0]]["data"] = np.array([])
rawData[keys[0]]["timestamps"] = np.array([])

rawData[keys[0]]["elements_names"] = np.append(rawData[keys[0]]["elements_names"], value)
else:
self.__populateRealtimeLoggerMetadata(rawData[keys[0]], keys[1:], value)


def establish_connection(self):
if not self.realtimeNetworkInit:
yarp.Network.init()

param_handler = blf.parameters_handler.YarpParametersHandler()
param_handler.set_parameter_string("remote", "/rtLoggingVectorCollections") # you must have some local port as well
param_handler.set_parameter_string("local", "/visualizerInput") # remote must match the server
param_handler.set_parameter_string("carrier", "udp")
self.vectorCollectionsClient.initialize(param_handler)

self.vectorCollectionsClient.connect()
self.realtimeNetworkInit = True
self.rtMetadataDict = self.vectorCollectionsClient.get_metadata().getVectors()
if not self.rtMetadataDict:
print("Failed to read realtime YARP port, closing")
return False

self.joints_name = self.rtMetadataDict["robot_realtime::description_list"]
self.robot_name = self.rtMetadataDict["robot_realtime::yarp_robot_name"][0]
for keyString, value in self.rtMetadataDict.items():
keys = keyString.split("::")
self.__populateRealtimeLoggerMetadata(self.data, keys, value)
del self.data["robot_realtime"]["description_list"]
del self.data["robot_realtime"]["yarp_robot_name"]
del self.data["robot_realtime"]["newMetadata"]

input = self.vectorCollectionsClient.read_data(True)

if not input:
print("Failed to read realtime YARP port, closing")
return False
else:
# Update the timestamps
recentTimestamp = input["robot_realtime::timestamps"][0]
self.timestamps = np.append(self.timestamps, recentTimestamp).reshape(-1)
del input["robot_realtime::timestamps"]

# Keep the data within the fixed time interval
while recentTimestamp - self.timestamps[0] > self.realtimeFixedPlotWindow:
self.initial_time = self.timestamps[0]
self.end_time = self.timestamps[-1]
self.timestamps = np.delete(self.timestamps, 0).reshape(-1)
self.initial_time = self.timestamps[0]
self.end_time = self.timestamps[-1]

# Check if new metadata arrived for a new signal
newMetadataInputVal = input["robot_realtime::newMetadata"][0]
self.updateMetadata = newMetadataInputVal != self.updateMetadataVal
del input["robot_realtime::newMetadata"]

# If there is new metadata, populate it
if self.updateMetadata:
self.updateMetadataVal = newMetadataInputVal
metadata = self.vectorCollectionsClient.get_metadata().getVectors()
difference = { k : metadata[k] for k in set(metadata) - set(self.rtMetadataDict) }
self.rtMetadataDict = metadata
for keyString, value in difference.items():
keys = keyString.split("::")
self.__populateRealtimeLoggerMetadata(self.data, keys, value)

# Store the new data that comes in
for keyString, value in input.items():
keys = keyString.split("::")
self.__populateRealtimeLoggerData(self.data, keys, value, recentTimestamp)

return True

def open_mat_file(self, file_name: str):
with h5py.File(file_name, "r") as file:

root_variable = file.get(self.root_name)
self.data = self.__populate_numerical_data(file)

Expand Down
48 changes: 27 additions & 21 deletions robot_log_visualizer/plotter/matplotlib_viewer_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# Released under the terms of the BSD 3-Clause License

# PyQt
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
from matplotlib.figure import Figure
Expand Down Expand Up @@ -64,27 +65,28 @@ def quit_animation(self):
if self.vertical_line_anim:
self.vertical_line_anim._stop()

def update_plots(self, paths, legends):
def update_plots(self, paths, legends, realtimePlot=False):
self.axes.cla()
for path, legend in zip(paths, legends):
path_string = "/".join(path)
legend_string = "/".join(legend[1:])

if path_string not in self.active_paths.keys():
data = self.signal_provider.data
for key in path[:-1]:
data = data[key]
try:
datapoints = data["data"][:, int(path[-1])]
except IndexError:
# This happens in the case the variable is a scalar.
datapoints = data["data"][:]

timestamps = data["timestamps"] - self.signal_provider.initial_time

(self.active_paths[path_string],) = self.axes.plot(
timestamps, datapoints, label=legend_string
)

#if path_string not in self.active_paths.keys():
data = self.signal_provider.data.copy()
for key in path[:-1]:
data = data[key]
try:
datapoints = data["data"][:, int(path[-1])]
except IndexError:
# This happens in the case the variable is a scalar.
datapoints = data["data"][:]

timestamps = data["timestamps"] - self.signal_provider.initial_time

(self.active_paths[path_string],) = self.axes.plot(
timestamps, datapoints, label=legend_string
)
self.axes.grid()
paths_to_be_canceled = []
for active_path in self.active_paths.keys():
path = active_path.split("/")
Expand All @@ -96,9 +98,13 @@ def update_plots(self, paths, legends):
self.active_paths[path].remove()
self.active_paths.pop(path)

self.axes.set_xlim(
0, self.signal_provider.end_time - self.signal_provider.initial_time
)
if realtimePlot:
#self.axes.autoscale()
self.axes.set_xlim(0, self.signal_provider.realtimeFixedPlotWindow)
else:
self.axes.set_xlim(
0, self.signal_provider.end_time - self.signal_provider.initial_time
)

# Since a new plot has been added/removed we delete the old animation and we create a new one
# TODO: this part could be optimized
Expand Down Expand Up @@ -127,4 +133,4 @@ def update_vertical_line(self, _):
# Draw vertical line at current index

self.vertical_line.set_data([current_time, current_time], self.axes.get_ylim())
return self.vertical_line, *(self.active_paths.values())
return self.vertical_line, *(self.active_paths.values())
15 changes: 15 additions & 0 deletions robot_log_visualizer/robot_visualizer/meshcat_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ def __init__(self, signal_provider, period):
self._registered_3d_points = set()
self._registered_3d_trajectories = dict()

self._realtimeMeshUpdate = False

@property
def state(self):
locker = QMutexLocker(self.state_lock)
Expand Down Expand Up @@ -175,6 +177,7 @@ def run(self):
index = self._signal_provider.index
if self.state == PeriodicThreadState.running and self._is_model_loaded:
robot_state = self._signal_provider.get_robot_state_at_index(index)

self.meshcat_visualizer_mutex.lock()
# These are the robot measured joint positions in radians
self._meshcat_visualizer.set_multibody_system_state(
Expand Down Expand Up @@ -224,3 +227,15 @@ def run(self):

if self.state == PeriodicThreadState.closed:
return

# For the real-time logger
def updateMeshRealtime(self):
self._signal_provider.index = len(self._signal_provider.timestamps) - 1
robot_state = self._signal_provider.get_robot_state_at_index(self._signal_provider.index)

self._meshcat_visualizer.set_multibody_system_state(
base_position=robot_state["base_position"],
base_rotation=robot_state["base_orientation"],
joint_value=robot_state["joints_position"][self.model_joints_index],
model_name="robot",
)
10 changes: 10 additions & 0 deletions robot_log_visualizer/ui/autogenerated/visualizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,24 @@ def setupUi(self, MainWindow):
icon = QtGui.QIcon.fromTheme("exit")
self.actionQuit.setIcon(icon)
self.actionQuit.setObjectName("actionQuit")

# Add the GUI components for the open action
self.actionOpen = QtWidgets.QAction(MainWindow)
icon = QtGui.QIcon.fromTheme("document-open")
self.actionOpen.setIcon(icon)
self.actionOpen.setObjectName("actionOpen")

# Add a GUI action for connecting to the YARP port
# for real-time logging
self.actionConnect = QtWidgets.QAction(MainWindow)
self.actionConnect.setObjectName("actionConnect")

self.actionAbout = QtWidgets.QAction(MainWindow)
self.actionAbout.setObjectName("actionAbout")
self.actionSet_Robot_Model = QtWidgets.QAction(MainWindow)
self.actionSet_Robot_Model.setObjectName("actionSet_Robot_Model")
self.menuFile.addAction(self.actionOpen)
self.menuFile.addAction(self.actionConnect)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionQuit)
self.menuHelp.addAction(self.actionAbout)
Expand Down Expand Up @@ -275,6 +284,7 @@ def retranslateUi(self, MainWindow):
self.actionQuit.setText(_translate("MainWindow", "&Quit"))
self.actionQuit.setShortcut(_translate("MainWindow", "Ctrl+Q"))
self.actionOpen.setText(_translate("MainWindow", "&Open"))
self.actionConnect.setText(_translate("MainWindow", "Realtime Connect"))
self.actionOpen.setShortcut(_translate("MainWindow", "Ctrl+O"))
self.actionAbout.setText(_translate("MainWindow", "About"))
self.actionSet_Robot_Model.setText(_translate("MainWindow", "Set Robot Model"))
Expand Down
Loading