Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 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
0d2408c
Simplified for first part of the PR
nicktrem Mar 4, 2024
4eacfa2
Merge branch 'main' into rtCapabilitiesP1Visualizer
nicktrem Mar 4, 2024
296ee38
Changed to account for python bindings
nicktrem Mar 6, 2024
d8b573b
Changed variable name
nicktrem Mar 6, 2024
ecdf600
Fixed merge
nicktrem Mar 6, 2024
059c6d6
Changed function names to make more sense
nicktrem Mar 13, 2024
a02f24e
Merge branch 'main' into rtCapabilitiesP1Visualizer
nicktrem Apr 8, 2024
621ce36
Rebased for recent changes
nicktrem Apr 8, 2024
b98edec
Removed print statements
nicktrem May 17, 2024
221711a
Removed whitespace
nicktrem May 17, 2024
49880bf
Edited to only clear plot if rt is enabled
nicktrem May 17, 2024
8cb0209
Removed prints, moved some prints to integrated console screen
nicktrem May 17, 2024
989426c
Removed unused function
nicktrem May 17, 2024
40f406b
Removed unused variables/imports
nicktrem May 17, 2024
fa4282f
Fixed bug with text logging
nicktrem May 17, 2024
0b033ba
Converted to snake_case
nicktrem May 17, 2024
f8b88fa
Added exception for if BLF is not installed
nicktrem May 17, 2024
59953ef
Fixed bug regarding naming
nicktrem May 17, 2024
ad96b95
Fixed small bug with the grid
nicktrem May 17, 2024
76b3129
Updated to support latest python binding for vectors collection
nicktrem Sep 2, 2024
767c83c
Fixed bug displaying data offline
nicktrem Sep 3, 2024
2cc7277
Added better exception handling
nicktrem Sep 25, 2024
f5b4a65
Removed unused icon
nicktrem Sep 25, 2024
be77f98
Added gui changes to the visualizer.ui
nicktrem Sep 25, 2024
5df2f88
updates to autogenerated files from qt creator
nicktrem Sep 25, 2024
5461cbe
refactor after RT meeting
nicktrem Jan 31, 2025
05f2a9b
Refactor SignalProvider to include provider type and clean up unused …
GiulioRomualdi Feb 4, 2025
ca2088c
Apply black formatter
GiulioRomualdi Feb 4, 2025
e6a4f67
meeting on Feburary 7th
nicktrem Feb 7, 2025
b469e6c
Fixed lingering plot for rt visualization
nicktrem Feb 7, 2025
d0cd098
Autoscaled y axis in rt plot animation
nicktrem Feb 7, 2025
7c04af1
Fixed issue setting x limits for rt and mat
nicktrem Feb 7, 2025
b41f386
Added ability to gracefully close the program
nicktrem Feb 7, 2025
ccfc2f0
Fixed bug when closing rt plot
nicktrem Feb 7, 2025
b6e372d
Removed unused imports
nicktrem Feb 7, 2025
42b935b
Removed unnecessary plotting lock
nicktrem Feb 7, 2025
2222eaa
Removed unused variables and code
nicktrem Feb 7, 2025
dd9ec7c
Removed maintain_connection function
nicktrem Feb 7, 2025
7a51724
Fixed odd bug where slider moves during rt visualization
nicktrem Feb 7, 2025
f79308e
Added a clarification comment
nicktrem Feb 7, 2025
9c09faa
Removed update_mesh_realtime
nicktrem Feb 7, 2025
d43cb37
Removed online init plot function
nicktrem Feb 7, 2025
f7751bb
Refactor signal provider connection path and remove unused imports
xela-95 Aug 1, 2025
451a1eb
Log potential dependency import error handling
xela-95 Aug 1, 2025
a3fe125
Merge branch 'main' into rtCapabilitiesP1Visualizer
xela-95 Aug 1, 2025
8ebefbc
Refactor signal provider handling in PyQtGraphViewerCanvas and PlotItem
xela-95 Sep 5, 2025
68f2dcb
Enhance real-time signal provider integration in PyQtGraphViewerCanvas
xela-95 Sep 5, 2025
36d72ac
Fix SignalProvider by setting provider_type attribute
xela-95 Sep 5, 2025
6dd7436
Adjust X axis range and enable Y axis autoscaling based on provider type
xela-95 Sep 5, 2025
bb2af57
Implement real-time signal selection and buffer updates in `PyQtGraph…
xela-95 Sep 10, 2025
3867f3d
Update `RobotViewerMainWindow`
xela-95 Sep 10, 2025
ada3b82
Refactor `env_list` to include new gazebo env variable and use raw st…
xela-95 Sep 10, 2025
99614ef
Fix selection logic in RobotViewerMainWindow
xela-95 Sep 10, 2025
cc10065
Enhance real-time signal handling and robot state buffering
xela-95 Sep 12, 2025
dc79e96
Add override method to retrieve latest data from path in RealtimeSign…
xela-95 Sep 12, 2025
8cd3edf
Add error message for missing model in MeshcatProvider
xela-95 Sep 12, 2025
0948672
Update docstrings in realtime_signal_provider
xela-95 Sep 12, 2025
ec70ce6
Remove rt_selector.xml
xela-95 Sep 16, 2025
7c4f927
Restore files in `ui/autogenerated` to main version
xela-95 Sep 16, 2025
c11c1a5
Merge branch 'main' into rtCapabilitiesP1Visualizer
xela-95 Sep 16, 2025
fe843d1
Update robot_log_visualizer/signal_provider/signal_provider.py
GiulioRomualdi Sep 18, 2025
30f1921
Update robot_log_visualizer/signal_provider/realtime_signal_provider.py
GiulioRomualdi Sep 18, 2025
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
15 changes: 2 additions & 13 deletions robot_log_visualizer/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@
# Released under the terms of the BSD 3-Clause License

import sys
import os

# GUI
from robot_log_visualizer.ui.gui import RobotViewerMainWindow
from PyQt5.QtWidgets import QApplication

from robot_log_visualizer.file_reader.signal_provider import SignalProvider

# Meshcat
from robot_log_visualizer.robot_visualizer.meshcat_provider import MeshcatProvider

Expand All @@ -24,29 +21,21 @@ def main():
"plot_animation": 0.03,
}

# instantiate device_manager
signal_provider = SignalProvider(period=thread_periods["signal_provider"])

meshcat_provider = MeshcatProvider(
period=thread_periods["meshcat_provider"], signal_provider=signal_provider
)
meshcat_provider = MeshcatProvider(period=thread_periods["meshcat_provider"])

# instantiate a QApplication
app = QApplication(sys.argv)

# instantiate the main window
gui = RobotViewerMainWindow(
signal_provider=signal_provider,
signal_provider_period=thread_periods["signal_provider"],
meshcat_provider=meshcat_provider,
animation_period=thread_periods["plot_animation"],
)

# show the main window
gui.show()

signal_provider.start()
meshcat_provider.start()

return app.exec_()


Expand Down
85 changes: 72 additions & 13 deletions robot_log_visualizer/plotter/pyqtgraph_viewer_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

from __future__ import annotations

from typing import Dict, Tuple, Sequence, Iterable
from typing import Dict, Iterable, Sequence, Tuple

from PyQt5 import QtCore, QtWidgets # type: ignore
import pyqtgraph as pg # type: ignore
import numpy as np
import pyqtgraph as pg # type: ignore
from PyQt5 import QtCore, QtWidgets # type: ignore

from robot_log_visualizer.plotter.color_palette import ColorPalette
from robot_log_visualizer.signal_provider.signal_provider import ProviderType

# ------------------------------------------------------------------------
# Type aliases
Expand All @@ -31,7 +32,6 @@ class PyQtGraphViewerCanvas(QtWidgets.QWidget):
def __init__(
self,
parent: QtWidgets.QWidget | None,
signal_provider,
period: float,
*,
click_radius: float | None = None,
Expand All @@ -41,16 +41,14 @@ def __init__(

Args:
parent: Parent widget or *None*.
signal_provider: Object exposing ``data``, ``initial_time``,
``end_time`` and a **dynamic** ``current_time`` attribute.
period: Update period for the vertical line (seconds).
click_radius: Override :pyattr:`DEFAULT_RADIUS`.
marker_size: Override :pyattr:`DEFAULT_MARKER_SIZE`.
"""
super().__init__(parent)

# injected dependencies
self._signal_provider = signal_provider
self._signal_provider = None
self._period_ms: int = int(period * 1000)
self._click_radius: float = click_radius or self.DEFAULT_RADIUS
self._marker_size: int = marker_size or self.DEFAULT_MARKER_SIZE
Expand All @@ -66,21 +64,58 @@ def __init__(
self._connect_signals()

# -------------------------------------------------------------#
# Public API (called from the outside) #
# Public API (called from the outside) #
# -------------------------------------------------------------#

def set_signal_provider(self, signal_provider) -> None:
"""Set the signal provider to fetch data from.

Args:
signal_provider: An instance of `SignalProvider`.
"""

if signal_provider is None:
return

self._signal_provider = signal_provider

# Connect to real-time updates for real-time provider
if self._signal_provider.provider_type == ProviderType.REALTIME:
self._signal_provider.update_index_signal.connect(
self._update_realtime_curves
)

def update_plots(self, paths: Sequence[Path], legends: Sequence[Legend]) -> None:
"""Synchronise plots with the *paths* list.

New items are added, disappeared items removed. Existing ones are
left untouched to avoid flicker.
"""
if self._signal_provider is None:
return

# For real-time provider, update the set of selected signals to buffer
if self._signal_provider.provider_type == ProviderType.REALTIME:
selected_keys = ["::".join(path) for path in paths]
self._signal_provider.add_signals_to_buffer(selected_keys)

self._add_missing_curves(paths, legends)
self._remove_obsolete_curves(paths)
# Always show the full time span
self._plot.setXRange(
0.0,
self._signal_provider.end_time - self._signal_provider.initial_time,
)

# Set the X axis range based on the provider type
if self._signal_provider.provider_type == ProviderType.REALTIME:
# For real-time data, show a fixed window with 0 set at the right edge for the latest data
self._plot.setXRange(-self._signal_provider.realtime_fixed_plot_window, 0.0)
# Disable mouse panning on x axis
self._plot.plotItem.vb.setMouseEnabled(x=False, y=True)
# For real-time data enable autoscaling of Y axis
self._plot.plotItem.vb.enableAutoRange(axis=pg.ViewBox.YAxis, enable=True)
else:
# Default behavior
self._plot.setXRange(
0.0,
self._signal_provider.end_time - self._signal_provider.initial_time,
)

# The following trio is wired to whoever controls the replay/stream
def pause_animation(self) -> None: # noqa: D401
Expand Down Expand Up @@ -173,8 +208,32 @@ def _remove_obsolete_curves(self, paths: Sequence[Path]) -> None:

def _update_vline(self) -> None:
"""Move the vertical line to ``current_time``."""
if self._signal_provider is None:
return

self._vline.setValue(self._signal_provider.current_time)

def _update_realtime_curves(self):
"""Update all curves with the latest data from the signal provider."""

if self._signal_provider is None:
return
for key, curve in self._curves.items():
# Drill down to the data array using the path
path = key.split("/")
data = self._signal_provider.data
for subkey in path[:-1]:
data = data[subkey]
try:
y = data["data"][:, int(path[-1])]
except (IndexError, ValueError):
y = data["data"][:]

# Set the 0 of the x axis to the latest timestamp
latest_time = data["timestamps"][-1] if len(data["timestamps"]) > 0 else 0
x = data["timestamps"] - latest_time
curve.setData(x, y)

def _on_mouse_click(self, event) -> None: # noqa: N802
"""Handle a left‑click: select or unselect the nearest data point."""
if event.button() != QtCore.Qt.MouseButton.LeftButton:
Expand Down
30 changes: 21 additions & 9 deletions robot_log_visualizer/robot_visualizer/meshcat_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,21 @@
# This software may be modified and distributed under the terms of the
# Released under the terms of the BSD 3-Clause License

from PyQt5.QtCore import QThread, QMutex, QMutexLocker

import os
import re
from pathlib import Path

import numpy as np
import time
from pathlib import Path

import idyntree.swig as idyn
import numpy as np
from idyntree.visualize import MeshcatVisualizer
from PyQt5.QtCore import QMutex, QMutexLocker, QThread

from robot_log_visualizer.utils.utils import PeriodicThreadState


class MeshcatProvider(QThread):
def __init__(self, signal_provider, period):
def __init__(self, period):
QThread.__init__(self)

self._state = PeriodicThreadState.pause
Expand All @@ -29,12 +27,17 @@ def __init__(self, signal_provider, period):
self.meshcat_visualizer_mutex = QMutex()

self._is_model_loaded = False
self._signal_provider = signal_provider
self._signal_provider = None

self.model_path = ""
self.custom_package_dir = ""
self.base_frame = ""
self.env_list = ["GAZEBO_MODEL_PATH", "ROS_PACKAGE_PATH", "AMENT_PREFIX_PATH"]
self.env_list = [
"GAZEBO_MODEL_PATH",
"GZ_SIM_RESOURCE_PATH",
"ROS_PACKAGE_PATH",
"AMENT_PREFIX_PATH",
]
self._registered_3d_points = set()
self._registered_3d_trajectories = dict()
self._register_3d_arrow = set()
Expand Down Expand Up @@ -87,6 +90,9 @@ def unregister_3d_arrow(self, arrow_path):
self._register_3d_arrow.remove(arrow_path)
self._meshcat_visualizer.delete(shape_name=arrow_path)

def set_signal_provider(self, signal_provider):
self._signal_provider = signal_provider

def load_model(self, considered_joints, model_name, base_frame=None):
def get_model_path_from_envs(env_list):
return [
Expand Down Expand Up @@ -148,7 +154,7 @@ def find_model_joints(model_name, considered_joints):
model_filenames = [
folder_model_path / Path(f)
for f in os.listdir(folder_model_path.absolute())
if re.search("[a-zA-Z0-9_]*\.urdf", f)
if re.search(r"[a-zA-Z0-9_]*\.urdf", f)
]

if model_filenames:
Expand All @@ -158,6 +164,9 @@ def find_model_joints(model_name, considered_joints):

# If the model is not found we exit
if not model_found_in_env_folders:
print(
f"MeshcatProvider: Unable to find the model {model_name} in the environment folders."
)
return False

self.model_joints_index = find_model_joints(
Expand Down Expand Up @@ -234,6 +243,9 @@ def run(self):

index = self._signal_provider.index
if self.state == PeriodicThreadState.running and self._is_model_loaded:
if len(self._signal_provider) == 0:
time.sleep(self._period)
continue
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
Expand Down
Loading