<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tutorials &#8211; PlotJuggler</title>
	<atom:link href="https://plotjuggler.com/tutorials-cat/feed/" rel="self" type="application/rss+xml" />
	<link>https://plotjuggler.com</link>
	<description>The Ultimate Resource for Time-Series Data Visualization</description>
	<lastBuildDate>Thu, 22 Oct 2026 04:24:09 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.3</generator>

<image>
	<url>https://plotjuggler.com/wp-content/uploads/2026/05/favicon-150x150.png</url>
	<title>Tutorials &#8211; PlotJuggler</title>
	<link>https://plotjuggler.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Live Streaming to PlotJuggler: WebSocket, MQTT, ZMQ and UDP</title>
		<link>https://plotjuggler.com/plotjuggler-streaming-websocket-mqtt/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:21:17 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-streaming-websocket-mqtt/</guid>

					<description><![CDATA[Files are for what already happened. Streaming is for what is happening now: a robot on the bench, a test running in the next room, a process emitting values you want to watch as curves rather than as scrolling numbers. PlotJuggler has streamed since the PJ3 era — its documented streamers cover WebSocket, MQTT, ZeroMQ...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Files are for what already happened. Streaming is for what is happening <em>now</em>: a robot on the bench, a test running in the next room, a process emitting values you want to watch as curves rather than as scrolling numbers. PlotJuggler has streamed since the PJ3 era — its documented streamers cover <strong>WebSocket, MQTT, ZeroMQ (ZMQ) and UDP</strong>, plus ROS topics and Lab Streaming Layer — and <strong>PJ4</strong> carries the same family forward and adds <strong>WebRTC</strong>. This tutorial explains the model, helps you choose a transport, and walks a complete streaming setup. The recorded-data counterpart workflows are in <a href="/plotjuggler-rosbag-mcap/">the bag and MCAP tutorial</a>; the series index is the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">The streaming model, in one paragraph</h2><p class="wp-block-paragraph">A streamer is the live twin of a file loader. Your program — the publisher — sends timestamped values over a network connection; PlotJuggler’s streamer for that transport receives them, decodes the messages, and the values appear as series you drag onto plots exactly as if they had come from a file, except the right edge of the plot keeps moving. Two choices define any setup: the <strong>transport</strong> (how the bytes travel: WebSocket, MQTT, ZMQ, UDP) and the <strong>encoding</strong> (how each message represents its values). PJ3 understands <strong>JSON, CBOR, BSON and MessagePack</strong> encodings; PJ4 widens that list (adding Protobuf and Apache Arrow, among others) and keeps the same transports, plus WebRTC for live video. Get transport and encoding to match on both ends and streaming is, honestly, the easy part.</p><h2 class="wp-block-heading">Choosing a transport</h2><figure class="wp-block-table"><table><thead><tr><th>Transport</th><th>Character</th><th>Reach for it when</th></tr></thead><tbody><tr><td>WebSocket</td><td>A persistent connection over web-friendly plumbing; the natural output of anything that already speaks HTTP-era protocols</td><td>Your publisher is a web service, a script, or a tool that already offers a WebSocket endpoint; PJ3 even grew a WebSocket URL feature in its final release, 3.17.2</td></tr><tr><td>MQTT</td><td>Publish/subscribe through a broker; publishers and subscribers never address each other directly</td><td>The data already lives on an MQTT broker (IoT fleets, building and test-rig telemetry), or several consumers need the same stream</td></tr><tr><td>ZeroMQ (ZMQ)</td><td>Lightweight socket patterns without a broker; common in research and robotics codebases</td><td>Your stack already uses ZMQ, or you want brokerless point-to-point streaming with minimal ceremony</td></tr><tr><td>UDP</td><td>Fire-and-forget datagrams: the lowest-overhead option, with no delivery guarantee</td><td>High-rate local telemetry where an occasional lost packet is acceptable and simplicity beats ceremony</td></tr><tr><td>ROS topics / LSL</td><td>The domain-native options: PJ3 subscribes to ROS 1/ROS 2 topics directly; Lab Streaming Layer serves lab and neuroscience rigs</td><td>The source is a running ROS system (see the <a href="/install-plotjuggler-ros2/">ROS 2 install guide</a>) or an LSL outlet</td></tr></tbody></table></figure><h2 class="wp-block-heading">The message: a labelled example</h2><p class="wp-block-paragraph">Whatever the transport, the payload must tell PlotJuggler which values arrived and when. With JSON — the most readable encoding, and the right choice while you are setting up — one message typically carries a timestamp and a set of named values, like this <em>illustrative example</em>:</p><pre class="wp-block-code"><code>{
  "timestamp": 1712345678.42,
  "temperature": 24.6,
  "pressure_kpa": 101.3
}
# Illustrative example only — a generic JSON payload shape for explanation,
# not a captured message from a specific device.</code></pre><p class="wp-block-paragraph">Each named value becomes (or updates) a series; the timestamp places it on the timeline. Binary encodings — CBOR, BSON, MessagePack — express the same content more compactly, which is why high-rate streams prefer them once the setup is proven in JSON. The design rule on the publisher side is the same as for <a href="/plotjuggler-csv-import/">CSV files</a>: stable value names and a consistent, honest timestamp on every message. Rename a value mid-stream and you have started a new series whether you meant to or not.</p><h2 class="wp-block-heading">A complete streaming setup, step by step</h2><ol class="wp-block-list"><li><strong>Start the publisher first.</strong> Streaming has two ends and only one of them is PlotJuggler; have your device, script or broker demonstrably emitting before debugging the receiving side. Most “PlotJuggler shows nothing” reports end here.</li><li><strong>Choose the matching streamer in PlotJuggler</strong> for your transport, and point it at the publisher (or broker) with the connection details your setup uses — host, port and topic or endpoint as applicable to that transport.</li><li><strong>Match the encoding.</strong> Tell the streamer which encoding the messages use (JSON while proving the link). A transport that connects and an encoding that mismatches produces silence, not errors — this is the second most common setup trap.</li><li><strong>Watch the series appear.</strong> As messages arrive, their named values show up as available series. Drag them onto a plot, as in <a href="/getting-started-with-plotjuggler/">Getting Started</a>, and confirm the curves move and the values agree with a known reference (a sensor you can warm with a fingertip beats any amount of log staring).</li><li><strong>Then tune.</strong> Switch to a binary encoding if the rate demands it, add a <a href="/plotjuggler-lua-custom-functions/">derived series</a> if the raw values need combining, and save the arrangement as a <a href="/plotjuggler-layouts/">layout</a> so the live view is rebuilt in one step next session.</li></ol><h2 class="wp-block-heading">Streaming in PJ4: what is added</h2><p class="wp-block-paragraph">PJ4’s live list — <strong>ROS 2, MQTT, ZeroMQ, UDP, WebSocket bridges, WebRTC and Lab Streaming Layer</strong> — keeps every PJ3-era transport that matters here and adds the piece PJ3 never had: <strong>WebRTC</strong>, for live camera streams that join your plots on the shared timeline (the multimodal picture is toured in <a href="/plotjuggler-4-video-3d/">PJ4 Video and 3D Visualisation</a>). Its encoding list also grows to include ROS 1/2 messages, Protobuf and Apache Arrow alongside JSON, CBOR, BSON and MessagePack. The workflow in this tutorial — publisher first, match transport, match encoding, verify against reality — is unchanged by the generation; only the menu of options grows.</p><h2 class="wp-block-heading">When nothing arrives: the short checklist</h2><ul class="wp-block-list"><li><strong>Is the publisher actually publishing?</strong> Verify at the source, with its own tools or a second consumer — never debug two ends at once.</li><li><strong>Do the connection details match?</strong> Host, port, and topic/endpoint naming must agree exactly; “almost the same topic” is a different topic.</li><li><strong>Do the encodings match?</strong> JSON sent to a MessagePack-configured streamer (or vice versa) fails quietly. Set both ends to JSON while diagnosing.</li><li><strong>Is anything between the ends?</strong> Firewalls, broker authentication and containers that publish on a different network than you think are the classic in-between culprits — general networking checks, applied in that order.</li><li><strong>Are the timestamps sane?</strong> Values arriving with a frozen or wildly wrong timestamp plot somewhere you are not looking; zoom out to the full timeline once before concluding nothing arrived.</li></ul><p class="wp-block-paragraph">Live data handled well becomes the best data: when a stream surprises you, the same session can be inspected with every tool in this series — offsets, XY views, transforms — because streamed series are ordinary series. And when the moment matters, record it and analyse the file with the <a href="/plotjuggler-rosbag-mcap/">recorded-data workflows</a>. The full learning path is on the <a href="/tutorials/">Tutorials hub</a>; install files are on the <a href="/download/">Download page</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Saving and Sharing PlotJuggler Layouts</title>
		<link>https://plotjuggler.com/plotjuggler-layouts/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:21:04 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-layouts/</guid>

					<description><![CDATA[Every tutorial in this series ends the same way — “save the layout” — and this one explains why that advice matters. A layout is PlotJuggler’s saved form of a view: which plots you built, which signals sit on them, and how the view is arranged, captured to a file you can reload instead of...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Every tutorial in this series ends the same way — “save the layout” — and this one explains why that advice matters. A <strong>layout</strong> is PlotJuggler’s saved form of a view: which plots you built, which signals sit on them, and how the view is arranged, captured to a file you can reload instead of rebuilding. Save/reload layouts are a long-standing PJ3 feature, and PJ4 keeps the concept while changing the format — its layouts use <strong>schema v4</strong>. This guide covers what a layout does for you, the save and reload workflows, sharing layouts with a team, and the one migration note PJ4 users must know. Series index: the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">What a layout is — and is not</h2><p class="wp-block-paragraph">Think of the split this way: your <em>data files</em> (a bag, an MCAP, a CSV) hold the measurements; a <em>layout</em> holds the <strong>view of measurements</strong> — the plots, the chosen series on each, the arrangement you arrived at while analysing. Reloading a layout against data restores that view, which is why the same layout can serve recording after recording from the same rig: the series names recur, the questions recur, only the run changes. PJ4’s schema v4 additionally stores the <strong>viewport</strong> and the <strong>per-dataset time offsets</strong> you set, so an aligned comparison reopens aligned. What a layout is not: it is not a copy of your data, and it is not a screenshot. Send a colleague only the layout and they have your questions, not your answers — they still need the data files (and the same loaders or plugins) to see anything.</p><h2 class="wp-block-heading">Saving a layout, step by step</h2><ol class="wp-block-list"><li><strong>Build the view worth saving.</strong> The plots, overlays and zooms you would want back: for example the standard post-flight arrangement from <a href="/plotjuggler-rosbag-mcap/">the bag and MCAP tutorial</a>, or the two-run overlay from <a href="/compare-multiple-bag-files-plotjuggler/">comparing multiple recordings</a>.</li><li><strong>Add the derived series.</strong> If the view uses built-in transforms or <a href="/plotjuggler-lua-custom-functions/">Lua custom functions</a>, make sure they are part of the view before saving — in PJ4, layouts and transforms are saved together, which is what makes a shared layout reproduce its derived curves too.</li><li><strong>Align, then save.</strong> If the view depends on <a href="/plotjuggler-time-offset/">time offsets</a>, apply them first; on PJ4 those offsets are stored in the layout, so saving after aligning is the difference between a reusable asset and a recurring chore.</li><li><strong>Save the layout under a deliberate name.</strong> Name it for the rig and the question (“quad-postflight-attitude”), not the date or the run — the layout is the reusable thing, the run was just its first use.</li><li><strong>Reload it once, immediately.</strong> Close the view, reopen the layout, and confirm the plots, series and offsets come back as saved. A layout verified at birth will not surprise you mid-incident.</li></ol><h2 class="wp-block-heading">Reloading against new data</h2><p class="wp-block-paragraph">The payoff workflow is short: record a new run, load the new data file, apply the saved layout. The same signals land on the same plots in the same arrangement — and because the series are matched by their identity in the data, a well-kept rig (consistent topic and column names across runs) makes this nearly free. When a series the layout expects is absent from the new file — a topic renamed, a column dropped by a new exporter — that part of the view simply has nothing to draw; treat a strangely empty plot after a reload as a data-lineage clue first and a PlotJuggler problem second. This reload habit is what turns the other tutorials from one-off analyses into a repeatable process: the <a href="/plotjuggler-csv-import/">CSV view</a> for every export from the same logger, the comparison layout for every A/B test.</p><h2 class="wp-block-heading">Sharing layouts with a team</h2><ul class="wp-block-list"><li><strong>Send three things:</strong> the layout file, a note of which PlotJuggler line it was saved from (PJ3 vs PJ4 — see the schema note below), and any custom transforms or plugins the view depends on. A layout that silently assumes a Lua function the recipient lacks will under-deliver without explaining itself.</li><li><strong>Agree series naming at the source.</strong> Layouts pay compound interest when every logger, exporter and bag in the team uses stable topic and column names. The cheapest team standard you will ever adopt is “we do not rename topics casually”.</li><li><strong>Keep a small team library.</strong> A handful of named, verified layouts — post-flight, bench test, regression comparison — beats a folder of personal one-offs. Retire layouts when the rig changes, and say so in the name or the note.</li><li><strong>Share the data expectation, not the data.</strong> Because a layout carries the view rather than the measurements, it is safe to circulate where the recordings themselves are not — a genuinely useful property when data is large or sensitive.</li></ul><h2 class="wp-block-heading">The PJ4 note: layout schema v4</h2><p class="wp-block-paragraph">PJ4 rewrote the layout format: its layouts are <strong>schema v4</strong>, which is how they come to store viewports and per-dataset offsets alongside the plots. The practical guidance, stated in our <a href="/migrate-plotjuggler-3-to-4/">PJ3-to-PJ4 migration guide</a> and repeated here because layout users are exactly who it affects: <strong>check your layouts after upgrading</strong>. Open each important PJ3 layout in PJ4, confirm the curves, arrangement and offsets look as saved, and re-save from PJ4 once satisfied. Do not assume a silent perfect conversion of every saved view, do not delete the PJ3 copies until the PJ4 versions are verified — and do not expect PJ4-saved layouts to travel backwards into PJ3. Treat the first open as an inspection, and the migration is uneventful.</p><h2 class="wp-block-heading">Layouts as a habit, not a feature</h2><p class="wp-block-paragraph">The teams that get the most from PlotJuggler are not the ones with the cleverest single analysis; they are the ones whose tenth analysis starts from the saved form of their best one. Every workflow in this series deposits something into a layout — the <a href="/getting-started-with-plotjuggler/">first plot</a>, the <a href="/plotjuggler-time-offset/">aligned comparison</a>, the <a href="/plotjuggler-xy-plot/">XY view</a> next to its time plots. Save them, name them, share them. The rest of the series is on the <a href="/tutorials/">Tutorials hub</a>; install files are on the <a href="/download/">Download page</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PlotJuggler Lua Custom Functions: Build Your Own Transforms</title>
		<link>https://plotjuggler.com/plotjuggler-lua-custom-functions/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:20:50 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-lua-custom-functions/</guid>

					<description><![CDATA[Built-in transforms handle the everyday jobs — a derivative here, a moving average there. The moment your question becomes “…and now combine these signals in a way the toolbox does not offer”, you need a custom function. PlotJuggler’s Custom Function Editor provides exactly that: you write a small function in Lua that takes multiple recorded...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Built-in transforms handle the everyday jobs — a derivative here, a moving average there. The moment your question becomes “…and now combine these signals in a way the toolbox does not offer”, you need a custom function. PlotJuggler’s <strong>Custom Function Editor</strong> provides exactly that: you write a small function in <strong>Lua</strong> that takes multiple recorded series as inputs and produces one new series as output. It is a defining PJ3 feature that PJ4 keeps (adding Python as an alternative — the Lua craft in this tutorial remains fully relevant). This guide explains the model, works a real pattern, and stays honest about what is concept versus API. Set-up context: <a href="/getting-started-with-plotjuggler/">Getting Started</a> and the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">First, exhaust the built-ins</h2><p class="wp-block-paragraph">Custom code is the second resort, not the first — a built-in transform is maintained, tested and instant. PlotJuggler’s built-in editor offers transforms including <strong>derivative</strong> (rate of change), <strong>moving average</strong> (smoothing), <strong>integral</strong> (accumulated quantity) and <strong>scale</strong> (unit conversions and calibration factors), among others; PJ4’s analysis set also names filters and FFT. Reach for Lua when the computation genuinely combines signals — a ratio, a magnitude from components, an angle from a quaternion — or applies logic a fixed transform cannot express. A useful rule: if the formula fits in one arithmetic expression over several series, it is custom-function territory; if it is one operation on one series, a built-in almost certainly already does it.</p><h2 class="wp-block-heading">The model: multiple inputs, one output</h2><p class="wp-block-paragraph">Everything about the Custom Function Editor follows from one sentence in the project’s own description: custom functions are <strong>multi-input, single-output functions written in Lua</strong>. Concretely:</p><ul class="wp-block-list"><li><strong>Inputs:</strong> you select the recorded series the computation reads — one, two, or several. At each timestamp, the function sees the inputs’ values at that moment.</li><li><strong>The function:</strong> your Lua code combines those values into a single result for that timestamp — arithmetic, trigonometry, conditionals, whatever the language offers.</li><li><strong>Output:</strong> the results form one new series. It is a normal PlotJuggler series in every respect: plot it, overlay it, feed it into further transforms, save it in a <a href="/plotjuggler-layouts/">layout</a>.</li><li><strong>One output per function:</strong> need three derived quantities? Define three functions. That constraint keeps each definition small, nameable and reusable — embrace it rather than fighting it.</li></ul><h2 class="wp-block-heading">A real-world pattern: quaternion to Euler angles (PX4)</h2><p class="wp-block-paragraph">The best-documented real use of this feature comes from the PX4 ecosystem: the official PX4 documentation carries a full “Log Analysis using PlotJuggler” guide whose workflow uses Lua custom series to convert a logged <strong>quaternion</strong> attitude into <strong>roll, pitch and yaw</strong>. Described conceptually — which is how you should design any custom function before writing it — the pattern is:</p><ul class="wp-block-list"><li><strong>Inputs:</strong> the four quaternion component series from the ULog (the vehicle’s attitude estimate, as recorded).</li><li><strong>Computation:</strong> the standard quaternion-to-Euler conversion, applied sample by sample — trigonometry over the four inputs, nothing more exotic.</li><li><strong>Outputs:</strong> three separate custom functions, one each for roll, pitch and yaw, each emitting a single angle series — the multi-input/single-output model, applied three times.</li><li><strong>Payoff:</strong> angles a human can read, plotted against time and against each other, from a log that only ever stored quaternions.</li></ul><p class="wp-block-paragraph">We deliberately describe the inputs and outputs rather than printing “the API”: exact editor mechanics belong to the application in front of you and the PX4 guide itself, and copied signatures rot quickly. The transferable skill is the decomposition — inputs, per-sample computation, one output — which fits every custom function you will ever write here, from a two-input power calculation (voltage × current, straight from a <a href="/plotjuggler-csv-import/">CSV</a>) to a four-input attitude conversion.</p><h2 class="wp-block-heading">Your first custom function, step by step</h2><ol class="wp-block-list"><li><strong>Pick a computation you can verify by hand.</strong> A scale-and-combine you can check at one timestamp with a calculator — power from voltage and current is perfect. First functions are for learning the workflow, not for solving the hard problem.</li><li><strong>Load data containing the inputs</strong> and plot the inputs themselves first. If an input is wrong, the function will be wrong more confusingly.</li><li><strong>Create the custom function</strong> in the Custom Function Editor, select the input series, and write the Lua expression of your formula: read the inputs, compute, return the single output value.</li><li><strong>Plot the output against its inputs.</strong> Check the hand-verifiable timestamp. Check a second one. Check behaviour at the interesting event (the step, the peak) — not just at rest.</li><li><strong>Name it like you mean it.</strong> The output series joins your signal list and your layouts; a name that states quantity and unit (“power_watts”) saves every future reader a trip back into the editor.</li><li><strong>Save the layout.</strong> Custom functions are part of the view you built; saving the layout is how the derived series survives to the next session and the next recording (see <a href="/plotjuggler-layouts/">layouts</a>).</li></ol><pre class="wp-block-code"><code>-- Illustrative pattern only — pseudocode showing the shape of a custom function,
-- NOT PlotJuggler editor syntax. Real mechanics: the Custom Function Editor
-- and, for the quaternion case, the PX4 documentation guide.
inputs:  voltage, current        -- recorded series, selected in the editor
output:  power = voltage * current   -- one derived value per timestamp</code></pre><h2 class="wp-block-heading">Design habits that pay off</h2><ul class="wp-block-list"><li><strong>Keep functions small and single-purpose.</strong> Chain two clear functions instead of writing one cryptic one; the intermediate series is often worth plotting in its own right.</li><li><strong>Mind the edges.</strong> Decide what your function should do where inputs are missing or zero — a division that is elegant mid-flight can be a spike factory at the boundary. A conditional in Lua is cheap insurance.</li><li><strong>Prefer built-ins inside functions sparingly.</strong> Smooth an input with a moving average first if the raw signal is too noisy for stable derivatives or ratios; transforms compose, in the editor as elsewhere.</li><li><strong>Re-verify after changing inputs.</strong> Pointing an existing function at a different recording assumes the same series names and units. The two-timestamp hand check takes a minute and catches both assumptions.</li></ul><h2 class="wp-block-heading">And in PlotJuggler 4: Lua, or Python</h2><p class="wp-block-paragraph">PJ4 keeps this entire model and adds <strong>Python scripting as an alternative to Lua</strong> for custom transforms — the subject of our <a href="/plotjuggler-4-python-scripting/">PJ4 Python scripting guide</a>. Nothing about the decomposition changes: inputs in, one derived series out. If your team lives in Python, PJ4 lets the transform live there too; if your Lua functions already work, they remain the right tool. Either way, the derived series you build become candidates for the two views that exploit them best — <a href="/plotjuggler-xy-plot/">XY plots</a> and the multi-recording overlays in <a href="/compare-multiple-bag-files-plotjuggler/">comparing bag files</a>. The full series is indexed on the <a href="/tutorials/">Tutorials hub</a>, and the program itself is on the <a href="/download/">Download page</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Make XY Plots in PlotJuggler</title>
		<link>https://plotjuggler.com/plotjuggler-xy-plot/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:20:36 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-xy-plot/</guid>

					<description><![CDATA[Every plot so far has put time on the horizontal axis. An XY plot asks a different question: not “how did this signal evolve?” but “how do these two signals relate?” — position against position, output against input, one joint against another. XY plots are a long-standing PlotJuggler feature in PJ3, and PJ4 keeps them...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Every plot so far has put time on the horizontal axis. An XY plot asks a different question: not “how did this signal evolve?” but “how do these two signals relate?” — position against position, output against input, one joint against another. XY plots are a long-standing PlotJuggler feature in <strong>PJ3</strong>, and PJ4 keeps them at the centre of its workflow — you drag signals into time-series <em>and</em> XY plots. This tutorial shows how to build one, what to use it for, and how to read what it draws. It builds on <a href="/getting-started-with-plotjuggler/">Getting Started</a>; the whole series is on the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">Time plot vs XY plot: the one idea</h2><p class="wp-block-paragraph">In a time plot, each curve is a function of time: at every timestamp, a value. In an XY plot, time steps aside and becomes the invisible parameter that walks the point along its path: the horizontal position comes from one signal (X), the vertical position from another (Y), and each timestamp contributes one point. Connect the points in time order and the plot draws the <em>path of the relationship itself</em> — a trajectory through the space the two signals define. That is why XY plots reveal things time plots hide: loops, limits, dead zones and phase differences become shapes you can see at a glance, instead of two wiggly lines you must mentally correlate.</p><h2 class="wp-block-heading">Step-by-step: your first XY plot</h2><ol class="wp-block-list"><li><strong>Load data with at least two related numeric signals.</strong> A CSV with two position columns, or a bag with a robot’s x and y fields, is ideal. Loading is the usual drag-and-drop workflow from <a href="/getting-started-with-plotjuggler/">Getting Started</a> or <a href="/plotjuggler-csv-import/">the CSV tutorial</a>.</li><li><strong>Create an XY view of the data.</strong> PlotJuggler’s workflow is drag-based in both generations: you build an XY plot by bringing the two chosen signals into an XY plotting area, designating which signal plays the X role and which plays the Y role. X is the reference — the signal you want to read the other “against”.</li><li><strong>Check the pairing makes sense.</strong> Both signals must describe the same moments. Signals recorded together in one file pair naturally; signals from different files need their timelines aligned first — that is the <a href="/plotjuggler-time-offset/">time offset workflow</a>, and skipping it is the main way XY plots lie.</li><li><strong>Read the path, then zoom.</strong> The whole-recording view shows the overall shape; zoom into a section of interest to inspect a single manoeuvre, limit cycle or excursion in detail, exactly as you would on a time plot.</li><li><strong>Save the arrangement.</strong> If the pairing answers a recurring question, capture it in a <a href="/plotjuggler-layouts/">layout</a> alongside your time plots — the two views side by side are worth more than either alone.</li></ol><h2 class="wp-block-heading">Three jobs XY plots do better than time plots</h2><figure class="wp-block-table"><table><thead><tr><th>Job</th><th>X and Y</th><th>What the shape tells you</th></tr></thead><tbody><tr><td>Trajectory</td><td>X = x-position, Y = y-position</td><td>The actual path travelled, drawn to scale: missed waypoints, overshoot at corners, and drift appear as geometry, not as two separate wiggles</td></tr><tr><td>Phase / relationship</td><td>X = reference or input signal, Y = response or output signal</td><td>A clean proportional response draws a straight line; lag draws a loop whose width grows with the delay; saturation flattens the ends; a dead zone flattens the middle</td></tr><tr><td>Joint / state space</td><td>X = one joint or state, Y = another joint or state</td><td>Coordination between the two — which leads, which follows, and whether the pair traces the same path in both directions or encloses a hysteresis loop</td></tr></tbody></table></figure><h2 class="wp-block-heading">How to read what it draws</h2><ul class="wp-block-list"><li><strong>Straight diagonal line:</strong> the signals are proportional. The slope is the ratio between them — a calibration error often shows up here before it shows up anywhere else.</li><li><strong>Closed loop:</strong> the pair returns to its start with Y lagging or leading X. Loop area grows with phase difference; a loop in a position-vs-position plot is simply a closed path travelled.</li><li><strong>Loop that does not close:</strong> the system drifted during the recording — the end state differs from the start state. Compare with the time plot to find when.</li><li><strong> dense scribble in one region:</strong> the system spent most of its time there — dwell, oscillation around a setpoint, or noise dominating a near-constant signal.</li><li><strong>Direction matters:</strong> the path is traced in time order. Where the story is ambiguous, drop back to the time plot for the same interval and read the two views together.</li></ul><h2 class="wp-block-heading">Combining XY with the rest of the toolkit</h2><p class="wp-block-paragraph">XY plots get stronger with derived signals. Plot velocity against position by first computing the velocity with the built-in derivative transform, or define a combined quantity with a <a href="/plotjuggler-lua-custom-functions/">Lua custom function</a> and use its output as an axis. Compare two runs by building the same XY view for each recording — the <a href="/compare-multiple-bag-files-plotjuggler/">multiple-recording comparison workflow</a> — and aligning their timelines first so the pairings are honest. In PJ4, XY plots also sit next to the new video and 3D views on the same timeline, so the abstract relationship and the physical scene can be inspected in the same pass.</p><h2 class="wp-block-heading">Limits, honestly stated</h2><p class="wp-block-paragraph">An XY plot is only as trustworthy as the pairing behind it: mismatched sample times, unaligned recordings and signals in different units all produce confident-looking nonsense. It also discards time as an axis — when “when” is the question, the time plot is still the right tool. Used for the questions it suits, though, it is the fastest diagnostic in this entire series: one picture, and the relationship is either healthy or it is not. Continue with <a href="/plotjuggler-lua-custom-functions/">Lua custom functions</a> to build the derived axes you just read about, or return to the <a href="/tutorials/">Tutorials hub</a> for the full path. If PlotJuggler is not installed yet, the <a href="/install/">Install guides</a> and the <a href="/download/">Download page</a> will get you there first.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PlotJuggler Time Offset: Align Signals That Start at Different Times</title>
		<link>https://plotjuggler.com/plotjuggler-time-offset/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:20:23 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-time-offset/</guid>

					<description><![CDATA[Two signals, two clocks, two start buttons pressed a second apart — and a plot that insists your systems disagreed when they actually agreed perfectly, just not simultaneously. Time offset is PlotJuggler’s answer: shift a curve (in PJ3) or a whole dataset (in PJ4) along the time axis until events that happened together line up...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Two signals, two clocks, two start buttons pressed a second apart — and a plot that insists your systems disagreed when they actually agreed perfectly, just not simultaneously. Time offset is PlotJuggler’s answer: shift a curve (in PJ3) or a whole dataset (in PJ4) along the time axis until events that happened together line up together. The GSC queries behind this tutorial — time offset, time shift, aligning signals — all describe the same underlying need. This guide explains the concept, including the <strong>T=0</strong> idea, then gives a careful alignment workflow. Related: <a href="/compare-multiple-bag-files-plotjuggler/">comparing multiple bag files</a> and the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">The problem, precisely</h2><p class="wp-block-paragraph">A time offset problem exists when the <em>relative</em> timing inside each recording is correct, but the recordings (or signals) do not share a common zero. Each file is honest about its own timeline; they simply started counting at different moments — a logger armed before the controller booted, a bag recorded a breath after the test rig’s CSV export began. No amount of zooming fixes that, because the data itself needs to move. That is what an offset does: it adds a constant shift to a timeline, leaving every interval inside the recording untouched. If intervals themselves are stretched or drifting, you have a clock-rate problem instead — offsets will align one event and misalign the rest, which is exactly the diagnostic the verification step below exploits.</p><h2 class="wp-block-heading">The T=0 concept</h2><p class="wp-block-paragraph">Alignment needs a reference. The PJ3 interface expresses this with a <strong>“T=0” time-offset control</strong>: in effect, you declare which moment counts as zero, and the curves are viewed relative to it. The mental model generalises to both generations: pick one event as the anchor — the instant the test truly began — and express everything else relative to that anchor. PJ4 formalises the same idea at the dataset level: its layouts and views work with <strong>per-dataset time offsets</strong>, one shift per loaded recording, stored in the layout (schema v4) together with the viewport. PJ3’s offsets, by contrast, are applied <strong>per curve</strong>. Same goal, different granularity — and the granularity matters when a recording holds thirty signals you need to move together.</p><h2 class="wp-block-heading">Step-by-step: aligning two signals</h2><ol class="wp-block-list"><li><strong>Load both signals and plot them together.</strong> Overlay is essential — you cannot align what you cannot see together. The loading workflows are in <a href="/getting-started-with-plotjuggler/">Getting Started</a> and <a href="/plotjuggler-rosbag-mcap/">the bag and MCAP tutorial</a>.</li><li><strong>Choose a reference event that exists in both.</strong> The best anchors are sharp and unambiguous: a step command and its response, a mode switch, a distinctive spike. Slow ramps and periodic waves make poor anchors — a repeating wave aligns at every period, most of them wrong.</li><li><strong>Measure the gap.</strong> Read the event’s time in the first signal and in the second. The difference is your candidate offset. (Illustrative numbers: if the step appears at 14.2 s in one recording and 15.0 s in the other, the second recording’s timeline needs to move 0.8 s earlier to coincide.)</li><li><strong>Apply the offset to the late signal.</strong> In PJ3, apply the time shift to the curve; in PJ4, set the dataset’s offset so the whole recording moves as one. Direction matters: you are moving the late recording <em>earlier</em> (or, equivalently, delaying the early one) — if the gap doubles instead of closing, you shifted the wrong way; undo and reverse the sign.</li><li><strong>Verify on a second, independent event.</strong> Find a different event later in the recording and check it also lines up. One event can always be forced into alignment; two events aligning after a single constant shift is real evidence the offset is right.</li><li><strong>Save the layout.</strong> An alignment you do not save is an alignment you will redo. Layouts are covered in <a href="/plotjuggler-layouts/">Saving and Sharing PlotJuggler Layouts</a> — and in PJ4 the offsets themselves are part of what the layout stores.</li></ol><h2 class="wp-block-heading">Per-curve or per-dataset: which are you moving?</h2><figure class="wp-block-table"><table><thead><tr><th>Situation</th><th>Right tool</th><th>Why</th></tr></thead><tbody><tr><td>Two recordings of the same rig, started at different moments</td><td>Per-dataset offset (PJ4) or per-curve shifts applied uniformly (PJ3)</td><td>The whole recording shares one wrong zero; move it as a unit so internal timing is preserved by construction</td></tr><tr><td>One sensor in a single recording lags the others (a delayed topic, a buffered device)</td><td>Per-curve offset</td><td>Only that signal is displaced; shifting everything else would break the signals that were right</td></tr><tr><td>Comparing runs for a fair overlay</td><td>Per-dataset offset, aligned on a shared event</td><td>This is the alignment step inside the <a href="/compare-multiple-bag-files-plotjuggler/">multiple-bag comparison workflow</a></td></tr></tbody></table></figure><h2 class="wp-block-heading">Pitfalls worth knowing</h2><ul class="wp-block-list"><li><strong>Aligning on a periodic signal.</strong> A sine wave matches itself every period. Anchor on an aperiodic event, then use the wave only as a fine check.</li><li><strong>Chasing drift with offsets.</strong> If event one aligns and event two, minutes later, does not — with the error growing over time — the clocks ran at different rates. A constant offset cannot fix drift; align near the interval you are analysing and say so in your notes.</li><li><strong>Forgetting the offset is active.</strong> An offset left on a curve makes later absolute-time readings wrong. Note offsets in the layout name or your test log, especially when sharing the layout with colleagues.</li><li><strong>Over-aligning noise.</strong> On noisy signals, align on the feature (the step, the edge), not on the nearest noise peak.</li></ul><p class="wp-block-paragraph">Master this one control and two other tutorials unlock: fair <a href="/compare-multiple-bag-files-plotjuggler/">multi-recording comparisons</a> and meaningful <a href="/plotjuggler-xy-plot/">XY plots</a> between signals recorded together. The rest of the series is on the <a href="/tutorials/">Tutorials hub</a>; the program and its checksums are on the <a href="/download/">Download page</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Compare Multiple Bag Files in PlotJuggler</title>
		<link>https://plotjuggler.com/compare-multiple-bag-files-plotjuggler/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:20:10 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/compare-multiple-bag-files-plotjuggler/</guid>

					<description><![CDATA[One recording tells you what happened. Two recordings — before and after a fix, run A against run B — tell you whether anything actually improved. Comparing multiple bag files is one of the most searched PlotJuggler workflows, and it is also where the two generations differ most clearly: PlotJuggler 4 explicitly supports loading several...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">One recording tells you what happened. Two recordings — before and after a fix, run A against run B — tell you whether anything actually improved. Comparing multiple bag files is one of the most searched PlotJuggler workflows, and it is also where the two generations differ most clearly: <strong>PlotJuggler 4 explicitly supports loading several recordings, aligning their timelines and overlaying their signals</strong>, while <strong>PJ3</strong> offers the same goal through a more manual workflow built on its per-curve time offsets. This tutorial covers both, carefully labelled. Foundations: <a href="/plotjuggler-rosbag-mcap/">opening ROS bags and MCAP files</a> and the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">What a fair comparison requires</h2><p class="wp-block-paragraph">Overlaying two recordings is easy; overlaying them <em>fairly</em> is the skill. Three conditions decide whether the picture means anything:</p><ul class="wp-block-list"><li><strong>The same signals.</strong> The same topics and fields from each recording, plotted the same way — ideally by reusing one <a href="/plotjuggler-layouts/">layout</a> for both, so nothing about the view changes except the data.</li><li><strong>Aligned timelines.</strong> Recordings almost never start at the same instant. Until their starts are aligned on a shared reference event, every difference you see may be nothing but a time shift. Alignment uses time offsets — the whole subject of the <a href="/plotjuggler-time-offset/">time offset tutorial</a>.</li><li><strong>The same conditions, noted.</strong> Different firmware, a different course, a windy afternoon — write down what else changed, or the prettier curve will get credit it has not earned.</li></ul><h2 class="wp-block-heading">In PlotJuggler 4: several recordings, one timeline</h2><p class="wp-block-paragraph">PJ4 was designed for this job. Its documented workflow is to <strong>load several recordings, align their timelines, and overlay the signals</strong>, with a <strong>per-dataset time offset</strong> available for each loaded recording so you can shift an entire run earlier or later as one unit. The practical sequence:</p><ol class="wp-block-list"><li>Load the first recording and build the view you want — the signals, the plots, the arrangement — following the <a href="/plotjuggler-rosbag-mcap/">bag and MCAP workflow</a>.</li><li>Load the second recording alongside it. Its signals join the workspace as their own dataset, distinguishable from the first.</li><li>Plot the corresponding signals from both datasets on the same axes. Same topic, same field, two runs — the overlay is the comparison.</li><li>Align before judging: pick an event that exists in both runs (a launch, a step input, a distinctive manoeuvre), measure how far apart it sits in the two datasets, and apply a per-dataset offset to the later-starting run so the events coincide. The method is worked through in the <a href="/plotjuggler-time-offset/">time offset tutorial</a>.</li><li>Save the result as a layout. PJ4 layouts (schema v4) store the viewport and the per-dataset offsets, so the aligned comparison reopens as you left it instead of needing re-alignment every session.</li></ol><h2 class="wp-block-heading">In PlotJuggler 3: the careful manual workflow</h2><p class="wp-block-paragraph">PJ3 predates the per-dataset model, so be precise about what it offers. PJ3 loads data files, plots their series, saves and reloads layouts, and — the key tool here — supports <strong>per-curve time offset / time-shift</strong> alignment, a long-standing PJ3 feature. A careful PJ3 comparison therefore goes like this: open the recordings, plot the matching curves, and use the per-curve time-shift controls to bring the shared reference event into alignment curve by curve. It reaches the same picture, but the alignment lives on individual curves rather than on a dataset as a whole, which has two consequences you should plan around: it takes longer as the number of compared signals grows, and it is easier to shift one curve of a run and forget another. Work systematically — align every compared curve against the same reference event, then verify with a second event elsewhere in the recording (see the checks in the <a href="/plotjuggler-time-offset/">time offset tutorial</a>) — and save the aligned view as a layout immediately.</p><h2 class="wp-block-heading">Reading the overlay without fooling yourself</h2><ul class="wp-block-list"><li><strong>Compare shapes first, peaks second.</strong> A run that reaches the same peak more smoothly is usually the better run; a single higher spike is often noise wearing a costume.</li><li><strong>Zoom to the event, not the whole run.</strong> Differences that matter happen at transitions — starts, stops, disturbances. The shared timeline lets you inspect the same seconds in both runs.</li><li><strong>Quantify one thing.</strong> Pick a number the overlay suggests (settling time, overshoot, steady error) and verify it with the curves — or with a derived series from a <a href="/plotjuggler-lua-custom-functions/">Lua custom function</a> — before declaring a winner.</li><li><strong>Distrust perfect alignment.</strong> If every event lines up to the sample after shifting, suspect that you aligned on the event you are now “discovering”. Check against an independent second event.</li></ul><h2 class="wp-block-heading">A worked example (illustrative)</h2><p class="wp-block-paragraph">A test team records a bag before and after a controller tuning change. They load both recordings, overlay the commanded and measured velocity from each, and find the “after” run apparently responding a full second sooner. Before celebrating, they align on the step input that exists in both bags — and discover the second recording simply started half a second later relative to the manoeuvre. After applying that offset, a real improvement remains, but it is a faster settle, not a magical one-second gain. The offset step is what separated the two conclusions; skip it and the comparison would have published the wrong one. (This example is illustrative — the workflow it demonstrates is the documented one above.)</p><p class="wp-block-paragraph">When the recordings are not bags but repeated CSV exports, the same workflow applies unchanged — see <a href="/plotjuggler-csv-import/">CSV import</a>. And when the runs are still being produced, <a href="/plotjuggler-streaming-websocket-mqtt/">live streaming</a> plus saved layouts gives you the comparison view in real time. The install routes for both lines are in the <a href="/install/">Install guides</a>, the files themselves are on the <a href="/download/">Download page</a>, and everything in the series is indexed on the <a href="/tutorials/">Tutorials hub</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Open ROS Bag and MCAP Files in PlotJuggler</title>
		<link>https://plotjuggler.com/plotjuggler-rosbag-mcap/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:19:57 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-rosbag-mcap/</guid>

					<description><![CDATA[For ROS users, PlotJuggler’s real test is not CSV — it is the recordings the robots already produce: ROS 1 .bag files, ROS 2 bags, and MCAP files. Both PlotJuggler lines open them, but by different routes, and confusing the routes is the main source of “it will not open my bag” frustration. This tutorial...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">For ROS users, PlotJuggler’s real test is not CSV — it is the recordings the robots already produce: ROS 1 <code>.bag</code> files, ROS 2 bags, and MCAP files. Both PlotJuggler lines open them, but by different routes, and confusing the routes is the main source of “it will not open my bag” frustration. This tutorial maps the routes honestly — what <strong>PJ3</strong> does through its ROS plugin set, what <strong>PJ4</strong> reads natively — then walks the opening workflow. It assumes a working install; ROS users who have not installed yet should start with the <a href="/install-plotjuggler-ros2/">ROS 2 install guide</a>. All guides are collected on the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">The three recording types, in one paragraph each</h2><ul class="wp-block-list"><li><strong>ROS 1 bags (<code>.bag</code>).</strong> The original rosbag format: messages recorded from ROS 1 topics into a single file. In PlotJuggler, opening them is a <strong>PJ3 + ROS plugins</strong> workflow — the plugins are what understand the bag and expose its topics as series.</li><li><strong>ROS 2 bags (rosbag2).</strong> ROS 2’s recorder stores recordings in its own layout, with <strong>MCAP</strong> among its storage options. The fact-base position is precise here: rosbag2/MCAP support in the PJ3 world lives in the <strong>ROS 2 / MCAP plugin set</strong> for PlotJuggler.</li><li><strong>MCAP files.</strong> A container format for timestamped, topic-structured recordings, used well beyond ROS. This is where the generations split: PJ3 reaches MCAP through that plugin set, while <strong>PJ4 lists MCAP as a natively supported file format</strong> — and can even attempt to read corrupted MCAP files, a capability added during the 3.999.x previews.</li></ul><h2 class="wp-block-heading">Which line should open your file?</h2><figure class="wp-block-table"><table><thead><tr><th>Your file</th><th>Route</th><th>Notes</th></tr></thead><tbody><tr><td>ROS 1 .bag</td><td>PJ3 with the ROS plugins</td><td>Install via the ROS packages — see the <a href="/install-plotjuggler-ros2/">ROS 2 install guide</a> for the package model and distro list</td></tr><tr><td>ROS 2 bag (rosbag2)</td><td>PJ3 with the ROS 2 plugin set</td><td>The plugins line (v2.3+) supports ROS 2; ROS 1 plugin support is frozen in older versions</td></tr><tr><td>MCAP from a ROS 2 recording</td><td>PJ3 plugin set, or PJ4 natively</td><td>PJ4 reads MCAP directly as a file format; PJ3 needs the plugin set installed</td></tr><tr><td>MCAP from a non-ROS source</td><td>PJ4 natively</td><td>MCAP is on PJ4’s file list alongside CSV, Parquet, ULog, LeRobot, MP4, PCD and PLY</td></tr></tbody></table></figure><h2 class="wp-block-heading">Before you open anything: the install check</h2><p class="wp-block-paragraph">Most failures happen before the file is involved. On the PJ3 route, PlotJuggler and its ROS plugins arrive together through the ROS packages — <code>sudo apt install ros-$ROS_DISTRO-plotjuggler-ros</code>, launched with <code>ros2 run plotjuggler plotjuggler</code>, for the distributions the ROS Index lists (humble, jazzy, kilted, lyrical, rolling). If you installed a standalone PJ3 binary without the ROS plugin set, bag files have no loader and will not open — that is an install gap, not a corrupt file. The full walkthrough is the <a href="/install-plotjuggler-ros2/">ROS 2 install guide</a> in our <a href="/install/">Install section</a>. On the PJ4 route, no ROS install is needed for MCAP files themselves, because MCAP reading is built into the application’s file support.</p><h2 class="wp-block-heading">The opening workflow, step by step</h2><ol class="wp-block-list"><li><strong>Load the recording</strong> by dragging it into the PlotJuggler window — the same drag-and-drop workflow as any other file. For a rosbag2 recording, you open the recording as the unit it was recorded as, rather than hunting for a single data file inside it.</li><li><strong>Let the loader enumerate the topics.</strong> A bag is many message streams in one container; the loader presents the topics, and the numeric fields inside those messages become the plottable series. Large bags expose a lot of series — that is normal.</li><li><strong>Choose deliberately.</strong> Find the topics your question is about (the command topic and the measured topic, the sensor and the estimate) and ignore the rest for now. Plotting everything is how people conclude, wrongly, that the tool is cluttered.</li><li><strong>Drag the fields onto plots.</strong> Overlay commanded vs measured on one plot; put diagnostics on their own plot below. From here the workflow is the standard one from <a href="/getting-started-with-plotjuggler/">Getting Started</a> — navigate the timeline, add transforms, save the view.</li><li><strong>Save a layout.</strong> Bag investigations repeat: the same topics, the same arrangement, the next recording. Capture it once with <a href="/plotjuggler-layouts/">layouts</a> and reopen it against tomorrow’s bag.</li></ol><h2 class="wp-block-heading">Working across recordings</h2><p class="wp-block-paragraph">One bag is rarely the whole story. The natural follow-ups all have their own tutorials: overlay yesterday’s and today’s bags to see what a change actually did with <a href="/compare-multiple-bag-files-plotjuggler/">comparing multiple bag files</a>; fix recordings that started their clocks at different moments with <a href="/plotjuggler-time-offset/">time offsets</a>; and compute the series the bag does not contain — error signals, magnitudes, rates — with <a href="/plotjuggler-lua-custom-functions/">Lua custom functions</a>. If the data is still being produced, the same topics can also be watched live through the ROS topic streaming covered in the <a href="/plotjuggler-streaming-websocket-mqtt/">streaming tutorial</a>.</p><h2 class="wp-block-heading">When a bag will not open</h2><ul class="wp-block-list"><li><strong>“No loader” behaviour on PJ3.</strong> Almost always the missing ROS plugin set — install the ROS package route above rather than reinstalling the standalone app.</li><li><strong>An MCAP that opens in PJ4 but not PJ3 (or the reverse expectation).</strong> Check which route your install actually has; the generations genuinely differ here, as the table above shows.</li><li><strong>A damaged MCAP.</strong> PJ4 added the ability to attempt reading corrupted MCAP files during its preview cycle — a damaged recording that a strict reader rejects is worth one attempt in PJ4 before you declare it lost.</li><li><strong>Everything opens, nothing is findable.</strong> A workflow problem, not a file problem: narrow to the topics for your question, name a layout, and reuse it.</li></ul><p class="wp-block-paragraph">Files for both lines, with checksums, are on the <a href="/download/">Download page</a>; the rest of the series is on the <a href="/tutorials/">Tutorials hub</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>How to Import and Plot CSV Files in PlotJuggler</title>
		<link>https://plotjuggler.com/plotjuggler-csv-import/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:19:43 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-csv-import/</guid>

					<description><![CDATA[CSV is where most PlotJuggler journeys begin: it is the format every logger, spreadsheet and test rig can produce, and both PlotJuggler lines read it — CSV is a core loader in PJ3 and stays on the supported file list in PJ4 alongside MCAP, Parquet and PX4 ULog. This tutorial covers the one concept that...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">CSV is where most PlotJuggler journeys begin: it is the format every logger, spreadsheet and test rig can produce, and both PlotJuggler lines read it — CSV is a core loader in <strong>PJ3</strong> and stays on the supported file list in <strong>PJ4</strong> alongside MCAP, Parquet and PX4 ULog. This tutorial covers the one concept that decides whether a CSV plots correctly — the timestamp column — plus a complete import workflow and the fixes for the CSV problems everyone hits once. It assumes the basics from <a href="/getting-started-with-plotjuggler/">Getting Started with PlotJuggler</a>; all the guides live on the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">What a PlotJuggler-ready CSV looks like</h2><p class="wp-block-paragraph">PlotJuggler plots values <em>against time</em>, so a usable CSV needs three things: a header row naming the columns, one column that carries time, and numeric columns to plot. The illustrative example below shows the shape — a time column first, then one column per signal, one row per sample:</p><pre class="wp-block-code"><code>time,voltage,current,temperature
0.00,12.61,1.82,24.5
0.10,12.58,1.85,24.6
0.20,12.55,1.91,24.6
# Illustrative example only — a generic CSV shape, not output from a specific tool.</code></pre><ul class="wp-block-list"><li><strong>The timestamp column</strong> is the column PlotJuggler uses as the horizontal axis. It can hold elapsed seconds, epoch time or another consistent time representation — what matters is that it increases monotonically down the file and that PlotJuggler can identify it as the time column during import.</li><li><strong>One column per signal.</strong> Each numeric column becomes a series you can drag onto a plot. Name columns meaningfully in the header — those names are what you will see in the signal list.</li><li><strong>Consistent rows.</strong> Keep the column count the same on every row, and keep units consistent within a column. Mixed text annotations inside a numeric column are the classic cause of a series that loads partially or not at all.</li></ul><h2 class="wp-block-heading">Step 1 — Prepare the file</h2><p class="wp-block-paragraph">Before importing, open the CSV in a text editor or spreadsheet and spend sixty seconds as a sceptic. Confirm the header row exists and names every column. Confirm the time column really is time — sorted, increasing, and in units you can state out loud (seconds? milliseconds?). If your export wrote timestamps as formatted date-time text, consider whether your exporter can emit a numeric epoch or elapsed-seconds column instead; a plain numeric time column is the least ambiguous input you can give any plotting tool. Save a copy rather than editing your only original.</p><h2 class="wp-block-heading">Step 2 — Load the file</h2><p class="wp-block-paragraph">The documented workflow is drag-and-drop: <strong>drag the CSV file into the PlotJuggler window</strong>. The CSV loader reads the file and the import step asks you, in effect, the defining question of this tutorial: <em>which column is time?</em> Designate your timestamp column there. Every other numeric column is then offered as a plottable series, sampled at the times that column provides. Get this choice right and everything downstream — overlays, transforms, XY plots — behaves; get it wrong and the data plots against row index or against one of your signals, which looks bizarre and wastes debugging time.</p><h2 class="wp-block-heading">Step 3 — Choose and plot series</h2><p class="wp-block-paragraph">With the file loaded, the signal list shows your columns. Drag the series you care about onto the plotting area, exactly as in the <a href="/getting-started-with-plotjuggler/">getting started walkthrough</a>: overlay signals that share a story, give unrelated signals their own plots. Two CSV-specific checks are worth doing on first plot:</p><ol class="wp-block-list"><li><strong>Check the time span.</strong> The plot’s horizontal extent should match your recording’s real duration. A span of thousands of “seconds” for a one-minute test usually means the time column was in milliseconds — the data is fine, the unit assumption is not.</li><li><strong>Check a known value.</strong> Pick a moment you know (a switch flipped, a motor started) and confirm the curve does what the real system did at that time. This catches swapped columns and off-by-one header mistakes in seconds.</li></ol><h2 class="wp-block-heading">Working with the imported data</h2><p class="wp-block-paragraph">Once plotted, a CSV series is a first-class PlotJuggler series. Apply built-in transforms such as a moving average or derivative to it, combine columns with a <a href="/plotjuggler-lua-custom-functions/">Lua custom function</a> (total power from the voltage and current columns, in the example above, is the textbook case), plot one column against another in an <a href="/plotjuggler-xy-plot/">XY plot</a>, and capture the whole arrangement as a <a href="/plotjuggler-layouts/">layout</a> so the next CSV from the same rig opens into the same view. If you import several CSVs from repeated runs, the <a href="/compare-multiple-bag-files-plotjuggler/">multiple-recording comparison workflow</a> applies to them just as it does to bag files.</p><h2 class="wp-block-heading">Common CSV problems, and fixes</h2><figure class="wp-block-table"><table><thead><tr><th>Symptom</th><th>Likely cause</th><th>Fix</th></tr></thead><tbody><tr><td>Data plots against row number</td><td>No column was designated as time, or the time column was not recognised</td><td>Re-import and designate the timestamp column explicitly</td></tr><tr><td>Timeline spans absurd durations</td><td>Time values are in milliseconds (or microseconds) while the plot reads them as seconds</td><td>Convert the column in your exporter, or rescale the series with a scale transform after import</td></tr><tr><td>A series is missing or stops early</td><td>Non-numeric text inside that column, or rows with missing fields</td><td>Clean the column in the source export; keep one consistent value format per column</td></tr><tr><td>Signals from two files do not line up</td><td>The recordings started at different times</td><td>That is a time-alignment problem, not a CSV problem — see the <a href="/plotjuggler-time-offset/">time offset tutorial</a></td></tr><tr><td>The file will not load at all</td><td>The file is not really CSV (wrong delimiter saved under a .csv name, or an Excel workbook renamed)</td><td>Export a genuine comma-separated text file from the source tool</td></tr></tbody></table></figure><h2 class="wp-block-heading">CSV in PJ4: one extra worth knowing</h2><p class="wp-block-paragraph">PJ4 keeps CSV import and adds an export direction: the 3.999.9 preview introduced an <strong>export tool to CSV and Arrow-Parquet</strong>, which shipped in the 4.0 line. That makes CSV a two-way street in PJ4 — bring data in as CSV, and send selected data back out as CSV for a spreadsheet or a colleague. The CSV workflow itself, timestamp column and all, is unchanged between the lines, which is why this tutorial can honestly cover both. For the files themselves and every other format, see the <a href="/download/">Download page</a> and the <a href="/tutorials/">Tutorials hub</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Getting Started with PlotJuggler: Your First Plot in 10 Minutes</title>
		<link>https://plotjuggler.com/getting-started-with-plotjuggler/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:19:29 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/getting-started-with-plotjuggler/</guid>

					<description><![CDATA[Ten minutes from now you will have a real signal on a real plot — and, more importantly, you will understand the handful of ideas every other PlotJuggler workflow is built from: loading data, dragging signals onto a plot, navigating the timeline, and saving the view as a layout. This walkthrough works for both current...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Ten minutes from now you will have a real signal on a real plot — and, more importantly, you will understand the handful of ideas every other PlotJuggler workflow is built from: loading data, dragging signals onto a plot, navigating the timeline, and saving the view as a layout. This walkthrough works for both current lines, <strong>PlotJuggler 4 (4.0.0)</strong> and the classic <strong>PlotJuggler 3 (3.17.2)</strong>, because the core workflow is the one thing the PJ4 rewrite deliberately kept. All nine guides in this series are collected on the <a href="/tutorials/">Tutorials hub</a>.</p><h2 class="wp-block-heading">Before you start (2 minutes)</h2><ul class="wp-block-list"><li><strong>A working install.</strong> If PlotJuggler is not on your machine yet, follow the <a href="/install/">Install guides</a> for your platform first — Windows, Ubuntu, ROS 2 and macOS are covered, and the files with their checksums are on the <a href="/download/">Download page</a>.</li><li><strong>A small data file.</strong> A short CSV export is the friendliest first file; a ROS bag, MCAP recording or PX4 ULog works too. Keep it small for the first run — you are learning the tool, not stress-testing it (PlotJuggler is built for thousands of series and millions of points, but that is for later).</li><li><strong>Know your line.</strong> PJ4 and PJ3 look similar and work similarly, but a few details differ. This guide flags the differences where they matter.</li></ul><h2 class="wp-block-heading">Step 1 — Open PlotJuggler and look around</h2><p class="wp-block-paragraph">Launch the application and take stock before touching any data. The interface is organised around three ideas: a list of the signals available in your data, a plotting area where signals become curves, and a timeline you can scrub, zoom and pan. PlotJuggler renders with OpenGL, which is why it stays responsive with very large recordings once you get there. Do not worry about panels you do not recognise yet — streaming controls, transform editors and (in PJ4) video and 3D views all build on the basics you are about to use.</p><h2 class="wp-block-heading">Step 2 — Load your first file by drag-and-drop</h2><p class="wp-block-paragraph">The documented way to bring data in is deliberately simple: <strong>drag the file into the PlotJuggler window and drop it</strong>. PlotJuggler works out which loader handles the file — CSV, PX4 ULog, ROS bags and (in PJ4) MCAP, Parquet and more are all supported formats — and reads it in. When the load finishes, the signals contained in the file appear in the signal list. If you used a CSV, you have just exercised the workflow our <a href="/plotjuggler-csv-import/">CSV import tutorial</a> covers in depth, including what to do when a CSV needs a timestamp column sorted out first. Bag and MCAP users get the same treatment in the <a href="/plotjuggler-rosbag-mcap/">ROS bag and MCAP tutorial</a>.</p><h2 class="wp-block-heading">Step 3 — Put signals on a plot</h2><p class="wp-block-paragraph">This is the gesture the whole tool is named around: <strong>drag a signal from the list and drop it onto the plotting area</strong>. A curve appears, drawn against time. Drag a second signal onto the same plot to overlay it — comparing related signals on shared axes is the most common analysis move there is. Drag a signal to a fresh area instead if you want it on its own plot, stacked with the others and sharing the timeline. A few habits worth forming immediately:</p><ul class="wp-block-list"><li>Start with two or three signals, not thirty. Add more only when you know what question each one answers.</li><li>Overlay signals that share a unit or a story (commanded vs measured, left vs right). Unrelated signals deserve separate plots.</li><li>If a curve looks flat, check its scale against the others before assuming the data is wrong — a small signal next to a large one is easy to miss.</li></ul><h2 class="wp-block-heading">Step 4 — Navigate the timeline</h2><p class="wp-block-paragraph">With curves on screen, learn to move: zoom into a short interval to inspect detail, pan along the recording, and zoom back out to recover context. Everything in PlotJuggler shares one timeline, so zooming reframes every plot at once — and in PJ4 it also reframes any video or 3D view you have open, which is the heart of its multimodal workflow. Practise on an event you can identify in your test file: find it, zoom to it, zoom out. That loop — overview, zoom, inspect, overview — is 90% of daily work in this tool.</p><h2 class="wp-block-heading">Step 5 — Try one transform</h2><p class="wp-block-paragraph">Transforms turn the series you logged into the series you actually wanted. Before writing anything custom, meet the built-ins: PlotJuggler offers ready-made transforms such as the <strong>derivative</strong>, <strong>moving average</strong>, <strong>integral</strong> and <strong>scale</strong> operations (PJ4 adds filters and FFT to the same toolbox). Apply a moving average to a noisy signal and plot the result next to the original: the derived series behaves like any other curve. When a built-in is not enough, the Custom Function Editor lets you define your own computation — that is the subject of the <a href="/plotjuggler-lua-custom-functions/">Lua custom functions tutorial</a>.</p><h2 class="wp-block-heading">Step 6 — Save the view as a layout</h2><p class="wp-block-paragraph">You have built something worth keeping: a chosen set of plots, curves and zoom. PlotJuggler saves that arrangement as a <strong>layout</strong> you can reload against the same data — or the same kind of data — later, instead of rebuilding it by hand. Save one now, close the plots, and reload it to prove the round trip works. Layouts deserve their own guide, and have one: <a href="/plotjuggler-layouts/">Saving and Sharing PlotJuggler Layouts</a> covers what is stored, how sharing works, and the PJ4 layout schema v4 note.</p><h2 class="wp-block-heading">If something did not work</h2><ul class="wp-block-list"><li><strong>The file loaded but no signals appeared.</strong> The file may need a loader or plugin your install lacks — ROS bags in PJ3, for example, come through the ROS plugin set; see the <a href="/plotjuggler-rosbag-mcap/">ROS bag and MCAP tutorial</a>.</li><li><strong>The CSV plotted against row numbers.</strong> Your file’s time column was not recognised as time — the fix is in the <a href="/plotjuggler-csv-import/">CSV import tutorial</a>.</li><li><strong>Signals start at different times.</strong> Normal for multi-device recordings; align them with the <a href="/plotjuggler-time-offset/">time offset tutorial</a> rather than fighting the timeline.</li></ul><h2 class="wp-block-heading">Where to go next</h2><p class="wp-block-paragraph">You now hold the core workflow: load, drag, navigate, transform, save. Deepen it in the order that matches your data — <a href="/plotjuggler-csv-import/">CSV import</a> or <a href="/plotjuggler-rosbag-mcap/">ROS bags and MCAP</a> for loading, <a href="/plotjuggler-xy-plot/">XY plots</a> for signal-against-signal views, and <a href="/plotjuggler-streaming-websocket-mqtt/">live streaming</a> when you are ready to watch data arrive in real time. The full path is mapped on the <a href="/tutorials/">Tutorials hub</a>.</p>]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>PlotJuggler Extensions Marketplace: Installing Plugins in PJ4</title>
		<link>https://plotjuggler.com/plotjuggler-extensions-marketplace/</link>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Thu, 22 Oct 2026 04:11:47 +0000</pubDate>
				<category><![CDATA[Tutorials]]></category>
		<guid isPermaLink="false">https://plotjuggler.com/plotjuggler-extensions-marketplace/</guid>

					<description><![CDATA[PlotJuggler has always been extensible — PJ3’s C++ plugins added data loaders, streamers, message parsers and toolboxes — but finding and installing those plugins was a manual, expert-only exercise. PlotJuggler 4 gives the ecosystem a front door: the Extensions Marketplace, built into the application (File → Marketplace), backed by a public plugin registry and a...]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">PlotJuggler has always been extensible — PJ3’s C++ plugins added data loaders, streamers, message parsers and toolboxes — but finding and installing those plugins was a manual, expert-only exercise. PlotJuggler 4 gives the ecosystem a front door: the <strong>Extensions Marketplace</strong>, built into the application (File → Marketplace), backed by a public plugin registry and a plugin SDK. This guide explains how the model works, using only what the project documents. Hub: <a href="/plotjuggler-4/">PlotJuggler 4</a>; downloads: <a href="/download/">Download page</a>.</p><h2 class="wp-block-heading">What the Marketplace is</h2><p class="wp-block-paragraph">The Extensions Marketplace is PJ4’s in-app channel for discovering, installing and updating plugins. It first appeared in the 3.999.3 preview (20 July 2026) and ships in 4.0.0. Behind the interface sits a registry: the project maintains a public repository, <code>pj-plugin-registry</code>, which is the catalogue the Marketplace draws from, alongside an official-plugins repository (<code>pj-official-plugins</code>). The architectural point matters more than the mechanics: PJ4 plugins are no longer things you hunt across the internet — they are published to a registry the application itself can read, which is what makes one-click-style installation and, just as importantly, <em>updates</em> possible.</p><h2 class="wp-block-heading">The four plugin types you will meet</h2><figure class="wp-block-table"><table><thead><tr><th>Plugin type</th><th>What it adds</th><th>PJ4 context</th></tr></thead><tbody><tr><td>Data loaders</td><td>Support for reading additional file formats and sources</td><td>PJ4 itself reads CSV, MCAP, Parquet, ULog, LeRobot, MP4, PCD and PLY — loaders extend that list</td></tr><tr><td>Data streamers</td><td>Connections to additional live data sources</td><td>Built-in streaming already covers ROS 2, MQTT, ZeroMQ, UDP, WebSocket and WebRTC</td></tr><tr><td>Message parsers</td><td>Understanding of additional message encodings</td><td>Built-in encodings include ROS 1/2, Protobuf, JSON, CBOR, BSON, Arrow and MessagePack; OMG IDL (DDS) parsing arrived in the previews</td></tr><tr><td>Toolboxes</td><td>Extra analysis tools and integrations</td><td>The Mosaico cloud-download integration, for example, is delivered as the ToolboxMosaico plugin</td></tr></tbody></table></figure><p class="wp-block-paragraph">These four categories are the same plugin families the project has used since PJ3 (which also had state publishers in its plugin set) — PJ4’s novelty is not the idea of plugins but their distribution.</p><h2 class="wp-block-heading">Installing a plugin, step by step</h2><ol class="wp-block-list"><li><strong>Open PlotJuggler 4</strong> — the Marketplace exists only in PJ4 (4.0.0 or the later 3.999.x previews); PJ3 has no equivalent menu.</li><li><strong>Choose File → Marketplace.</strong> This is the documented entry point.</li><li><strong>Browse or search the registry catalogue</strong> by what you need — a loader for a format, a streamer for a source, a parser for an encoding, or a toolbox.</li><li><strong>Install the plugin</strong> from its Marketplace entry, and use the same entry later to <strong>update</strong> it when a new version is published.</li><li><strong>Use it where its type belongs</strong>: an installed loader shows up in the file-open flow, a streamer among the live sources, a parser where encodings are chosen, a toolbox among the tools.</li></ol><p class="wp-block-paragraph">Because we only publish what the project documents, two cautions belong here. First, this guide deliberately names <strong>no third-party plugins and no prices</strong>: the registry is young and growing, and the Marketplace inside your copy of PJ4 is the authoritative, current catalogue — check names, authors and descriptions there before installing anything. Second, treat plugins with the normal care you would give any extension: prefer entries from the official registry and known authors, especially in work environments.</p><h2 class="wp-block-heading">Building your own: the plugin SDK</h2><p class="wp-block-paragraph">For authors, PJ4 ships a dedicated SDK: the <code>plotjuggler_sdk</code> repository, described by the project as the “SDK to create PlotJuggler 4 plugins”, and actively maintained alongside the official plugins. That continues a deliberate project tradition — PJ3’s MPL-2.0 licensing was explicitly noted as allowing closed-source plugins, and a sample-plugins repository existed for PJ3 authors — while giving PJ4 developers a proper, current toolkit and a registry to publish into. If you maintain a PJ3 plugin, the SDK plus the registry are where your PJ4 port begins; the <a href="/migrate-plotjuggler-3-to-4/">migration guide</a> covers the user side of that transition.</p><h2 class="wp-block-heading">Why this matters for PlotJuggler’s future</h2><p class="wp-block-paragraph">A marketplace changes what a small core team has to build. Niche file formats, vendor-specific streamers and specialised toolboxes can be contributed — and kept updated — by the people who need them, without waiting for a core release; users get discovery and updates in one place instead of forum attachments. It is one of the clearest signals that PJ4 is designed as a platform rather than a single-purpose app, alongside the multimodal views in <a href="/plotjuggler-4-video-3d/">Video and 3D Visualisation</a> and the dual-language scripting in <a href="/plotjuggler-4-python-scripting/">Python Scripting</a>. The full picture is in <a href="/whats-new-in-plotjuggler-4/">What’s New in PlotJuggler 4.0</a>.</p>]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
