<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>ReductStore Blog</title>
        <link>https://www.reduct.store/blog</link>
        <description>ReductStore Blog</description>
        <lastBuildDate>Mon, 22 Jun 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[How to Persist Zenoh Data]]></title>
            <link>https://www.reduct.store/blog/how-to-persist-zenoh-data</link>
            <guid>https://www.reduct.store/blog/how-to-persist-zenoh-data</guid>
            <pubDate>Mon, 22 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A tutorial for ROS 2 / Zenoh engineers: connect a Zenoh network to a persistent, queryable store for replay, debugging, and ML training without building a custom storage backend or an HTTP bridge.]]></description>
            <content:encoded><![CDATA[<div class="videoWrapper_VWB9"><div class="responsiveIframe__DxY"></div></div>
<p>&nbsp;</p>
<p>If you're running Zenoh, whether as <code>rmw_zenoh</code> in ROS 2 or as the transport between a robot, a gateway, and the cloud, you'll eventually run into the same question:</p>
<p><em>"Where does this data actually go? I need to replay yesterday's run, debug a fault from a remote robot, and pull a few thousand frames for training."</em></p>
<p>There's plenty of material on the communication layer itself: pub/sub primitives, how Zenoh compares to DDS, multi-node setups down to a Raspberry Pi. <code>rmw_zenoh</code> reached Tier-1 status starting with ROS 2 Kilted Kaiju, and it's getting real attention at events like ROSCon.</p>
<p>What's harder to find is the next layer: what happens to a sample <em>after</em> it's published. Connecting a live Zenoh network to something persistent and queryable usually means working around the limits of Zenoh's existing storage options.</p>
<p>This post shows another way. By the end you'll have a Python publisher sending data over Zenoh, a storage backend persisting it automatically, and a query client pulling exact time ranges back out, all without a custom backend or a bridge process.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-existing-storage-backends-leave-out">What the existing storage backends leave out<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#what-the-existing-storage-backends-leave-out" class="hash-link" aria-label="Direct link to What the existing storage backends leave out" title="Direct link to What the existing storage backends leave out" translate="no">​</a></h2>
<p>Zenoh ships official storage backends: memory, S3, InfluxDB, RocksDB, and filesystem via the <code>zenoh-plugin-storage-manager</code>. By default, queries return the latest stored value for each matching key. The InfluxDB backend goes further with a <code>_time</code> selector for historical queries:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">robot/alpha/**?_time=[now(-5d)..now()]</span><br></span></code></pre></div></div>
<p>That works for structured numeric telemetry. The problem is that InfluxDB stores binary payloads as base64-encoded strings, wildcard queries only work with v1.x, and sample attachments don't map to InfluxDB tags. The memory, RocksDB, filesystem, and S3 backends don't have a time-range query mechanism at all.</p>
<p>What was missing was a backend built for binary time-series data: one that stores every sample as-is, indexes it by time, and fits Zenoh's data model without a translation layer. That's what led to building a native Zenoh API in ReductStore.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-storage-backend-that-speaks-zenoh-natively">A storage backend that speaks Zenoh natively<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#a-storage-backend-that-speaks-zenoh-natively" class="hash-link" aria-label="Direct link to A storage backend that speaks Zenoh natively" title="Direct link to A storage backend that speaks Zenoh natively" translate="no">​</a></h2>
<p>Starting with version 1.19, <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a> (an open source time series object store built for robotics and edge data) ships a native Zenoh API. It keeps every sample, not just the latest, indexed by time, so the queries above just work. A ReductStore instance joins your Zenoh network as a regular peer: it registers a <strong>subscriber</strong> for writes and a <strong>queryable</strong> for reads, talking directly to the storage engine. No bridge process, no extra protocol, no custom backend to maintain.</p>
<svg xmlns="http://www.w3.org/2000/svg" width="520" height="448" class="svgbob"><style></style><defs><marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,0 0,4 4,2 0,0"></polygon></marker><marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,2 2,0 4,2 2,4 0,2"></polygon></marker><marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="filled"></circle></marker><marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="bg_filled"></circle></marker><marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="3" class="bg_filled"></circle></marker></defs><rect class="backdrop" x="0" y="0" width="520" height="448"></rect><rect x="44" y="8" width="208" height="48" class="solid nofill" rx="0"></rect><text x="98" y="28">robot-alpha</text><text x="82" y="44">robot/alpha/**</text><rect x="300" y="8" width="208" height="48" class="solid nofill" rx="0"></rect><text x="354" y="28">robot-beta</text><text x="338" y="44">robot/beta/**</text><rect x="68" y="264" width="240" height="48" class="solid nofill" rx="0"></rect><text x="146" y="284">ReductStore</text><text x="114" y="300">fleet (1 GB FIFO)</text><rect x="324" y="264" width="168" height="32" class="solid nofill" rx="0"></rect><text x="362" y="284">remote app</text><rect x="116" y="392" width="176" height="32" class="solid nofill" rx="0"></rect><text x="154" y="412">web console</text><line x1="132" y1="64" x2="132" y2="80" class="solid"></line><line x1="396" y1="64" x2="396" y2="80" class="solid"></line><line x1="132" y1="96" x2="132" y2="116" class="solid"></line><line x1="396" y1="96" x2="396" y2="116" class="solid"></line><polygon points="128,116 136,116 132,128" class="filled"></polygon><polygon points="392,116 400,116 396,128" class="filled"></polygon><polygon points="176,188 180,176 184,188" class="filled"></polygon><line x1="180" y1="188" x2="180" y2="208" class="solid"></line><line x1="180" y1="224" x2="180" y2="244" class="solid"></line><polygon points="176,244 184,244 180,256" class="filled"></polygon><line x1="188" y1="320" x2="188" y2="336" class="solid"></line><line x1="404" y1="224" x2="404" y2="244" class="solid"></line><polygon points="400,244 408,244 404,256" class="filled"></polygon><line x1="188" y1="352" x2="188" y2="372" class="solid"></line><polygon points="184,372 192,372 188,384" class="filled"></polygon><text x="122" y="92">pub</text><text x="386" y="92">pub</text><text x="226" y="156">zenoh-router</text><text x="138" y="220">sub/query</text><text x="290" y="220">get(...?start=…;stop=…)</text><text x="146" y="348">HTTP 8383</text><g><line x1="68" y1="136" x2="492" y2="136" class="solid"></line><line x1="68" y1="136" x2="68" y2="168" class="solid"></line><line x1="492" y1="136" x2="492" y2="168" class="solid"></line><line x1="68" y1="168" x2="492" y2="168" class="solid"></line><line x1="404" y1="168" x2="404" y2="208" class="solid"></line></g></svg>
<p>The mapping between Zenoh and ReductStore's data model is close to one-to-one:</p>
<table><thead><tr><th>Zenoh sample</th><th>ReductStore record</th></tr></thead><tbody><tr><td>Key</td><td>Entry name</td></tr><tr><td>Timestamp</td><td>Timestamp</td></tr><tr><td>Payload</td><td>Payload</td></tr><tr><td>Attachment</td><td>Labels</td></tr></tbody></table>
<p>A Zenoh key like <code>robot/alpha/camera</code> becomes an entry in a bucket. The attachment, Zenoh's mechanism for attaching metadata to a sample, becomes a set of labels you can later filter on. Everything else (HTTP API, SDKs, web console, retention policies, replication) works on that same data, regardless of whether it arrived via Zenoh or HTTP.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-it-up">Setting it up<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#setting-it-up" class="hash-link" aria-label="Direct link to Setting it up" title="Direct link to Setting it up" translate="no">​</a></h2>
<p>The full runnable example, two simulated robots, a Zenoh router, ReductStore, and a query client, is on GitHub at <a href="https://github.com/reductstore/zenoh-example" target="_blank" rel="noopener noreferrer" class=""><strong>reductstore/zenoh-example</strong></a>. Clone it and <code>docker compose up</code> if you want to follow along directly. Below is the minimal setup, piece by piece.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-run-reductstore-as-a-zenoh-peer">1. Run ReductStore as a Zenoh peer<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#1-run-reductstore-as-a-zenoh-peer" class="hash-link" aria-label="Direct link to 1. Run ReductStore as a Zenoh peer" title="Direct link to 1. Run ReductStore as a Zenoh peer" translate="no">​</a></h3>
<p>The entire integration is configuration, with no code changes to ReductStore itself. Add this service to your <code>docker-compose.yml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">zenoh-router</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> eclipse/zenoh</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"7447:7447"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reductstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reduct/store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest </span><span class="token comment" style="color:#999988;font-style:italic"># v1.19+ has a native Zenoh API</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_API_TOKEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"reductstore"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_0_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fleet"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_0_QUOTA_TYPE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"FIFO"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_0_QUOTA_SIZE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1 GB"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_ZENOH_ENABLED</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"true"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_ZENOH_CONFIG</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mode=client;connect/endpoints=[tcp/zenoh-router:7447]"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_ZENOH_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fleet"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_ZENOH_SUB_KEYEXPRS</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"robot/**"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_ZENOH_QUERY_KEYEXPRS</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"robot/**"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_ZENOH_QUERY_LOCALITY</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Any"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8383:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">depends_on</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">router</span><br></span></code></pre></div></div>
<p><code>RS_ZENOH_SUB_KEYEXPRS</code> and <code>RS_ZENOH_QUERY_KEYEXPRS</code> are key expressions, wildcard patterns like <code>robot/**</code>, that define which keys ReductStore subscribes to and answers queries for. The <code>fleet</code> bucket is created automatically on startup, and the quota settings make it a rolling store that keeps the most recent 1 GB of data.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-publish-from-your-ros-2--zenoh-node">2. Publish from your ROS 2 / Zenoh node<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#2-publish-from-your-ros-2--zenoh-node" class="hash-link" aria-label="Direct link to 2. Publish from your ROS 2 / Zenoh node" title="Direct link to 2. Publish from your ROS 2 / Zenoh node" translate="no">​</a></h3>
<p>Any node publishing on a matching key gets persisted automatically. No SDK calls, no extra client, just publish the way you already do, with timestamps and an optional attachment for metadata:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> zenoh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ROBOT_ID </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"alpha"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"robot/</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">ROBOT_ID</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">/telemetry"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pub </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">declare_publisher</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">KEY</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    telemetry </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"battery"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">87</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ok"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"speed"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.2</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    labels </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"robot"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> ROBOT_ID</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> telemetry</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"status"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    pub</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">telemetry</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        encoding</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Encoding</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">APPLICATION_JSON</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        attachment</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">labels</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>The JSON attachment becomes record labels in ReductStore. ReductStore also adds two labels automatically to every record: <code>zenoh_source_id</code> (which node published it, useful for tracing data provenance across a fleet) and <code>zenoh_ts_ntp64</code> (the original Zenoh HLC timestamp at full precision).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-query-a-time-range-back-out-over-zenoh">3. Query a time range back out over Zenoh<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#3-query-a-time-range-back-out-over-zenoh" class="hash-link" aria-label="Direct link to 3. Query a time range back out over Zenoh" title="Direct link to 3. Query a time range back out over Zenoh" translate="no">​</a></h3>
<p>This is the part that's usually missing: querying isn't bolted on as a separate HTTP step. It's the same Zenoh session, using <code>get()</code> with a selector that carries a time range:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> zenoh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"robot/alpha/telemetry"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">start_us </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1_700_000_000_000_000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">stop_us </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1_700_000_600_000_000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    replies </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">KEY</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">?start=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">start_us</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">;stop=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">stop_us</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        consolidation</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ConsolidationMode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">NONE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> reply </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> replies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">payload</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to_bytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p><code>ConsolidationMode.NONE</code> matters here: without it, Zenoh may consolidate replies down to a single sample per key. For a time-series query you want every record in the range, so consolidation has to be disabled.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-filter-by-label-while-youre-at-it">4. Filter by label while you're at it<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#4-filter-by-label-while-youre-at-it" class="hash-link" aria-label="Direct link to 4. Filter by label while you're at it" title="Direct link to 4. Filter by label while you're at it" translate="no">​</a></h3>
<p>Because the JSON attachment becomes labels, you can filter server-side using the same conditional query language ReductStore's HTTP API uses, with no client-side filtering required:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> zenoh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attachment </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"when"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"&amp;status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$eq"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"warn"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    replies </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string-interpolation string" style="color:#e3116c">f"robot/alpha/camera?start=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">start_us</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">;stop=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">stop_us</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        attachment</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">attachment</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        consolidation</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ConsolidationMode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">NONE</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> reply </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> replies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">payload</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to_bytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>"Every camera frame from the last ten minutes where the robot was in a warn state" is one <code>get()</code> call. The <code>when</code> syntax supports comparison, logical, arithmetic, and aggregation operators. See the <a class="" href="https://www.reduct.store/docs/conditional-query"><strong>Conditional Query Reference</strong></a> for the full list.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-replay-and-inspect-with-the-python-sdk">5. Replay and inspect with the Python SDK<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#5-replay-and-inspect-with-the-python-sdk" class="hash-link" aria-label="Direct link to 5. Replay and inspect with the Python SDK" title="Direct link to 5. Replay and inspect with the Python SDK" translate="no">​</a></h3>
<p>For replay, debugging, or building a training set, you don't have to stay on the Zenoh API. The same data is available over HTTP via the <code>reduct-py</code> SDK (or the JS, Go, Rust, and C++ SDKs):</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> BucketSettings</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> QuotaType</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"fleet"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> record </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"robot/alpha/camera"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            start</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"2026-06-15T00:00:00Z"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            stop</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"2026-06-15T01:00:00Z"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            when</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"&amp;status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$eq"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"warn"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            frame </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic"># feed `frame` into your replay tool, notebook, or training pipeline</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Live ingestion via Zenoh and historical queries via HTTP (or Zenoh) both read from the same bucket, so there's no separate export or sync step.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-same-query-works-from-anywhere-on-the-network">The same query works from anywhere on the network<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#the-same-query-works-from-anywhere-on-the-network" class="hash-link" aria-label="Direct link to The same query works from anywhere on the network" title="Direct link to The same query works from anywhere on the network" translate="no">​</a></h2>
<p>This is Zenoh's location transparency at work: it routes a query to whoever answers for a key, so the same <code>get()</code> call works whether ReductStore is running next to the robot, on a gateway, or in the cloud:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># on the gateway</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python query_zenoh.py </span><span class="token parameter variable" style="color:#36acaa">--robot</span><span class="token plain"> alpha </span><span class="token parameter variable" style="color:#36acaa">--last</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># from a laptop on a different network</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python query_zenoh.py </span><span class="token parameter variable" style="color:#36acaa">--robot</span><span class="token plain"> alpha </span><span class="token parameter variable" style="color:#36acaa">--last</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--endpoint</span><span class="token plain"> tcp/gateway.example.com:7447</span><br></span></code></pre></div></div>
<p><code>RS_ZENOH_QUERY_LOCALITY</code> controls whether a given ReductStore instance answers local queries, remote queries, or both, useful if you want an edge instance to serve replay requests only from the robot itself, while a cloud instance serves everyone else.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="retention-and-replication-for-free">Retention and replication, for free<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#retention-and-replication-for-free" class="hash-link" aria-label="Direct link to Retention and replication, for free" title="Direct link to Retention and replication, for free" translate="no">​</a></h2>
<p>Once data is flowing in, two problems show up fast: disks fill up, and you want a long-term copy of <em>some</em> of it in the cloud.</p>
<p>For retention, ReductStore buckets support a size-based FIFO quota: set a limit, and the oldest blocks are dropped to make room:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_0_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fleet"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_0_QUOTA_TYPE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"FIFO"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_0_QUOTA_SIZE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1 GB"</span><br></span></code></pre></div></div>
<p>This is more robust at the edge than a time-based "keep the last 24 hours" policy: if a robot goes offline for a few days, a time-based rule would silently wipe everything by the time it reconnects. A size-based policy just keeps the most recent 1 GB, however long that takes to fill.</p>
<p>For replication, you can forward a filtered subset to a cloud instance using the same conditional query syntax, for example, only frames and telemetry around a "warn" event:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"warn_to_cloud"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_SRC_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fleet"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_DST_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"fleet"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_DST_HOST</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://reduct.example-cloud.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_DST_TOKEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"${CLOUD_TOKEN}"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_ENTRIES</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"robot/*/camera,robot/*/telemetry"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_WHEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{"&amp;status": {"$eq": "warn"}}'</span><br></span></code></pre></div></div>
<p>The label you attach at ingest, <code>status: warn</code>, is the same label that drives the read query, the retention policy, and the replication rule. One concept, three jobs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="try-it-yourself">Try it yourself<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#try-it-yourself" class="hash-link" aria-label="Direct link to Try it yourself" title="Direct link to Try it yourself" translate="no">​</a></h2>
<p>The runnable version of everything above (two simulated robots, a router, ReductStore, and a query client) is on GitHub:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/reductstore/zenoh-example</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> zenoh-example</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> compose up </span><span class="token parameter variable" style="color:#36acaa">--build</span><br></span></code></pre></div></div>
<p>Once it's running, open the ReductStore web console at <code>http://localhost:8383</code> (API token: <code>reductstore</code>) to browse the <code>fleet</code> bucket, inspect labels, and preview frames directly in the browser. From a separate terminal, query over Zenoh:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">cd</span><span class="token plain"> query </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-r</span><span class="token plain"> requirements.txt</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># latest 60 seconds of telemetry for robot alpha</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python query_zenoh.py </span><span class="token parameter variable" style="color:#36acaa">--robot</span><span class="token plain"> alpha </span><span class="token parameter variable" style="color:#36acaa">--last</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># last 10 minutes, only records where status was "warn"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python query_zenoh.py </span><span class="token parameter variable" style="color:#36acaa">--robot</span><span class="token plain"> alpha </span><span class="token parameter variable" style="color:#36acaa">--last</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">600</span><span class="token plain"> --only-warn</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next steps<a href="https://www.reduct.store/blog/how-to-persist-zenoh-data#next-steps" class="hash-link" aria-label="Direct link to Next steps" title="Direct link to Next steps" translate="no">​</a></h2>
<p>If you're running <code>rmw_zenoh</code> in production or evaluating it for a fleet, the gap between "messages are flowing" and "I can replay, debug, and train on this data" is smaller than it looks. It's a Docker Compose service and a handful of environment variables, not a new component to build and maintain.</p>
<p>For the full configuration reference, including all <code>RS_ZENOH_*</code> settings, key expression syntax, and current limitations (single-bucket mode, no deletion via the Zenoh API), see the <a class="" href="https://www.reduct.store/docs/integrations/zenoh"><strong>ReductStore Zenoh integration docs</strong></a>. For label-based filtering across queries, retention, and replication, see the <a class="" href="https://www.reduct.store/docs/conditional-query"><strong>Conditional Query Reference</strong></a> and the <a class="" href="https://www.reduct.store/docs/guides/data-replication"><strong>Data Replication guide</strong></a>.</p>
<p>If you have any questions or comments, feel free to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>robotics</category>
            <category>zenoh</category>
            <category>ros2</category>
        </item>
        <item>
            <title><![CDATA[ReductStore v1.20: Lifecycle Policies, Protobuf, SQL, and Parquet]]></title>
            <link>https://www.reduct.store/blog/news/reductstore-v1_20_0-released</link>
            <guid>https://www.reduct.store/blog/news/reductstore-v1_20_0-released</guid>
            <pubDate>Thu, 18 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[ReductStore v1.20 introduces lifecycle policies for automated data deletion and compression, Protobuf support across the ecosystem, and SQL/Parquet support in the ReductSelect extension.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="ReductStore v1.20.0 Released" src="https://www.reduct.store/assets/images/release-v1-20-0fb2adef5d6c2c19c8edcbc36d369209.png" width="800" height="448" class="img_ev3q"></p>
<p>ReductStore <a href="https://github.com/reductstore/reductstore/releases/tag/v1.20.0" target="_blank" rel="noopener noreferrer" class=""><strong>1.20.0</strong></a> is now available. This release adds lifecycle policies for automated retention and compression, expands Protobuf support across the ingestion and query ecosystem, and brings SQL and Parquet workflows to the ReductSelect extension.</p>
<p>To download the latest release, visit the <a class="" href="https://www.reduct.store/download"><strong>Download Page</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-new-in-1200">What's new in 1.20.0?<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#whats-new-in-1200" class="hash-link" aria-label="Direct link to What's new in 1.20.0?" title="Direct link to What's new in 1.20.0?" translate="no">​</a></h2>
<p>The first major change in v1.20 is <a class="" href="https://www.reduct.store/docs/guides/lifecycle-policies"><strong>lifecycle policies</strong></a>. ReductStore can now run background tasks that delete or compress old records based on age, entry names, and optional label conditions. This makes retention and storage-efficiency rules part of the database configuration instead of an external cleanup script.</p>
<p>The second major change is Protobuf support across the ecosystem. <a class="" href="https://www.reduct.store/docs/reduct-bridge"><strong>ReductBridge</strong></a> can ingest Protobuf payloads from MQTT, extract labels from message fields, and store schema metadata as a <code>$schema</code> entry attachment. <a class="" href="https://www.reduct.store/docs/extensions/official/select-ext"><strong>ReductSelect</strong></a> can then use the same attachment to decode records and run SQL over the decoded fields.</p>
<p>The release also extends ReductSelect with SQL processing for structured records and Parquet support for analytics-oriented pipelines. You can query CSV, JSON, Protobuf, and Parquet records with the same <code>ENTRY()</code> table function and export selected results as Parquet files.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lifecycle-policies">Lifecycle Policies<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#lifecycle-policies" class="hash-link" aria-label="Direct link to Lifecycle Policies" title="Direct link to Lifecycle Policies" translate="no">​</a></h3>
<p>Lifecycle policies are background tasks that automatically manage old records in a bucket. A policy can delete records or compress persisted blocks with zstd, and it can be scoped by entry names, record age, and, for delete policies, conditional expressions.</p>
<p>This is useful when data has different operational and historical value over time. For example, recent telemetry can stay uncompressed for frequent access, older telemetry can be compressed to reduce storage usage, and very old data can be deleted later by a separate policy.</p>
<p>Policies support three modes: <code>enabled</code>, <code>dry_run</code>, and <code>disabled</code>. Every run writes diagnostics to the <code>$system/lifecycle</code> system entry, so operators can inspect policy behavior before and after enabling automatic actions.</p>
<p>Here is a provisioning example for deleting temperature records older than 30 days from selected sensor entries:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reductstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reduct/store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">v1.20.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8383:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_API_TOKEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> my</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">api</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> telemetry</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> purge</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">sensors</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">30d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> telemetry</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_TYPE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> delete</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_OLDER_THAN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 30d</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_INTERVAL</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 10m</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_ENTRIES</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"sensor-1,sensor-2"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_LIFECYCLE_1_WHEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        {</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          "&amp;sensor_type": { "$eq": "temperature" }</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        }</span><br></span></code></pre></div></div>
<p>For the full configuration reference and SDK examples, see the <a class="" href="https://www.reduct.store/docs/guides/lifecycle-policies"><strong>Lifecycle Policies</strong></a> guide.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="protobuf-support-in-the-ecosystem">Protobuf Support in the Ecosystem<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#protobuf-support-in-the-ecosystem" class="hash-link" aria-label="Direct link to Protobuf Support in the Ecosystem" title="Direct link to Protobuf Support in the Ecosystem" translate="no">​</a></h3>
<p>v1.20 connects Protobuf ingestion, storage, and querying through a shared schema-attachment convention. This makes binary telemetry easier to keep compact at ingest time while preserving the metadata required for downstream processing.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductselect-extension">ReductSelect Extension<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#reductselect-extension" class="hash-link" aria-label="Direct link to ReductSelect Extension" title="Direct link to ReductSelect Extension" translate="no">​</a></h4>
<p>ReductSelect processes Protobuf records with content types such as <code>application/protobuf</code>, <code>application/x-protobuf</code>, and <code>application/vnd.google.protobuf</code>. By default, it reads the message descriptor from the entry's <code>$schema</code> attachment and exposes decoded fields to SQL.</p>
<p>The attachment stores the payload encoding, message type, and base64-encoded <code>FileDescriptorSet</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"encoding"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"protobuf"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"schema_name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pkg.SensorReading"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"schema"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;base64-encoded FileDescriptorSet&gt;"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>With the schema attached to the entry, a ReductSelect query can stay small:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"protobuf"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"sql"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SELECT device_id, temperature FROM ENTRY() WHERE temperature &gt; 20"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>For lightweight cases, ReductSelect can also extract top-level scalar fields directly by Protobuf field number without a full descriptor. See the <a class="" href="https://www.reduct.store/docs/extensions/official/select-ext"><strong>ReductSelect Protobuf documentation</strong></a> for both modes.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductbridge">ReductBridge<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#reductbridge" class="hash-link" aria-label="Direct link to ReductBridge" title="Direct link to ReductBridge" translate="no">​</a></h4>
<p>ReductBridge's MQTT input can now ingest Protobuf payloads and extract labels from message fields. In schema mode, the bridge stores the descriptor as a <code>$schema</code> attachment so the same data can later be decoded by ReductSelect or exported by ReductROS.</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">inputs.mqtt.main.topics</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory/electrical/+/power"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">entry_name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"power"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">content_type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"application/protobuf"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">schema_path</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./factory.desc"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">schema_name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory.PowerReading"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">field</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"device_id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"meter_id"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">field</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"panel"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"panel"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>If a pipeline only needs a few top-level scalar fields, ReductBridge can also extract labels from raw wire fields without a schema. For details, see the <a class="" href="https://www.reduct.store/docs/reduct-bridge/input/mqtt"><strong>MQTT input documentation</strong></a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sql-and-parquet-support-in-reductselect">SQL and Parquet Support in ReductSelect<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#sql-and-parquet-support-in-reductselect" class="hash-link" aria-label="Direct link to SQL and Parquet Support in ReductSelect" title="Direct link to SQL and Parquet Support in ReductSelect" translate="no">​</a></h3>
<p>ReductSelect is now better suited for structured data processing directly inside ReductStore queries. The extension exposes each queried record through the <code>ENTRY()</code> table function, allowing SQL selection, filtering, aggregation, and computed output columns over supported structured payloads.</p>
<p>Parquet records are processed when the record content type is <code>application/vnd.apache.parquet</code> or when the query explicitly enables Parquet parsing. The same extension can export query results as Parquet, with batching controlled by row count or duration.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"parquet"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"sql"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"SELECT name, value FROM ENTRY() WHERE value &gt; 10"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"export"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"format"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"parquet"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"rows"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>SQL result columns can also be written back as computed labels, which is useful when a pipeline extracts metadata from payloads and wants later queries or replication rules to use it as regular ReductStore labels.</p>
<p>For query parameters and examples, see the <a class="" href="https://www.reduct.store/docs/extensions/official/select-ext"><strong>ReductSelect extension</strong></a> documentation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What's Next<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#whats-next" class="hash-link" aria-label="Direct link to What's Next" title="Direct link to What's Next" translate="no">​</a></h2>
<p>The next area of work is making ReductStore's integration ecosystem easier to operate in production. We plan to keep improving ReductBridge pipelines, schema-aware tooling, and examples that connect MQTT, ROS, Zenoh, and analytics workflows around a shared storage layer.</p>
<p>We are also continuing to improve the extension model so data can be transformed, queried, and exported closer to where it is stored, without forcing every workflow to move raw payloads into separate processing systems first.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="compatibility-and-migration">Compatibility and Migration<a href="https://www.reduct.store/blog/news/reductstore-v1_20_0-released#compatibility-and-migration" class="hash-link" aria-label="Direct link to Compatibility and Migration" title="Direct link to Compatibility and Migration" translate="no">​</a></h2>
<p>This release is expected to be compatible with existing v1.19 deployments. Lifecycle policies are opt-in, and ReductSelect and ReductBridge additions only affect deployments that enable those workflows.</p>
<p>The <code>$audit</code> bucket is deprecated in v1.20. Audit events are now written to the <code>$system/audit</code> entry, together with other system events. Persistence of system events is enabled by default and can be disabled with <code>RS_SYSTEM_EVENTS_ENABLED=false</code>; quota and replication-related options are documented in the <a class="" href="https://www.reduct.store/docs/configuration/settings#system-event-settings"><strong>System Event Settings</strong></a> section.</p>
<hr>
<p>If you have questions or feedback, join the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>
<p>Thanks for using <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>!</p>]]></content:encoded>
            <category>news</category>
        </item>
        <item>
            <title><![CDATA[AMR fleet data infrastructure at scale: Four Problems and How to Solve Them]]></title>
            <link>https://www.reduct.store/blog/amr-fleet-data-infrastructure</link>
            <guid>https://www.reduct.store/blog/amr-fleet-data-infrastructure</guid>
            <pubDate>Mon, 15 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[You deployed ten robots. The system worked. Now you have a hundred, and your engineering team is complaining about things that didn't exist six months ago: storage filling up on edge devices, cloud bills growing faster than the fleet, retraining cycles that take weeks instead of days, and incident investigations lack the data they need. This is a predictable consequence of crossing a scaling threshold that almost every AMR operator hits.]]></description>
            <content:encoded><![CDATA[<p>You deployed ten robots. The system worked. Now you have a hundred, and your engineering team is complaining about things that didn't exist six months ago: storage filling up on edge devices, cloud bills growing faster than the fleet, retraining cycles that take weeks instead of days, and incident investigations lack the data they need.</p>
<p>This is a predictable consequence of crossing a scaling threshold that almost every AMR operator hits.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-inflection-point">The Inflection Point<a href="https://www.reduct.store/blog/amr-fleet-data-infrastructure#the-inflection-point" class="hash-link" aria-label="Direct link to The Inflection Point" title="Direct link to The Inflection Point" translate="no">​</a></h2>
<p>A single AMR collecting camera frames, LiDAR scans, and control logs generates somewhere between 30 and 100 GB per shift, depending on sensor configuration and logging frequency. At ten robots, that's manageable. A custom script, a shared drive, maybe an S3 bucket — it works well enough that nobody questions it.</p>
<p>At one hundred robots, you're collecting 3–10 TB per shift. The custom script doesn't scale. The shared drive is full. The S3 bucket costs more than expected. And none of these tools were designed for the actual shape of the data: binary sensor payloads that are too large for a time-series database and too time-sensitive for a generic object store.</p>
<p>The failures start slowly and accumulate over time.</p>
<p>Some teams sidestep this entirely by processing at the edge by extracting metrics, downsampling, discarding the raw frames before they ever leave the robot. That keeps storage small and transfer costs low. The tradeoff is irreversibility: once the raw record is gone, it is gone. When a model needs retraining on a different feature representation, when an incident investigation requires the original sensor data, or when a regulator asks for the record that predates the processed output, there is nothing to retrieve.</p>
<p>The infrastructure problem described below is the cost of doing something valuable: keeping the raw data that makes your fleet improvable over time.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="four-problems-that-grow-with-the-fleet">Four Problems That Grow With the Fleet<a href="https://www.reduct.store/blog/amr-fleet-data-infrastructure#four-problems-that-grow-with-the-fleet" class="hash-link" aria-label="Direct link to Four Problems That Grow With the Fleet" title="Direct link to Four Problems That Grow With the Fleet" translate="no">​</a></h2>
<p><strong>Storage overflow at the edge.</strong> Most AMR deployments use some form of local storage on the robot itself to buffer data before it transfers to a central server or cloud. When that buffer fills and there is no intelligent retention policy, robots start dropping records. The newest data overwrites the oldest without any selection logic. By the time an incident occurs and someone asks for the sensor data from the previous three shifts, it is gone.</p>
<p><strong>Cloud transfer cost explosion.</strong> Object storage is cheap per GB. What is not cheap is the API call volume that high-frequency sensor data generates. A fleet writing thousands of small records per second to S3 can generate millions of PUT requests per day. At standard pricing, that cost compounds fast and it scales with fleet size in a way that storage cost alone does not.</p>
<p><strong>Model drift without a feedback loop.</strong> Retraining a perception model requires labelled examples of edge cases: near-misses, unusual lighting, unexpected obstacles, novel environments. Those examples only exist in operational sensor data. If the data pipeline doesn't preserve the right records — specifically, the records around anomalous events — retraining becomes either impossible or biased toward common cases. The model drifts. Failure rates climb slowly enough that the connection to data quality is rarely made until much later.</p>
<p><strong>Audit gaps when incidents happen.</strong> Regulatory and insurance requirements for autonomous systems are tightening, particularly in the EU under the Cyber Resilience Act and related frameworks. An incident investigation requires a continuous, tamper-evident record: what the robot saw, what it decided, what happened next. If records were dropped because local storage filled, or if the audit trail has gaps because replication wasn't reliable, that investigation stalls — or produces a conclusion that is hard to defend.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-infrastructure-actually-needs-to-do">What the Infrastructure Actually Needs to Do<a href="https://www.reduct.store/blog/amr-fleet-data-infrastructure#what-the-infrastructure-actually-needs-to-do" class="hash-link" aria-label="Direct link to What the Infrastructure Actually Needs to Do" title="Direct link to What the Infrastructure Actually Needs to Do" translate="no">​</a></h2>
<p>A requirements framework for AMR data infrastructure has three layers, and most off-the-shelf tools cover at most one of them.</p>
<p><strong>Edge durability with intelligent retention.</strong> The robot's local storage needs a FIFO-by-volume quota: when the disk fills, the oldest records roll off automatically, keeping the system stable regardless of how long a mission runs or how long connectivity is interrupted. Critically, high-priority records — fault windows, flagged events, anything tagged for retraining — need to be preserved ahead of routine telemetry. This is not what generic object stores do.</p>
<p><strong>Selective replication, not full sync.</strong> Sending everything to the cloud is how you generate the cost explosion described above. The alternative is conditional replication: a filter that runs on labels attached to records at ingest time, and sends only the subset that matches. Fault events go through. Routine navigation frames from uneventful shifts stay on the edge until the FIFO rolls them off. The bandwidth bill reflects the operational value of the data, not its raw volume.</p>
<p><strong>Time-indexed auditability across tiers.</strong> Every record needs a precise timestamp and an immutable chain from the sensor to wherever it ends up. When an incident investigation asks "what did robot 47 see at 14:23:07 on Thursday," the answer should be retrievable in seconds — not reconstructed from fragmented logs across three storage systems.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-self-assessment-for-operations-teams">A Self-Assessment for Operations Teams<a href="https://www.reduct.store/blog/amr-fleet-data-infrastructure#a-self-assessment-for-operations-teams" class="hash-link" aria-label="Direct link to A Self-Assessment for Operations Teams" title="Direct link to A Self-Assessment for Operations Teams" translate="no">​</a></h2>
<p>Before evaluating vendors, run these checks against your current setup:</p>
<ul>
<li class="">If a robot loses connectivity for 8 hours, does it continue logging without dropping records? What happens when it reconnects?</li>
<li class="">Can you retrieve the sensor data from a specific 30-second window on a specific robot from 6 weeks ago?</li>
<li class="">Do you know which records from last month's fleet operation are currently stored in the cloud, and why those ones specifically?</li>
<li class="">If a model retraining job requires 500 labelled fault examples from the past 90 days, how long does it take to locate and export them?</li>
<li class="">In the event of a fleet incident, what is your maximum gap in the sensor record?</li>
</ul>
<p>If any of these questions produces a slow or uncertain answer, the infrastructure has a gap. The question is whether that gap is large enough to matter at your current fleet size and whether it will still be acceptable when the fleet doubles again.</p>
<p>If you want to walk through these questions against your current setup, we offer a <a class="" href="https://www.reduct.store/architecture-review"><strong>free architecture review</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-good-looks-like">What Good Looks Like<a href="https://www.reduct.store/blog/amr-fleet-data-infrastructure#what-good-looks-like" class="hash-link" aria-label="Direct link to What Good Looks Like" title="Direct link to What Good Looks Like" translate="no">​</a></h2>
<p>A well-designed AMR data stack has the same shape at 10 robots as at 1,000, because the architecture scales rather than requiring a rebuild at each inflection point.</p>
<p>On each robot: a lightweight storage engine with FIFO-by-volume quotas, label-based filtering at ingest, and continuous local buffering. When the robot is connected, it replicates only the records that match a pre-configured conditional query — fault windows, flagged events, training candidates. When it's not connected, it keeps logging.</p>
<p>At the fleet server tier: aggregated data from all robots, with the same query interface. Engineers can pull a specific time window from a specific robot without knowing which physical server holds it.</p>
<p>In the cloud: a cost-efficient long-term store using batched writes to object storage, with lifecycle-aware tiering that moves infrequently accessed data to cold storage automatically. The same query interface applies.</p>
<p>Tools like ReductStore are built specifically for this architecture: binary payloads stored with microsecond-precision timestamps and labels, replicated conditionally across tiers, queryable by time window and label combination from any tier. The same model from edge to cloud, with no translation layer between them.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-board-framing">The Board Framing<a href="https://www.reduct.store/blog/amr-fleet-data-infrastructure#the-board-framing" class="hash-link" aria-label="Direct link to The Board Framing" title="Direct link to The Board Framing" translate="no">​</a></h2>
<p>If you need to explain this problem upward, the frame is straightforward: the data your fleet generates is the feedstock for every improvement you will make to it — model updates, route optimization, incident response, regulatory compliance. Infrastructure that drops records, transfers data indiscriminately, or lacks a continuous audit trail is eroding the value of the fleet as it scales, silently and in advance of any visible failure.</p>
<p>The cost of fixing it before the fleet grows is a fraction of the cost of rebuilding the pipeline after it breaks. And rebuilding under pressure, after an incident, is when mistakes get made.</p>]]></content:encoded>
            <category>autonomous-robots</category>
            <category>data-infrastructure</category>
            <category>fleet-management</category>
        </item>
        <item>
            <title><![CDATA[How to Store MQTT Camera Frames and Binary Sensor Data with a Time Index]]></title>
            <link>https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index</link>
            <guid>https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index</guid>
            <pubDate>Wed, 10 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A tutorial on how to store binary MQTT payloads — camera frames, waveforms, audio clips — with a proper time index using ReductBridge and ReductStore, enabling time-range queries and replay.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Storing MQTT data in ReductStore&amp;quot;" src="https://www.reduct.store/assets/images/featured-picture-1ed1f3882ddcbe175a174f747b7945cd.png" width="1255" height="758" class="img_ev3q"></p>
<p>MQTT is a common choice for the communication stack in IoT and robotics applications because it is lightweight and easy to integrate.
But many of those applications do not send only small JSON telemetry messages.
They also publish JPEG frames, vibration waveforms, audio clips, protobuf messages, and other binary payloads that need to be stored and queried later.</p>
<p>This is where a regular MQTT broker or a traditional time-series database starts to fall short.
Brokers are designed for message delivery, not long-term historical storage, and many databases either expect structured numeric fields or make it hard to keep large binary records tied to accurate timestamps.</p>
<p>In this tutorial, we will use <a href="https://github.com/reductstore/reduct-bridge" target="_blank" rel="noopener noreferrer" class=""><strong>ReductBridge</strong></a> to subscribe to MQTT topics and write the raw binary payloads into <a href="https://github.com/reductstore/reductstore" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore</strong></a> with a time index.
This lets you keep camera frames and sensor payloads as they are, while still querying them by time range, labels, and entry name for replay, debugging, and offline analysis.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-existing-stacks-fail-for-binary-mqtt-payloads">Why Existing Stacks Fail for Binary MQTT Payloads<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#why-existing-stacks-fail-for-binary-mqtt-payloads" class="hash-link" aria-label="Direct link to Why Existing Stacks Fail for Binary MQTT Payloads" title="Direct link to Why Existing Stacks Fail for Binary MQTT Payloads" translate="no">​</a></h2>
<p>Most MQTT storage solutions work well for small structured telemetry: temperature, pressure, status values, or JSON events.
They start to break down when the same system needs to store camera frames, audio chunks, vibration buffers, protobuf messages, or other binary payloads.
These records are larger, often opaque to the storage layer, and still need to be queried later by time, device, topic, and process metadata.</p>
<p><strong>Traditional SQL databases</strong> can store binary objects, but they are not a good default for this workload.
Large blobs quickly make tables heavy, backups slow, and queries expensive.
They also do not give you a natural way to keep an append-only history of records by topic and timestamp.
You can build this model yourself, but then you need to manage schema, indexes, retention, and cleanup logic on top of the database.</p>
<p><strong>Time-series databases</strong> are closer to the problem, but they are usually designed for metrics.
They work best when each record is a small set of numeric fields with tags.
Binary MQTT payloads do not fit that shape.
If you store only a file path in the time-series database and put the binary data somewhere else, you now have two systems that must stay in sync.</p>
<p><strong>Object storage and local files</strong> solve the blob part, but not the history part.
They are good at storing large objects, but they do not provide a proper time index for range queries.
Putting timestamps into file names or object keys works for a demo, but it becomes fragile when you need to ask practical questions such as: give me all frames from this camera between two timestamps, only for this production line, and only when this label was present.</p>
<p><strong>Disk usage</strong> is another problem.
Binary data can fill a disk very quickly, and the rate is not always predictable.
A camera may switch to a higher resolution, a sensor may start publishing faster, or a fault may produce more events than usual.
Time-series databases usually have retention based on time, but that is not always enough when the data volume can spike.
That can lead to out-of-disk errors, data loss, and downtime.</p>
<p>FIFO (first-in-first-out) quota is a better fit for this workload because it removes the oldest data when the disk budget is reached
and guarantees that the newest data is always accepted and stored.
This way, you can keep a rolling history of the most recent data without worrying about disk management</p>
<p><strong>Data replication</strong> also needs a different model.
For MQTT binary data at the edge, copying the whole database or mirroring all files is usually too expensive.
Most teams need to stream only new records that match metadata: a site, a device, a topic, a camera, an event type, or an error label.
That way, important data can be moved to another node or to the cloud, while raw high-volume data can stay close to the device.</p>
<p>So the storage layer needs to handle three things together: raw binary payloads, a time index, and metadata for filtering.
Without all three, the system becomes a collection of workarounds instead of a queryable history of MQTT data.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-timestamp-problem-mqtt-does-not-timestamp-messages">The Timestamp Problem: MQTT Does Not Timestamp Messages<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#the-timestamp-problem-mqtt-does-not-timestamp-messages" class="hash-link" aria-label="Direct link to The Timestamp Problem: MQTT Does Not Timestamp Messages" title="Direct link to The Timestamp Problem: MQTT Does Not Timestamp Messages" translate="no">​</a></h2>
<p>MQTT is good at moving messages between devices and services, but it does not define when the data inside a message was created.
A broker may know when it received a publish packet, and a subscriber may know when it processed the message.
For historical storage, however, you usually need a different timestamp: when the camera captured the frame, when the waveform was sampled, or when the sensor event happened.</p>
<p>If you store MQTT data by arrival time, the history can look correct but still be shifted from reality.
Network delay, broker queues, reconnects, QoS retries, batching, and slow consumers can all move the stored timestamp away from the capture time.
This may be acceptable for simple monitoring, but it becomes a problem when you need to replay data or compare several streams.</p>
<p>For example, you may need to match a camera frame with a PLC state, a vibration buffer with a machine alarm, or an audio clip with a robot event.
If each stream uses a different idea of time, the correlation becomes unreliable.</p>
<p>For binary MQTT payloads, the timestamp should usually come from the producer and stay outside the payload.
You should not need to decode a JPEG frame, waveform buffer, or protobuf message only to find its time.
It is better to pass the timestamp as metadata, for example in an MQTT user property, a topic convention, or another field that can be mapped to the stored record.</p>
<p>When the producer cannot provide a timestamp, using ingestion time is still useful.
But it should be treated as the time when the bridge received the message, not necessarily the time when the data was captured.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="architecture-overview">Architecture Overview<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#architecture-overview" class="hash-link" aria-label="Direct link to Architecture Overview" title="Direct link to Architecture Overview" translate="no">​</a></h2>
<p>In this tutorial, we use MQTT as the transport layer and ReductStore as the historical storage layer.
ReductBridge connects them together and converts MQTT messages into time-indexed records.</p>
<p>The data flow looks like this:</p>
<svg xmlns="http://www.w3.org/2000/svg" width="576" height="480" class="svgbob"><style></style><defs><marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,0 0,4 4,2 0,0"></polygon></marker><marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,2 2,0 4,2 2,4 0,2"></polygon></marker><marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="filled"></circle></marker><marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="bg_filled"></circle></marker><marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="3" class="bg_filled"></circle></marker></defs><rect class="backdrop" x="0" y="0" width="576" height="480"></rect><text x="410" y="28">ReductStore</text><text x="362" y="60">*</text><text x="378" y="60">FIFO</text><text x="418" y="60">quota</text><text x="362" y="76">*</text><text x="378" y="76">queries</text><text x="362" y="92">*</text><text x="378" y="92">replication</text><polygon points="456,124 460,112 464,124" class="filled"></polygon><line x1="460" y1="124" x2="460" y2="160" class="solid"></line><polygon points="360,244 368,248 360,252" class="filled"></polygon><polygon points="248,300 252,288 256,300" class="filled"></polygon><line x1="252" y1="300" x2="252" y2="336" class="solid"></line><text x="210" y="252">MQTT</text><text x="250" y="252">broker</text><text x="394" y="252">ReductBridge</text><text x="82" y="428">Camera</text><text x="210" y="428">Vibration</text><text x="370" y="428">Automation</text><text x="82" y="444">frames</text><text x="210" y="444">waveforms</text><text x="298" y="172">raw data + timestamp + labels</text><text x="106" y="348">MQTT v5 payload + user properties</text><text x="362" y="444">state/events</text><g><path d="M 368,8 A 8,8 0,0,0 362,12" class="nofill"></path><line x1="362" y1="12" x2="348" y2="40" class="solid"></line><line x1="368" y1="8" x2="544" y2="8" class="solid"></line><path d="M 544,8 A 8,8 0,0,1 550,12" class="nofill"></path><line x1="550" y1="12" x2="564" y2="40" class="solid"></line><line x1="348" y1="40" x2="564" y2="40" class="solid"></line><line x1="348" y1="40" x2="348" y2="104" class="solid"></line><line x1="564" y1="40" x2="564" y2="104" class="solid"></line><line x1="348" y1="104" x2="564" y2="104" class="solid"></line></g><g><line x1="460" y1="176" x2="460" y2="216" class="solid"></line><line x1="372" y1="216" x2="540" y2="216" class="solid"></line><line x1="372" y1="216" x2="372" y2="280" class="solid"></line><line x1="540" y1="216" x2="540" y2="280" class="solid"></line><line x1="372" y1="280" x2="540" y2="280" class="solid"></line></g><g><path d="M 208,216 A 8,8 0,0,0 202,220" class="nofill"></path><line x1="202" y1="220" x2="188" y2="248" class="solid"></line><line x1="208" y1="216" x2="296" y2="216" class="solid"></line><path d="M 296,216 A 8,8 0,0,1 302,220" class="nofill"></path><line x1="302" y1="220" x2="316" y2="248" class="solid"></line><line x1="188" y1="248" x2="202" y2="276" class="solid"></line><line x1="316" y1="248" x2="360" y2="248" class="solid"></line><line x1="316" y1="248" x2="302" y2="276" class="solid"></line><path d="M 202,276 A 8,8 0,0,0 208,280" class="nofill"></path><line x1="208" y1="280" x2="296" y2="280" class="solid"></line><path d="M 302,276 A 8,8 0,0,1 296,280" class="nofill"></path></g><g><line x1="108" y1="360" x2="412" y2="360" class="solid"></line><line x1="108" y1="360" x2="108" y2="408" class="solid"></line><line x1="252" y1="360" x2="252" y2="408" class="solid"></line><line x1="412" y1="360" x2="412" y2="408" class="solid"></line><line x1="68" y1="408" x2="148" y2="408" class="solid"></line><line x1="68" y1="408" x2="68" y2="456" class="solid"></line><line x1="148" y1="408" x2="148" y2="456" class="solid"></line><line x1="196" y1="408" x2="308" y2="408" class="solid"></line><line x1="196" y1="408" x2="196" y2="456" class="solid"></line><line x1="308" y1="408" x2="308" y2="456" class="solid"></line><line x1="356" y1="408" x2="476" y2="408" class="solid"></line><line x1="356" y1="408" x2="356" y2="456" class="solid"></line><line x1="476" y1="408" x2="476" y2="456" class="solid"></line><line x1="68" y1="456" x2="148" y2="456" class="solid"></line><line x1="196" y1="456" x2="308" y2="456" class="solid"></line><line x1="356" y1="456" x2="476" y2="456" class="solid"></line></g></svg>
<p>Several producers can publish different types of data to the same broker.
A camera may publish JPEG frames, a vibration sensor may publish waveform buffers, and an automation system may publish the current machine state or events.</p>
<p>We use MQTT v5 because it can attach user properties to every message.
The binary payload stays unchanged, while metadata travels next to it.
For example, a camera frame can include properties such as <code>device_id</code>, <code>line</code>, <code>content_type</code>, and <code>event_time</code>.</p>
<p>The <code>event_time</code> property contains the original capture timestamp as Unix time with microsecond resolution.
ReductBridge reads this property and uses it as the timestamp of the ReductStore record.
Other MQTT user properties are mapped to ReductStore labels, so the data can be filtered later without decoding the binary payload.</p>
<p>ReductBridge can also use metadata from one topic to enrich data from another topic.
For example, an automation topic may publish the current machine <code>state</code>.
The bridge can remember this label and apply it to camera frames or sensor buffers, so later you can query data by both time and process state.</p>
<p>ReductStore stores the resulting records as append-only time-indexed data.
It keeps the original binary payload, stores labels for filtering, applies FIFO quota rules to control disk usage, and can optionally replicate selected data to another storage node.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="install-reductstore-and-reductbridge">Install ReductStore and ReductBridge<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#install-reductstore-and-reductbridge" class="hash-link" aria-label="Direct link to Install ReductStore and ReductBridge" title="Direct link to Install ReductStore and ReductBridge" translate="no">​</a></h2>
<p>This tutorial assumes that your MQTT v5 broker is already running and that your producers already publish binary payloads to it.
The broker can be Mosquitto, EMQX, HiveMQ, or another MQTT v5-compatible broker.
We only need to add two components: ReductStore for storage and ReductBridge for moving MQTT messages into the store.</p>
<p>The quickest way to start ReductStore is with Docker:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> ./reduct-data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">chown</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-R</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10001</span><span class="token plain">:10001 ./reduct-data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">-d</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--name</span><span class="token plain"> reductstore </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8383</span><span class="token plain">:8383 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_API_TOKEN</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-v</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string environment constant" style="color:#36acaa">$PWD</span><span class="token string" style="color:#e3116c">/reduct-data:/data"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  reduct/store:latest</span><br></span></code></pre></div></div>
<p>ReductStore will listen on <code>http://127.0.0.1:8383</code> and store data in <code>./reduct-data</code>.
The <code>RS_API_TOKEN</code> value is used later by ReductBridge and <code>reduct-cli</code>.</p>
<p>For ReductBridge, use the <code>iot</code> build because it includes the MQTT input:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> pull reduct/bridge:latest-iot</span><br></span></code></pre></div></div>
<p>After creating the configuration file in the next section, run the bridge with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">--rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--network</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">host</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-v</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"</span><span class="token string environment constant" style="color:#36acaa">$PWD</span><span class="token string" style="color:#e3116c">/bridge.toml:/etc/reduct-bridge/config.toml:ro"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  reduct/bridge:latest-iot reduct-bridge /etc/reduct-bridge/config.toml</span><br></span></code></pre></div></div>
<p>The <code>--network host</code> option keeps the example short because the container can reach <code>127.0.0.1:8383</code> and a broker running on the host.
In production, you can run ReductStore and ReductBridge as systemd services or in your own container stack.</p>
<p>If you do not use Docker, download a ReductStore binary from the <a href="https://github.com/reductstore/reductstore/releases/latest" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore releases</strong></a> and the <code>iot</code> ReductBridge build from the <a href="https://github.com/reductstore/reduct-bridge/releases/latest" target="_blank" rel="noopener noreferrer" class=""><strong>ReductBridge releases</strong></a>.
Then run them with the same data path, API token, and config file:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">RS_DATA_PATH</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">./reduct-data </span><span class="token assign-left variable" style="color:#36acaa">RS_API_TOKEN</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token plain"> ./reductstore</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-bridge ./bridge.toml</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configure-reductbridge-for-binary-payloads">Configure ReductBridge for Binary Payloads<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#configure-reductbridge-for-binary-payloads" class="hash-link" aria-label="Direct link to Configure ReductBridge for Binary Payloads" title="Direct link to Configure ReductBridge for Binary Payloads" translate="no">​</a></h2>
<p>Create <code>bridge.toml</code> with the MQTT input, a pipeline, and a ReductStore remote:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># MQTT input named "factory". It connects to the existing broker and</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># subscribes to the topics declared below.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">inputs.mqtt.factory</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">broker</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mqtt://127.0.0.1:1883"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">client_id</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"reduct-bridge-mqtt-binary"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">version</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"v5"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">qos</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Camera frames are binary JPEG payloads. The MQTT topic becomes the source,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># while entry_name controls where records are stored in ReductStore.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">inputs.mqtt.factory.topics</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory/camera/+"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">entry_name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"camera_frames"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">content_type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"image/jpeg"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">timestamp</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"event_time"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">format</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"unix_us"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"device_id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"device_id"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"line"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"line"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">source</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mqtt"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">kind</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"camera"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Vibration messages are also stored as raw bytes. Only MQTT v5 properties are</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># read for timestamp and labels, so the waveform payload is not decoded.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">inputs.mqtt.factory.topics</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory/vibration/+"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">entry_name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vibration"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">content_type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"application/octet-stream"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">timestamp</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"event_time"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">format</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"unix_us"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"device_id"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"device_id"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"line"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"line"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">source</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mqtt"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">kind</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"vibration"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Automation state is a lightweight JSON stream. The state label is extracted</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># from the JSON payload and can be copied to other entries by the pipeline.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">inputs.mqtt.factory.topics</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory/automation/state"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">entry_name</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"automation_state"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">content_type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"application/json"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">timestamp</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"event_time"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">format</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"unix_us"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"line"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"line"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">field</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"state"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"state"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">static</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">source</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mqtt"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">kind</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"automation"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Pipeline routes records from the MQTT input to the ReductStore remote.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># The label rule copies the latest JSON-derived automation state to records.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">pipelines.mqtt_to_store</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">remote</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"local"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">inputs</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"factory"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">from</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"automation_state"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"state"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">to</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"*"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># ReductStore remote. All records are written to this bucket, and the prefix is</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># prepended to each entry name, for example mqtt/camera_frames.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">remotes.reduct.local</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">url</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://127.0.0.1:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">token_api</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">bucket</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mqtt-binary"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">prefix</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"mqtt/"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Create the bucket on startup with a FIFO quota so the edge node keeps the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># newest data when the storage budget is reached.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">remotes.reduct.local.create_bucket</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">quota_type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"FIFO"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">quota_size</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"20GB"</span><br></span></code></pre></div></div>
<p>The camera and vibration topics are treated as binary streams.
ReductBridge does not decode their payloads.
It writes the MQTT payload bytes as the ReductStore record body and uses metadata from MQTT v5 properties.</p>
<p>The important part is the <code>timestamp</code> line:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key property" style="color:#36acaa">timestamp</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">property</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"event_time"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">format</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"unix_us"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>This tells ReductBridge to read the MQTT v5 user property <code>event_time</code> and use it as the ReductStore record timestamp.
The value must be Unix time in microseconds.
Other supported formats include <code>unix_s</code>, <code>unix_ms</code>, <code>unix_ns</code>, <code>iso8601</code>, and <code>ros_stamp</code>.
If the property is missing or cannot be parsed, ReductBridge falls back to ingest time.</p>
<p>The <code>labels</code> arrays copy metadata into ReductStore labels.
For binary topics, labels come from MQTT v5 properties so the payload remains untouched.
For the automation topic, <code>state</code> comes from the JSON payload field:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">field</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"state"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">label</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"state"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>For example, a camera publisher can send these user properties with each JPEG frame:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">event_time=1780582163840000</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">device_id=cam-01</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">line=line-a</span><br></span></code></pre></div></div>
<p>ReductBridge stores that frame in the <code>mqtt/camera_frames</code> entry because the remote adds the <code>mqtt/</code> prefix.
It also stores labels such as <code>device_id=cam-01</code>, <code>line=line-a</code>, and <code>kind=camera</code>.
The record content type is handled separately by the topic-level <code>content_type = "image/jpeg"</code> setting.</p>
<p>The pipeline-level <code>labels</code> rules show the enrichment pattern from the architecture section.
When the <code>automation_state</code> entry receives a <code>state</code> label extracted from JSON, the pipeline can copy the latest value to later records.
That lets you query camera frames not only by time and device, but also by process state:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">from</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"automation_state"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">labels</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"state"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token key property" style="color:#36acaa">to</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"*"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>The remote section creates the <code>mqtt-binary</code> bucket automatically with a FIFO quota.
With this setting, ReductStore keeps accepting new records and removes the oldest data when the bucket reaches <code>20GB</code>.
Adjust this value to the disk budget of your edge node:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">[</span><span class="token table class-name">remotes.reduct.local.create_bucket</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">quota_type</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"FIFO"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key property" style="color:#36acaa">quota_size</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"20GB"</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="query-data-by-time-range">Query Data by Time Range<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#query-data-by-time-range" class="hash-link" aria-label="Direct link to Query Data by Time Range" title="Direct link to Query Data by Time Range" translate="no">​</a></h2>
<p>After ReductBridge is running and messages are arriving, use the Python SDK to verify the entries and query records by time range and labels:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> pathlib </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Path</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://127.0.0.1:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"mqtt-binary"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        output_dir </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Path</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"records"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        output_dir</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mkdir</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">exist_ok</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> record </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"mqtt/*"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            start</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"2026-06-04T10:00:00Z"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            stop</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"2026-06-04T10:05:00Z"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            when</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"&amp;state"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$eq"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"running"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            payload </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            entry </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">entry</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">replace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"_"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            extension </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"image/jpeg"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">".jpg"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"application/json"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">".json"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">content_type</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">".bin"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            filename </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> output_dir </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">entry</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">-</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">record</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">timestamp</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">extension</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> </span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">filename</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"wb"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> output</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                output</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">payload</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">filename</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">content_type</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">labels</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>You should see entries such as <code>mqtt/camera_frames</code>, <code>mqtt/vibration</code>, and <code>mqtt/automation_state</code>.
The timestamps should match the producer-provided <code>event_time</code> property when it is present.
The query uses the <code>mqtt/*</code> wildcard to select all entries, then filters records by time range and the copied <code>state=running</code> label.
Each payload is saved as-is, with the file extension selected from the record content type where possible.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next Steps<a href="https://www.reduct.store/blog/tutorials/iot/mqtt-binary-data-time-index#next-steps" class="hash-link" aria-label="Direct link to Next Steps" title="Direct link to Next Steps" translate="no">​</a></h2>
<p>This tutorial focused on the bridge configuration because the MQTT broker and data sources were assumed to exist.
From here, you can adapt the same pattern to your own topics, labels, and retention policy.</p>
<ul>
<li class="">Learn how to store MQTT data with the <a class="" href="https://www.reduct.store/blog/tutorials/iot/how-to-keep-mqtt-data-python"><strong>Python MQTT tutorial</strong></a>.</li>
<li class="">See a Rust implementation in the <a class="" href="https://www.reduct.store/blog/tutorials/iot/how-to-keep-mqtt-data-rust"><strong>Rust MQTT tutorial</strong></a>.</li>
<li class="">Compare storage choices in <a class="" href="https://www.reduct.store/blog/advice/database/mqtt-data-storage"><strong>How to Choose the Right MQTT Database</strong></a>.</li>
<li class="">Review the <a class="" href="https://www.reduct.store/docs/next/reduct-bridge"><strong>ReductBridge documentation</strong></a> and the <a class="" href="https://www.reduct.store/docs/next/reduct-bridge/input/mqtt"><strong>MQTT input reference</strong></a>.</li>
<li class="">Read about ReductStore <a class="" href="https://www.reduct.store/docs/guides/data-querying"><strong>data querying</strong></a>, <a class="" href="https://www.reduct.store/docs/guides/buckets"><strong>bucket quotas</strong></a>, and <a class="" href="https://www.reduct.store/docs/guides/data-replication"><strong>data replication</strong></a>.</li>
</ul>
<hr>
<p>I hope you found this article helpful. If you have any questions or feedback, don't hesitate to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>
<p>Thanks for reading!</p>]]></content:encoded>
            <category>tutorials</category>
            <category>iot</category>
            <category>mqtt</category>
        </item>
        <item>
            <title><![CDATA[CRA-Compliant Robotics Data Storage 2026: How to Solve the Data Storage Challenges of the CRA]]></title>
            <link>https://www.reduct.store/blog/cra-compliant-robotics-data-storage</link>
            <guid>https://www.reduct.store/blog/cra-compliant-robotics-data-storage</guid>
            <pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[EU Cyber Resilience Act (CRA) vulnerability reporting starts 11 September 2026. Learn how ReductStore provides native audit trails, integrity protection, selective replication, and token-based access for robotics fleets — delivering CRA readiness with 10x faster ingestion and up to 90% lower cloud costs than generic time-series or object storage solutions.]]></description>
            <content:encoded><![CDATA[<p><strong>The CRA Deadline Every German Robot Operator Must Face</strong></p>
<p>The EU Cyber Resilience Act (Regulation (EU) 2024/2847) is the “GDPR for connected products.” It entered into force on 10 December 2024, with critical milestones approaching fast:</p>
<ul>
<li class=""><strong>11 September 2026</strong>: Mandatory reporting of actively exploited vulnerabilities and severe incidents (24-hour early warning, 72-hour full notification).</li>
<li class=""><strong>11 December 2027</strong>: Full compliance — Security by Design, lifecycle support (minimum 5 years), technical documentation, and CE marking.</li>
</ul>
<p>For robotics fleets (AMRs, cobots, autonomous systems, and ROS 2-based platforms) the stakes are particularly high. These systems are “products with digital elements” (often Class II or critical), generating massive multimodal data streams (camera feeds, LiDAR, IMU, logs, ROS bags) under real production constraints: intermittent connectivity, edge hardware limits, and high physical safety risks.</p>
<p>Generic storage solutions force painful trade-offs: either accept data loss and compliance gaps, or accept exploding costs and slow performance. ReductStore eliminates this trade-off.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-common-storage-solutions-fall-short-for-cra-in-production-robotics">Why Common Storage Solutions Fall Short for CRA in Production Robotics<a href="https://www.reduct.store/blog/cra-compliant-robotics-data-storage#why-common-storage-solutions-fall-short-for-cra-in-production-robotics" class="hash-link" aria-label="Direct link to Why Common Storage Solutions Fall Short for CRA in Production Robotics" title="Direct link to Why Common Storage Solutions Fall Short for CRA in Production Robotics" translate="no">​</a></h2>
<p>Most robotics teams still rely on a patchwork of <strong>rosbag2</strong>, <strong>MinIO/S3</strong>, <strong>InfluxDB</strong>, or <strong>TimescaleDB</strong>. While these tools can technically meet some basic CRA requirements with significant custom development, they create substantial challenges when applied to real-world ROS production fleets:</p>
<ul>
<li class=""><strong>rosbag2</strong> is excellent for local single-robot recording, but it is not designed for fleet-scale edge-to-cloud architectures, selective replication, or long-term auditability. It lacks native mechanisms for data minimisation, granular access control, and automated compliance reporting.</li>
<li class=""><strong>MinIO/S3 and general-purpose object stores</strong> provide scalable blob storage, but they offer no built-in time-series indexing, label-based filtering, or efficient selective replication. Achieving CRA-compliant audit trails and data minimisation usually requires complex additional layers and custom glue code.</li>
<li class=""><strong>InfluxDB and TimescaleDB</strong> are strong for structured metrics, but they have limited native support for large binary objects (images, LiDAR, ROS bags) and are not optimized for high-throughput multimodal ingestion or production-grade edge-to-cloud workflows under intermittent connectivity.</li>
</ul>
<p>In practice, these solutions force teams to invest heavily in custom engineering to achieve CRA compliance — often at the expense of performance, simplicity, and cost efficiency. Most importantly, this custom-built approach increases the risk of documentation gaps during a CRA conformity assessment.</p>
<table><thead><tr><th style="text-align:left">Requirement (CRA Annex I)</th><th style="text-align:left">Generic TSDB / Object Store</th><th style="text-align:left">ReductStore Advantage</th></tr></thead><tbody><tr><td style="text-align:left">Protection from unauthorised access + logging</td><td style="text-align:left">Basic or bolt-on</td><td style="text-align:left">Native token auth + per-record audit trail</td></tr><tr><td style="text-align:left">Integrity protection &amp; corruption reporting</td><td style="text-align:left">Manual or expensive</td><td style="text-align:left">Label-based hash verification + automatic alerts</td></tr><tr><td style="text-align:left">Data minimisation</td><td style="text-align:left">All-or-nothing replication</td><td style="text-align:left">Conditional, label-based selective replication</td></tr><tr><td style="text-align:left">Secure by design &amp; default</td><td style="text-align:left">Add-on layer</td><td style="text-align:left">Built into edge-first architecture</td></tr><tr><td style="text-align:left">Lifecycle support &amp; updates</td><td style="text-align:left">Complex versioning</td><td style="text-align:left">Versioned replication + dedicated security paths</td></tr></tbody></table>
<p>The result? Many operators risk non-compliance, higher insurance premiums, or blocked EU market access after 2027.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductstores-cra-native-architecture-for-ros-fleets">ReductStore's CRA-Native Architecture for ROS Fleets<a href="https://www.reduct.store/blog/cra-compliant-robotics-data-storage#reductstores-cra-native-architecture-for-ros-fleets" class="hash-link" aria-label="Direct link to ReductStore's CRA-Native Architecture for ROS Fleets" title="Direct link to ReductStore's CRA-Native Architecture for ROS Fleets" translate="no">​</a></h2>
<p>ReductStore is purpose-built as a time-series blob storage engine for multimodal robotics data. It turns compliance from a burden into a competitive advantage.</p>
<p><strong>Key CRA-Aligned Features:</strong></p>
<ol>
<li class="">
<p><strong>Token-Based Authentication &amp; Granular Access Control</strong>
Every device, service, or user receives scoped tokens. Access is logged at Bucket/Entry/Record level with timestamps. Perfect for proving “who accessed what data and when” — a core CRA requirement.</p>
</li>
<li class="">
<p><strong>Label-Based Integrity Protection</strong>
Attach cryptographic labels or hashes to records. The engine supports automatic corruption detection and reporting. Write-once-append-only semantics ensure data cannot be silently altered.</p>
</li>
<li class="">
<p><strong>Selective Edge-to-Cloud Replication with Data Minimisation</strong>
Replicate only records matching specific labels or conditions (e.g., “security-critical”, “anomaly detected”, or specific ROS topics). This dramatically reduces attack surface and cloud costs while meeting CRA’s data-minimisation principle.</p>
</li>
<li class="">
<p><strong>Encryption at Rest and in Transit</strong>
ReductStore enables strong data confidentiality through multiple proven approaches. Sensitive payloads can be encrypted client-side before ingestion and server-side (e.g., via S3 backend options), while preserving its high-performance batching architecture. All network communication is secured via HTTPS/TLS. The system is fully compatible with ROS 2 Security (SROS2). Native built-in client- and server-side encryption is on the near-term roadmap.</p>
</li>
<li class="">
<p><strong>Extended Audit Logging &amp; Grafana Integration</strong>
Detailed logs of all read/write/replication events. Visualize compliance status directly in Grafana dashboards — ready for technical documentation and conformity assessment.</p>
</li>
<li class="">
<p><strong>SBOM Export &amp; Versioned Updates</strong>
Generate Software Bill of Materials for ReductStore itself. Security updates are delivered via dedicated channels without disrupting production data flows.</p>
</li>
</ol>
<p>These capabilities are already running in 100+ production deployments managing over 1 PB of multimodal robotics data.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-world-results-from-production-fleets">Real-World Results from Production Fleets<a href="https://www.reduct.store/blog/cra-compliant-robotics-data-storage#real-world-results-from-production-fleets" class="hash-link" aria-label="Direct link to Real-World Results from Production Fleets" title="Direct link to Real-World Results from Production Fleets" translate="no">​</a></h2>
<p>Operators using ReductStore report:</p>
<ul>
<li class="">“We have forgotten about disk overrun problems on our edge devices.” — Ingo Kaiser, PANDA GmbH</li>
<li class="">“ReductStore handles terabytes of unstructured data in a production environment.” — Michael Welsch, Metric Space UG</li>
<li class="">“We can stop worrying about data collection and focus entirely on building our robots and autonomous layers.” — Victor Massagué Respall, INSAION</li>
</ul>
<p>In one fleet deployment, selective replication reduced cloud data volume by approximately 85% while maintaining full auditability for CRA preparedness.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="free-cra-checklist-for-robotics-operators-focus-data-integrity--storage-layer">Free CRA Checklist for Robotics Operators (Focus: Data Integrity &amp; Storage Layer)<a href="https://www.reduct.store/blog/cra-compliant-robotics-data-storage#free-cra-checklist-for-robotics-operators-focus-data-integrity--storage-layer" class="hash-link" aria-label="Direct link to Free CRA Checklist for Robotics Operators (Focus: Data Integrity &amp; Storage Layer)" title="Direct link to Free CRA Checklist for Robotics Operators (Focus: Data Integrity &amp; Storage Layer)" translate="no">​</a></h2>
<p>Download our practical checklist covering:</p>
<ul>
<li class="">Immediate actions before September 2026 reporting deadline</li>
<li class="">Mapping ReductStore features to specific CRA Annex I requirements</li>
<li class="">Step-by-step migration guide for existing ROS fleets</li>
<li class="">Risk assessment template for multimodal robotics data</li>
</ul>
<p><strong><a class="" href="https://www.reduct.store/cra-checklist">Download CRA Checklist for ROS Fleets (PDF)</a></strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ready-to-make-your-data-streaming-and-storage-cra-ready-without-compromising-performance">Ready to Make Your Data Streaming And Storage CRA-Ready Without Compromising Performance?<a href="https://www.reduct.store/blog/cra-compliant-robotics-data-storage#ready-to-make-your-data-streaming-and-storage-cra-ready-without-compromising-performance" class="hash-link" aria-label="Direct link to Ready to Make Your Data Streaming And Storage CRA-Ready Without Compromising Performance?" title="Direct link to Ready to Make Your Data Streaming And Storage CRA-Ready Without Compromising Performance?" translate="no">​</a></h2>
<p>The September 2026 reporting deadline is approaching fast. Don’t wait until conformity assessment bodies are fully operational in mid-2026.</p>
<p><strong>Book a 20-minute production fleet audit</strong> with our team. We will:</p>
<ul>
<li class="">Analyze your current data pipeline against CRA requirements</li>
<li class="">Run a live benchmark with your own ROS data</li>
<li class="">Show exactly how ReductStore delivers compliance + 10x performance at lower cost</li>
</ul>
<p>→ <strong><a class="" href="https://www.reduct.store/fleet-audit">Schedule Your Free Fleet Audit</a></strong></p>
<p><strong>Next up in this series:</strong> Physical AI Training Data at Scale: Why Foundation Models Need Specialized Edge-to-Cloud Storage</p>]]></content:encoded>
            <category>robotics</category>
            <category>compliance</category>
            <category>Cyber Resilience Act (CRA)</category>
        </item>
        <item>
            <title><![CDATA[How to Store and Manage Robotics Data]]></title>
            <link>https://www.reduct.store/blog/store-robotic-data</link>
            <guid>https://www.reduct.store/blog/store-robotic-data</guid>
            <pubDate>Sat, 11 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Explore how ReductStore is optimized for managing robotics data, offering superior performance in handling time series data. Learn about its efficient data batching, advanced replication capabilities, and robust retention policies designed for the needs of robotics applications.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Introduction Diagram" src="https://www.reduct.store/assets/images/intro-picture-225294a7575e209aff07b69109e8acbd.png" width="1928" height="1084" class="img_ev3q"></p>
<p>Robots generate <em>massive</em> amounts of data, and managing it well is harder than it looks. Storage fills up fast, cloud transfer gets expensive, and real time ingestion is unforgiving when you're running cameras and sensors at high frequency.</p>
<p>This article covers practical strategies for handling robotic data, introduces <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>, and walks through a hands on example. Along the way, we cover native ROS integration, Grafana dashboards, MCAP export for Foxglove, a Zenoh API, and native S3 and Azure backends. We also compare ReductStore against Rosbag and MongoDB so you can pick the right tool for each part of your stack.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="challenges-in-robotic-data-management">Challenges in Robotic Data Management<a href="https://www.reduct.store/blog/store-robotic-data#challenges-in-robotic-data-management" class="hash-link" aria-label="Direct link to Challenges in Robotic Data Management" title="Direct link to Challenges in Robotic Data Management" translate="no">​</a></h2>
<p>Robots operate in dynamic environments and continuously produce large volumes of data. The core challenges engineers run into are:</p>
<ul>
<li class=""><strong>High frequency, real time requirements</strong>: A drone navigating a city must process camera and sensor data in milliseconds. Storage solutions need to keep up with these streams and make data accessible fast enough for real time decision making.</li>
<li class=""><strong>Limited on device storage</strong>: Most robots can't store everything they generate. Size, weight, and power constraints limit local capacity, so good data management strategies are essential.</li>
<li class=""><strong>High data volume</strong>: An autonomous vehicle can produce up to <a href="https://www.datacenterfrontier.com/connected-cars/article/11429212/rolling-zettabytes-quantifying-the-data-impact-of-connected-cars" target="_blank" rel="noopener noreferrer" class=""><strong>5 terabytes per hour</strong></a> across camera feeds, LiDAR, radar, GPS, and sensors. Most databases weren't designed around that kind of throughput.</li>
<li class=""><strong>Cloud storage costs</strong>: Pushing everything to the cloud isn't practical. Providers charge per gigabyte stored and transferred, and with robots producing terabytes, the bill adds up fast. What you send to the cloud is a decision worth making deliberately.</li>
<li class=""><strong>Data reduction complexity</strong>: Filtering out irrelevant data without losing important information is tricky. Without a clear strategy, you either waste storage or throw away data you later need.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="general-strategies-for-managing-robotic-data">General Strategies for Managing Robotic Data<a href="https://www.reduct.store/blog/store-robotic-data#general-strategies-for-managing-robotic-data" class="hash-link" aria-label="Direct link to General Strategies for Managing Robotic Data" title="Direct link to General Strategies for Managing Robotic Data" translate="no">​</a></h2>
<p>A few approaches that work well in practice:</p>
<ul>
<li class=""><strong>Use a time series object store</strong>: Systems like ReductStore are designed specifically for high frequency, timestamped binary data. They offer fast writes, efficient querying, and manageable costs at scale.</li>
<li class=""><strong>Balance edge and cloud storage</strong>: Keep recent, critical data on the robot for fast access. Move older or lower priority data to the cloud for long term storage and analysis. Splitting storage this way cuts costs without adding much latency to what matters.</li>
<li class=""><strong>Use volume based retention policies</strong>: Rather than deleting data based on age, use a FIFO (first in, first out) quota. Data is only removed when the storage limit is reached, which avoids unnecessary deletion during downtime or low activity periods.</li>
<li class=""><strong>Compress where it makes sense</strong>: H.265 for video, JPEG for images. The right format can cut storage by an order of magnitude without losing anything you actually need.</li>
<li class=""><strong>Prioritize what to keep</strong>: Not all sensor data has equal value. Define what's critical upfront and discard the rest early in the pipeline.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductstore-built-for-robotic-data">ReductStore: Built for Robotic Data<a href="https://www.reduct.store/blog/store-robotic-data#reductstore-built-for-robotic-data" class="hash-link" aria-label="Direct link to ReductStore: Built for Robotic Data" title="Direct link to ReductStore: Built for Robotic Data" translate="no">​</a></h2>
<p><a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a> is a time series database built specifically for unstructured, binary data. It's designed to handle high frequency sensor streams from autonomous vehicles, drones, industrial robots, and IoT. It stores data with timestamps and labels, supports fast real time ingestion, and comes with batching, filtering, and edge to cloud replication built in.</p>
<p>Here's what's available today.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ros-integration-with-reduct-bridge">ROS Integration with reduct-bridge<a href="https://www.reduct.store/blog/store-robotic-data#ros-integration-with-reduct-bridge" class="hash-link" aria-label="Direct link to ROS Integration with reduct-bridge" title="Direct link to ROS Integration with reduct-bridge" translate="no">​</a></h3>
<p><a href="https://github.com/reductstore/reduct-bridge" target="_blank" rel="noopener noreferrer" class=""><strong>reduct-bridge</strong></a> connects live ROS 1 and ROS 2 systems directly to ReductStore. You configure it with a simple TOML file that defines your ROS topic inputs, pipelines, and the ReductStore destination. It subscribes to topics and stores each message as an individual record.</p>
<p>It also writes a <code>$ros</code> attachment to each entry with the message schema, topic name, and encoding — that metadata is what powers MCAP export and Grafana visualization, both covered below. If your stack is already on ROS, setup takes minutes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloud-storage-native-s3-and-azure">Cloud Storage: Native S3 and Azure<a href="https://www.reduct.store/blog/store-robotic-data#cloud-storage-native-s3-and-azure" class="hash-link" aria-label="Direct link to Cloud Storage: Native S3 and Azure" title="Direct link to Cloud Storage: Native S3 and Azure" translate="no">​</a></h3>
<p>ReductStore supports native cloud storage backends for both <a class="" href="https://www.reduct.store/docs/integrations/cloud-storage"><strong>Amazon S3 and Azure Blob Storage</strong></a>, no FUSE drivers needed. It uses a local cache for hot data and the cloud bucket for long term retention.</p>
<table><thead><tr><th>Option</th><th>What it means</th></tr></thead><tbody><tr><td>S3 compatible</td><td>Works with AWS S3, MinIO, Ceph, Cloudflare R2, and any S3 compatible service</td></tr><tr><td>Azure Blob Storage</td><td>Switch backends by changing a few environment variables. Same deployment patterns.</td></tr><tr><td>Tiered access</td><td>Recent data stays in the local cache for fast reads. Older data lives in the cloud bucket.</td></tr><tr><td>Read replicas</td><td>Add read only replicas pointing at the same S3 bucket, close to your consumers.</td></tr></tbody></table>
<p>For records around 100KB (e.g. JPEG images), ReductStore can be &gt;10x faster than traditional time series object stores at a fraction of the cost.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="visualize-ros-data-in-grafana">Visualize ROS Data in Grafana<a href="https://www.reduct.store/blog/store-robotic-data#visualize-ros-data-in-grafana" class="hash-link" aria-label="Direct link to Visualize ROS Data in Grafana" title="Direct link to Visualize ROS Data in Grafana" translate="no">​</a></h3>
<p>ReductStore has a <a class="" href="https://www.reduct.store/blog/grafana-visualization-ros-data"><strong>Grafana integration</strong></a> through the ReductStore data source plugin. You can query ROS 2 messages directly in Grafana thanks to the <a class="" href="https://www.reduct.store/docs/extensions/official/ros-ext/raw"><strong>ReductROS extension</strong></a>. The extension decodes binary CDR messages into JSON on the fly.</p>
<p><img decoding="async" loading="lazy" alt="Grafana ROS Dashboard" src="https://www.reduct.store/assets/images/grafana-00de17bcbe51c17412d3a3bfd5fbd557.png" width="1900" height="1086" class="img_ev3q"></p>
<p>You can monitor sensor streams live, compare data across multiple robots, and set up alerts when metrics drift.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="export-to-mcap-for-foxglove">Export to MCAP for Foxglove<a href="https://www.reduct.store/blog/store-robotic-data#export-to-mcap-for-foxglove" class="hash-link" aria-label="Direct link to Export to MCAP for Foxglove" title="Direct link to Export to MCAP for Foxglove" translate="no">​</a></h3>
<p>You can export raw ROS messages stored in ReductStore directly to MCAP and open them in Foxglove. This is powered by the <a class="" href="https://www.reduct.store/docs/extensions/official/ros-ext/raw"><strong>ReductROS extension</strong></a> as well, which reconstructs valid MCAP files on demand.</p>
<p><img decoding="async" loading="lazy" alt="MCAP Export" src="https://www.reduct.store/assets/images/mcap-export-7300282ea009d452bd17f8e91f4738be.png" width="2932" height="1686" class="img_ev3q"></p>
<p>When records are ingested via reduct-bridge, the <code>$ros</code> attachment carries the schema and topic information. The extension uses this to reconstruct valid MCAP files on demand. You can cover a full time range across multiple topics in a single query, and split by duration or file size for long recordings.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="zenoh-native-api">Zenoh Native API<a href="https://www.reduct.store/blog/store-robotic-data#zenoh-native-api" class="hash-link" aria-label="Direct link to Zenoh Native API" title="Direct link to Zenoh Native API" translate="no">​</a></h3>
<p>ReductStore now includes a <a class="" href="https://www.reduct.store/docs/integrations/zenoh"><strong>Zenoh native API</strong></a> alongside the existing HTTP API. Zenoh is a pub/sub protocol designed for robotics and distributed systems, and it's widely used in next generation ROS 2 deployments.</p>
<p><img decoding="async" loading="lazy" alt="Zenoh Native API" src="https://www.reduct.store/assets/images/zenoh-26762a7d41ee5cf25433038ad47863a9.png" width="1488" height="256" class="img_ev3q"></p>
<p>When enabled, ReductStore opens a Zenoh session with a subscriber for writes and a queryable for reads, both running in parallel with HTTP and sharing the same data. Zenoh keys become entry names, encodings map to content types, and attachments map to labels. Time range queries, conditional filters, and label lookups all work the same way they do over HTTP.</p>
<p>It's a natural fit if your robot stack already speaks Zenoh, or if you want to skip HTTP overhead on high frequency ingestion paths.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="query-language-and-batching">Query Language and Batching<a href="https://www.reduct.store/blog/store-robotic-data#query-language-and-batching" class="hash-link" aria-label="Direct link to Query Language and Batching" title="Direct link to Query Language and Batching" translate="no">​</a></h3>
<p>ReductStore uses a <a class="" href="https://www.reduct.store/docs/next/conditional-query#query-syntax"><strong>JSON based query language</strong></a> that supports filtering, aggregation, and time range operations. You can query data from a specific robot within a time window, compare sensor streams across robots in parallel, or filter by any label.</p>
<p>Retrieval is optimized through <strong>batching</strong>: multiple records are grouped into a single response based on a time range, which cuts down on request overhead and improves throughput. SDKs are available for Python, C++, JavaScript, Go, and Rust. More in the <a class="" href="https://www.reduct.store/docs/guides/data-querying"><strong>querying guide</strong></a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="replication-and-edge-to-cloud-replication">Replication and Edge to Cloud Replication<a href="https://www.reduct.store/blog/store-robotic-data#replication-and-edge-to-cloud-replication" class="hash-link" aria-label="Direct link to Replication and Edge to Cloud Replication" title="Direct link to Replication and Edge to Cloud Replication" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Replication Diagram" src="https://www.reduct.store/assets/images/replication.drawio-cec5446b9897c9263dbc561a4f2fa95b.png" width="1743" height="1416" class="img_ev3q"></p>
<p>ReductStore replicates at the bucket level based on conditions. That means you can choose to send only high priority sensor data to the cloud based on rules, labels, or events. Replication is incremental, so only new data is transferred. Labels stored alongside records let you define fine grained rules based on content, for example, only replicate records flagged as anomalies or sampled at 1 in 10 seconds.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="retention-strategies">Retention Strategies<a href="https://www.reduct.store/blog/store-robotic-data#retention-strategies" class="hash-link" aria-label="Direct link to Retention Strategies" title="Direct link to Retention Strategies" translate="no">​</a></h3>
<p><a class="" href="https://www.reduct.store/docs/guides/buckets#quota-type"><strong>Volume based retention</strong></a> follows the FIFO principle: data is only deleted when storage is full, making room for new records. This is different from time based retention, which deletes data after a fixed age regardless of whether storage is full. After an outage, a time based policy might delete data the system never had the chance to process. Volume based retention avoids that.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-applications">Example Applications<a href="https://www.reduct.store/blog/store-robotic-data#example-applications" class="hash-link" aria-label="Direct link to Example Applications" title="Direct link to Example Applications" translate="no">​</a></h3>
<table><thead><tr><th>Application</th><th>How ReductStore helps</th></tr></thead><tbody><tr><td>Autonomous Vehicles</td><td>Handles high throughput sensor streams with efficient querying and selective edge to cloud replication</td></tr><tr><td>Industrial Robots</td><td>Stores diagnostic data for predictive maintenance and continuous performance monitoring</td></tr><tr><td>Drones and UAVs</td><td>Supports offline operation in remote areas and syncs data when connectivity is restored</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="comparing-reductstore-with-rosbag-and-mongodb">Comparing ReductStore with Rosbag and MongoDB<a href="https://www.reduct.store/blog/store-robotic-data#comparing-reductstore-with-rosbag-and-mongodb" class="hash-link" aria-label="Direct link to Comparing ReductStore with Rosbag and MongoDB" title="Direct link to Comparing ReductStore with Rosbag and MongoDB" translate="no">​</a></h2>
<p>Three tools come up most often when robotics teams think about data storage: Rosbag and MCAP, MongoDB, and ReductStore. They're built for different things.</p>
<table><thead><tr><th></th><th>Rosbag / MCAP</th><th>MongoDB</th><th>ReductStore</th></tr></thead><tbody><tr><td>Data type</td><td>Binary ROS messages</td><td>Semi structured documents</td><td>Binary timestamped records</td></tr><tr><td>Query across recordings</td><td>No</td><td>Yes</td><td>Yes</td></tr><tr><td>Large binary payloads</td><td>Yes</td><td>Slow via GridFS</td><td>Yes, natively</td></tr><tr><td>Retention</td><td>Manual file management</td><td>Time based</td><td>Volume based FIFO</td></tr><tr><td>ROS integration</td><td>Native</td><td>None</td><td>Via reduct-bridge</td></tr><tr><td>Cloud storage</td><td>No</td><td>Atlas</td><td>Native S3 and Azure</td></tr><tr><td>Write (100 KB blobs)</td><td>—</td><td>694 blob/s</td><td>3,612 blob/s (+420%)</td></tr><tr><td>Read (100 KB blobs)</td><td>—</td><td>1,730 blob/s</td><td>6,250 blob/s (+260%)</td></tr></tbody></table>
<p><strong>Rosbag and MCAP</strong> are the standard for recording ROS sessions. They're great for capturing a snapshot during a test run. But they're file formats, not databases. Querying across many recordings requires custom scripts, there's no built in content indexing, and managing thousands of bag files quickly becomes its own problem. Use them for short recordings and local playback, not long term storage or fleet wide analysis.</p>
<p><strong>MongoDB</strong> is flexible and works well for structured metadata, labels, and event logs. But it wasn't built for large binary payloads. For blob data, it relies on GridFS, which adds complexity and hurts performance. It also uses time based retention, so data can be deleted during idle periods even when storage isn't full.</p>
<p><strong>ReductStore</strong> is designed specifically for the data robots actually produce: large, binary, timestamped records at high frequency. It connects to ROS via reduct-bridge, exports to MCAP for Foxglove, plugs into Grafana, and replicates selectively to S3 or Azure.</p>
<p>In practice: use Rosbag or MCAP for short test recordings, MongoDB for structured metadata and event logs, and ReductStore for raw sensor data that needs to be stored at scale, queried efficiently, and managed over time.</p>
<p>For a deeper look: <a class="" href="https://www.reduct.store/blog/robotics-mongodb-vs-reductstore"><strong>MongoDB vs ReductStore: Choosing the Right Database for Robotics Applications</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hands-on-example-storing-robotic-data-in-reductstore">Hands-On Example: Storing Robotic Data in ReductStore<a href="https://www.reduct.store/blog/store-robotic-data#hands-on-example-storing-robotic-data-in-reductstore" class="hash-link" aria-label="Direct link to Hands-On Example: Storing Robotic Data in ReductStore" title="Direct link to Hands-On Example: Storing Robotic Data in ReductStore" translate="no">​</a></h2>
<p>Let's walk through a <a href="https://github.com/reductstore/reduct-robotics-example/blob/main/StoreQueryData.py" target="_blank" rel="noopener noreferrer" class=""><strong>practical example</strong></a> of storing and querying robotic data with ReductStore. We'll use trajectory data (coordinates, speed, orientation) to keep things simple, but the same approach works for any sensor stream.</p>
<p>You'll need <em>Python 3.8+</em> installed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-reductstore">Setting Up ReductStore<a href="https://www.reduct.store/blog/store-robotic-data#setting-up-reductstore" class="hash-link" aria-label="Direct link to Setting Up ReductStore" title="Direct link to Setting Up ReductStore" translate="no">​</a></h3>
<p>Create a folder and add a <em>docker-compose.yaml</em> file:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"3.8"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reductstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reduct/store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8383:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> RS_API_TOKEN=my</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">token</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">driver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> local</span><br></span></code></pre></div></div>
<p>Start it with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> compose up </span><span class="token parameter variable" style="color:#36acaa">-d</span><br></span></code></pre></div></div>
<p>ReductStore will be available at <a href="http://127.0.0.1:8383/" target="_blank" rel="noopener noreferrer" class="">http://127.0.0.1:8383</a>. Check the container is running with <code>docker ps</code>, then install the Python libraries:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> reduct-py numpy</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="store-and-query-data">Store and Query Data<a href="https://www.reduct.store/blog/store-robotic-data#store-and-query-data" class="hash-link" aria-label="Direct link to Store and Query Data" title="Direct link to Store and Query Data" translate="no">​</a></h3>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-a-bucket">Create a Bucket<a href="https://www.reduct.store/blog/store-robotic-data#create-a-bucket" class="hash-link" aria-label="Direct link to Create a Bucket" title="Direct link to Create a Bucket" translate="no">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">create_trajectory_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  settings </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> BucketSettings</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	 quota_type</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">QuotaType</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">FIFO</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	 quota_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1000_000_000</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">create_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"trajectory_data"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> settings</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> exist_ok</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>The bucket uses a FIFO quota of 1 GB. Old data is only deleted when the limit is reached.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="generate-trajectory-data">Generate Trajectory Data<a href="https://www.reduct.store/blog/store-robotic-data#generate-trajectory-data" class="hash-link" aria-label="Direct link to Generate Trajectory Data" title="Direct link to Generate Trajectory Data" translate="no">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">generate_trajectory_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frequency</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> duration</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> interval </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> frequency</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> start_time </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frequency </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> duration</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    time_step </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> interval</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    x </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> time_step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.2</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">randn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    y </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cos</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> time_step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.2</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">randn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    yaw </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">degrees</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">arctan2</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">y</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> x</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uniform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    speed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">abs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pi </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> time_step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">randn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    timestamp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> start_time </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> timedelta</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">seconds</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">time_step</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">yield</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">"timestamp"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> timestamp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">isoformat</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">"position"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">x</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"y"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">y</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">"orientation"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"yaw"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">yaw</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token string" style="color:#e3116c">"speed"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">speed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sleep</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">interval</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This simulates a robot moving in 2D at 10 Hz for 1 second. <em>X</em> and <em>y</em> are position coordinates, <em>yaw</em> is orientation, and <em>speed</em> is derived from position changes.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="calculate-metrics">Calculate Metrics<a href="https://www.reduct.store/blog/store-robotic-data#calculate-metrics" class="hash-link" aria-label="Direct link to Calculate Metrics" title="Direct link to Calculate Metrics" translate="no">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">calculate_trajectory_metrics</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trajectory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">list</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">tuple</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  positions </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">point</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"position"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> point</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"position"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"y"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain">   point </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> trajectory</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  speeds </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">point</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"speed"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> point </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> trajectory</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  deltas </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">diff</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">positions</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> axis</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  distances </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sqrt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">deltas</span><span class="token operator" style="color:#393A34">**</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> axis</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  total_distance </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">sum</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">distances</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  average_speed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">speeds</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> total_distance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> average_speed</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="write-to-reductstore">Write to ReductStore<a href="https://www.reduct.store/blog/store-robotic-data#write-to-reductstore" class="hash-link" aria-label="Direct link to Write to ReductStore" title="Direct link to Write to ReductStore" translate="no">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">store_trajectory_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  trajectory_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> data_point </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> generate_trajectory_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">frequency</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> duration</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    trajectory_data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data_point</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  total_distance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> average_speed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> calculate_trajectory_metrics</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trajectory_data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  labels </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"total_distance"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> total_distance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"average_speed"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> average_speed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  packed_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> pack_trajectory_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trajectory_data</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  timestamp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> datetime</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">now</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-bucket"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"trajectory"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> packed_data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> labels</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">labels</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">pack_trajectory_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trajectory</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">list</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token builtin">bytes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token triple-quoted-string string" style="color:#e3116c">"""Pack trajectory data json format"""</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">trajectory</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"utf-8"</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>The labels (total distance and average speed) are stored alongside each record. You can later filter or replicate records based on these values.</p>
<p>Once data is written, you'll see the bucket populate in ReductStore:</p>
<p><img decoding="async" loading="lazy" alt="ReductStore Bucket" src="https://www.reduct.store/assets/images/bucket-ada3e96ae8bc959b1165ad671a12cf2e.png" width="3046" height="1522" class="img_ev3q"></p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="query-by-label">Query by Label<a href="https://www.reduct.store/blog/store-robotic-data#query-by-label" class="hash-link" aria-label="Direct link to Query by Label" title="Direct link to Query by Label" translate="no">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">query_by_label</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bucket_name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> entry_name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> label_key</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> label_value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bucket_name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> record </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      entry_name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      when</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        label_key</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$gt"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> label_value</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Do something with the record</span><br></span></code></pre></div></div>
<p>Remove the <em>'when'</em> condition to return all records with no filtering.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="main-function">Main Function<a href="https://www.reduct.store/blog/store-robotic-data#main-function" class="hash-link" aria-label="Direct link to Main Function" title="Direct link to Main Function" translate="no">​</a></h4>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> create_trajectory_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> store_trajectory_data</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    label_query_result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> query_by_label</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-bucket"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"trajectory"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&amp;total_distance"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> HIGH_DISTANCE</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> label_query_result</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"Data queried by label: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">label_query_result</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/store-robotic-data#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Robotics data pipelines don't have to be a mess of bag files, overpriced cloud storage, and custom scripts duct taped together. ReductStore covers ingestion, retention, replication, and querying in one place, with direct integrations into ROS, Foxglove, Grafana, S3, and Azure.</p>
<p>Start with what you need, and add the rest as your system grows. Check out <a class="" href="https://www.reduct.store/"><strong>reduct.store</strong></a> or read through the <a class="" href="https://www.reduct.store/docs/how-does-it-work"><strong>documentation</strong></a> to get going.</p>
<hr>
<p>If you have any questions or comments, feel free to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>robotics</category>
        </item>
        <item>
            <title><![CDATA[ReductStore v1.19: Open Data Backbone for Robotics and ROS]]></title>
            <link>https://www.reduct.store/blog/news/reductstore-v1_19_0-released</link>
            <guid>https://www.reduct.store/blog/news/reductstore-v1_19_0-released</guid>
            <pubDate>Wed, 08 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[ReductStore v1.19 introduces Apache 2.0 open-source licensing, a ROS-inspired hierarchical data model with attachments for schemas and metadata, a native Zenoh API for edge communication, and ReductBridge for seamless integration with ROS1 and ROS2.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="ReductStore v1.19.0 Released" src="https://www.reduct.store/assets/images/release-v1-19-49cfe49eac38b335b63b6161c7675aa7.png" width="1298" height="728" class="img_ev3q"></p>
<p>ReductStore <a href="https://github.com/reductstore/reductstore/releases/tag/v1.19.0" target="_blank" rel="noopener noreferrer" class=""><strong>1.19.0</strong></a> is now available. This release extends the storage model for robotics and telemetry workloads and introduces new integration points for ROS and Zenoh.</p>
<p>To download the latest release, visit the <a class="" href="https://www.reduct.store/download"><strong>Download Page</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-new-in-1190">What's new in 1.19.0?<a href="https://www.reduct.store/blog/news/reductstore-v1_19_0-released#whats-new-in-1190" class="hash-link" aria-label="Direct link to What's new in 1.19.0?" title="Direct link to What's new in 1.19.0?" translate="no">​</a></h2>
<p>The first major change in v1.19 is licensing. <a class="" href="https://www.reduct.store/blog/news/reductstore-core-apache-2-0"><strong>ReductStore Core is now open source under Apache 2.0</strong></a>, which makes the core database easier to evaluate, integrate, and extend in production systems.</p>
<p>The second major change is the data model. ReductStore now supports hierarchical entry names, similar to ROS topics, and adds entry attachments for schemas and metadata. This makes it possible to represent structured robotics data without flattening topic hierarchies or moving context into external systems.</p>
<p>The release also introduces a <a class="" href="https://www.reduct.store/docs/integrations/zenoh"><strong>native Zenoh API</strong></a> for direct ingestion and querying over Zenoh, and <a class="" href="https://www.reduct.store/docs/reduct-bridge"><strong>ReductBridge</strong></a> for ROS1 and ROS2 integration.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="nested-data-model-with-attachments">Nested Data Model with Attachments<a href="https://www.reduct.store/blog/news/reductstore-v1_19_0-released#nested-data-model-with-attachments" class="hash-link" aria-label="Direct link to Nested Data Model with Attachments" title="Direct link to Nested Data Model with Attachments" translate="no">​</a></h3>
<p>The new hierarchical data model lets you organize data in a path-based structure, similar to ROS topics, Zenoh key expressions, or MQTT topics. Instead of relying on a flat namespace, ReductStore can now store data in a form that matches the structure used by upstream systems.</p>
<p>Each entry can also include attachments for schemas and metadata. These attachments preserve the context required by downstream tooling without changing the record payload itself. For example, the <a class="" href="https://www.reduct.store/docs/extensions/official/ros-ext"><strong>ROS Extension</strong></a> can use them to decode serialized ROS messages and export them to MCAP files.</p>
<p><img decoding="async" loading="lazy" alt="Web Console with Nested Data Model" src="https://www.reduct.store/assets/images/web-console-nested-3384b563af4ba642e073a80dc50d5e37.png" width="1825" height="875" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="native-zenoh-api">Native Zenoh API<a href="https://www.reduct.store/blog/news/reductstore-v1_19_0-released#native-zenoh-api" class="hash-link" aria-label="Direct link to Native Zenoh API" title="Direct link to Native Zenoh API" translate="no">​</a></h3>
<p>Zenoh is increasingly used in robotics and edge environments as a low-overhead protocol for distributed data exchange. With the <a class="" href="https://www.reduct.store/docs/integrations/zenoh"><strong>native Zenoh API</strong></a>, ReductStore can participate directly in Zenoh-based systems without requiring an additional bridge or adapter.</p>
<p>You can start ReductStore with the Zenoh API enabled using a minimal Docker configuration:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> pull reduct/store:v1.19.0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">--env</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"RS_ZENOH_ENABLED=ON"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--env</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"RS_ZENOH_CONFIG={}"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">--env</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"RS_ZENOH_SUB_KEYEXPRS=**"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8383</span><span class="token plain">:8383 </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">36597</span><span class="token plain">:36597 </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">7446</span><span class="token plain">:7446 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  reduct/store:v1.19.0</span><br></span></code></pre></div></div>
<p>Once enabled, the API allows you to write data to ReductStore directly over Zenoh. If a sample includes a JSON attachment, ReductStore stores it as record labels:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> zenoh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory/line1/camera"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">PAYLOAD </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">b"&lt;binary payload&gt;"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">LABELS </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"robot"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"alpha"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ok"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        PAYLOAD</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        attachment</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">LABELS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>You can also query data through Zenoh. For conditional queries, pass a <code>when</code> expression in the query attachment. If you want all matching records returned individually, use <code>zenoh.ConsolidationMode.NONE</code>:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> zenoh</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">KEY </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"factory/line1/when-query"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">CONSOLIDATION </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ConsolidationMode</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">NONE</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">attachment </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dumps</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"when"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"&amp;status"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$eq"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ok"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">zenoh</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    replies </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        reply</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> reply </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            timeout</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">5.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            attachment</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">attachment</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            consolidation</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">CONSOLIDATION</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> reply </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> replies</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">reply</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">payload</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">to_bytes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>Data ingested through Zenoh is stored in ReductStore like data written through the HTTP API, so it remains available for querying, replication, and downstream tools and extensions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductbridge-for-ros-integration">ReductBridge for ROS Integration<a href="https://www.reduct.store/blog/news/reductstore-v1_19_0-released#reductbridge-for-ros-integration" class="hash-link" aria-label="Direct link to ReductBridge for ROS Integration" title="Direct link to ReductBridge for ROS Integration" translate="no">​</a></h3>
<p>This release also introduces <a href="https://github.com/reductstore/reduct-bridge" target="_blank" rel="noopener noreferrer" class=""><strong>ReductBridge</strong></a>, a new project for integrating ReductStore with ROS1 and ROS2. ReductBridge automatically labels ROS messages and stores related schemas and metadata as attachments.</p>
<p>This makes raw ROS payloads usable after ingestion: they can be decoded later with the ROS Extension or exported to MCAP files. ROS support is the first target, but the same pattern can be extended to operating system metrics, logs, and other telemetry to build a unified storage layer with consistent labeling and metadata.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What’s Next<a href="https://www.reduct.store/blog/news/reductstore-v1_19_0-released#whats-next" class="hash-link" aria-label="Direct link to What’s Next" title="Direct link to What’s Next" translate="no">​</a></h2>
<p>The next area of work is data compression and storage efficiency, especially for robotics workloads where payload sizes and formats vary significantly. We plan to introduce backend compression optimized for ReductStore's storage model, where many records are packed into a single block.</p>
<p>We also plan to store metadata in Parquet format. This should improve query efficiency, make metadata accessible without scanning the underlying data blocks, and simplify integration with analytics and data lake tooling.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="compatibility-and-migration">Compatibility and Migration<a href="https://www.reduct.store/blog/news/reductstore-v1_19_0-released#compatibility-and-migration" class="hash-link" aria-label="Direct link to Compatibility and Migration" title="Direct link to Compatibility and Migration" translate="no">​</a></h2>
<p>Starting with v1.19, ReductStore Docker images no longer run as <code>root</code> for security reasons. If you deploy with Docker, make sure the mounted data directory is writable by UID/GID <code>10001:10001</code> before upgrading.</p>
<hr>
<p>If you have questions or feedback, join the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>
<p>Thanks for using <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>!</p>]]></content:encoded>
            <category>news</category>
        </item>
        <item>
            <title><![CDATA[Visualize Robotics Data in Grafana with ReductStore]]></title>
            <link>https://www.reduct.store/blog/grafana-visualization-ros-data</link>
            <guid>https://www.reduct.store/blog/grafana-visualization-ros-data</guid>
            <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Outline for a tutorial showing how to use ReductStore and the ReductROS extension to extract ROS 2 data to JSON and visualize it in Grafana dashboards.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Grafana query editor with ReductROS extension" src="https://www.reduct.store/assets/images/panel-e7fa8b5d10e3aa787dc737a5de17cc73.webp" width="1221" height="909" class="img_ev3q"></p>
<p><strong>Grafana</strong> is a powerful tool for visualizing time-series data, and it is widely used for monitoring and analysis.
However, it does not natively understand robotics data formats, such as ROS 2 messages, since they are usually stored in binary formats (e.g., CDR).
<strong><a class="" href="https://www.reduct.store/">ReductStore's</a></strong> flexible query engine and extension system can bridge this gap. With the <strong><a class="" href="https://www.reduct.store/docs/extensions/official/ros-ext">ReductROS</a></strong> extension,
you can extract ROS 2 messages as JSON directly in Grafana queries.
This enables you to build rich dashboards and alerts on your robotics data without preprocessing it into a different format.</p>
<!-- -->
<p>In this tutorial, we will create a Grafana dashboard supported by the <strong><a class="" href="https://www.reduct.store/docs/integrations/grafana">ReductStore data source plugin</a></strong>.
Using the ReductROS extension, the dashboard will convert ROS 2 messages to JSON upon query, enabling us to visualize and analyze the data directly in Grafana.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerequisites">Prerequisites<a href="https://www.reduct.store/blog/grafana-visualization-ros-data#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<p>This tutorial assumes that you have the following set up:</p>
<ul>
<li class="">A ReductStore instance with ROS 2 data (either your own or from the demo instance). Some robotics data is available on <strong><a href="https://play.reduct.store/replica/" target="_blank" rel="noopener noreferrer" class="">our demo ReductStore instance</a></strong>.</li>
<li class="">A Grafana 10+ instance (local or cloud) with the ReductStore data source plugin installed. You can use our Docker image, or install Grafana separately and add the plugin manually, following the documentation.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="set-up-the-reductstore-data-source-in-grafana">Set up the ReductStore data source in Grafana<a href="https://www.reduct.store/blog/grafana-visualization-ros-data#set-up-the-reductstore-data-source-in-grafana" class="hash-link" aria-label="Direct link to Set up the ReductStore data source in Grafana" title="Direct link to Set up the ReductStore data source in Grafana" translate="no">​</a></h2>
<p>Let's run Grafana locally using Docker and connect it to ReductStore:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">-d</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000</span><span class="token plain">:3000 reduct/grafana:latest</span><br></span></code></pre></div></div>
<p>Open Grafana at <code>http://localhost:3000</code> and log in with the default credentials, <strong>admin/admin</strong>.
Then, go to the "Data Sources" panel. There, you can find the default ReductStore data source if you are using our Docker image.
Otherwise, you can add it manually on the "Add new connection" page.</p>
<p>Once the data source has been added, configure it using the following parameters:</p>
<ul>
<li class="">Name: <code>reductstore-datasource</code></li>
<li class="">Server URL: <code>https://play.reduct.store/replica</code> (or your ReductStore instance URL)</li>
<li class="">API Token: <code>reductstore</code> for the demo instance (or your API token)</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Grafana ReductStore data source" src="https://www.reduct.store/assets/images/datasource-6072f369e0864b3cac8a641809f1ab9f.webp" width="1102" height="773" class="img_ev3q"></p>
<p>Click "Save &amp; Test" to verify the connection. You should see a success message confirming that Grafana can connect to ReductStore.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="query-and-extract-ros-2-data-to-json">Query and extract ROS 2 data to JSON<a href="https://www.reduct.store/blog/grafana-visualization-ros-data#query-and-extract-ros-2-data-to-json" class="hash-link" aria-label="Direct link to Query and extract ROS 2 data to JSON" title="Direct link to Query and extract ROS 2 data to JSON" translate="no">​</a></h2>
<p>Now we can create a new dashboard and add a panel to query ROS 2 data from ReductStore. Go to Dashboards -&gt; New Dashboard -&gt; Add new panel. In the query editor, select the ReductStore data source and provide the following query parameters to extract a ROS 2 topic as JSON:</p>
<ul>
<li class="">Bucket: <code>orion</code></li>
<li class="">Entry: any entry, you can use wildcards (e.g., <code>*</code>) to query multiple entries</li>
<li class="">Scope: LabelAndContent , which allows us to view both labels and content of the messages if it's JSON</li>
</ul>
<p>when the parameters are set, we can read data with the following query:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"$each_t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"$__interval"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"#ext"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"ros"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"extract"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>The magic happens in the <code>#ext</code> section, where we activate the <code>ros</code> extension and specify that we want to extract data.
The extension reads the serialized ROS content for the specified bucket/entry, decodes the ROS 2 messages, and returns them as JSON records with timestamps and labels.
You can read more about the query format in the <strong><a class="" href="https://www.reduct.store/docs/next/extensions/official/ros-ext/raw#query-format">ReductROS documentation</a></strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="scale-and-performance">Scale and performance<a href="https://www.reduct.store/blog/grafana-visualization-ros-data#scale-and-performance" class="hash-link" aria-label="Direct link to Scale and performance" title="Direct link to Scale and performance" translate="no">​</a></h2>
<p>Since your data may be large and data extraction can be computationally intensive, here are some tips to ensure good performance:</p>
<ul>
<li class="">Use Grafana <code>$__interval</code> with <code>$each_t</code> for interval-based queries</li>
<li class="">Add label filters to narrow down the data (e.g., filter by topic name or message type)</li>
<li class="">Start with a narrow time range and expand as needed</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="optional-visualize-binary-topics-images">Optional: Visualize binary topics (images)<a href="https://www.reduct.store/blog/grafana-visualization-ros-data#optional-visualize-binary-topics-images" class="hash-link" aria-label="Direct link to Optional: Visualize binary topics (images)" title="Direct link to Optional: Visualize binary topics (images)" translate="no">​</a></h2>
<p>If you're interested in visualizing binary data, such as images or point clouds, you might be interested in the ReductROS extension.
The ReductROS extension can encode binary data as base64 strings or in JPEG format.
This data can then be displayed in Grafana's "Text" panel with HTML rendering.
For instance, if your topic contains raw images, you can specify the encoding in the query.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"$each_t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"$__interval"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"#ext"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"ros"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"extract"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"encode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"data"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"jpeg"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/grafana-visualization-ros-data#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Using ReductStore and the ReductROS extension allows you to easily extract and visualize ROS 2 data in Grafana without preprocessing it into a different format.
This enables you to build rich dashboards and alerts on your robotics data, allowing for better monitoring and analysis of your robotic systems.</p>
<p>Importantly, this approach preserves your data's original format, enabling flexible querying without the need for additional pipelines or transformations.
You can also combine it with other Grafana features, such as alerting and annotations, to gain additional insights from your robotics data.</p>
<hr>
<p>I hope this tutorial gives you a good starting point for visualizing your ROS 2 data in Grafana using ReductStore.
If you have questions or want to discuss which edition fits your use case, please reach out on the <strong><a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class="">ReductStore Community</a></strong> forum or via our <strong><a class="" href="https://www.reduct.store/contact">contact page</a></strong>.</p>]]></content:encoded>
            <category>tutorial</category>
            <category>grafana</category>
            <category>ros</category>
        </item>
        <item>
            <title><![CDATA[ReductStore Core Adopts Apache 2.0 License]]></title>
            <link>https://www.reduct.store/blog/news/reductstore-core-apache-2-0</link>
            <guid>https://www.reduct.store/blog/news/reductstore-core-apache-2-0</guid>
            <pubDate>Tue, 17 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Starting with v1.19, ReductStore is split into Core (Apache 2.0) and Pro (commercial).]]></description>
            <content:encoded><![CDATA[<p>Hello, everyone!</p>
<p>Starting with <strong>ReductStore v1.19</strong>, we are changing how we license and package the project. From this version onward, ReductStore is split into two editions: <strong><a href="https://github.com/reductstore/reductstore" target="_blank" rel="noopener noreferrer" class="">ReductStore Core</a></strong> and <strong>ReductStore Pro</strong>.</p>
<!-- -->
<p>Previously, ReductStore shipped as a single edition under <strong>BUSL-1.1 (Business Source License)</strong>. With v1.19, the core database is open source under Apache 2.0, while Pro continues under commercial terms.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-changing">What's changing<a href="https://www.reduct.store/blog/news/reductstore-core-apache-2-0#whats-changing" class="hash-link" aria-label="Direct link to What's changing" title="Direct link to What's changing" translate="no">​</a></h2>
<p>With this release, <strong><a href="https://github.com/reductstore/reductstore" target="_blank" rel="noopener noreferrer" class="">ReductStore Core</a></strong> becomes the open-source foundation of the project and is now available under the <strong>Apache License 2.0</strong>. It includes the database server and the core functionality that most users rely on for edge deployments and everyday data workflows.</p>
<p>Alongside Core, we will continue offering <strong>ReductStore Pro</strong>, which is distributed under a <strong>commercial license</strong>. The Pro edition provides additional capabilities and support options for teams with more advanced requirements.
See <strong><a class="" href="https://www.reduct.store/pricing">Pricing</a></strong> for a clear comparison of what is included in each edition.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-were-doing-this">Why we're doing this<a href="https://www.reduct.store/blog/news/reductstore-core-apache-2-0#why-were-doing-this" class="hash-link" aria-label="Direct link to Why we're doing this" title="Direct link to Why we're doing this" translate="no">​</a></h2>
<p>We want to better support open source communities and make it easier to contribute to, integrate with, and build on top of <strong><a href="https://github.com/reductstore/reductstore" target="_blank" rel="noopener noreferrer" class="">ReductStore Core</a></strong>. Just as importantly, we want users to be able to run ReductStore on the edge without licensing restrictions for the core database use cases.</p>
<p>At the same time, we want to keep a sustainable commercial model for companies that build more complex cloud setups or need advanced support and functionality around robotics and IIoT data formats. That is the role of ReductStore Pro.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-this-means-for-you">What this means for you<a href="https://www.reduct.store/blog/news/reductstore-core-apache-2-0#what-this-means-for-you" class="hash-link" aria-label="Direct link to What this means for you" title="Direct link to What this means for you" translate="no">​</a></h2>
<p>If you use the core database functionality, you can freely adopt, integrate, and distribute <strong><a href="https://github.com/reductstore/reductstore" target="_blank" rel="noopener noreferrer" class="">ReductStore Core</a></strong> under the Apache 2.0 license. Existing workflows and upgrades remain straightforward.</p>
<p>For teams that rely on advanced functionality or require commercial support, <strong>ReductStore Pro</strong> remains available as the supported commercial edition. We will clearly document which features belong to Core and which are part of Pro in the documentation and release notes.</p>
<p>If you have questions or want to discuss which edition fits your use case, please reach out on the <strong><a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class="">ReductStore Community</a></strong> forum or via our <strong><a class="" href="https://www.reduct.store/contact">contact page</a></strong>.</p>]]></content:encoded>
            <category>news</category>
        </item>
        <item>
            <title><![CDATA[Air-Gapped Drone Data Operations with Delayed Sync and Auditability]]></title>
            <link>https://www.reduct.store/blog/air-gapped-drone-data</link>
            <guid>https://www.reduct.store/blog/air-gapped-drone-data</guid>
            <pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to set up local storage on drones with delayed replication and audit trail using ReductStore.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Architecture for Air-Gapped Drone Data" src="https://www.reduct.store/assets/images/architecture-drone.drawio-d19f009e30b8fb521f7165376d7a46da.svg" class="img_ev3q"></p>
<p>Drones in air-gapped environments produce a <strong>lot</strong> of data (camera images, telemetry, logs, model outputs). Storing this data reliably on each drone and syncing it to a ground station later can be hard. <strong>ReductStore</strong> makes this easier: it's a lightweight, time-series object store that works offline and replicate data when a connection is available.</p>
<p>This guide explains a simple setup where each drone stores data locally with labels, replicates records to a ground station based on what it detects, and keeps a clear audit trail of what was captured and replicated.</p>
<!-- -->
<p>What we'll cover:</p>
<ul>
<li class=""><a href="https://www.reduct.store/blog/air-gapped-drone-data#drone-to-ground-architecture" class=""><strong>Drone-to-Ground Architecture</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/air-gapped-drone-data#setting-up-the-drone-node" class=""><strong>Setting Up the Drone Node</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/air-gapped-drone-data#storing-drone-data-with-labels" class=""><strong>Storing Drone Data with Labels</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/air-gapped-drone-data#setting-up-selective-replication" class=""><strong>Setting Up Selective Replication</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/air-gapped-drone-data#querying-for-audit-reports" class=""><strong>Querying for Audit Reports</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/air-gapped-drone-data#why-this-setup-works-well-for-drones" class=""><strong>Why This Setup Works Well for Drones</strong></a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="drone-to-ground-architecture">Drone-to-Ground Architecture<a href="https://www.reduct.store/blog/air-gapped-drone-data#drone-to-ground-architecture" class="hash-link" aria-label="Direct link to Drone-to-Ground Architecture" title="Direct link to Drone-to-Ground Architecture" translate="no">​</a></h2>
<p>The architecture has three main components:</p>
<ul>
<li class=""><strong>Each drone runs a small ReductStore server</strong> to save images and telemetry locally on disk (this lets the drone operate fully offline).</li>
<li class=""><strong>A ground station runs a ReductStore instance</strong> that receives replicated data for analysis and archiving.</li>
<li class=""><strong>ReductStore replication tasks</strong> copy data from drone to ground based on labels and conditions (e.g., only records flagged as anomalies, plus context around them).</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Drone Workflow" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2MDAgNzAwIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIj4KICA8ZGVmcz4KICAgIDxzdHlsZT4KICAgICAgOnJvb3QgewogICAgICAgIC8qIExpZ2h0IE1vZGUgQ29sb3JzICovCiAgICAgICAgLS1jYXJkLWJnOiAjRkZGRkZGOwogICAgICAgIC0tY2FyZC1icmFuZDogIzJCMDU0ODsKICAgICAgICAtLWNhcmQtc3Ryb2tlOiAjMzMzMzMzOwogICAgICAgIC0tc2hhZG93OiAjMDAwMDAwOwogICAgICAgIC0tdGV4dC1tYWluOiAjMzMzMzMzOwogICAgICAgIC0tdGV4dC1tdXRlZDogIzY2NjY2NjsKICAgICAgICAtLXRleHQtaW52ZXJzZTogI0ZGRkZGRjsKICAgICAgICAtLWxpbmU6ICMzMzMzMzM7CiAgICAgIH0KCiAgICAgIEBtZWRpYSAocHJlZmVycy1jb2xvci1zY2hlbWU6IGRhcmspIHsKICAgICAgICA6cm9vdCB7CiAgICAgICAgICAvKiBEYXJrIE1vZGUgQ29sb3JzICovCiAgICAgICAgICAtLWNhcmQtYmc6ICMxRTFFMUU7CiAgICAgICAgICAtLWNhcmQtc3Ryb2tlOiAjNTU1NTU1OwogICAgICAgICAgLS1zaGFkb3c6ICMwMDAwMDA7CiAgICAgICAgICAtLXRleHQtbWFpbjogI0VGRUZFRjsKICAgICAgICAgIC0tdGV4dC1tdXRlZDogI0EwQTBBMDsKICAgICAgICAgIC0tbGluZTogI0VGRUZFRjsKICAgICAgICB9CiAgICAgIH0KCiAgICAgIHRleHQgewogICAgICAgIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWksIC1hcHBsZS1zeXN0ZW0sICJTZWdvZSBVSSIsIFJvYm90bywgSGVsdmV0aWNhLCBBcmlhbCwgc2Fucy1zZXJpZjsKICAgICAgfQogICAgICAKICAgICAgLmNhcmQtc3RhbmRhcmQgeyBmaWxsOiB2YXIoLS1jYXJkLWJnKTsgc3Ryb2tlOiB2YXIoLS1jYXJkLXN0cm9rZSk7IHN0cm9rZS13aWR0aDogMjsgfQogICAgICAuY2FyZC1icmFuZCB7IGZpbGw6IHZhcigtLWNhcmQtYnJhbmQpOyBzdHJva2U6IHZhcigtLWNhcmQtc3Ryb2tlKTsgc3Ryb2tlLXdpZHRoOiAyOyB9CiAgICAgIC5zaGFkb3cgeyBmaWxsOiB2YXIoLS1zaGFkb3cpOyB9CiAgICAgIAogICAgICAudGV4dC10aXRsZSB7IGZvbnQtd2VpZ2h0OiA2MDA7IGZvbnQtc2l6ZTogMThweDsgZmlsbDogdmFyKC0tdGV4dC1tYWluKTsgfQogICAgICAudGV4dC10aXRsZS1pbnZlcnNlIHsgZm9udC13ZWlnaHQ6IDYwMDsgZm9udC1zaXplOiAxOHB4OyBmaWxsOiB2YXIoLS10ZXh0LWludmVyc2UpOyB9CiAgICAgIC50ZXh0LWJ1bGxldCB7IGZvbnQtc2l6ZTogMTVweDsgZm9udC13ZWlnaHQ6IDQwMDsgZmlsbDogdmFyKC0tdGV4dC1tYWluKTsgfQogICAgICAudGV4dC1idWxsZXQtaW52ZXJzZSB7IGZvbnQtc2l6ZTogMTVweDsgZm9udC13ZWlnaHQ6IDQwMDsgZmlsbDogdmFyKC0tdGV4dC1pbnZlcnNlKTsgfQogICAgICAudGV4dC1zdWJ0aXRsZSB7IGZvbnQtc2l6ZTogMTRweDsgZmlsbDogdmFyKC0tdGV4dC1tdXRlZCk7IH0KICAgICAgLnRleHQtbm90ZSB7IGZvbnQtc2l6ZTogMTNweDsgZm9udC1zdHlsZTogaXRhbGljOyBmaWxsOiB2YXIoLS10ZXh0LW11dGVkKTsgfQogICAgICAKICAgICAgLmNvbm5lY3RvciB7IHN0cm9rZTogdmFyKC0tbGluZSk7IHN0cm9rZS13aWR0aDogMjsgfQogICAgICAuYXJyb3ctaGVhZCB7IGZpbGw6IHZhcigtLWxpbmUpOyB9CiAgICA8L3N0eWxlPgoKICAgIDxtYXJrZXIgaWQ9ImFycm93IiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI2IiBtYXJrZXJIZWlnaHQ9IjYiIG9yaWVudD0iYXV0by1zdGFydC1yZXZlcnNlIj4KICAgICAgPHBhdGggZD0iTSAwIDAgTCAxMCA1IEwgMCAxMCB6IiBjbGFzcz0iYXJyb3ctaGVhZCIgLz4KICAgIDwvbWFya2VyPgogIDwvZGVmcz4KCiAgPHJlY3QgeD0iMTU4IiB5PSI0OCIgd2lkdGg9IjI4NCIgaGVpZ2h0PSI4MCIgcng9IjE2IiBjbGFzcz0ic2hhZG93IiAvPgogIDxyZWN0IHg9IjE1MCIgeT0iNDAiIHdpZHRoPSIyODQiIGhlaWdodD0iODAiIHJ4PSIxNiIgY2xhc3M9ImNhcmQtc3RhbmRhcmQiIC8+CiAgCiAgPHRleHQgeD0iMjkyIiB5PSI3MCIgY2xhc3M9InRleHQtdGl0bGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPkRyb25lIFNlbnNvcnM8L3RleHQ+CiAgPHRleHQgeD0iMjkyIiB5PSI5NCIgY2xhc3M9InRleHQtc3VidGl0bGUiIHRleHQtYW5jaG9yPSJtaWRkbGUiPmNhbWVyYSwgSU1VLCBsaWRhcjwvdGV4dD4KCiAgPGxpbmUgeDE9IjI5MiIgeTE9IjEyMCIgeDI9IjI5MiIgeTI9IjE3NSIgY2xhc3M9ImNvbm5lY3RvciIgbWFya2VyLWVuZD0idXJsKCNhcnJvdykiIC8+CgoKICA8cmVjdCB4PSIxNTgiIHk9IjE5MyIgd2lkdGg9IjI4NCIgaGVpZ2h0PSI5NiIgcng9IjE2IiBjbGFzcz0ic2hhZG93IiAvPgogIDxyZWN0IHg9IjE1MCIgeT0iMTg1IiB3aWR0aD0iMjg0IiBoZWlnaHQ9Ijk2IiByeD0iMTYiIGNsYXNzPSJjYXJkLWJyYW5kIiAvPgogIAogIDx0ZXh0IHg9IjI5MiIgeT0iMjE1IiBjbGFzcz0idGV4dC10aXRsZS1pbnZlcnNlIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5FZGdlIFJlZHVjdFN0b3JlPC90ZXh0PgogIDx0ZXh0IHg9IjE3NSIgeT0iMjQ0IiBjbGFzcz0idGV4dC1idWxsZXQtaW52ZXJzZSI+4oCiIHdyaXRlIHdpdGggbGFiZWxzPC90ZXh0PgogIDx0ZXh0IHg9IjE3NSIgeT0iMjY2IiBjbGFzcz0idGV4dC1idWxsZXQtaW52ZXJzZSI+4oCiIEZJRk8gcmV0ZW50aW9uPC90ZXh0PgoKICA8bGluZSB4MT0iMjkyIiB5MT0iMjgxIiB4Mj0iMjkyIiB5Mj0iMzU1IiBjbGFzcz0iY29ubmVjdG9yIiBtYXJrZXItZW5kPSJ1cmwoI2Fycm93KSIgLz4KICA8dGV4dCB4PSIzMDIiIHk9IjMyNSIgY2xhc3M9InRleHQtbm90ZSI+KHdoZW4gdHJ1c3RlZCBsaW5rIGlzIGF2YWlsYWJsZSk8L3RleHQ+CgoKICA8cmVjdCB4PSIxNTgiIHk9IjM3MyIgd2lkdGg9IjI4NCIgaGVpZ2h0PSI5NiIgcng9IjE2IiBjbGFzcz0ic2hhZG93IiAvPgogIDxyZWN0IHg9IjE1MCIgeT0iMzY1IiB3aWR0aD0iMjg0IiBoZWlnaHQ9Ijk2IiByeD0iMTYiIGNsYXNzPSJjYXJkLXN0YW5kYXJkIiAvPgogIAogIDx0ZXh0IHg9IjI5MiIgeT0iMzk1IiBjbGFzcz0idGV4dC10aXRsZSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+UmVwbGljYXRpb24gVGFzazwvdGV4dD4KICA8dGV4dCB4PSIxNzUiIHk9IjQyNCIgY2xhc3M9InRleHQtYnVsbGV0Ij7igKIgZmlsdGVyIGJ5IGxhYmVsczwvdGV4dD4KICA8dGV4dCB4PSIxNzUiIHk9IjQ0NiIgY2xhc3M9InRleHQtYnVsbGV0Ij7igKIgaW5jbHVkZSBjb250ZXh0PC90ZXh0PgoKICA8bGluZSB4MT0iMjkyIiB5MT0iNDYxIiB4Mj0iMjkyIiB5Mj0iNTM1IiBjbGFzcz0iY29ubmVjdG9yIiBtYXJrZXItZW5kPSJ1cmwoI2Fycm93KSIgLz4KCgogIDxyZWN0IHg9IjE1OCIgeT0iNTUzIiB3aWR0aD0iMjg0IiBoZWlnaHQ9IjgwIiByeD0iMTYiIGNsYXNzPSJzaGFkb3ciIC8+CiAgPHJlY3QgeD0iMTUwIiB5PSI1NDUiIHdpZHRoPSIyODQiIGhlaWdodD0iODAiIHJ4PSIxNiIgY2xhc3M9ImNhcmQtYnJhbmQiIC8+CiAgCiAgPHRleHQgeD0iMjkyIiB5PSI1NzUiIGNsYXNzPSJ0ZXh0LXRpdGxlLWludmVyc2UiIHRleHQtYW5jaG9yPSJtaWRkbGUiPkdyb3VuZCBSZWR1Y3RTdG9yZTwvdGV4dD4KICA8dGV4dCB4PSIxNzUiIHk9IjYwNCIgY2xhc3M9InRleHQtYnVsbGV0LWludmVyc2UiPuKAoiBxdWVyeSAmYW1wOyBhdWRpdDwvdGV4dD4KCjwvc3ZnPgo=" width="600" height="700" class="img_ev3q"></p>
<p>Each drone pushes its data to the ground whenever it is connected. If the network disconnects, replication continues when the drone reconnects. This approach provides offline capability, lets you decide which data to replicate, and keeps a clear record of what happened.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-the-drone-node">Setting Up the Drone Node<a href="https://www.reduct.store/blog/air-gapped-drone-data#setting-up-the-drone-node" class="hash-link" aria-label="Direct link to Setting Up the Drone Node" title="Direct link to Setting Up the Drone Node" translate="no">​</a></h2>
<p>Start by running ReductStore on the drone's companion computer. Here is a minimal <code>docker-compose.yml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reductstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reduct/store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8383:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_API_TOKEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;DRONE_TOKEN</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_QUOTA_TYPE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> FIFO</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_QUOTA_SIZE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10000000000</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># 10 GB</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">user</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0:0"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Run as root with host bind mount in this example</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/data</span><br></span></code></pre></div></div>
<p>Run it with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> compose up </span><span class="token parameter variable" style="color:#36acaa">-d</span><br></span></code></pre></div></div>
<p>This starts a ReductStore server with a <code>mission-data</code> bucket that uses FIFO retention. Old data is deleted only when the 10 GB limit is reached, so the drone always keeps as much history as possible.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>FIFO quota is volume-based, not time-based. This means data is only deleted when disk space runs out, not after a fixed time period. This is important for drones that may sit idle between missions.</p></div></div>
<p>If you prefer Snap instead of Docker:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> snap </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> reductstore</span><br></span></code></pre></div></div>
<p>That starts a ReductStore server on port <code>8383</code> by default. You can then create the bucket using the <strong><a href="https://github.com/reductstore/reduct-cli" target="_blank" rel="noopener noreferrer" class="">Reduct CLI</a></strong>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> drone </span><span class="token parameter variable" style="color:#36acaa">-L</span><span class="token plain"> http://localhost:8383 </span><span class="token parameter variable" style="color:#36acaa">-t</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;DRONE_TOKEN&gt;"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli bucket create drone/mission-data --quota-type FIFO --quota-size 10GB</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="storing-drone-data-with-labels">Storing Drone Data with Labels<a href="https://www.reduct.store/blog/air-gapped-drone-data#storing-drone-data-with-labels" class="hash-link" aria-label="Direct link to Storing Drone Data with Labels" title="Direct link to Storing Drone Data with Labels" translate="no">​</a></h2>
<p>Use labels to tag every record with mission context. This is what makes selective replication and auditing possible later. Here is an example using the Python SDK:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> time</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> hashlib</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"&lt;DRONE_TOKEN&gt;"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"mission-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Read a camera frame</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        payload </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"frame.jpg"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"rb"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        checksum </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> hashlib</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sha256</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">payload</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hexdigest</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        timestamp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1_000_000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># microseconds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Write with mission labels</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"camera"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            payload</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            timestamp</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            labels</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"mission_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"m-2026-02-24-01"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"platform_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"uav-07"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"anomaly"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"false"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"confidence"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0.95"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"checksum"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> checksum</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            content_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"image/jpeg"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Write telemetry as a CSV batch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csv_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ts,lat,lon,alt,speed\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csv_data </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1708771200000000,47.3769,8.5417,450.2,12.5\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csv_data </span><span class="token operator" style="color:#393A34">+=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1708771201000000,47.3770,8.5418,451.0,12.8\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"telemetry"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            csv_data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            timestamp</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            labels</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"mission_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"m-2026-02-24-01"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"platform_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"uav-07"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"anomaly"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"false"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"checksum"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hashlib</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sha256</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">csv_data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">hexdigest</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            content_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"text/csv"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>The <code>anomaly</code> label is important: it lets the replication task decide what to sync based on what the drone actually sees. For example, if the drone detects something unusual (an object, a warning, a low confidence score), it sets <code>anomaly=true</code>. The replication task can then automatically sync that record — plus the context around it.</p>
<p>The <code>checksum</code> label gives you a simple way to verify data integrity during audits.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-selective-replication">Setting Up Selective Replication<a href="https://www.reduct.store/blog/air-gapped-drone-data#setting-up-selective-replication" class="hash-link" aria-label="Direct link to Setting Up Selective Replication" title="Direct link to Setting Up Selective Replication" translate="no">​</a></h2>
<p>Once the drone connects to a trusted network, replication sends only the relevant records to the ground station. The simplest approach is to replicate based on a label, for example only records where the drone detected an anomaly:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> drone </span><span class="token parameter variable" style="color:#36acaa">-L</span><span class="token plain"> http://localhost:8383 </span><span class="token parameter variable" style="color:#36acaa">-t</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;DRONE_TOKEN&gt;"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli replica create drone/mission-to-ground </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    mission-data </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    https://</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">GROUND_TOKEN</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">@</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ground-address</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">/drone-data </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--when</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{"&amp;anomaly": {"$eq": "true"}}'</span><br></span></code></pre></div></div>
<p>This creates a replication task that copies only records where <code>anomaly=true</code> from the drone's <code>mission-data</code> bucket to the ground station.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="replicating-with-context-before-and-after">Replicating with context (before and after)<a href="https://www.reduct.store/blog/air-gapped-drone-data#replicating-with-context-before-and-after" class="hash-link" aria-label="Direct link to Replicating with context (before and after)" title="Direct link to Replicating with context (before and after)" translate="no">​</a></h3>
<p>In many cases, you don't just want the anomaly record itself — you also want to see what happened <strong>before</strong> it. ReductStore supports this with the <code>#ctx_before</code> and <code>#ctx_after</code> directives. For example, to replicate each anomaly record plus 30 seconds of data before it and 10 seconds after:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"&amp;anomaly"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"$eq"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"true"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"#ctx_before"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"30s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"#ctx_after"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"10s"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>This is powerful for drone operations: imagine the drone's onboard model detects an unexpected object. ReductStore will replicate that record <strong>and</strong> the 30 seconds of camera frames leading up to the detection, so the ground team can review what happened.</p>
<p>You can provision this directly in Docker using environment variables:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reductstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reduct/store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8383:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_API_TOKEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;DRONE_TOKEN</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_QUOTA_TYPE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> FIFO</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_1_QUOTA_SIZE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10000000000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">to</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">ground</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_SRC_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mission</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_DST_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> drone</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_DST_HOST</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//&lt;ground</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">address</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_DST_TOKEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;GROUND_TOKEN</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REPLICATION_1_WHEN</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        {</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          "&amp;anomaly": { "$$eq": "true" },</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          "#ctx_before": "30s",</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">          "#ctx_after": "10s"</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">        }</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">user</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0:0"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Run as root with host bind mount in this example</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/data</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>With this setup, the drone can operate fully offline. Replication runs automatically when a connection is available and waits when it's not. It's also possible to pause replication tasks if needed. And because context is included, the ground team always has enough data to understand what triggered the event.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="querying-for-audit-reports">Querying for Audit Reports<a href="https://www.reduct.store/blog/air-gapped-drone-data#querying-for-audit-reports" class="hash-link" aria-label="Direct link to Querying for Audit Reports" title="Direct link to Querying for Audit Reports" translate="no">​</a></h2>
<p>After a mission, you can query the ground station to check what was captured and replicated. Here is a simple example that lists all records from a specific mission:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://&lt;ground-address&gt;"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"&lt;GROUND_TOKEN&gt;"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"drone-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Query all camera records from a specific mission</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> record </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"camera"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            when</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"&amp;mission_id"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$eq"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"m-2026-02-24-01"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f"ts=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">record</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">timestamp</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">, "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f"anomaly=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">record</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">labels</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">get</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation string" style="color:#e3116c">'anomaly'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">, "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f"checksum=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">record</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">labels</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">get</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">(</span><span class="token string-interpolation interpolation string" style="color:#e3116c">'checksum'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">)</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">, "</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f"size=</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">record</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">size</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This gives you a clear log of every record in that mission: timestamp, anomaly flag, checksum, and size. You can use this to verify that all expected data arrived on the ground side.</p>
<p>To go further, compare the checksums on the drone with the ground side to confirm nothing was altered during transfer. You can also check the error logs of the replication task to see if any records failed to replicate.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-setup-works-well-for-drones">Why This Setup Works Well for Drones<a href="https://www.reduct.store/blog/air-gapped-drone-data#why-this-setup-works-well-for-drones" class="hash-link" aria-label="Direct link to Why This Setup Works Well for Drones" title="Direct link to Why This Setup Works Well for Drones" translate="no">​</a></h2>
<p>Drones have specific constraints that general purpose databases don't handle well. Here is what makes this setup practical:</p>
<ul>
<li class=""><strong>Full offline operation.</strong> Drones store everything locally and don't need a network connection during the mission. Data is safe on disk until sync happens.</li>
<li class=""><strong>Automatic sync when connected.</strong> When the drone lands or connects to a trusted network, replication picks up where it left off. No manual file transfers, no rsync scripts, no USB sticks.</li>
<li class=""><strong>Smart replication with context.</strong> You don't have to sync everything. The replication task filters by labels and can include past records around each event using <code>#ctx_before</code>. The ground team gets exactly what they need to understand what happened.</li>
<li class=""><strong>Disk never fills up unexpectedly.</strong> FIFO retention removes the oldest data only when the disk is full. The drone always keeps as much history as possible without running out of space mid mission.</li>
<li class=""><strong>Easy auditing.</strong> Every record has a timestamp, labels, and a checksum. After a mission, you can query the ground station and verify exactly what was captured and what was synced.</li>
<li class=""><strong>Store any file type.</strong> Camera frames, telemetry CSV, logs, MCAP files, model outputs. Everything goes into the same system with the same interface.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next Steps<a href="https://www.reduct.store/blog/air-gapped-drone-data#next-steps" class="hash-link" aria-label="Direct link to Next Steps" title="Direct link to Next Steps" translate="no">​</a></h2>
<p>If you want to go deeper, check out these articles:</p>
<ul>
<li class=""><strong><a class="" href="https://www.reduct.store/blog/distributed-storage-mobile-robotics">Distributed Storage in Mobile Robotics</a></strong> for a similar setup with mobile robots and S3 cloud backend</li>
<li class=""><strong><a class="" href="https://www.reduct.store/blog/store-robotic-data">How to Store and Manage Robotics Data</a></strong> for a broader look at ReductStore features for robotics</li>
<li class=""><strong><a class="" href="https://www.reduct.store/docs/guides/data-replication">Data Replication Guide</a></strong> for the full documentation on replication tasks, filters, and modes</li>
<li class=""><strong><a class="" href="https://www.reduct.store/docs/conditional-query">Conditional Query Reference</a></strong> for all available conditional query operators you can use in replication filters and queries</li>
</ul>
<hr>
<p>I hope you found this article helpful! If you have any questions or feedback, don't hesitate to reach out on our <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>]]></content:encoded>
            <category>aerospace</category>
            <category>robotics</category>
            <category>database</category>
        </item>
        <item>
            <title><![CDATA[ReductStore v1.18.0 Released with Resilient Deployments and the Multi-entry API]]></title>
            <link>https://www.reduct.store/blog/news/reductstore-v1_18_0-released</link>
            <guid>https://www.reduct.store/blog/news/reductstore-v1_18_0-released</guid>
            <pubDate>Thu, 05 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[We are pleased to announce the release of ReductStore v1.18.0, which includes support for resilient deployments and a new Multi-entry API for efficient data management.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="ReductStore v1.18.0 Released" src="https://www.reduct.store/assets/images/release-v1-18-96f3024f193dca30accc55d094e47536.png" width="1298" height="728" class="img_ev3q"></p>
<p>We are pleased to announce the release of the latest minor version of <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>, <a href="https://github.com/reductstore/reductstore/releases/tag/v1.18.0" target="_blank" rel="noopener noreferrer" class=""><strong>1.18.0</strong></a>. ReductStore is a high-performance storage and streaming solution designed for storing and managing large volumes of historical data.</p>
<p>To download the latest released version, please visit our <a class="" href="https://www.reduct.store/download"><strong>Download Page</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-new-in-1180">What's new in 1.18.0?<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#whats-new-in-1180" class="hash-link" aria-label="Direct link to What's new in 1.18.0?" title="Direct link to What's new in 1.18.0?" translate="no">​</a></h2>
<p>In this release, we have added support for resilient deployments to build a more robust, fault-tolerant, and highly available ReductStore cluster.
Now, you can implement hot-standby configurations, automatic failover, and seamless recovery to ensure uninterrupted service even in the face of hardware failures or network issues. You can also elastically scale read-only nodes to handle increased read workloads without impacting the performance of the primary nodes.</p>
<p>Additionally, we have introduced a new Multi-entry API that allows you to efficiently manage and query multiple entries in a single request. This API is designed to optimize performance and reduce latency when working with large datasets, making it easier to retrieve and manipulate data in bulk.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="resilient-deployments">Resilient Deployments<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#resilient-deployments" class="hash-link" aria-label="Direct link to Resilient Deployments" title="Direct link to Resilient Deployments" translate="no">​</a></h2>
<p>In ReductStore v1.18.0, resilient deployments are now a first-class feature. Using the <code>RS_INSTANCE_ROLE</code> setting, you can build topologies that keep your ingestion endpoint available during node failures and scale reads independently from writes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hot-standby-active-passive-for-write-availability">Hot standby (active-passive) for write availability<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#hot-standby-active-passive-for-write-availability" class="hash-link" aria-label="Direct link to Hot standby (active-passive) for write availability" title="Direct link to Hot standby (active-passive) for write availability" translate="no">​</a></h3>
<p>Run two nodes against the same backend (a shared filesystem or the same remote backend). Only one node is active at a time: the active node holds a lock file and refreshes it, while the standby waits and takes over when the lock becomes stale.</p>
<p><img decoding="async" loading="lazy" alt="ReductStore Hot standby deployment" src="https://www.reduct.store/assets/images/disaster_recovery_hot_standby-5bc5b7e647b22ba8037a164b4ace6d04.webp" width="1250" height="1240" class="img_ev3q"></p>
<ul>
<li class="">Set <code>RS_INSTANCE_ROLE=PRIMARY</code> for the active node and <code>RS_INSTANCE_ROLE=SECONDARY</code> for the standby.</li>
<li class="">Put both nodes behind a single virtual endpoint (load balancer / reverse proxy).</li>
<li class="">Route traffic only to the node that returns <code>200 OK</code> on <code>GET /api/v1/ready</code> (the inactive node returns <code>503</code>).</li>
<li class="">Tune failover behavior with <code>RS_LOCK_FILE_TTL</code> (how long the standby waits) and <code>RS_LOCK_FILE_TIMEOUT</code> (how long a node waits to acquire the lock).</li>
</ul>
<p>To avoid split-brain writes, don’t run both nodes in <code>STANDALONE</code> mode against the same dataset.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="read-only-replicas-for-read-scaling">Read-only replicas for read scaling<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#read-only-replicas-for-read-scaling" class="hash-link" aria-label="Direct link to Read-only replicas for read scaling" title="Direct link to Read-only replicas for read scaling" translate="no">​</a></h3>
<p>Add one or more <code>REPLICA</code> nodes to serve queries from the same dataset. Replicas never write and periodically refresh bucket metadata and indexes from the backend, so newly written data may appear with a small delay.</p>
<p><img decoding="async" loading="lazy" alt="ReductStore Read-only replicas" src="https://www.reduct.store/assets/images/disaster_recovery_read_replicas-47156ee2571769c2ee74aca896aafa88.webp" width="1250" height="1011" class="img_ev3q"></p>
<ul>
<li class="">Route writes to a dedicated ingestion node (or the active node in a hot-standby pair).</li>
<li class="">Route reads to replicas to scale query workloads horizontally.</li>
<li class="">Tune staleness with <code>RS_ENGINE_REPLICA_UPDATE_INTERVAL</code>.</li>
</ul>
<p>For an end-to-end walkthrough (including S3-based standalone, active-passive, and replicas), see the <strong><a class="" href="https://www.reduct.store/docs/1.18.x/integrations/s3">S3 Backend</a></strong> tutorial. For architecture options and operational notes, see the <strong><a class="" href="https://www.reduct.store/docs/guides/disaster-recovery">Disaster Recovery</a></strong> guide.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="multi-entry-api">Multi-entry API<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#multi-entry-api" class="hash-link" aria-label="Direct link to Multi-entry API" title="Direct link to Multi-entry API" translate="no">​</a></h2>
<p>The new <strong>Multi-entry API</strong> makes it possible to work with multiple entries in a single request. In practice, this is most useful for <strong>querying</strong>: instead of running one query per sensor/stream and merging results on the client, you can request all the entries you need at once and process a single result stream (each returned record includes its <code>entry</code> name).</p>
<p>Here is a Python example using <code>reduct-py</code> to query multiple entries in one call:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"my-token"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-bucket"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Query multiple entries in a single request (since ReductStore v1.18).</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># You can mix exact names and wildcards.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        entries </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"sensor-*"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"camera"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> record </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            entries</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            start</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"2026-02-05T10:00:00Z"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            stop</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"2026-02-05T10:05:00Z"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            when</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"&amp;score"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$gte"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            payload </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">entry</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> record</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token builtin">len</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">payload</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> __name__ </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"__main__"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What’s Next<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#whats-next" class="hash-link" aria-label="Direct link to What’s Next" title="Direct link to What’s Next" translate="no">​</a></h2>
<p>We’re already working on the next improvements to make ReductStore easier to integrate into real-world data pipelines:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="native-zenoh-api">Native Zenoh API<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#native-zenoh-api" class="hash-link" aria-label="Direct link to Native Zenoh API" title="Direct link to Native Zenoh API" translate="no">​</a></h3>
<p>Zenoh is becoming a common choice for data exchange in distributed, edge-first systems (robotics, industrial IoT, and telemetry). In upcoming releases, we plan to add a <strong>native Zenoh API</strong> so ReductStore can join Zenoh networks seamlessly.</p>
<p>This will make it easier to ingest and serve data directly through Zenoh—without custom bridges—so your storage layer fits naturally into existing Zenoh-based deployments.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="entry-attachments-metadata">Entry attachments (metadata)<a href="https://www.reduct.store/blog/news/reductstore-v1_18_0-released#entry-attachments-metadata" class="hash-link" aria-label="Direct link to Entry attachments (metadata)" title="Direct link to Entry attachments (metadata)" translate="no">​</a></h3>
<p>Today, labels work well for filtering and replication, but many projects also need structured metadata tied to an entry itself: data format, schema version, units, encoding, calibration details, and other context.</p>
<p>We plan to introduce <strong>attachments for entries</strong>, allowing you to store and retrieve this kind of metadata alongside your data streams, making datasets more self-describing and easier to consume across teams and tools.</p>
<hr>
<p>I hope you find those new features useful. If you have any questions or feedback, don’t hesitate to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>
<p>Thanks for using <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>!</p>]]></content:encoded>
            <category>news</category>
        </item>
        <item>
            <title><![CDATA[Comparing Data Management Tools for Robotics]]></title>
            <link>https://www.reduct.store/blog/data-management-tools</link>
            <guid>https://www.reduct.store/blog/data-management-tools</guid>
            <pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A comprehensive overview of data management tools for robotics, including ReductStore, Foxglove, Rerun, and Heex. In this article, we will compare their key features, strengths, and use cases to help you choose the right tool for your robotics projects.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Data Management Tools for Robotics" src="https://www.reduct.store/assets/images/intro-67f55423e89c2e59976823cb70415b51.png" width="1776" height="1178" class="img_ev3q"></p>
<p>Modern robots collect a lot of data from sensors, cameras, logs, and system outputs. Managing this data well is important for debugging, performance tracking, and training machine learning models.</p>
<p>Over the past few years, we've been building a storage system from scratch. As part of that work, we spoke with many robotics teams across different industries to understand their challenges with data management.</p>
<p>Here's what we heard often:</p>
<ul>
<li class="">Only a subset of what robots generate is actually useful</li>
<li class="">Network connections are not always stable or fast</li>
<li class="">On-device storage is limited (hard drive swaps is not practical)</li>
<li class="">Teams rely on manual workflows with scripts and raw files</li>
<li class="">It's hard to find and extract the right data later</li>
<li class="">ROS bag files get large quickly and are difficult to manage</li>
</ul>
<p>In this article, we compare four tools built to handle robotics data: <strong>ReductStore</strong>, <strong>Foxglove</strong>, <strong>Rerun</strong>, and <strong>Heex</strong>. We look at how they work, what they're good at, and which use cases they support.</p>
<p>If you're working with robots and need to organize, stream, or store data more effectively, this overview should help.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-criteria-for-comparison">Key Criteria for Comparison<a href="https://www.reduct.store/blog/data-management-tools#key-criteria-for-comparison" class="hash-link" aria-label="Direct link to Key Criteria for Comparison" title="Direct link to Key Criteria for Comparison" translate="no">​</a></h2>
<p>When picking a data tool for robotics, focus on these areas:</p>
<ul>
<li class=""><strong>Data Types</strong>
Robotics is a large field with many sensor types. The tool should support the data you work with, such as:<!-- -->
<ul>
<li class=""><em>Telemetry:</em> Lightweight (GPS, IMU, joints), ideal for monitoring.</li>
<li class=""><em>Downsampled Data:</em> Lower-rate images or lidar for incident review without high storage cost.</li>
<li class=""><em>Full-Resolution:</em> Raw sensor outputs for deep debugging or training. This is storage-intensive but essential for some applications.</li>
</ul>
</li>
<li class=""><strong>Integration</strong>
The tool should work with what you already use, like ROS, Grafana, MQTT, cloud platforms (S3, Azure, Google Cloud), and your development environment to avoid extra glue code and simplify workflows.</li>
<li class=""><strong>Performance and Scalability</strong>
Data must move quickly (both locally and to the cloud). Large files or slow queries can block robots or delay analysis.</li>
<li class=""><strong>Ease of Use and APIs</strong>
A simple UI and solid API support make it easier to automate, scale, and adapt the tool to different use cases.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tool-overviews">Tool Overviews<a href="https://www.reduct.store/blog/data-management-tools#tool-overviews" class="hash-link" aria-label="Direct link to Tool Overviews" title="Direct link to Tool Overviews" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductstore">ReductStore<a href="https://www.reduct.store/blog/data-management-tools#reductstore" class="hash-link" aria-label="Direct link to ReductStore" title="Direct link to ReductStore" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="ReductStore Dashboard" src="https://www.reduct.store/assets/images/reductstore_dashboard-63ff3ec191275942dba98021b8c94f56.png" width="1913" height="901" class="img_ev3q"></p>
<p><strong>ReductStore</strong> is a storage and streaming system designed for robotics data. It works both on the robot and in central storage (on-premise/self-hosted or in the cloud) with the same interface and SDKs (in Python, C++, Go, Javascript/TypeScript or Rust). That means your code stays the same whether you're reading local, remote data (or creating a browser-based dashboard).</p>
<p>To move data to the cloud, ReductStore uses <strong>conditional replication</strong>. You can define rules to upload only certain records: by label, rules, or event. For example, replicate all incident data, or just 1 out of 10 entries for routine monitoring.</p>
<p>ReductStore handles storage limits on edge devices with <strong>FIFO retention</strong>. Old data is deleted only when the device is full. Each bucket can have different rules, so you can keep more images and less telemetry, for example.</p>
<p>With an <strong>S3 backend</strong>, ReductStore batches small records together before uploading. This cuts down the number of requests and lowers cloud storage costs. For observability, you can connect <strong>Grafana</strong> to ReductStore to create dashboards with system metrics and sensor data. For MCAP files, ReductStore supports shareable query links that open directly in <strong>Foxglove v1/v2</strong>.</p>
<p>It also lets you <strong>filter or merge records server-side</strong>. For example, you can pull all temperature readings above a threshold over a time range without downloading full datasets.</p>
<p>Want more technical detail? Check out <a class="" href="https://www.reduct.store/blog/database-for-robotics"><strong>The Missing Database for Robotics Is Out</strong></a>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="foxglove-and-mcap">Foxglove and MCAP<a href="https://www.reduct.store/blog/data-management-tools#foxglove-and-mcap" class="hash-link" aria-label="Direct link to Foxglove and MCAP" title="Direct link to Foxglove and MCAP" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Foxglove Dashboard" src="https://www.reduct.store/assets/images/foxglove_dashboard-86121525884ddb5927b16393d226c94d.png" width="1918" height="826" class="img_ev3q"></p>
<p><strong>Foxglove</strong> is a browser-based visualization and observability tool for robotics. It supports <strong>ROS 1, ROS 2</strong>, and <strong>MCAP logs</strong>, and handles data types like telemetry, camera feeds, lidar, and depth maps.</p>
<p>It uses <strong>MCAP</strong>, an open-source log format built for robotics, to store high-resolution data efficiently. You can explore MCAP files interactively in <strong>Foxglove Studio</strong> or stream them programmatically.</p>
<p>Foxglove provides an <strong>agent</strong> that detects new MCAP files on the robot and uploads them to the cloud automatically. This requires robots to record short rosbag segments (typically a few minutes each) which are closed and rotated continuously.</p>
<p>It integrates natively with <strong>ROS topics, services, and actions</strong>, and offers <strong>WebSocket and REST APIs</strong>. It also connects to major cloud providers like <strong>AWS, Azure,</strong> and <strong>Google Cloud</strong> for scalable storage.</p>
<p>The interface is built for time-series and sensor data, with interactive 2D/3D views, plots, and drag-and-drop panels for quick setup and review.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rerun">Rerun<a href="https://www.reduct.store/blog/data-management-tools#rerun" class="hash-link" aria-label="Direct link to Rerun" title="Direct link to Rerun" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Rerun Dashboard" src="https://www.reduct.store/assets/images/rerun_dashboard-e77864ae302d1c550f87bc390635d612.png" width="1918" height="993" class="img_ev3q"></p>
<p><strong>Rerun</strong> is an open-source visualization solution for time-series and multimodal data. It supports data types like images, point clouds, lidar, depth maps, tensors, and other sensor streams.</p>
<p>Its main strength is combining flexible logging with a fast, built-in 3D viewer designed for robotics and extended reality (XR) applications. For large datasets, Rerun provides a <strong>column-oriented API</strong> to speed up ingestion and reduce memory usage. It also uses efficient internal structures to minimize allocations and optimize performance on edge devices.</p>
<p>Rerun doesn't offer native ROS integration yet, but it can be used in ROS projects by adding custom logging to nodes.</p>
<p>You can embed Rerun in <strong>Jupyter notebooks</strong> or web pages, and use loggers for <strong>Python, Rust, and C++</strong> to stream data into the viewer.</p>
<p>The UI is built for <strong>real-time 3D exploration</strong>, with overlays and live tracking that make it easy to inspect different data types in the same visual space.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="heex">Heex<a href="https://www.reduct.store/blog/data-management-tools#heex" class="hash-link" aria-label="Direct link to Heex" title="Direct link to Heex" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" alt="Heex Dashboard" src="https://www.reduct.store/assets/images/heex_dashboard-bff181c7f6f137bb6036fd73ca8ad322.png" width="1638" height="987" class="img_ev3q"></p>
<p><strong>Heex</strong> is a data capture and review platform for autonomous systems that focuses on collecting only key moments—like errors or specific events instead of logging everything. This reduces bandwidth and storage needs while keeping important context.</p>
<p>Robots using Heex record data continuously in short ROSbag segments. A small agent on the robot watches for triggers and uploads only selected segments to the cloud based on rules.</p>
<p>A core feature is <strong>RDA (Resource and Data Automation)</strong> for ROS 2, which automates what to record and when. Rules can be changed remotely without restarting the robot.</p>
<p>Data is stored in <strong>ROSbag</strong> and can be reviewed directly in the <strong>Heex dashboard</strong>, which includes built-in open-source version of <strong>Foxglove</strong>. This setup makes it easy to manage data across fleets and locations.</p>
<p>Heex supports both <strong>ROS 1 and ROS 2</strong>, and integrates with other systems through <strong>SDKs, APIs, and a CLI</strong>.</p>
<p>The interface includes customizable dashboards to monitor sensor data, errors, and system status. Timelines and streams are easy to navigate for quick analysis.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="comparative-analysis-table">Comparative Analysis Table<a href="https://www.reduct.store/blog/data-management-tools#comparative-analysis-table" class="hash-link" aria-label="Direct link to Comparative Analysis Table" title="Direct link to Comparative Analysis Table" translate="no">​</a></h2>
<p>To help visualize the differences between the tools, here is a comparison table summarizing their main characteristics:</p>
<table><thead><tr><th><strong>Tool</strong></th><th><strong>Core Focus</strong></th><th><strong>Data Types</strong></th><th><strong>Storage Strategy</strong></th><th><strong>Visualization</strong></th><th><strong>ROS Integration</strong></th><th><strong>Unique Features</strong></th></tr></thead><tbody><tr><td><strong>ReductStore</strong></td><td>Time-series storage and streaming for robotics</td><td>Telemetry, camera images, lidar, logs</td><td>Local + cloud with same API (supports S3, FIFO retention, conditional replication)</td><td>Grafana, Foxglove (via MCAP links)</td><td>Integrated with ROS via extensions</td><td>Filter/merge on server, batch uploads, topic-level control, efficient on edge</td></tr><tr><td><strong>Foxglove</strong></td><td>Visualization and observability for robotics logs</td><td>MCAP logs (telemetry, lidar, camera, depth)</td><td>ROSbag short segments, auto-upload with agent</td><td>Foxglove Studio (2D/3D, timeline, plots)</td><td>Native ROS 1 &amp; 2</td><td>Drag-and-drop views, real-time stream inspection, cloud integration</td></tr><tr><td><strong>Rerun</strong></td><td>Real-time 3D visualization of multimodal time-series data</td><td>Images, lidar, point clouds, tensors, metrics</td><td>User-defined logging; logs streamed into viewer or embedded in notebooks</td><td>Built-in viewer (3D overlays, tracking)</td><td>Not native (custom logging)</td><td>Column-oriented API, fast ingestion, selective logging, notebook/web integration</td></tr><tr><td><strong>Heex</strong></td><td>Event-driven data capture for fleets of robots</td><td>ROSbag (telemetry, images, lidar, metrics)</td><td>Continuous recording, uploads filtered by event-based rules via onboard agent</td><td>Built-in Foxglove in dashboard</td><td>Native ROS 1 &amp; 2</td><td>RDA (automated capture rules), remote config, scalable fleet-wide dashboards</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/data-management-tools#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Each tool addresses a different part of the robotics data workflow. <strong>ReductStore</strong> is ideal for distributed storage across many robots, with selective replication to the cloud and flexible integration with tools like Grafana and Foxglove. <strong>Foxglove</strong> excels at visualizing MCAP logs and ROS topics. <strong>Rerun</strong> offers flexible, real-time 3D inspection for custom applications. <strong>Heex</strong> focuses on capturing just the important moments for efficient fleet analysis.</p>
<p>Choosing the right tool depends on what kind of data you collect, how you process it, and where you need it to go. In many cases, combining tools can give you the best of all worlds.</p>
<hr>
<p>Thanks for reading. I hope this article helps you decide on the right storage strategy for your vibration data.
If you have questions or comments, feel free to visit the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>robotics</category>
            <category>ros</category>
            <category>comparison</category>
        </item>
        <item>
            <title><![CDATA[Distributed Storage in Mobile Robotics]]></title>
            <link>https://www.reduct.store/blog/distributed-storage-mobile-robotics</link>
            <guid>https://www.reduct.store/blog/distributed-storage-mobile-robotics</guid>
            <pubDate>Mon, 17 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[How to set up local storage on mobile robots with automatic cloud replication tasks using ReductStore.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Distributed Storage in Mobile Robotics" src="https://www.reduct.store/assets/images/intro-image-1efc59d65f96bc931e7454938655b29d.png" width="1472" height="825" class="img_ev3q"></p>
<p>Mobile robots produce a <strong>lot</strong> of data (camera images, IMU readings, logs, etc). Storing this data reliably on each robot and syncing it to the cloud can be hard. <strong>ReductStore</strong> makes this easier: it's a lightweight, time-series object store built for robotics and industrial IoT. It stores binary blobs (images, logs, CSV sensor data, MCAP, JSON) with timestamps and labels so you can quickly find and query them later.</p>
<p>This introduction guide explains a simple setup where each robot stores data locally and automatically syncs it to a cloud ReductStore instance backed by Amazon S3.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="edge-to-cloud-architecture">Edge-to-Cloud Architecture<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#edge-to-cloud-architecture" class="hash-link" aria-label="Direct link to Edge-to-Cloud Architecture" title="Direct link to Edge-to-Cloud Architecture" translate="no">​</a></h2>
<p>The architecture has three main components:</p>
<ul>
<li class=""><strong>Each robot runs a small ReductStore server</strong> in order to save images and IMU data locally on disk (this let the robot operate offline).</li>
<li class=""><strong>A cloud ReductStore instance runs on a server (e.g., EC2)</strong> and uses S3 for long-term storage.</li>
<li class=""><strong>ReductStore replication tasks</strong> copies data from robot to cloud based on labels, events, or rules (e.g., 1 record every minute).</li>
</ul>
<p>Each robot pushes its data to the cloud whenever it is connected to the network. This approach provides the robots with offline capability, allows you to decide which data to replicate, and easily scales to support many robots.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-replication-works">How Replication Works<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#how-replication-works" class="hash-link" aria-label="Direct link to How Replication Works" title="Direct link to How Replication Works" translate="no">​</a></h2>
<p>ReductStore uses an <strong>append-only</strong> replication model:</p>
<ul>
<li class="">The robot stores new data locally.</li>
<li class="">ReductStore automatically detects new records.</li>
<li class="">It sends them to the cloud in batches (or streams large files).</li>
<li class="">If the network disconnects, replication continues when the robot reconnects.</li>
</ul>
<p>You can replicate:</p>
<ul>
<li class=""><em>everything</em></li>
<li class="">or only specific sensors</li>
<li class="">or only records with certain labels</li>
<li class="">or based on rules (e.g., 1 record every S seconds or every N records)</li>
</ul>
<p>This can be configured per robot using environment variables (provisioning), with the web console or via the CLI (as shown in this guide).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloud-reductstore-with-s3-backend">Cloud ReductStore With S3 Backend<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#cloud-reductstore-with-s3-backend" class="hash-link" aria-label="Direct link to Cloud ReductStore With S3 Backend" title="Direct link to Cloud ReductStore With S3 Backend" translate="no">​</a></h2>
<p>ReductStore supports storing all records directly in S3. It keeps a local cache for fast access and batches many small blobs into larger blocks to save on S3 costs.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>By batching data into S3 objects, you can save <strong>significantly</strong> on storage costs compared to storing many small files individually.</p></div></div>
<p>Here is an example <code>docker-compose.yml</code> to run a ReductStore server that uses S3 as the remote backend and provisions buckets for robots:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">services</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">reductstore</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">image</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reduct/store</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">container_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> reductstore</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">ports</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"8383:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">environment</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># AWS credentials and S3 bucket configuration</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REMOTE_BACKEND_TYPE</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> s3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REMOTE_BUCKET</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;YOUR_S3_BUCKET_NAME</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REMOTE_REGION</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;YOUR_S3_REGION</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REMOTE_ACCESS_KEY</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;YOUR_AWS_ACCESS_KEY_ID</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REMOTE_SECRET_KEY</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> &lt;YOUR_AWS_SECRET_ACCESS_KEY</span><span class="token punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_REMOTE_CACHE_PATH</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> /data/cache</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># Bucket provisioning</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_ROBOT_1_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> robot1</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">RS_BUCKET_ROBOT_2_NAME</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> robot2</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token comment" style="color:#999988;font-style:italic"># .. additional buckets as needed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">volumes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">user</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0:0"</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Run as root with host bind mount in this example</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> ./cache</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">/data/cache</span><br></span></code></pre></div></div>
<p>Run it with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> compose up </span><span class="token parameter variable" style="color:#36acaa">-d</span><br></span></code></pre></div></div>
<p>This starts a ReductStore server that writes to S3 automatically. There are many more configuration options available in the <strong><a class="" href="https://www.reduct.store/docs/configuration">configuration documentation</a></strong>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-replication">Setting Up Replication<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#setting-up-replication" class="hash-link" aria-label="Direct link to Setting Up Replication" title="Direct link to Setting Up Replication" translate="no">​</a></h2>
<p>First spin up a local ReductStore on each robot. Here with Snap:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> snap </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> reductstore</span><br></span></code></pre></div></div>
<p>That starts a ReductStore server on port <code>8383</code> by default. Then you can use the <strong><a href="https://github.com/reductstore/reduct-cli" target="_blank" rel="noopener noreferrer" class="">Reduct CLI</a></strong> to set up replication from the robot to the cloud instance:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Point the CLI to the robot's local ReductStore</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> </span><span class="token builtin class-name">local</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-L</span><span class="token plain"> http://localhost:8383 </span><span class="token parameter variable" style="color:#36acaa">-t</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"&lt;ROBOT_API_TOKEN&gt;"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Create a bucket for that robot</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli bucket create local/robot1-data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Create a replication task to the cloud</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli replica create local/robot1-to-cloud </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    robot1-data </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    https://</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">CLOUD_API_TOKEN</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">@</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">cloud-address</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">/robot1-data</span><br></span></code></pre></div></div>
<p>This creates a replication task called <code>robot1-to-cloud</code> that copies all data from the robot's local <code>robot1-data</code> bucket to the cloud instance. You can customize replication further by adding filters or rules. See the <strong><a class="" href="https://www.reduct.store/docs/guides/data-replication">replication guide</a></strong> for more details.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="storing-sensor-data">Storing Sensor Data<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#storing-sensor-data" class="hash-link" aria-label="Direct link to Storing Sensor Data" title="Direct link to Storing Sensor Data" translate="no">​</a></h2>
<p>There are many ways to store data. When it comes to high-frequency sensor data like IMU readings, a common approach is to store them in 1-second files. Images can be stored as binary blobs (e.g., JPEG or PNG files). Here is an example of storing IMU data as CSV files and images as binary blobs using the Python SDK (this stores 10,000 samples and one camera image for a given timestamp as an example):</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> time</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> random</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"http://localhost:8383"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"&lt;ROBOT_API_TOKEN&gt;"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"robot1-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Current timestamp to index the data by time in ReductStore</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        timestamp </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">int</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1_000_000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># microseconds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Generate 10'000 IMU samples</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        rows </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> i </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> </span><span class="token builtin">range</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">10_000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            rows</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">append</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"ts"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> timestamp </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> i </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">100</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># microseconds</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"linear_acceleration_x"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uniform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"linear_acceleration_y"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uniform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    </span><span class="token string" style="color:#e3116c">"linear_acceleration_z"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">round</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">random</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uniform</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">8.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Convert to CSV (store 1 seconds of data per file)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csv </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"ts,linear_acceleration_x,linear_acceleration_y,linear_acceleration_z\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"\n"</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">join</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">r</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation string" style="color:#e3116c">'ts'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">,</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">r</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation string" style="color:#e3116c">'linear_acceleration_x'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">,"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string-interpolation string" style="color:#e3116c">f"</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">r</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation string" style="color:#e3116c">'linear_acceleration_y'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">,</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">r</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">[</span><span class="token string-interpolation interpolation string" style="color:#e3116c">'linear_acceleration_z'</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">]</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> r </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> rows</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Write the IMU batch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            entry_name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"imu_logs"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            data</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">csv</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">encode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            timestamp</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            labels</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"sensor"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"imu"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"rows"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1000"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            content_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"text/csv"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># MIME type</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Write one camera image</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> </span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"camera_image.png"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"rb"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> img</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">write</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                entry_name</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"images"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                data</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">img</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                timestamp</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">timestamp</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                labels</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"sensor"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"camera"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                content_type</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"image/png"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>warning</div><div class="admonitionContent_BuS1"><p>If you are considering storing all IMU data as individual records in a time series database (TSDB) like Timescale or InfluxDB, keep in mind that high-frequency sensors (e.g., 1000 Hz) can lead to performance and cost issues. Batching samples into files (e.g., one second of data per CSV file) is a more efficient storage and querying method.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="querying-sensor-data-using-reductselect">Querying Sensor Data Using ReductSelect<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#querying-sensor-data-using-reductselect" class="hash-link" aria-label="Direct link to Querying Sensor Data Using ReductSelect" title="Direct link to Querying Sensor Data Using ReductSelect" translate="no">​</a></h2>
<p>If your IMU data is stored as CSV, the <strong>ReductSelect extension</strong> lets you:</p>
<ul>
<li class="">extract only certain columns</li>
<li class="">filter rows based on conditions</li>
</ul>
<p>Example: filter CSV rows where <code>acc_x &gt; 10</code>:</p>
<div class="language-json5 codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json5 codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    "#ext": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "select": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "csv": {"has_headers": True},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            "columns": [</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                {"name": "ts", "as_label": "ts_ns"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                {"name": "linear_acceleration_x", "as_label": "acc_x"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                {"name": "linear_acceleration_y"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                {"name": "linear_acceleration_z"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            ],</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        "when": {"@acc_x": {"$gt": 1.9}},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>Python example:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> asyncio</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">when </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># the JSON condition from above</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://&lt;cloud-address&gt;"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> api_token</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"&lt;TOKEN&gt;"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"robot1-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> rec </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"imu_logs"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> when</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">when</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> rec</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This returns only the rows where <code>linear_acceleration_x &gt; 1.9</code>, along with the timestamp.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-setup-works-well-for-robotics">Why This Setup Works Well for Robotics<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#why-this-setup-works-well-for-robotics" class="hash-link" aria-label="Direct link to Why This Setup Works Well for Robotics" title="Direct link to Why This Setup Works Well for Robotics" translate="no">​</a></h2>
<p>There are several advantages to using a specialized storage solution like ReductStore for mobile robotics:</p>
<ul>
<li class=""><strong>Robots can store data locally</strong> and operate offline without network connectivity.</li>
<li class=""><strong>Automatic replication when connected</strong> to avoid manual uploads and simplify data management.</li>
<li class=""><strong>Selective replication</strong> lets you control what data is sent to the cloud (i.e. decide on your reduction strategy) to save bandwidth and storage.</li>
<li class=""><strong>Labels and timestamps</strong> make it easy to organize and query sensor data later.</li>
<li class=""><strong>Store files of any type</strong> (images, CSV, logs, MCAP) in a single system without needing separate storage solutions for each data type.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next Steps<a href="https://www.reduct.store/blog/distributed-storage-mobile-robotics#next-steps" class="hash-link" aria-label="Direct link to Next Steps" title="Direct link to Next Steps" translate="no">​</a></h2>
<p>ReductStore also integrates into robotics observability stacks such as the Canonical Observability Stack (COS) for robotics. You can visualize sensor data, logs, and metrics in Grafana dashboards alongside your other robot telemetry. More details in our blog post <strong><a class="" href="https://www.reduct.store/blog/database-for-robotics">The Missing Database for Robotics Is Out</a></strong>.</p>
<hr>
<p>I hope you found this article helpful! If you have any questions or feedback, don't hesitate to reach out on our <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>]]></content:encoded>
            <category>database</category>
            <category>ros</category>
            <category>robotics</category>
        </item>
        <item>
            <title><![CDATA[The Missing Database for Robotics Is Out]]></title>
            <link>https://www.reduct.store/blog/database-for-robotics</link>
            <guid>https://www.reduct.store/blog/database-for-robotics</guid>
            <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Introducing the missing database for robotics, an open, efficient, and developer friendly solution for managing robotics data. Capture, query, and manage sensor and image streams from robot to cloud with full control, lower cost, and integration with ROS and observability tools.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Img example" src="https://www.reduct.store/assets/images/intro-image-f0aa613eb96c5ddf00d9873dd664eb53.png" width="1666" height="930" class="img_ev3q"></p>
<p>Robotics teams today wrestle with data that grows faster than their infrastructure. Every robot generates streams of images, sensor readings, logs, and events in different formats. These data piles are fragmented, expensive to move, and slow to analyze. Teams often rely on generic cloud tools that are not built for robotics. They charge way too much per gigabyte (when it should cost little per terabyte), hide the raw data behind proprietary APIs, and make it hard for robots (and developers) to access or use their own data.</p>
<p>ReductStore introduces a new category: a database purpose built for robotics data pipelines. It is open, efficient, and developer friendly. It lets teams store, query, and manage any time series of unstructured data directly from robots to the cloud.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-makes-it-a-new-category">What Makes It a New Category<a href="https://www.reduct.store/blog/database-for-robotics#what-makes-it-a-new-category" class="hash-link" aria-label="Direct link to What Makes It a New Category" title="Direct link to What Makes It a New Category" translate="no">​</a></h2>
<p>ReductStore treats robotics with the respect it deserves. It captures everything in its raw form and stores it with a time index and labels for flexible querying and management. It ingests and streams any type of data (images, sensor frames, logs, MCAP files, CSVs, JSON, etc) without forcing developers to convert or reformat it.</p>
<p>It works on robots and in the cloud using the same interface and SDKs (Python, C++, Rust, Javascript, Go). This means developers can build data pipelines that run the same way on robots or in the cloud without needing to change code or learn new tools.</p>
<p>Developers can run ReductStore on an edge device for local data capture and replicate to a cloud instance (with S3 backend) for cloud analytics or archiving.</p>
<blockquote>
<p>It is the first and only database designed specifically for unstructured, time series robotics data.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-handling-and-querying">Data Handling and Querying<a href="https://www.reduct.store/blog/database-for-robotics#data-handling-and-querying" class="hash-link" aria-label="Direct link to Data Handling and Querying" title="Direct link to Data Handling and Querying" translate="no">​</a></h2>
<p>Developers can work directly with data using simple queries and SDKs. The focus is speed and flexibility.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-mcap-topic-filtering">1. MCAP topic filtering<a href="https://www.reduct.store/blog/database-for-robotics#1-mcap-topic-filtering" class="hash-link" aria-label="Direct link to 1. MCAP topic filtering" title="Direct link to 1. MCAP topic filtering" translate="no">​</a></h3>
<p>You can filter topics directly from multiple MCAP files stored in ReductStore without needing to download and reprocess everything locally.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> json</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> pandas </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> pd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Extract only the IMU topic from MCAP&nbsp;files</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ext </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"ros"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"extract"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"topic"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/imu/data"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://test.reduct.store"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-robotics-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    parts </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> rec </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"mcap-entry"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ext</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        blob </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> rec</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> json</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">loads</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">blob</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">decode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"utf-8"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        rows </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"ts"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"header"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"stamp"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"sec"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1_000_000_000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"header"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"stamp"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"nanosec"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"linear_acceleration_x"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"linear_acceleration"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"x"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"linear_acceleration_y"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"linear_acceleration"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"y"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token string" style="color:#e3116c">"linear_acceleration_z"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"linear_acceleration"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"z"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">]</span><br></span></code></pre></div></div>
<p>This allows you to extract only the relevant topics from multiple bags. In this example, we extract only the IMU topic as a stream of JSON records, which would look like this:</p>
<table><thead><tr><th>ts</th><th>linear_acceleration_x</th><th>linear_acceleration_y</th><th>linear_acceleration_z</th></tr></thead><tbody><tr><td>1633024800000</td><td>0.1</td><td>0.3</td><td>-9.8</td></tr><tr><td>1633024801000</td><td>0.0</td><td>0.1</td><td>-9.7</td></tr><tr><td>...</td><td>...</td><td>...</td><td>...</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-csvjson-field-extraction-and-filtering">2. CSV/JSON field extraction and filtering<a href="https://www.reduct.store/blog/database-for-robotics#2-csvjson-field-extraction-and-filtering" class="hash-link" aria-label="Direct link to 2. CSV/JSON field extraction and filtering" title="Direct link to 2. CSV/JSON field extraction and filtering" translate="no">​</a></h3>
<p>You can extract specific JSON fields or CSV columns when querying data.
This lets you select only the information you need, for example, filtering and visualizing certain fields from streams of JSON or CSV sensor readings.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> pandas </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> pd</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Select specific CSV columns and filter rows</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ext </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"select"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"csv"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"has_headers"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Use "json": {}, for JSON data</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">"columns"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"ts"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"linear_acceleration_x"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"as_label"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"acc_x"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"linear_acceleration_y"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"name"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"linear_acceleration_z"</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string" style="color:#e3116c">"when"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$gt"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$abs"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">"@acc_x"</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://test.reduct.store"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-robotics-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># Loop over filtered CSV entries</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> rec </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"csv_sensor_readings"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> ext</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">ext</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        blob </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> rec</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        csv_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> pd</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_csv</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">io</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">BytesIO</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">blob</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>The tabular result will only include the selected columns and rows that match the filter <code>abs(linear_acceleration_x) &gt; 10</code>:</p>
<table><thead><tr><th>ts</th><th>linear_acceleration_x</th><th>linear_acceleration_y</th><th>linear_acceleration_z</th></tr></thead><tbody><tr><td>1633024800000</td><td>12.5</td><td>0.3</td><td>-9.8</td></tr><tr><td>1633024801000</td><td>-15.2</td><td>0.1</td><td>-9.7</td></tr><tr><td>...</td><td>...</td><td>...</td><td>...</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-query-any-type-of-data">3. Query any type of data<a href="https://www.reduct.store/blog/database-for-robotics#3-query-any-type-of-data" class="hash-link" aria-label="Direct link to 3. Query any type of data" title="Direct link to 3. Query any type of data" translate="no">​</a></h3>
<p>ReductStore automatically batches small records and streams large ones for efficient storage and access. You can query any type of data, from lightweight telemetry to high-resolution images or point clouds, efficiently.</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> io</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> PIL </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> reduct </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Client</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Every 5 seconds, limit to 5 records</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">when </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">"$each_t"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"5s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"$limit"</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">with</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"https://test.reduct.store"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_bucket</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"my-robotics-data"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> rec </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> bucket</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"camera_frames"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> when</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">when</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        blob </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> rec</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">read_all</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Image</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">open</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">io</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">BytesIO</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">blob</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>The example above retrieves camera frames at 5-second intervals. You can then process or visualize these images as needed.</p>
<p><img decoding="async" loading="lazy" alt="Query Images Example" src="https://www.reduct.store/assets/images/query-images-example-db408b2acecc16dd773f1ecb748d4aca.png" width="2398" height="617" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-browse-petabytes-of-data">4. Browse petabytes of data<a href="https://www.reduct.store/blog/database-for-robotics#4-browse-petabytes-of-data" class="hash-link" aria-label="Direct link to 4. Browse petabytes of data" title="Direct link to 4. Browse petabytes of data" translate="no">​</a></h3>
<p>ReductStore is designed to handle massive volumes of data. Its indexing and storage architecture allows you to efficiently browse data at scale without downloading everything locally.</p>
<p>For example, you can quickly navigate records and preview your data directly in the ReductStore <a class="" href="https://www.reduct.store/docs/glossary#web-console"><strong>web console</strong></a>, even when working with petabytes of robotics data.</p>
<p><img decoding="async" loading="lazy" alt="Browse Large Datasets" src="https://www.reduct.store/assets/images/browse-data-e77c3b5d657482afbe49245b0447855c.png" width="2210" height="1796" class="img_ev3q"></p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>You can build custom applications on top of ReductStore using its SDKs for Python, C++, Rust, Javascript, and Go. This makes it easy to build data pipelines, dashboards that works in the browser, or integrate with existing tools.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cloud-integration-and-cost-savings">Cloud Integration and Cost Savings<a href="https://www.reduct.store/blog/database-for-robotics#cloud-integration-and-cost-savings" class="hash-link" aria-label="Direct link to Cloud Integration and Cost Savings" title="Direct link to Cloud Integration and Cost Savings" translate="no">​</a></h2>
<p>ReductStore connects robots and the cloud in a simple and flexible way. It works with S3-compatible storage and includes a robust replication system to transfer data from robots to the cloud (even when the network is unstable or intermittent), making it perfect for field robots that often go offline.</p>
<p>Replication tasks can be configured to replicate only specific data based on labels or any criteria (for example, only replicate data when the confidence score is below a threshold, or <strong>replicate everything from a 10-minute window around a specific event</strong>).</p>
<p>In the cloud, by batching multiple records into single data blocks, ReductStore minimizes both the number of blobs and the number of API calls to S3. This design reduces storage and retrieval costs by leveraging S3's pricing model.</p>
<p><img decoding="async" loading="lazy" alt="Diagram Cloud Integration" src="https://www.reduct.store/assets/images/cloud-integration.tinified-b61efa81653bbd4c27389e94d0828758.png" width="2280" height="660" class="img_ev3q"></p>
<blockquote>
<p>This approach can deliver major savings when working with large volumes of robotics data.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="observability-stack-integration">Observability Stack Integration<a href="https://www.reduct.store/blog/database-for-robotics#observability-stack-integration" class="hash-link" aria-label="Direct link to Observability Stack Integration" title="Direct link to Observability Stack Integration" translate="no">​</a></h2>
<p>ReductStore works with the tools robotics engineers already trust.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="foxglove-studio">Foxglove Studio<a href="https://www.reduct.store/blog/database-for-robotics#foxglove-studio" class="hash-link" aria-label="Direct link to Foxglove Studio" title="Direct link to Foxglove Studio" translate="no">​</a></h3>
<p>Foxglove is an amazing tool for visualizing robotics data and debugging robots for the MCAP format.</p>
<p>To share data from ReductStore to Foxglove, you can use the ReductStore web console (or the SDKs) to generate a <a class="" href="https://www.reduct.store/docs/glossary#query-link"><strong>query link</strong></a> that Foxglove can open directly.</p>
<p><img decoding="async" loading="lazy" alt="ReductStore Query Link" src="https://www.reduct.store/assets/images/reductstore-query-link-93659ef634ecccdcd7ece12ecba15fa1.png" width="1014" height="565" class="img_ev3q"></p>
<p>You can then paste the query link into Foxglove Studio to visualize the data.</p>
<p><img decoding="async" loading="lazy" alt="Foxglove Studio" src="https://www.reduct.store/assets/images/foxglove-studio-14b2a510c8269bd02f4c4e5928f0f08f.png" width="1038" height="711" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="grafana">Grafana<a href="https://www.reduct.store/blog/database-for-robotics#grafana" class="hash-link" aria-label="Direct link to Grafana" title="Direct link to Grafana" translate="no">​</a></h3>
<p>Grafana is a popular open-source tool for creating dashboards and visualising time-series data. You can connect Grafana to ReductStore using the ReductStore data source plugin, which allows you to query and visualise data stored in ReductStore.</p>
<p>You can query data using labels, for example, localization coordinates, object detected, confidence score, etc:</p>
<p><img decoding="async" loading="lazy" alt="Grafana Query Labels" src="https://www.reduct.store/assets/images/grafana-query-labels-c872613e7f724222e30dac23c4d22b5b.png" width="1558" height="922" class="img_ev3q"></p>
<p>Or you can query based on content, such as JSON files with sensor readings or other structured data:</p>
<p><img decoding="async" loading="lazy" alt="Grafana Query Content" src="https://www.reduct.store/assets/images/grafana-query-content-b86a7f12db45f33cd4128a86ec9def71.png" width="1556" height="1170" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="canonical-observability-stack-cos">Canonical Observability Stack (COS)<a href="https://www.reduct.store/blog/database-for-robotics#canonical-observability-stack-cos" class="hash-link" aria-label="Direct link to Canonical Observability Stack (COS)" title="Direct link to Canonical Observability Stack (COS)" translate="no">​</a></h3>
<p>Canonical's COS (Canonical Observability Stack) for robotics is an end to end observability framework built on open source tools such as Prometheus, Loki, Grafana, and Foxglove.</p>
<p>The missing piece in this stack has always been a purpose built system for storing and managing robotics data efficiently from robot to cloud.</p>
<p><img decoding="async" loading="lazy" alt="Diagram Observability Stack Integration" src="https://www.reduct.store/assets/images/observability-stack-integration.tinified-177c4cda4661c27a1c83f0684875e50a.png" width="2070" height="1920" class="img_ev3q"></p>
<p>ReductStore closes that gap. It provides a data storage and streaming solution optimized for both edge and cloud environments, along with an agent that captures data directly from ROS and streams it into the observability pipeline.</p>
<p><img decoding="async" loading="lazy" alt="COS with ReductStore" src="https://www.reduct.store/assets/images/cos-with-reductstore-0e689c85f02e54e0b6aa5e243ffc41e5.png" width="2486" height="1884" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="closing-thoughts">Closing Thoughts<a href="https://www.reduct.store/blog/database-for-robotics#closing-thoughts" class="hash-link" aria-label="Direct link to Closing Thoughts" title="Direct link to Closing Thoughts" translate="no">​</a></h2>
<p>Robotics teams no longer need to choose between control and convenience. ReductStore gives full ownership of data from robot to cloud. It removes vendor lock, cuts cost, and keeps everything observable and connected. It is the new foundation for robotics data infrastructure (the missing database for robotics).</p>
<p>If you are interested to compare ReductStore with other databases (like MongoDB or InfluxDB), you can read our <a class="" href="https://www.reduct.store/whitepaper"><strong>white paper</strong></a> that goes deeper into the architecture and design choices.</p>
<hr>
<p>I hope you found this article helpful! If you have any questions or feedback, don't hesitate to reach out on our <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>]]></content:encoded>
            <category>ros</category>
            <category>robotics</category>
        </item>
        <item>
            <title><![CDATA[ReductStore v1.17.0 Released with Query Links and S3 Storage Backend Support]]></title>
            <link>https://www.reduct.store/blog/news/reductstore-v1_17_0-released</link>
            <guid>https://www.reduct.store/blog/news/reductstore-v1_17_0-released</guid>
            <pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[We are pleased to announce the release of ReductStore v1.17.0, which introduces query links for data access and support for S3-compatible storage backends.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="ReductStore v1.17.0 Released" src="https://www.reduct.store/assets/images/release_v117-4a2f08e70dccd28d1d2d5578c0254b38.png" width="1658" height="938" class="img_ev3q"></p>
<p>We are pleased to announce the release of the latest minor version of <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>, <a href="https://github.com/reductstore/reductstore/releases/tag/v1.17.0" target="_blank" rel="noopener noreferrer" class=""><strong>1.17.0</strong></a>. ReductStore is a high-performance storage and streaming solution designed for storing and managing large volumes of historical data.</p>
<p>To download the latest released version, please visit our <a class="" href="https://www.reduct.store/download"><strong>Download Page</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-new-in-1170">What's new in 1.17.0?<a href="https://www.reduct.store/blog/news/reductstore-v1_17_0-released#whats-new-in-1170" class="hash-link" aria-label="Direct link to What's new in 1.17.0?" title="Direct link to What's new in 1.17.0?" translate="no">​</a></h2>
<p>This release includes several new features and enhancements. First, there are query links for simplified data access. Second, there is support for S3-compatible storage backends.</p>
<p>These new features enhance the usability and flexibility of ReductStore for various use cases in the cloud and on-premises environments and make it easier to share and access data stored in the database.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-query-links-for-data-access">🔗 Query Links for Data Access<a href="https://www.reduct.store/blog/news/reductstore-v1_17_0-released#-query-links-for-data-access" class="hash-link" aria-label="Direct link to 🔗 Query Links for Data Access" title="Direct link to 🔗 Query Links for Data Access" translate="no">​</a></h3>
<p>ReductStore now supports <strong><a class="" href="https://www.reduct.store/docs/glossary#query-link">query links</a></strong>, enabling users to generate temporary, public URLs for specific data records — without requiring authentication.
This makes it easier to share datasets with <strong>external collaborators</strong>, embed links into dashboards, or integrate with <strong>third-party systems</strong> that need read-only access to specific data.</p>
<p><img decoding="async" loading="lazy" alt="Generate Query Links in ReductStore Web Console" src="https://www.reduct.store/assets/images/shared-link-2ff001300231ab16df01e9e2de68b9ce.webp" width="1602" height="814" class="img_ev3q"></p>
<p>You can create query links directly from the <strong>Web Console</strong> (or any SDKs):</p>
<ol>
<li class="">Open the <strong>Data Browser</strong> page and select a record you want to share.</li>
<li class="">Click the <strong>“Share record”</strong> icon in the action panel.</li>
<li class="">Configure an <strong>expiration time</strong> to automatically revoke access after a defined period.</li>
</ol>
<p>Once generated, anyone with the link can access the selected record via a simple HTTP(S) request — no access token required.
The link only has access to the specific query for which it was created, along with the creator's permissions.
This provides a secure and convenient way to expose selected data for collaboration and analysis.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="️-s3-compatible-storage-backend">☁️ S3-Compatible Storage Backend<a href="https://www.reduct.store/blog/news/reductstore-v1_17_0-released#%EF%B8%8F-s3-compatible-storage-backend" class="hash-link" aria-label="Direct link to ☁️ S3-Compatible Storage Backend" title="Direct link to ☁️ S3-Compatible Storage Backend" translate="no">​</a></h3>
<p>ReductStore now supports <strong>S3-compatible storage backends</strong>, allowing you to use <strong>object storage</strong> instead of a local file system for your underlying data.
This update brings greater flexibility and scalability for managing large datasets in the cloud.</p>
<p>Previously, ReductStore supported only local disk storage, and users had to mount S3 buckets as local disks via FUSE drivers.
With this release, ReductStore can now natively integrate with S3-compatible backends — no additional software or mounting is required.</p>
<p>This feature is designed with performance and <strong>cost optimization</strong> in mind.
ReductStore uses a local disk cache layer to speed up read and write operations, while batching multiple records into a single data block to reduce storage and retrieval costs.
This approach works especially well with cost-efficient AWS S3 storage classes such as <strong>S3 Standard-IA</strong> or <strong>S3 Glacier</strong>.</p>
<p>To run ReductStore with an S3-compatible backend, use the following environment variables:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> ./data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">chown</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-R</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10001</span><span class="token plain">:10001 ./data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8383</span><span class="token plain">:8383 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_REMOTE_BACKEND_TYPE</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">s3 </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_REMOTE_BUCKET</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">YOUR_S3_BUCKET_NAME</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_REMOTE_REGION</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">YOUR_S3_REGION</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_REMOTE_ACCESS_KEY</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">YOUR_S3_ACCESS_KEY_ID</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_REMOTE_SECRET_KEY</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">YOUR_S3_SECRET_ACCESS_KEY</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_REMOTE_CACHE_PATH</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">/data/cache </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#36acaa">RS_LICENSE_PATH</span><span class="token operator" style="color:#393A34">=</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">PATH_TO_YOUR_LICENSE_FILE</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">-v</span><span class="token plain"> </span><span class="token variable" style="color:#36acaa">${</span><span class="token variable environment constant" style="color:#36acaa">PWD</span><span class="token variable" style="color:#36acaa">}</span><span class="token plain">/data:/data/cache </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    reduct/store:latest</span><br></span></code></pre></div></div>
<p>Read more about configuring S3-compatible storage backend in the <a class="" href="https://www.reduct.store/docs/configuration/settings#remote-backend-settings"><strong>documentation</strong></a></p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>This feature requires a commercial license. Please see the <strong><a class="" href="https://www.reduct.store/pricing">Pricing page</a></strong> for more details.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-next">What’s Next<a href="https://www.reduct.store/blog/news/reductstore-v1_17_0-released#whats-next" class="hash-link" aria-label="Direct link to What’s Next" title="Direct link to What’s Next" translate="no">​</a></h2>
<p>We’re continuing to develop new features to make ReductStore even more powerful and user-friendly.
Here’s a preview of what’s coming in the next releases:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-multiple-entries-in-a-single-request">📦 Multiple Entries in a Single Request<a href="https://www.reduct.store/blog/news/reductstore-v1_17_0-released#-multiple-entries-in-a-single-request" class="hash-link" aria-label="Direct link to 📦 Multiple Entries in a Single Request" title="Direct link to 📦 Multiple Entries in a Single Request" translate="no">​</a></h3>
<p>Currently, each write or query request must target a <strong>single entry</strong>.
This can be limiting when dealing with <strong>multiple entries</strong> or dynamic lists of entries in your applications.</p>
<p>In upcoming versions, ReductStore will support <strong>batch operations</strong> across multiple entries within a single API request.
This improvement will simplify integrations and reduce overhead for large-scale data ingestion and querying workflows.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="-read-only-mode-for-reductstore">🔒 Read-Only Mode for ReductStore<a href="https://www.reduct.store/blog/news/reductstore-v1_17_0-released#-read-only-mode-for-reductstore" class="hash-link" aria-label="Direct link to 🔒 Read-Only Mode for ReductStore" title="Direct link to 🔒 Read-Only Mode for ReductStore" translate="no">​</a></h3>
<p>Like most databases, ReductStore currently requires <strong>exclusive access</strong> to its data directory while running.
As a result, running multiple instances on the same dataset—for load balancing or high availability—is not yet possible.</p>
<p>To address this, we’re introducing a <strong>read-only mode</strong> that will allow one writer instance* and multiple reader instances to access the same dataset concurrently.
This approach will enable <strong>scalable read operations</strong> and <strong>improved availability</strong> without adding the complexity of clustering or replication mechanisms.</p>
<hr>
<p>I hope you find those new features useful. If you have any questions or feedback, don’t hesitate to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>
<p>Thanks for using <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>!</p>]]></content:encoded>
            <category>news</category>
        </item>
        <item>
            <title><![CDATA[Building a Resilient ReductStore Deployment with NGINX]]></title>
            <link>https://www.reduct.store/blog/nginx-resilient-deployment</link>
            <guid>https://www.reduct.store/blog/nginx-resilient-deployment</guid>
            <pubDate>Sat, 13 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to deploy ReductStore behind NGINX with an active–active model for zero-downtime ingestion and querying.]]></description>
            <content:encoded><![CDATA[<p>If you’re collecting high-rate sensor or video data at the edge and need zero-downtime ingestion and fault-tolerant querying, an <strong><a class="" href="https://www.reduct.store/docs/guides/disaster-recovery#hot-standby-setup">active–active ReductStore setup</a></strong> fronted by NGINX is a clean, practical pattern.</p>
<p>This tutorial walks you through the <strong><a href="https://github.com/reductstore/nginx-resilient-setup" target="_blank" rel="noopener noreferrer" class="">reference implementation</a></strong>, explains the architecture, and shows production-grade NGINX snippets you can adapt.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-well-build">What We’ll Build<a href="https://www.reduct.store/blog/nginx-resilient-deployment#what-well-build" class="hash-link" aria-label="Direct link to What We’ll Build" title="Direct link to What We’ll Build" translate="no">​</a></h2>
<p>We’ll set up a <strong>ReductStore cluster</strong> with NGINX as a reverse proxy, separating the <strong>ingress</strong> and <strong>egress</strong> layers.
This architecture allows for independent scaling of write and read workloads, ensuring high availability and performance.</p>
<p><img decoding="async" loading="lazy" alt="NGINX Resilient Deployment" src="https://www.reduct.store/assets/images/disaster_recovery_active_active-b1045adbdaee865e890be4fbd35f1eb7.png" width="1870" height="901" class="img_ev3q"></p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ingress-layer">Ingress layer<a href="https://www.reduct.store/blog/nginx-resilient-deployment#ingress-layer" class="hash-link" aria-label="Direct link to Ingress layer" title="Direct link to Ingress layer" translate="no">​</a></h3>
<p>The <strong>ingress layer</strong> handles all writes and replicates data to the egress layer. Its nodes may have limited storage capacity, while they need only to handle writes and replicate data to the <strong>egress</strong> nodes.
It can use high-rate storage like NVMe SSDs or even RAM disks, depending on your data volume.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="egress-layer">Egress layer<a href="https://www.reduct.store/blog/nginx-resilient-deployment#egress-layer" class="hash-link" aria-label="Direct link to Egress layer" title="Direct link to Egress layer" translate="no">​</a></h3>
<p>The <strong>egress layer</strong> handles all reads and serves data to clients. Its nodes are optimized for read performance and can use larger, slower storage like HDDs or cloud object storage.
Each egress node holds a complete copy of the dataset, allowing for high availability and load balancing.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="nginx-load-balancer">NGINX Load Balancer<a href="https://www.reduct.store/blog/nginx-resilient-deployment#nginx-load-balancer" class="hash-link" aria-label="Direct link to NGINX Load Balancer" title="Direct link to NGINX Load Balancer" translate="no">​</a></h3>
<p>The <strong>NGINX</strong> load balancer sits in front of both layers, exposing two stable endpoints:</p>
<ul>
<li class=""><code>http://&lt;host&gt;/ingress</code> → load balances writes across ingress nodes</li>
<li class=""><code>http://&lt;host&gt;/egress</code> → load balances reads across egress nodes</li>
</ul>
<p>This separation allows you to scale each layer independently and ensures that writes and reads are handled optimally.</p>
<p>It is also important to note that NGINX must maintain <strong>session affinity</strong> (stickiness) for both ingress and egress requests to ensure that queries remain consistent and throughput is maximized.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quick-start">Quick Start<a href="https://www.reduct.store/blog/nginx-resilient-deployment#quick-start" class="hash-link" aria-label="Direct link to Quick Start" title="Direct link to Quick Start" translate="no">​</a></h2>
<p>Clone the example and bring it up:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/reductstore/nginx-resilient-setup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> nginx-resilient-setup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> compose up </span><span class="token parameter variable" style="color:#36acaa">-d</span><br></span></code></pre></div></div>
<p>This will start two ingress nodes and two egress nodes with NGINX in front, all configured to replicate data between them.
Check the docker compose file for details on how the nodes are set up.</p>
<p>Now we need to write some data and verify that we can read it back.
<a class="" href="https://www.reduct.store/download"><strong>Install the <code>reduct-cli</code> tool</strong></a> if you haven't already, then run the following commands to set up aliases for the ingress and egress endpoints:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> ingress </span><span class="token parameter variable" style="color:#36acaa">-L</span><span class="token plain"> http://localhost:80/ingress </span><span class="token parameter variable" style="color:#36acaa">--token</span><span class="token plain"> secret</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> egress </span><span class="token parameter variable" style="color:#36acaa">-L</span><span class="token plain"> http://localhost:80/egress </span><span class="token parameter variable" style="color:#36acaa">--token</span><span class="token plain"> secret</span><br></span></code></pre></div></div>
<p>Then copy some data from our <a href="https://play.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>Demo Server</strong></a> to the ingress layer and read it back from the egress layer:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Add demo server alias to the CLI</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token builtin class-name">alias</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">add</span><span class="token plain"> play </span><span class="token parameter variable" style="color:#36acaa">-L</span><span class="token plain"> https://play.reduct.store </span><span class="token parameter variable" style="color:#36acaa">--token</span><span class="token plain"> reductstore</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Copy data from the demo server to ingress</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token function" style="color:#d73a49">cp</span><span class="token plain"> play/datasets ingress/bucket-1 </span><span class="token parameter variable" style="color:#36acaa">--limit</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># Read/export via egress</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">reduct-cli </span><span class="token function" style="color:#d73a49">cp</span><span class="token plain"> egress/bucket-1 ./export_folder </span><span class="token parameter variable" style="color:#36acaa">--limit</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1000</span><br></span></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="nginx-configuration">NGINX Configuration<a href="https://www.reduct.store/blog/nginx-resilient-deployment#nginx-configuration" class="hash-link" aria-label="Direct link to NGINX Configuration" title="Direct link to NGINX Configuration" translate="no">​</a></h2>
<p>Below is a distilled config you can adapt for open-source NGINX:</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain"># Upstreams</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Separate pools for ingress (writes) and egress (reads)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">upstream reduct_ingress {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ip_hash;   # stickiness for writes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    server ingress-1:8383 max_fails=3 fail_timeout=10s;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    server ingress-2:8383 max_fails=3 fail_timeout=10s;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    keepalive 64;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">upstream reduct_egress {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ip_hash;   # stickiness for queries</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    server egress-1:8383 max_fails=3 fail_timeout=10s;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    server egress-2:8383 max_fails=3 fail_timeout=10s;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    keepalive 64;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">server {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    listen 80;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    server_name _;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    client_max_body_size 512m;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    proxy_read_timeout 600s;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    proxy_send_timeout 600s;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    proxy_set_header Host $host;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    proxy_set_header X-Forwarded-For $remote_addr;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    location /ingress/ {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        proxy_http_version 1.1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        proxy_set_header Connection "";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        proxy_pass http://reduct_ingress/;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    location /egress/ {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        proxy_http_version 1.1;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        proxy_set_header Connection "";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        proxy_pass http://reduct_egress/;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre></div></div>
<p>In the config above, we define two upstream blocks: <code>reduct_ingress</code> for handling write requests and <code>reduct_egress</code> for handling read requests.
Each block uses <code>ip_hash</code> to ensure session affinity, which is crucial for maintaining consistent writes and reads.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductstore-configuration-notes">ReductStore Configuration Notes<a href="https://www.reduct.store/blog/nginx-resilient-deployment#reductstore-configuration-notes" class="hash-link" aria-label="Direct link to ReductStore Configuration Notes" title="Direct link to ReductStore Configuration Notes" translate="no">​</a></h2>
<p>The configuration between nodes of each layer is identical. To reach the desired architecture, you need to provision buckets and replication tasks for
ingress nodes and buckets only for egress nodes. See the configuration files in the example repo for details.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="failure-drills">Failure Drills<a href="https://www.reduct.store/blog/nginx-resilient-deployment#failure-drills" class="hash-link" aria-label="Direct link to Failure Drills" title="Direct link to Failure Drills" translate="no">​</a></h2>
<p>When the setup is running, you can simulate failures to see how it behaves:</p>
<ol>
<li class=""><strong>Kill an ingress node</strong> → writes continue via other ingress nodes.</li>
<li class=""><strong>Kill an egress node</strong> → reads continue via other egress nodes; replication resyncs when it’s back.</li>
<li class=""><strong>Simulate total ingress outage</strong> → analysis continues on egress; for true ingestion continuity, pair with a pilot-light instance in another location.</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="runbook">Runbook<a href="https://www.reduct.store/blog/nginx-resilient-deployment#runbook" class="hash-link" aria-label="Direct link to Runbook" title="Direct link to Runbook" translate="no">​</a></h2>
<p>Here’s a high-level runbook for deploying this architecture in production:</p>
<ol>
<li class="">Provision ingress + egress ReductStore nodes</li>
<li class="">Create buckets and replication tasks</li>
<li class="">Expose <code>/ingress</code> and <code>/egress</code> via NGINX with <code>ip_hash</code></li>
<li class="">Test with demo dataset</li>
<li class="">Validate reads from egress</li>
<li class="">Run failure drills</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="references">References<a href="https://www.reduct.store/blog/nginx-resilient-deployment#references" class="hash-link" aria-label="Direct link to References" title="Direct link to References" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://github.com/reductstore/nginx-resilient-setup" target="_blank" rel="noopener noreferrer" class="">NGINX Resilient Setup Example</a></li>
<li class=""><a class="" href="https://www.reduct.store/docs/guides/disaster-recovery">Disaster Recovery Guide</a></li>
</ul>
<hr>
<p>I hope you find this article interesting and useful. If you have any questions or feedback, don’t hesitate to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>nginx</category>
        </item>
        <item>
            <title><![CDATA[ReductStore v1.16.0 Released With New Extensions and Context Replication]]></title>
            <link>https://www.reduct.store/blog/news/reductstore-v1_16_0-released</link>
            <guid>https://www.reduct.store/blog/news/reductstore-v1_16_0-released</guid>
            <pubDate>Sat, 30 Aug 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[We are pleased to announce the release of ReductStore v1.16.0, which includes new extensions for robotics and columnar data, as well as the ability to replicate context records in queries.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="ReductStore v1.16.0 Released" src="https://www.reduct.store/assets/images/banner_release_1_16-fcc2e5daf681f6024817b5f4c1740f56.webp" width="1886" height="1066" class="img_ev3q"></p>
<p>We are pleased to announce the release of the latest minor version of <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>, <a href="https://github.com/reductstore/reductstore/releases/tag/v1.16.0" target="_blank" rel="noopener noreferrer" class=""><strong>1.16.0</strong></a>. ReductStore is a high-performance storage and streaming solution designed for storing and managing large volumes of historical data.</p>
<p>To download the latest released version, please visit our <a class="" href="https://www.reduct.store/download"><strong>Download Page</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="whats-new-in-1160">What's new in 1.16.0?<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#whats-new-in-1160" class="hash-link" aria-label="Direct link to What's new in 1.16.0?" title="Direct link to What's new in 1.16.0?" translate="no">​</a></h2>
<p>The v1.16.0 release introduces two new extensions designed to enhance data workflows for robotics and columnar data, along with support for replicating context records during queries.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="querying-and-replicating-data-with-context">Querying and Replicating Data with Context<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#querying-and-replicating-data-with-context" class="hash-link" aria-label="Direct link to Querying and Replicating Data with Context" title="Direct link to Querying and Replicating Data with Context" translate="no">​</a></h3>
<p>We’ve extended the conditional query syntax with <strong><a class="" href="https://www.reduct.store/docs/conditional-query/directives">directives</a></strong> that allow users to modify global query behavior.
The first directives introduced are <code>#ctx_before</code> and <code>#ctx_after</code>, which enable the inclusion of context records that occur before or after each matching record in a query.</p>
<p>This feature is particularly useful when analyzing specific events or conditions in your data, as it helps provide a clearer picture of the surrounding context. For instance, you can use these directives to include records from a few seconds before or after an anomaly or incident, aiding in root cause analysis or pattern recognition.</p>
<p>Here’s an example of how to use the <code>#ctx_before</code> directive in a query:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"#ctx_before"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"5s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"&amp;anomaly_score"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"$gt"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.8</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>This query returns all records with an anomaly score greater than 0.8, along with the context records that occurred within 5 seconds before each matching entry.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-reductselect-extension">New ReductSelect Extension<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#new-reductselect-extension" class="hash-link" aria-label="Direct link to New ReductSelect Extension" title="Direct link to New ReductSelect Extension" translate="no">​</a></h3>
<p>ReductStore is fundamentally a blob storage system and does not allow direct manipulation of stored data. However, with its extension mechanism, we can introduce new capabilities while keeping the core system simple.</p>
<p>The new <a class="" href="https://www.reduct.store/docs/extensions/official/select-ext"><strong>ReductSelect</strong></a> extension enables users to query and transform data stored in CSV or JSON formats, making it easier to build flexible and efficient data processing workflows.</p>
<p>For example, the following query uses ReductSelect to extract specific columns from CSV data and filter rows using the same conditional syntax available in ReductStore's native query language:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"ext"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"select"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"csv"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"has_headers"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"columns"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"temperature"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"as_labels"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"temp"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"humidity"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"when"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"&amp;temperature"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"$gt"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>This query selects the <code>temperature</code> and <code>humidity</code> columns from a CSV file, renames <code>temperature</code> to <code>temp</code>, and filters rows where the temperature is greater than 30°C.</p>
<p>These simple transformations enable you to ingest structured data very quickly and retrieve only subsets of it for further processing and analysis.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="new-reductros-extension">New ReductROS Extension<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#new-reductros-extension" class="hash-link" aria-label="Direct link to New ReductROS Extension" title="Direct link to New ReductROS Extension" translate="no">​</a></h3>
<p>Another exciting addition is the <a class="" href="https://www.reduct.store/docs/extensions/official/ros-ext"><strong>ReductROS</strong></a> extension, which provides tools for extracting and transforming data stored in ReductStore into formats compatible with the Robot Operating System (ROS).</p>
<p>With this extension, you can extract data from MCAP files containing ROS 2 messages and convert it into JSON format, making it easier to analyze and visualize.
It also supports transforming raw binary data—such as images—into more accessible formats like JPEG or base64 strings.</p>
<p>For example, the following query extracts data from a ROS 2 topic and encodes the image payload as a JPEG:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"ext"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"ros"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"extract"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"topic"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/camera/image"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"encode"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token property" style="color:#36acaa">"data"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"jpeg"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<p>ReductROS is still in active development, and we plan to expand its capabilities with support for additional ROS message types and more flexible extraction options in future releases.
Stay tuned for updates!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-next">What next?<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#what-next" class="hash-link" aria-label="Direct link to What next?" title="Direct link to What next?" translate="no">​</a></h2>
<p>We are constantly working on improving ReductStore and adding new features to provide the best experience for our users.
In the next release we plan to add new features and improvements, including:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="shareable-query-links">Shareable Query Links<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#shareable-query-links" class="hash-link" aria-label="Direct link to Shareable Query Links" title="Direct link to Shareable Query Links" translate="no">​</a></h3>
<p>We are developing a feature that allows users to generate and share links to specific queries in ReductStore.</p>
<p>This will simplify collaboration by enabling team members to access query results without needing direct access to the ReductStore instance. It will also allow users to download results directly via a link and support integration with external tools and platforms such as <strong><a href="https://foxglove.dev/" target="_blank" rel="noopener noreferrer" class="">Foxglove</a></strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="integration-with-grafana">Integration with Grafana<a href="https://www.reduct.store/blog/news/reductstore-v1_16_0-released#integration-with-grafana" class="hash-link" aria-label="Direct link to Integration with Grafana" title="Direct link to Integration with Grafana" translate="no">​</a></h3>
<p>We are also working on a <strong><a href="https://github.com/reductstore/reduct-grafana" target="_blank" rel="noopener noreferrer" class="">Grafana plugin</a></strong> that enables users to visualize and analyze data stored in ReductStore directly within Grafana dashboards.</p>
<p>This integration will provide a seamless experience with Grafana’s powerful visualization tools, allowing you to:</p>
<ul>
<li class="">Build custom dashboards using data from ReductStore.</li>
<li class="">Monitor your data streams and historical records in real time.</li>
<li class="">Visualize labels and data output in JSON or CSV formats.</li>
</ul>
<p>Stay tuned for the first release—coming soon!</p>
<hr>
<p>I hope you find those new features useful. If you have any questions or feedback, don’t hesitate to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>
<p>Thanks for using <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>!</p>]]></content:encoded>
            <category>news</category>
        </item>
        <item>
            <title><![CDATA[Comparing Robotics Visualization Tools: RViz, Foxglove, Rerun]]></title>
            <link>https://www.reduct.store/blog/comparison-rviz-foxglove-rerun</link>
            <guid>https://www.reduct.store/blog/comparison-rviz-foxglove-rerun</guid>
            <pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This article compares robotics visualization tools RViz, Foxglove, and Rerun. It covers their features, cross-platform compatibility, ROS integration, pricing, and performance to help developers understand their strengths and limitations.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Intro image" src="https://www.reduct.store/assets/images/intro_image-5fa22f5de0a323cfc1fb18edd77cdc1e.png" width="2202" height="1244" class="img_ev3q"></p>
<p>In robotics development, effective visualization and analysis tools are essential for monitoring, debugging, and interpreting complex sensor data. Platforms like RViz, Foxglove, and Rerun play a key role at the visualization layer of the observability stack. They help developers interact with both live and recorded data. These tools rely on timely, well-structured access to the underlying data streams. That's where <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a> comes in. It handles the data logging, storage, and processing, with a focus on capturing high-volume time-series data efficiently. ReductStore aims to integrate with tools like RViz, Foxglove, and Rerun, supporting a complete observability pipeline: from raw data ingestion to actionable insights.</p>
<!-- -->
<p>Each visualization platform has its unique role in the development workflow. <a href="https://wiki.ros.org/rviz" target="_blank" rel="noopener noreferrer" class=""><strong>RViz (ROS Visualization) is the classic 3D visualization tool built for the ROS ecosystem</strong></a>, widely used for real-time robot monitoring and debugging. <a href="https://foxglove.dev/about" target="_blank" rel="noopener noreferrer" class=""><strong>Foxglove is a modern data visualization and inspection platform for robotics and physical AI systems</strong></a>, aiming to simplify how teams collect, visualize, analyze, and manage large volumes of diverse sensor data. <a href="https://rerun.io/" target="_blank" rel="noopener noreferrer" class=""><strong>Rerun is a lightweight, native desktop application focused on fast and efficient visualization of robotics data</strong></a>, enabling developers to quickly explore and debug both live and recorded sensor streams with minimal setup.</p>
<p>This article compares RViz, Foxglove, and Rerun across key criteria: pricing, cross-platform support, remote collaboration, user interface, extensibility, ROS integration, performance with large datasets, and visualization and analysis features. The goal is to help robotics developers choose the right tool for their specific needs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pricing"><strong>Pricing</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#pricing" class="hash-link" aria-label="Direct link to pricing" title="Direct link to pricing" translate="no">​</a></h2>
<p><strong>RViz</strong> and <strong>RViz 2</strong> are part of the ROS ecosystem and released under the BSD 3-Clause License. This permissive open-source license allows free use, modification, and redistribution (including for commercial purposes), as long as the original copyright and license notices are preserved.</p>
<p><strong>Foxglove</strong> offers a free tier that includes core features for up to 3 users, 10 devices, and 10 GB of cloud storage. For larger teams or needs (e.g., extra users, storage, private extensions, enterprise integrations), paid subscriptions are available. Pricing is based on the number of users and storage volume, as well as usage and support level. There is also a free academic plan for qualified institutions, which includes more users and storage. Foxglove itself is proprietary software, though it is built on open protocols like MCAP and integrates with open-source ROS tools.</p>
<p><strong>Rerun</strong> is fully open-source under both the MIT and Apache 2.0 licenses. There are no current paid plans for the open-source core. The project follows an open-core model: the core visualizer and SDK are free, while a commercial platform is in early access for teams needing cloud-based storage, collaboration tools, advanced analytics, and scalable CI/CD workflows. This commercial layer is designed to build on top of the open-source foundation.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="platform--collaboration"><strong>Platform &amp; Collaboration</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#platform--collaboration" class="hash-link" aria-label="Direct link to platform--collaboration" title="Direct link to platform--collaboration" translate="no">​</a></h2>
<p><strong>RViz</strong> and <strong>RViz 2</strong> are primarily developed for Linux, where they offer the most stable and reliable performance. RViz 2 also supports Windows and macOS as part of ROS 2, but these versions are less mature and less commonly used. They often require manual setup or compilation, though support continues to improve with newer ROS 2 releases.</p>
<p>Both RViz versions are local desktop applications and are not designed for remote or multi-user use out of the box. Workarounds like SSH with X11 forwarding, VNC, or running RViz locally while connecting remotely to a ROS system are possible, but they are often fragile, require manual configuration, and may suffer from performance or latency issues depending on the network and hardware.</p>
<p>To address these limitations, early tools like <code>ROS3D.js</code> offered browser-based ROS 1 visualization, but they are now mostly unmaintained and incompatible with ROS 2. Modern web visualization is typically done with tools like Foxglove, Webviz, or custom WebSocket-based interfaces. Some cloud robotics platforms also offer remote ROS visualization, though they typically require extra integration work.</p>
<p><strong>Foxglove</strong> runs on Windows, macOS, and Linux, available both as a native desktop app and in a web browser. This gives users the flexibility to work locally or remotely without installing software. The browser version supports multi-user collaboration, allowing teams to share layouts and stream live data securely in real time from any internet-connected device.</p>
<p><strong>Rerun</strong> is a lightweight native desktop application for Windows, macOS, and Linux. It requires minimal setup and enables developers to quickly visualize and debug live or recorded sensor data without needing a browser or complex configuration. Although Rerun does not support multi-user or collaborative features, teams often share log files for offline review. This approach is usually more practical than using remote desktop tools. Rerun also integrates well into development workflows, such as Python environments, which typically require installing Rerun's SDKs and dependencies.</p>
<blockquote>
<p><strong>Note</strong>: All three tools support sharing of recorded data, such as rosbag files for RViz &amp; Rviz 2 (<code>.bag</code> for ROS 1 and <code>.db3</code>, <code>.mcap</code> for ROS 2), <code>.mcap</code> files for Foxglove, and <code>.rrd</code> for Rerun. To support these workflows at scale, you can use <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a> solutions to manage continuous recording, indexing, and long-term storage of these file types across teams and infrastructure.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="user-interface"><strong>User Interface</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#user-interface" class="hash-link" aria-label="Direct link to user-interface" title="Direct link to user-interface" translate="no">​</a></h2>
<p><strong>RViz</strong> and <strong>RViz 2</strong> have a powerful but somewhat dated interface that focuses more on functionality than modern design. The learning curve can be steep, especially for beginners, due to the complex layout and the need to manually configure displays, topics, coordinate frames, and tools. The interface is built around multiple panels and dialogs that require careful configuration. It lacks the visual polish and streamlined workflows of newer visualization tools.</p>
<p><strong>Foxglove</strong> features a modern, user-friendly interface with flexible dashboards and responsive controls. It is designed to be accessible to users at all experience levels, making it easier to explore, analyze, and share robotics data. The interface relies heavily on graphical elements instead of commands or configuration files, which lowers the entry barrier for users unfamiliar with ROS or robotics tools.</p>
<p><strong>Rerun</strong> offers a clean and straightforward interface focused on efficient data visualization. It balances ease of use with core functionality, providing easy-to-navigate views without overwhelming users. The interface requires minimal setup and supports intuitive exploration of data streams and logs. However, it currently has fewer customization options than RViz or Foxglove.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="extensibility"><strong>Extensibility</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#extensibility" class="hash-link" aria-label="Direct link to extensibility" title="Direct link to extensibility" translate="no">​</a></h2>
<p><strong>RViz</strong> (both ROS 1 and ROS 2) supports extensibility through C++ plugins, allowing users to develop and integrate custom visualizations, tools, and panels. This plugin architecture makes RViz highly adaptable across robotics domains such as perception, navigation, and manipulation. Many ROS packages include their own RViz plugins by default. However, developing and using plugins requires tight integration with the specific ROS environment. Plugins made for RViz in ROS 1 are not directly compatible with RViz 2; they often require modification or a complete rewrite.</p>
<p><strong>Foxglove</strong> offers extensibility through an Extensions SDK, which allows developers to build React-based visualizations using TypeScript. Extensions can be shared via an online registry and do not require recompilation. Foxglove also provides APIs and libraries in C++, Python, and Rust, primarily for working with the MCAP file format, enabling integration with ROS (both versions), WebSocket streams, and recorded sensor data. Foxglove's ecosystem also supports integration with popular robotics and simulation tools such as NVIDIA Isaac Sim, Velodyne LiDAR, and Jupyter Notebooks, either directly or via external bridges.</p>
<p><strong>Rerun</strong> focuses on extensibility through SDKs and APIs, especially for Python and other programming environments. It does not support plugin-based customization or drag-and-drop extensions like RViz or Foxglove. Instead, it prioritizes programmatic data embedding and visualization, making it well-suited for users who prefer scripting and code-driven workflows.</p>
<p>Rerun offers strong Python support, but its core is built with Rust and the egui GUI framework — technologies less familiar to many robotics developers. This can introduce a learning curve and limit low-level customization unless users are comfortable with Rust.</p>
<p>Rerun does not offer a simple or dynamic plugin system or scripting layer similar to RViz's C++ plugins or Foxglove's TypeScript extensions. This limits rapid prototyping or quick third-party integration.</p>
<p>Still, its APIs offer robust integration with diverse data sources, including ROS topics, sensor streams, and machine learning frameworks like TensorFlow and PyTorch. This makes Rerun a flexible tool for logging, visualizing, and debugging complex data pipelines.</p>
<p>Rerun is best suited for developers who prefer programming-driven customization over GUI-based tools. It provides direct control over data ingestion and visualization, enabling highly tailored, dynamic workflows that can grow with project needs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ros-integration"><strong>ROS Integration</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#ros-integration" class="hash-link" aria-label="Direct link to ros-integration" title="Direct link to ros-integration" translate="no">​</a></h2>
<p><strong>RViz</strong> is tightly integrated with ROS and supports direct interaction with live ROS topics. Originally developed for ROS 1, it was succeeded by <strong>RViz 2</strong> for ROS 2, and it remains a core visualization tool in many robotics workflows. However, this deep integration limits RViz's usability outside the ROS ecosystem. Both versions depend on a fully functioning ROS environment and are not designed to run independently or handle non-ROS data without conversion.</p>
<p><strong>Foxglove</strong> connects to live ROS systems using <code>foxglove_bridge</code>, a WebSocket-based bridge designed for this purpose. It runs on the same network as the ROS system and streams real-time ROS messages to Foxglove over WebSocket. This architecture allows remote monitoring and interaction with installing ROS locally. Unlike RViz, Foxglove can be used without a full ROS setup.</p>
<p>In addition to live data, Foxglove also supports opening and analyzing ROS bag files locally. This makes it easy to review recorded data, visualize topics, and troubleshoot issues offline, without needing an active ROS system.</p>
<p><strong>Rerun</strong> supports integration with both ROS 1 and ROS 2, enabling live topic visualization and recorded data inspection. For ROS 2, Rerun officially maintaines basic example scripts, hosted on GitHub, that use Python (<code>rclpy</code>) or C++ to subscribe to ROS 2 topics and forward selected data to the Rerun viewer. This is a user-defined bridge rather than a native-plugin integration. ROS 1 integration is possible using custom nodes written in either C++ or Python (<code>rospy</code>), but usually requires more manual setup. Unlike Foxglove, which uses standardized communication protocols like <code>foxglove_websocket</code> via <code>foxglove_bridge</code> (and optionally <code>rosbridge</code>), Rerun ingests data directly through user-defined code and does not rely on ROS-specific bridge protocols. While Rerun avoids protocol-based bridging, it still requires users to write custom nodes that translate ROS messages into its API.</p>
<p>Rerun is especially useful for visualizing time-synchronized multimodal data, such as sensor readings, 3D geometry, camera images, transforms, and trajectories. However, it currently lacks built-in support for certain ROS-specific features like interactive TF tree exploration, occupancy/grid map overlays, and full URDF-based robot model visualization. Community-maintained examples (e.g., the <code>urdf_loader</code>) offer partial support for URDF rendering, but do not yet match RViz’s depth or interactivity.</p>
<p>Rerun also cannot currently open ROS bag files directly (<code>.bag</code> for ROS 1 or <code>.db3</code> for ROS 2). Instead, users replay them with <code>rosbag play</code> or <code>ros2 bag play</code> and forward selected topics to Rerun using custom Python or C++ bridge nodes. This workflow offers flexibility and performance but requires additional configuration. Rerun uses its own <code>.rrd</code> log format, which is optimized for high-throughput, time-seekable storage and streaming.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="performance-with-large-data"><strong>Performance with Large Data</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#performance-with-large-data" class="hash-link" aria-label="Direct link to performance-with-large-data" title="Direct link to performance-with-large-data" translate="no">​</a></h2>
<p><strong>RViz</strong> is not fully optimized for very large datasets, such as dense point clouds, high-frequency topics, or long message histories. When visualizing large volumes of data, users may encounter performance issues like low frame rates, rendering lag, and high CPU or GPU usage. This happens because RViz continuously renders incoming ROS messages and stores message history in memory, which can quickly overwhelm system resources.</p>
<p><strong>RViz 2</strong> improves on this with better multithreading and more efficient message transport via DDS. These changes help boost performance and scalability in ROS 2 environments. However, RViz 2 still struggles with very dense or high-rate data streams, especially when rendering complex 3D data in real time, and these improvements do not fully solve the challenges of high-density visualization. To improve performance, users often reduce message history length, filter or downsample data, and disable non-essential displays.</p>
<p><strong>Foxglove</strong>, particularly its web version, can underperform RViz in high-data scenarios. Because it runs in a web browser, it's constrained by browser memory limits, single-threaded JavaScript execution, and limited access to hardware acceleration. As a result, visualizing large point clouds or streaming high-frequency topics may lead to lag, dropped frames, or browser instability. These limitations are especially evident when handling continuous 3D data or large bag files.</p>
<p>Performance can vary depending on the use case and browser environment. The desktop application bypasses some browser limitations and can perform better. However, since it is built on Electron, it still has overhead related to memory usage and resource management common to Electron-based apps. Though these issues are generally less severe than in the web version. For lighter workloads, such as 2D plots or moderate-frequency telemetry, Foxglove often performs well and benefits from its accessible UI and cross-platform support.</p>
<p><strong>Rerun</strong> is designed with high performance in mind for large-scale, multimodal data workflows. It is a native desktop application written in Rust and uses the modern WGPU rendering backend. This gives it direct access to system resources, helping it efficiently handle dense point clouds, long message histories, and high-frequency data streams. Behind the scenes, Rerun uses techniques such as memory-mapped I/O, zero-copy data handling, and intelligent batching to reduce latency and resource use.</p>
<p>Although there are only few formal benchmarks comparing Rerun with RViz or Foxglove, early community feedback and its architecture suggest that Rerun scales effectively with complex datasets. Performance can be further improved by filtering or downsampling data streams according to specific needs. Rerun is currently under active development to expand its capabilities for robotics visualization and analysis.</p>
<blockquote>
<p><strong>Note</strong>: Best practices for handling large datasets include splitting data files by time or size (e.g., every 1–5 minutes), using separate files for different topic groups, and automatically deleting old files when disk space is low. Chunk compression can also save disk space more efficiently than whole-file compression, but this approach consumes more CPU and memory resources, representing a trade-off between storage and performance.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="analysis--visualization"><strong>Analysis &amp; Visualization</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#analysis--visualization" class="hash-link" aria-label="Direct link to analysis--visualization" title="Direct link to analysis--visualization" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rviz--rviz-2"><strong>RViz &amp; RViz 2</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#rviz--rviz-2" class="hash-link" aria-label="Direct link to rviz--rviz-2" title="Direct link to rviz--rviz-2" translate="no">​</a></h3>
<p><strong>Key Capabilities:</strong></p>
<ul>
<li class="">
<p><strong>Real-Time Visualization &amp; Bag File Support</strong>: RViz and RViz 2 support real-time visualization by subscribing to live ROS topics. They also display data from recorded bag files (<code>.bag</code> for ROS 1, <code>.db3</code> and <code>.mcap</code> for ROS 2), when those files are replayed using tools like <code>rosbag play</code> or <code>ros2 bag play</code>.</p>
</li>
<li class="">
<p><strong>Data Format Support</strong>: RViz visualizes a wide range of robot state information, including URDF robot models, coordinate transforms (TF), and various sensor data such as LIDAR, IMU, depth, and RGB cameras. It also supports odometry, localization, occupancy grid maps (used in SLAM), navigation data (paths, goals, trajectories), and interactive markers for user interaction. RViz 2 supports the same data types with ROS 2 message compatibility.</p>
</li>
<li class="">
<p><strong>Interactive Markers</strong>: These 3D UI elements enable users to manipulate objects within the visualization: setting navigation goals, adjusting robot end-effector positions, or dragging points for motion planning. Using them requires writing supporting ROS nodes and configuring interaction logic.</p>
</li>
<li class="">
<p><strong>Configurable Interface</strong>: Users can add, remove, and arrange panels, and customize display properties such as colors, shapes, and update rates for each data type. These configurations can be saved and reloaded using <code>.rviz</code> files, streamlining repetitive workflows like navigation, debugging, or SLAM visualization. Multiple camera control modes (Orbit, FPS, Top-down) allow flexible 3D scene navigation.</p>
</li>
<li class="">
<p><strong>Plugin-Based Architecture</strong>: Developers can extend RViz by creating custom visualizations and tools through C++ plugins. RViz 2 supports plugins too, built on a more modern and modular architecture.</p>
</li>
</ul>
<p><img decoding="async" loading="lazy" alt="RViz" src="https://www.reduct.store/assets/images/rviz-2ea71829680281d7b700a19003333def.png" width="1300" height="844" class="img_ev3q"></p>
<small style="text-align:right"><p><a href="https://foxglove.dev/examples" target="_blank" rel="noopener noreferrer" class="">Data from Mobile Robot Example</a></p></small>
<p><strong>Limitations</strong>:</p>
<ul>
<li class="">
<p><strong>Limited Analysis</strong>: RViz and RViz 2 primarily serve visualization purposes and lack built-in tools for detailed message inspection, conditional logging, or advanced playback controls like pause, step, or speed adjustment. These features typically require external tools such as <code>rqt_bag</code>, ROS CLI utilities, or third-party RViz plugins (e.g., <code>rosbag_panel</code>). RViz also does not consistently warn about invalid data (e.g., NaNs or infinities), which can result in missing or misleading visuals. These tools are not designed for deep offline data analysis and are best used alongside more specialized logging or analysis solutions.</p>
</li>
<li class="">
<p><strong>No Time-Series Analysis</strong>: RViz and RViz 2 do not support time-series plotting or statistical analysis. For these tasks, dedicated tools like <code>rqt_plot</code>, PlotJuggler (with ROS 2 support), or external environments like Jupyter with Python are more appropriate.</p>
</li>
<li class="">
<p><strong>No Conditional Filtering</strong>: RViz and RViz 2 display all incoming data without the ability to filter messages based on content or fields. Filtering must be performed upstream, often by custom ROS nodes. Some plugins or panels offer limited filtering but are not general solutions.</p>
</li>
<li class="">
<p><strong>No Topic Synchronization</strong>: RViz and RViz 2 subscribe to each topic independently and display messages as they arrive. They do not synchronize data streams from different topics based on timestamps, which can cause misalignment or inconsistencies in time-sensitive visualizations (e.g., camera images, LIDAR scans, TF frames). Synchronization requires external tools like <code>message_filters</code> or custom nodes.</p>
</li>
<li class="">
<p><strong>No Built-In Logging or Export</strong>: RViz and RViz 2 cannot automatically export visualized data or record screencasts. Users are limited to manual screenshots unless using custom plugins or external tools to record sessions or extract data.</p>
</li>
<li class="">
<p><strong>Limited Multi-Robot Support</strong>: While RViz can display data from multiple robots using namespaces, the interface is not designed for straightforward multi-robot workflows. RViz 2 includes minor improvements, but still lacks dedicated features for managing multiple robots simultaneously.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="foxglove"><strong>Foxglove</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#foxglove" class="hash-link" aria-label="Direct link to foxglove" title="Direct link to foxglove" translate="no">​</a></h3>
<p><strong>Key Capabilities:</strong></p>
<ul>
<li class="">
<p><strong>Multi-Modal 3D Visualization</strong>: Foxglove provides comprehensive 3D visualization for a variety of robotics data, including URDF robot models, TF trees, sensor streams (LIDAR, point clouds, camera feeds), occupancy grids, and navigation elements such as paths, goals, and costmaps. Users can interact with the scene in real time: rotating the view, toggling layers, and focusing on specific frames or topics. Multi-camera views, tooltips, and overlays enhance spatial understanding. Synchronized multi-viewports and flexible camera modes (free, fixed, follow-frame, sensor-aligned) make it possible to examine several spatial data streams side by side. All streams are synchronized through a shared timeline for consistent context across modalities.</p>
</li>
<li class="">
<p><strong>Topic Synchronization &amp; Playback Timeline</strong>: Foxglove offers a unified, timestamp-based timeline that synchronizes data from multiple topics. This ensures time-aligned playback of sensor streams like RGB images, depth, point clouds, IMU, and TFs, useful both in real time and with recorded data. The timeline includes playback controls such as pause, frame-by-frame stepping, variable speed, and bookmarks for quickly navigating to key events. This tight time synchronization is a major advantage over RViz, enabling clearer insights into system behavior.</p>
</li>
<li class="">
<p><strong>Advanced Analysis &amp; Time-Series Tools</strong>: Foxglove offers a capable set of tools for offline analysis of recorded data. Users can inspect messages in detail, filter them by topic or namespace, and control playback through an integrated timeline with pause, step-by-step navigation, and adjustable speed. To view custom ROS 2 message types with full support, messages are best recorded in or converted to the MCAP format, although Foxglove can open other formats with some limitations.</p>
<p>For analyzing numeric data over time, Foxglove provides flexible plotting tools broadly comparable to <code>rqt_plot</code> or PlotJuggler. Users can visualize numeric fields from any message type, overlay multiple signals, zoom in on specific time ranges, and interactively adjust what's displayed.</p>
<p>These tools make it easy to explore sensor outputs, control signals, and algorithm behavior. Interactive graphs support dynamic switching between message series.</p>
</li>
<li class="">
<p><strong>Modular &amp; Configurable Interface</strong>: The Foxglove UI is fully modular, allowing users to add, remove, duplicate, and rearrange panels such as 3D views, image feeds, message viewers, plots, diagnostics, and consoles. Each panel is highly configurable, with settings for color, scale, transparency, update rate, and filtering. Users can save layouts as JSON files, enabling reproducible setups, role-based dashboards, and fast task switching (e.g., from SLAM debugging to perception analysis). Layouts can be shared across teams or versioned over time.</p>
</li>
<li class="">
<p><strong>Custom Panels &amp; Extensions</strong>: Foxglove allows users to build custom panels using plugins, enabling specialized interfaces tailored to specific workflows. These panels are embedded directly into the Foxglove interface, keeping everything streamlined and centralized. This is particularly valuable for teams developing internal tools or dashboards for robotics development and testing.</p>
<p>Extensions are packaged as <code>.foxe</code> files and can be shared via the Foxglove Extensions Registry, either privately within an organization or publicly. While layouts can be shared via URLs or layout IDs to replicate interface setups, this sharing does not include the actual panel code. Users must have the required extensions installed for full functionality.</p>
</li>
<li class="">
<p><strong>Cloud &amp; Collaboration</strong>: Foxglove can be run locally or in the cloud. Its cloud features include shared dashboards, timeline comments, and real-time collaboration, enabling teams to jointly review logs or live data remotely. This makes it particularly useful for distributed development, remote testing, or asynchronous data reviews.</p>
</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Foxglove" src="https://www.reduct.store/assets/images/foxglove-9be4d2bf3a7d3fcb098977e052ff6665.png" width="1193" height="700" class="img_ev3q"></p>
<small style="text-align:right"><p><a href="https://foxglove.dev/examples" target="_blank" rel="noopener noreferrer" class="">Autonomous Robotic Manipulation Example</a></p></small>
<p><strong>Limitations</strong>:</p>
<ul>
<li class="">
<p><strong>Limited Real-Time 3D Interactivity</strong>: Foxglove does not natively support interactive 3D markers like RViz. Users cannot directly manipulate objects in the 3D scene (e.g., setting goals, editing poses, or dragging elements) without building custom extensions. This limits Foxglove's out-of-the-box usability for real-time tasks such as motion planning, teleoperation, or interactive environment setup.</p>
</li>
<li class="">
<p><strong>Limited Advanced Features</strong>: Foxglove currently lacks certain advanced features found in tools like PlotJuggler. For example, Foxglove does not yet support strict axis ratio locking — a critical feature for accurately visualizing spatial data where maintaining proportional relationships between axes is important. Additionally, Foxglove's built-in data transformation capabilities are limited compared to PlotJuggler's comprehensive suite of statistical and signal-processing tools, such as moving averages, derivatives, filtering, and custom mathematical expressions. These advanced features make PlotJuggler especially useful for detailed signal analysis and fine-grained data manipulation, often essential when debugging sensor data or control signals.</p>
</li>
<li class="">
<p><strong>No Automated Anomaly Detection</strong>: Foxglove does not include built-in automated validation or anomaly detection. It does not use ML models or rule-based systems to automatically flag issues. Instead, it offers detailed message introspection and customizable visualizations that enable users to manually identify irregularities such as NaNs, infinities, or out-of-range values. This hands-on approach requires user expertise but provides flexible, in-depth analysis without automated alerts.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="rerun"><strong>Rerun</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#rerun" class="hash-link" aria-label="Direct link to rerun" title="Direct link to rerun" translate="no">​</a></h3>
<p><strong>Key Capabilities</strong>:</p>
<ul>
<li class="">
<p><strong>Real-time &amp; Recorded Data Visualization</strong>: Rerun supports both live-streamed and recorded sensor data visualization with minimal latency. It ingests data via Rust- or Python-based logging SDKs, handling a wide range of robotics sensor modalities including 3D spatial data, camera imagery, numeric time-series, semantic segmentation maps, depth maps, annotations (bounding boxes, keypoints), and textual or categorical event data. Recorded datasets can be replayed with full timeline control for stepwise inspection or smooth playback, aiding in bug reproduction and model validation.</p>
</li>
<li class="">
<p><strong>Collaboration &amp; Sharing Features</strong>: Rerun streamlines collaborative workflows through data export and session sharing via <code>.rrd</code> files. Teams can share recorded <code>.rrd</code> files for offline inspection, annotate data using Annotation Context (which supports labeling via class IDs and color mapping), and use shared Recording IDs to log streams from multiple processes or machines into a unified session, as long as the Recording ID is set consistently at the time of logging. Note: merging previously recorded <code>.rrd</code> files with different Recording IDs offline is currently not supported. Users can also export screenshots (for reports or dashboards) via the CLI or viewer options, depending on the version and available commands.</p>
</li>
<li class="">
<p><strong>Customizable &amp; Extensible UI</strong>: The Rerun Viewer offers a modular, layout-aware interface tailored for tasks such as SLAM debugging, multi-sensor calibration, and performance profiling. Users can save and reload Blueprints — serialized UI configurations that preserve panel layouts, timelines, selected entities, and styling (e.g., color, transparency, size). A full styling hierarchy (override → store → default → fallback) makes it easy to customize visuals without modifying source data. Multiple synchronized views (3D scenes, timelines, 2D plots, raw data inspectors) support comprehensive analysis.</p>
<p>Developers can extend functionality by adding custom egui-based panels, entity-aware views, or by embedding the Viewer into Rust applications via the <code>re_viewer</code> crate. Custom data loaders and visualization types can be created for proprietary or specialized formats. Interactive tools such as selection, pinning, and hover highlighting assist detailed exploration. The Viewer also supports interactive callbacks for events like hover, selection, and context menu actions, enabling highly tailored, real-time data interactions.</p>
</li>
<li class="">
<p><strong>Rich 3D Visualization with Spatial Context</strong>: Built on egui and WGPU, Rerun's 3D viewer efficiently renders large-scale scenes on consumer hardware. It uses an entity-path-based scene graph that reflects the hierarchical kinematic tree, allowing intuitive navigation and inspection of components, sensor frames, trajectories, bounding boxes, segmentation masks, dense point clouds, annotated images, 3D meshes, and time-series plots. Users can customize visual parameters (e.g., color maps, visibility, annotations, rendering modes) and navigate using orbit, zoom, and pan controls.</p>
</li>
<li class="">
<p><strong>Flexible Time-Series &amp; Event Logging</strong>: Rerun supports synchronized timeline playback of multiple data streams, using both explicit (user-defined) and implicit (auto-derived) timestamps. It manages multiple time domains (logical/log time and timeline time) to accurately align heterogeneous data sources. Timeline controls include zooming, scrubbing, filtering by entity path or timeline, and detailed event inspection with metadata. Conditional filtering and selective visibility help isolate anomalies or relevant events in complex multi-agent or multi-sensor deployments.</p>
</li>
<li class="">
<p><strong>Programmable Data Access &amp; Web Integration</strong>: The Rerun SDK provides semantic logging primitives (e.g., <code>log_scalar</code>, <code>log_image</code>, <code>log_point_cloud</code>, <code>log_text_entry</code>, <code>log_tensor</code>) that render automatically in the Viewer. Rerun uses Apache Arrow for efficient data handling, supporting advanced analysis with tools like Pandas and Jupyter. Direct export to formats like Parquet is supported via the API, making it suitable for both streaming visualization and offline batch analysis. The Viewer is also available as a React component, enabling seamless embedding within React applications and custom web dashboards, though integration with other JavaScript frameworks may require additional adaptation.</p>
<p>Underneath these features, Rerun's flexible and scalable logging is powered by an Entity-Component-System (ECS) data model, which organizes time-stamped components attached to hierarchical entities to maintain performance and extensibility.</p>
</li>
<li class="">
<p><strong>Emerging Features</strong>: Experimental capabilities include graph-based views for visualizing system architectures, connectivity, and agent interactions, extending Rerun's utility beyond traditional sensor data visualization into system design and research workflows.</p>
</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Rerun" src="https://www.reduct.store/assets/images/rerun-f88f42a3d62ddf2450faba65a0342626.png" width="1501" height="784" class="img_ev3q"></p>
<small style="text-align:right"><p><a href="https://rerun.io/examples" target="_blank" rel="noopener noreferrer" class="">nuScenes Example</a></p></small>
<p><strong>Limitations</strong>:</p>
<ul>
<li class="">
<p><strong>No Built-In Advanced Analytics</strong>: Rerun focuses primarily on visualization and lacks integrated statistical analysis, anomaly detection, or expression-based plotting features. In contrast, Foxglove provides richer analytics, including expression plots and integration with monitoring systems like Prometheus.</p>
</li>
<li class="">
<p><strong>Not Optimized for Live Robot Control</strong>: Although it supports real-time data streaming, Rerun is not designed for robot teleoperation or control input interaction. RViz and Foxglove offer more mature tools for monitoring and interacting with live robots.</p>
</li>
<li class="">
<p><strong>No Native Support for Navigation and SLAM Maps</strong>: Unlike RViz, Rerun does not natively visualize occupancy grids, costmaps, or SLAM results, limiting its utility for path planning or localization workflows.</p>
</li>
<li class="">
<p><strong>Limited Real-Time Collaboration</strong>: While Rerun supports offline session sharing, it lacks live multi-user collaboration features such as synchronized remote views or cloud-hosted live sessions, which are available in Foxglove.</p>
</li>
<li class="">
<p><strong>Limited Visualization of Large-Scale System Architectures</strong>: Rerun's entity-based model focuses on spatial and temporal data but does not yet offer comprehensive tools for exploring complex system communication graphs or architecture diagrams interactively.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion"><strong>Conclusion</strong><a href="https://www.reduct.store/blog/comparison-rviz-foxglove-rerun#conclusion" class="hash-link" aria-label="Direct link to conclusion" title="Direct link to conclusion" translate="no">​</a></h2>
<p>This article provided a detailed comparison of RViz, Foxglove, and Rerun, evaluating them across practical dimensions: pricing, platform, and collaboration support, user interface, extensibility, ROS integration, performance with large datasets, and analysis and visualization capabilities. By outlining their strengths and limitations, we offer a clear perspective to help robotics engineers and developers choose the right tool for their specific needs.</p>
<p>Choosing the right tool depends on your context: use RViz for real-time ROS development and interactive debugging, Foxglove for collaborative data analysis, time-synchronized playback, and remote team workflows, and Rerun for fast, developer-centric visualization of structured data in programmatic pipelines. In practice, many robotics teams find that combining these tools enables more effective development and validation across different stages of their workflows.</p>
<hr>
<p>We hope this comparison helps you make informed decisions and inspires you to keep exploring better tools and workflows. If you have questions, feedback, or insights to share, join the conversation on the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>robotics</category>
            <category>rviz</category>
            <category>foxglove</category>
            <category>rerun</category>
        </item>
        <item>
            <title><![CDATA[3 Ways to Store Computer Vision Data]]></title>
            <link>https://www.reduct.store/blog/computer-vision-applications</link>
            <guid>https://www.reduct.store/blog/computer-vision-applications</guid>
            <pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to store data in computer vision applications by using ReductStore, S3-like storage or a file system.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Computer Vision with ReductStore" src="https://www.reduct.store/assets/images/cv-intro-b29acb0263cd66ed25a5f8bdcee765dd.png" width="1584" height="888" class="img_ev3q"></p>
<p>When building computer vision systems, efficient data storage is a fundamental requirement. Whether you're capturing images for training, storing inference results for validation, or archiving sensor data for future analysis, your storage solution must be both reliable and high-performance.</p>
<p>Ingestion speed is especially critical. If your system can’t write data fast enough — whether it’s high-frequency frames or accompanying metadata — you risk losing valuable information or creating bottlenecks in the pipeline.</p>
<p>In this post, we’ll look at three common approaches to storing data in computer vision applications: a traditional file system, S3-compatible object storage, and <strong><a class="" href="https://www.reduct.store/">ReductStore</a></strong>, a time-series-optimized blob storage. We’ll explore the strengths and limitations of each approach to help you choose the best fit for your application.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-simple-computer-vision-application">A Simple Computer Vision Application<a href="https://www.reduct.store/blog/computer-vision-applications#a-simple-computer-vision-application" class="hash-link" aria-label="Direct link to A Simple Computer Vision Application" title="Direct link to A Simple Computer Vision Application" translate="no">​</a></h2>
<p>For demonstration purposes, we’ll use a simple computer vision (CV) application which is connected to a CV camera and runs on an edge device:</p>
<p><img decoding="async" loading="lazy" alt="Computer Vision Application" src="https://www.reduct.store/assets/images/computer-vision-application-7097274ff50f1c44a8129ede9aeb8ed2.png" width="875" height="290" class="img_ev3q"></p>
<p>The camera driver captures images from the CV camera every second and forwards them to the model, which then detects objects and displays the results in the user interface.</p>
<p>Your images and results need to be stored for training and validation purposes. The customer may also wish to view images featuring anomalous objects. These requirements present the challenge of maintaining a history of blob or unstructured data.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="file-system">File System<a href="https://www.reduct.store/blog/computer-vision-applications#file-system" class="hash-link" aria-label="Direct link to File System" title="Direct link to File System" translate="no">​</a></h2>
<p>One of the most straightforward ways to store images from a (CV) camera is to write them directly to a local file system. Each image can be saved using a timestamp as a unique identifier, and the directory structure can be organized by time intervals for easier access.</p>
<p><img decoding="async" loading="lazy" alt="File System Usage" src="https://www.reduct.store/assets/images/file-system-usage-5644a4ae749b1888f60e3040c347a02c.png" width="875" height="500" class="img_ev3q"></p>
<p>The main advantage of this approach is its simplicity — no external dependencies, no infrastructure setup, and it works out of the box. However, it comes with significant limitations:</p>
<ul>
<li class="">
<p><strong>Data retention</strong>
Disk space is finite. Eventually, you'll need to delete older data — either manually, using tools like <code>cron</code>, or by building this logic into your application.</p>
</li>
<li class="">
<p><strong>Data lookup</strong>
While naming conventions and folder hierarchies can help, accessing data by time or context becomes inefficient as the dataset grows. In practice, this often requires a companion database to index and query files — adding complexity and undermining the simplicity of the original setup.</p>
</li>
<li class="">
<p><strong>Labeling and search</strong>
If you want to tag images with labels (e.g., object type, camera location, quality), or filter by those labels later, the file system alone doesn’t support it. You’ll need to implement a separate mechanism.</p>
</li>
<li class="">
<p><strong>Replication</strong>
Copying data to other machines (e.g., for redundancy or aggregation) is possible using tools like <code>rsync</code> or <code>scp</code>, but it requires manual setup, SSH access, and custom scripts.</p>
</li>
<li class="">
<p><strong>Security</strong>
Providing access to stored data often requires giving users access to the entire file system or to network shares, which increases the risk of unauthorized access or accidental modification.
Additionally, if you need to share data externally — for example, with a remote team or a cloud service — you may end up copying large volumes of files. This not only consumes time and bandwidth but also raises concerns about secure transfer, data leaks, and access control once the data is outside your network.</p>
</li>
</ul>
<p>In summary, while the file system is an easy starting point, it rarely scales well. As your needs grow — especially around querying, labeling, replication, and security — you’ll often end up building database-like functionality on top of it. At that point, it may be worth reconsidering your storage architecture altogether.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="s3-like-object-storage">S3-like Object Storage<a href="https://www.reduct.store/blog/computer-vision-applications#s3-like-object-storage" class="hash-link" aria-label="Direct link to S3-like Object Storage" title="Direct link to S3-like Object Storage" translate="no">​</a></h2>
<p>A more advanced approach to storing images is to use an object storage system, such as an S3-compatible service. This allows data to be organized similarly to folders and files, but accessed and managed through an HTTP-based API.</p>
<p><img decoding="async" loading="lazy" alt="S3-like Storage Usage" src="https://www.reduct.store/assets/images/s3-like-storage-usage-b28c28c6e24f5534fa8a071d5cbc8d2e.png" width="875" height="506" class="img_ev3q"></p>
<p>This method offers more flexibility than a local file system and comes with several advantages:</p>
<ul>
<li class="">
<p><strong>Remote storage</strong>
Data can be stored on remote servers or in the cloud, where capacity is virtually unlimited. If your organization or customer already uses an object storage–based data lake, it's possible to write data there directly.</p>
</li>
<li class="">
<p><strong>Security</strong>
Access to the data can be controlled using fine-grained permissions. For example, the application might have write-only access, while users are granted read-only rights.</p>
</li>
<li class="">
<p><strong>Data replication</strong>
Many object storage systems support built-in replication, allowing data to be mirrored automatically or manually to other locations.</p>
</li>
</ul>
<p>However, this approach is not without its drawbacks:</p>
<ul>
<li class="">
<p><strong>Data retention</strong>
Although object storage typically offers more capacity, most systems don't provide real-time data expiration or fine-grained retention policies out of the box. Some platforms support time-based retention (e.g., lifecycle rules in S3), but these are often batch processes that run periodically, not in real time.
This can be problematic if your data ingestion rate is unpredictable or bursty — you might exceed storage limits before the cleanup is triggered. In such cases, you’ll need to implement custom logic or external tooling to ensure timely data reduction.</p>
</li>
<li class="">
<p><strong>Data accessibility</strong>
Despite its API, the data model still mimics a file system hierarchy, which may cause similar performance issues as the dataset grows. Moreover, access via HTTP introduces latency — each read or write requires a separate HTTP request, which can be inefficient for small objects.
<a class="" href="https://www.reduct.store/blog/comparisons/computer-vision/iot/performance-comparison-reductstore-vs-minio"><strong>See our benchmark on performance limitations</strong></a>.</p>
</li>
</ul>
<p>An S3-like object storage may seem like a natural upgrade from a file system, especially in terms of scalability and remote access. However, its hierarchical structure and limited native querying make it less suitable for time-series or high-ingestion workloads. Over time, you might find yourself building additional infrastructure — such as a time-series database or metadata index — to overcome these limitations.
We’ve explored this <strong><a class="" href="https://www.reduct.store/blog/comparison/iot/reductstore-benchmark">alternative in detail</a></strong> and found that it introduces its own trade-offs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reductstore">ReductStore<a href="https://www.reduct.store/blog/computer-vision-applications#reductstore" class="hash-link" aria-label="Direct link to ReductStore" title="Direct link to ReductStore" translate="no">​</a></h2>
<p><strong><a href="https://www.reduct.store/" target="_blank" rel="noopener noreferrer" class="">ReductStore</a></strong> is a high-performance storage and streaming solution for robotics, computer vision, and industrial IoT applications.
It is purpose-built to ingest and store unstructured time-series data — such as images, sensor payloads, or inference results — directly at the edge.
Once stored locally, the data can be selectively replicated to central storage or the cloud, enabling efficient data consolidation without sacrificing real-time performance at the edge.</p>
<p><img decoding="async" loading="lazy" alt="ReductStore Usage" src="https://www.reduct.store/assets/images/reductStore-usage-44dd3a77bd70bdc2d94110b447f2d740.png" width="875" height="610" class="img_ev3q"></p>
<p>As you can see, instead of a tree-like structure, it has a flat structure of entries where data is batched and partitioned into blocks.
This approach is more efficient for writing, since blocks can be pre-allocated for incoming records.
It also improves time-based querying: because data typically arrives in time order, records for a given time interval are physically located close to each other — making scans faster and more predictable.</p>
<p>Another very important feature of ReductStore is its support for data labeling. This allows you to store the image and the corresponding model output together in a single entry.
Labels not only help organize and filter data during queries — they can also be used for conditional replication, so only relevant data is streamed to central storage or other nodes,
or selectively retained on the edge for long-term storage.</p>
<p>Let’s summarize the benefits of using ReductStore:</p>
<ul>
<li class="">
<p><strong>Data Accessibility</strong>
ReductStore uses record timestamps as unique identifiers and provides an HTTP API for efficient access by exact time or time range.
It also <a class="" href="https://www.reduct.store/docs/guides/data-querying#concepts"><strong>batches small objects</strong></a> into larger HTTP requests, which significantly improves performance on high-latency networks.</p>
</li>
<li class="">
<p><strong>Data Reduction</strong>
Each bucket can be assigned a storage quota. When the quota is reached, the engine automatically removes the oldest data — enabling built-in, real-time retention management.</p>
</li>
<li class="">
<p><strong>Labeling</strong>
Data can be tagged with labels and used as <a class="" href="https://www.reduct.store/docs/guides/data-ingestion#annotating-data"><strong>annotations</strong></a> or <a class="" href="https://www.reduct.store/docs/guides/data-querying#using-labels-to-filter-data"><strong>search criteria</strong></a>.
Labels also enable advanced workflows such as conditional replication or selective long-term retention on the edge.</p>
</li>
<li class="">
<p><strong>Security</strong>
ReductStore supports role-based access with token authentication, allowing you to define fine-grained read and write permissions per bucket.
This makes it possible to isolate access between devices, applications, or users — ensuring that each has access only to the data it needs.</p>
</li>
<li class="">
<p><strong>Data Replication</strong>
ReductStore supports append-only <a class="" href="https://www.reduct.store/docs/guides/data-replication"><strong>data replication</strong></a>, enabling automatic streaming of data from one instance to another.
You can replicate all data or filter it using labels to replicate only what’s relevant.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-not-a-relational-database">Why Not a Relational Database?<a href="https://www.reduct.store/blog/computer-vision-applications#why-not-a-relational-database" class="hash-link" aria-label="Direct link to Why Not a Relational Database?" title="Direct link to Why Not a Relational Database?" translate="no">​</a></h2>
<p>Relational databases are great for structured data, but they’re not designed for the demands of computer vision applications — especially when it comes to storing and managing image data at scale.</p>
<p>In most CV pipelines, you deal with large volumes of <strong>unstructured binary data</strong>: raw images, video frames, model outputs, and metadata. Trying to store this data in a relational database quickly becomes inefficient for several reasons:</p>
<ul>
<li class="">
<p><strong>Inefficient for Large Binary Data</strong>
Relational databases can store images as blobs, but they aren’t optimized for high-throughput write operations. Ingesting large numbers of images per second puts significant strain on the database engine.</p>
</li>
<li class="">
<p><strong>Poor Time-Series Access</strong>
CV systems often need to query image data by time intervals — e.g., “give me all frames from the past 10 minutes.”
Relational databases can support this, but performance degrades with large datasets unless you implement table partitioning and maintain complex indexes.
This adds operational overhead and still may not scale well for high-ingestion workloads like real-time image capture.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/computer-vision-applications#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Choosing the right storage solution is critical for building efficient and reliable computer vision systems. While traditional file systems and object stores can serve as starting points, they quickly fall short when it comes to performance, searchability, and data lifecycle management — especially at the edge.</p>
<p>ReductStore offers a purpose-built alternative: a high-performance storage and streaming solution optimized for unstructured time-series data. With features like efficient time-based access, built-in data reduction, flexible labeling, and conditional replication, it bridges the gap between raw image capture and scalable, intelligent data pipelines.</p>
<p>If you're building applications in robotics, industrial vision, or edge-based AI, ReductStore can help you focus on what matters most — your models and insights — without getting bogged down in the complexity of data infrastructure.</p>
<hr>
<p>I hope you find this article interesting and useful. If you have any questions or feedback, don’t hesitate to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>computer vision</category>
        </item>
        <item>
            <title><![CDATA[How to Store and Manage ROS Data]]></title>
            <link>https://www.reduct.store/blog/tutorial-store-ros-data</link>
            <guid>https://www.reduct.store/blog/tutorial-store-ros-data</guid>
            <pubDate>Thu, 19 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This tutorial shows how to set up a Raspberry Pi with ROS 2, a USB camera, and YOLOv5 for object detection, and use a recorder node to upload data to ReductStore. From setting up the camera and YOLOv5 to configuring the recorder node and replicating data to a central ReductStore instance, this guide provides step-by-step instructions for building a robust ROS 2 data storage pipeline.]]></description>
            <content:encoded><![CDATA[<div class="videoWrapper_VWB9"><div class="responsiveIframe__DxY"></div></div>
<p>&nbsp;</p>
<p>At ReductStore, we specialize in the high-performance storage and streaming of robotics data from edge devices to the cloud. In this tutorial, we will demonstrate how to develop a robust ROS 2 data logging pipeline for practical robotics applications.</p>
<p>First, we will set up a Raspberry Pi with a USB camera running a lightweight YOLOv5n object detection model via ONNX Runtime. Then, a recorder node will capture selected ROS 2 topics, including images, detection results, and logs. Next, these topics will be saved as segmented MCAP files locally with ReductStore. Finally, we will configure automatic replication to stream data to another ReductStore instance.</p>
<p>This minimal setup shows how to efficiently capture, store, and replicate ROS 2 data from a robot to a central server or cloud instance. These techniques can be applied to any ROS 2 system, whether it's a single robot or a fleet of autonomous systems.</p>
<p>Let's get started!</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-prerequisites-and-architecture">1. Prerequisites and Architecture<a href="https://www.reduct.store/blog/tutorial-store-ros-data#1-prerequisites-and-architecture" class="hash-link" aria-label="Direct link to 1. Prerequisites and Architecture" title="Direct link to 1. Prerequisites and Architecture" translate="no">​</a></h2>
<p>Before beginning, ensure you have the following:</p>
<ul>
<li class=""><strong>Hardware:</strong> Raspberry Pi, a USB camera, a laptop, and an internet connection.</li>
<li class=""><strong>OS:</strong> Ubuntu or Desktop Server (22.04 LTS or later) on the Raspberry Pi.</li>
<li class=""><strong>Laptop/PC:</strong> A separate machine on the same network, to serve as a central data store.</li>
</ul>
<p>In this tutorial, we will set up the following architecture:</p>
<p><img decoding="async" loading="lazy" alt="Tutorial Architecture" src="https://www.reduct.store/assets/images/tutorial_architecture.tiny-63c85f39883b70de9f472c0455a72665.png" width="2910" height="2088" class="img_ev3q"></p>
<small style="display:block;margin-top:-20px"><p>Architecture: Raspberry Pi with ROS 2, USB camera, YOLOv5n, and ReductStore;
Laptop with another ReductStore instance and Web Console.</p></small>
<ul>
<li class=""><strong>Raspberry Pi:</strong> Running ROS 2, interfacing with a USB camera, and running a YOLOv5n object detection node with ONNX Runtime.</li>
<li class=""><strong>Laptop/PC:</strong> Running a ReductStore instance (acting as a central data store) to receive data from the Raspberry Pi.</li>
<li class=""><strong>Network:</strong> For simplicity, both the Raspberry Pi and the laptop are assumed to be on the same local network.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="11-install-ubuntu-on-raspberry-pi">1.1. Install Ubuntu on Raspberry Pi<a href="https://www.reduct.store/blog/tutorial-store-ros-data#11-install-ubuntu-on-raspberry-pi" class="hash-link" aria-label="Direct link to 1.1. Install Ubuntu on Raspberry Pi" title="Direct link to 1.1. Install Ubuntu on Raspberry Pi" translate="no">​</a></h3>
<p>If not already done, flash Ubuntu for Raspberry Pi on a microSD card and boot your Pi. You can <a href="https://ubuntu.com/download/raspberry-pi" target="_blank" rel="noopener noreferrer" class=""><strong>download Ubuntu images for Raspberry Pi from the official site</strong></a>. Ensure you have internet access and update the system:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># On Raspberry Pi</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> update </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> upgrade </span><span class="token parameter variable" style="color:#36acaa">-y</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="12-install-ros-2-on-raspberry-pi">1.2. Install ROS 2 on Raspberry Pi<a href="https://www.reduct.store/blog/tutorial-store-ros-data#12-install-ros-2-on-raspberry-pi" class="hash-link" aria-label="Direct link to 1.2. Install ROS 2 on Raspberry Pi" title="Direct link to 1.2. Install ROS 2 on Raspberry Pi" translate="no">​</a></h3>
<p>Follow the steps below to set up <strong>ROS 2</strong> on your Raspberry Pi. This guide assumes you are using a supported 64-bit Ubuntu OS (e.g., 22.04, 24.04). ROS 2 is available in multiple distributions such as <strong>Jazzy</strong>, <strong>Humble</strong>, and <strong>Noetic</strong>. If you're unsure, we recommend using the latest <strong>LTS version</strong>, which we can install via the commands below.</p>
<ol>
<li class="">
<p><strong>Setup Locale:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> locale-gen en_US en_US.UTF-8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> update-locale </span><span class="token assign-left variable environment constant" style="color:#36acaa">LC_ALL</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">en_US.UTF-8 </span><span class="token assign-left variable environment constant" style="color:#36acaa">LANG</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">en_US.UTF-8</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">export</span><span class="token plain"> </span><span class="token assign-left variable environment constant" style="color:#36acaa">LANG</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">en_US.UTF-8</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Add ROS 2 apt Repository:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> software-properties-common</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> add-apt-repository universe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> update </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-sSL</span><span class="token plain"> https://raw.githubusercontent.com/ros/rosdistro/master/ros.key </span><span class="token parameter variable" style="color:#36acaa">-o</span><span class="token plain"> /usr/share/keyrings/ros-archive-keyring.gpg</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">echo</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"deb [arch=</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable" style="color:#36acaa">dpkg --print-architecture</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c"> signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu </span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable builtin class-name" style="color:#36acaa">.</span><span class="token string variable" style="color:#36acaa"> /etc/os-release </span><span class="token string variable operator" style="color:#393A34">&amp;&amp;</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable builtin class-name" style="color:#36acaa">echo</span><span class="token string variable" style="color:#36acaa"> $UBUNTU_CODENAME</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c"> main"</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">|</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tee</span><span class="token plain"> /etc/apt/sources.list.d/ros2.list </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> /dev/null</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> update</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Install ROS 2 Packages:</strong> For a baseline, install ROS 2 base packages (or <code>ros-&lt;distro&gt;-desktop</code> if you need GUI tools):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> ros-</span><span class="token variable" style="color:#36acaa">${ROS_DISTRO}</span><span class="token plain">-ros-base </span><span class="token parameter variable" style="color:#36acaa">-y</span><br></span></code></pre></div></div>
<p>This will install core ROS 2 packages including <code>rosbag2</code>. You may verify the installation by sourcing the setup script:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">source</span><span class="token plain"> /opt/ros/</span><span class="token variable" style="color:#36acaa">${ROS_DISTRO}</span><span class="token plain">/setup.bash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 </span><span class="token parameter variable" style="color:#36acaa">-h</span><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic"># Should show ROS 2 commands</span><br></span></code></pre></div></div>
</li>
<li class="">
<p><strong>Install Build Tools:</strong> We will create custom packages, so install development tools:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> python3-colcon-common-extensions python3-rosdep </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> rosdep init</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rosdep update</span><br></span></code></pre></div></div>
</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="13-create-a-ros-2-workspace">1.3. Create a ROS 2 Workspace<a href="https://www.reduct.store/blog/tutorial-store-ros-data#13-create-a-ros-2-workspace" class="hash-link" aria-label="Direct link to 1.3. Create a ROS 2 Workspace" title="Direct link to 1.3. Create a ROS 2 Workspace" translate="no">​</a></h3>
<p>Set up a workspace for our project (if you don't have one):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># On Raspberry Pi</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> ~/ros2_ws/src</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> ~/ros2_ws</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">colcon build  </span><span class="token comment" style="color:#999988;font-style:italic"># just to initialize, will be empty initially</span><br></span></code></pre></div></div>
<p>Add <code>source ~/ros2_ws/install/setup.bash</code> to your <code>~/.bashrc</code> so that the workspace is sourced on each new shell, or remember to source it in each terminal when using the workspace. We will add packages to this workspace in subsequent steps.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-setting-up-the-usb-camera-with-v4l2_camera">2. Setting up the USB Camera with <code>v4l2_camera</code><a href="https://www.reduct.store/blog/tutorial-store-ros-data#2-setting-up-the-usb-camera-with-v4l2_camera" class="hash-link" aria-label="Direct link to 2-setting-up-the-usb-camera-with-v4l2_camera" title="Direct link to 2-setting-up-the-usb-camera-with-v4l2_camera" translate="no">​</a></h2>
<p>We'll use the <code>v4l2_camera</code> ROS 2 package to interface with the USB camera via Video4Linux2. This package publishes images from any V4L2-compatible camera (most USB webcams) as ROS 2 image topics.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="21-install-the-v4l2_camera-package">2.1. Install the <code>v4l2_camera</code> Package<a href="https://www.reduct.store/blog/tutorial-store-ros-data#21-install-the-v4l2_camera-package" class="hash-link" aria-label="Direct link to 21-install-the-v4l2_camera-package" title="Direct link to 21-install-the-v4l2_camera-package" translate="no">​</a></h3>
<p>On the Raspberry Pi, install the driver node via apt:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> ros-</span><span class="token variable" style="color:#36acaa">${ROS_DISTRO}</span><span class="token plain">-v4l2-camera </span><span class="token parameter variable" style="color:#36acaa">-y</span><br></span></code></pre></div></div>
<p>This installs the <code>v4l2_camera</code> node and its dependencies. Alternatively, you could build it from source, but the binary is available for many ROS 2 distributions.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="22-connect-and-verify-the-camera">2.2. Connect and Verify the Camera<a href="https://www.reduct.store/blog/tutorial-store-ros-data#22-connect-and-verify-the-camera" class="hash-link" aria-label="Direct link to 2.2. Connect and Verify the Camera" title="Direct link to 2.2. Connect and Verify the Camera" translate="no">​</a></h3>
<p>Plug in the USB camera to the Pi. Verify that it's recognized by listing video devices:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">ls</span><span class="token plain"> /dev/video*</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic"># You should see /dev/video0 (and possibly /dev/video1, etc. if multiple video capture interfaces are connected)</span><br></span></code></pre></div></div>
<p>If <code>/dev/video0</code> is present, the system sees the camera (at list one). You might also install <code>v4l2-utils</code> and run <code>v4l2-ctl --list-devices</code> to see the camera name and capabilities. You can also run <code>v4l2-ctl --list-formats-ext</code> to see supported resolutions and formats.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="23-run-the-camera-node">2.3. Run the Camera Node<a href="https://www.reduct.store/blog/tutorial-store-ros-data#23-run-the-camera-node" class="hash-link" aria-label="Direct link to 2.3. Run the Camera Node" title="Direct link to 2.3. Run the Camera Node" translate="no">​</a></h3>
<p>Launch the camera driver to start publishing images:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># In a sourced ROS 2 environment on the Pi</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run v4l2_camera v4l2_camera_node</span><br></span></code></pre></div></div>
<p>By default, this node will open <code>/dev/video0</code> and start publishing images to the <code>~/image_raw</code> topic (type <code>sensor_msgs/Image</code>) at a default resolution of 640x480 and pixel format YUYV converted to <code>rgb8</code> (see <a href="https://docs.ros.org/en/jazzy/p/v4l2_camera/" target="_blank" rel="noopener noreferrer" class=""><strong>ROS 2 camera driver for Video4Linux2 Documentation</strong></a>). You should see console output from the node indicating it opened the device and is streaming.</p>
<p>Open a new terminal (with ROS sourced) on the Pi (or from a laptop connected to the ROS 2 network) and verify images are coming through, e.g., by running <code>rqt_image_view</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> ros-</span><span class="token variable" style="color:#36acaa">${ROS_DISTRO}</span><span class="token plain">-rqt-image-view </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># if not installed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run rqt_image_view rqt_image_view</span><br></span></code></pre></div></div>
<p>In <code>rqt_image_view</code>, select <code>/image_raw</code> to view the camera feed. This confirms the camera setup is working.</p>
<p><strong>Note:</strong> You can adjust parameters by remapping or via ROS 2 parameters, e.g., to change resolution or device:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run v4l2_camera v4l2_camera_node --ros-args </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> image_size:</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"[1280,720]"</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> video_device:</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"/dev/video0"</span><br></span></code></pre></div></div>
<p>This would set the camera to 1280x720 resolution (if supported).</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-deploying-a-lightweight-yolov5-object-detection-node">3. Deploying a Lightweight YOLOv5 Object Detection Node<a href="https://www.reduct.store/blog/tutorial-store-ros-data#3-deploying-a-lightweight-yolov5-object-detection-node" class="hash-link" aria-label="Direct link to 3. Deploying a Lightweight YOLOv5 Object Detection Node" title="Direct link to 3. Deploying a Lightweight YOLOv5 Object Detection Node" translate="no">​</a></h2>
<p>Next, we set up an object detection node to analyze the camera images and output metadata (<code>object_detected</code> and <code>confidence_score</code>). We'll use <strong>YOLOv5n (Nano)</strong> - the smallest YOLOv5 model ("only" 1.9 million parameters) which is ideal for resource-constrained devices (<a href="https://github.com/ultralytics/yolov5/releases" target="_blank" rel="noopener noreferrer" class=""><strong>see releases at ultralytics/yolov5</strong></a>). We will run inference using the ONNX Runtime, which allows running the model without needing the full PyTorch framework on the Pi.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="31-install-onnx-runtime-and-dependencies">3.1. Install ONNX Runtime and Dependencies<a href="https://www.reduct.store/blog/tutorial-store-ros-data#31-install-onnx-runtime-and-dependencies" class="hash-link" aria-label="Direct link to 3.1. Install ONNX Runtime and Dependencies" title="Direct link to 3.1. Install ONNX Runtime and Dependencies" translate="no">​</a></h3>
<p>On the Raspberry Pi, install the ONNX Runtime Python package and OpenCV (for image processing):</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> onnxruntime opencv-python</span><br></span></code></pre></div></div>
<p><em>(If <code>pip</code> isn't available, use <code>sudo apt install python3-pip</code> to install it. You may also install <code>numpy</code> if not already present, as ONNX Runtime will likely need it.)</em></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="32-yolov5n-onnx-model">3.2. YOLOv5n ONNX Model<a href="https://www.reduct.store/blog/tutorial-store-ros-data#32-yolov5n-onnx-model" class="hash-link" aria-label="Direct link to 3.2. YOLOv5n ONNX Model" title="Direct link to 3.2. YOLOv5n ONNX Model" translate="no">​</a></h3>
<p>We need the YOLOv5n model in ONNX format. To do that, we can clone the YOLOv5 repository on a more powerful machine than the Pi and export the model:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># On a PC or via Colab:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/ultralytics/yolov5.git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> yolov5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-r</span><span class="token plain"> requirements.txt  </span><span class="token comment" style="color:#999988;font-style:italic"># includes PyTorch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">python export.py </span><span class="token parameter variable" style="color:#36acaa">--weights</span><span class="token plain"> yolov5n.pt </span><span class="token parameter variable" style="color:#36acaa">--include</span><span class="token plain"> onnx</span><br></span></code></pre></div></div>
<p>This will create <code>yolov5n.onnx</code>. Transfer that file to your Raspberry Pi (e.g., via SCP).</p>
<p>For this tutorial, assume <code>yolov5n.onnx</code> is now on the Raspberry Pi (e.g., placed in <code>~/ros2_ws/src</code>).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="33-create-a-ros-2-package-for-the-yolo-node-optional">3.3. Create a ROS 2 Package for the YOLO Node (optional)<a href="https://www.reduct.store/blog/tutorial-store-ros-data#33-create-a-ros-2-package-for-the-yolo-node-optional" class="hash-link" aria-label="Direct link to 3.3. Create a ROS 2 Package for the YOLO Node (optional)" title="Direct link to 3.3. Create a ROS 2 Package for the YOLO Node (optional)" translate="no">​</a></h3>
<p>You can integrate the YOLO inference in the same package as your data recorder, but for modularity, let's create a separate ROS 2 Python package called <code>yolo_detector</code>.</p>
<p>In the workspace src directory, run:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">cd</span><span class="token plain"> ~/ros2_ws/src</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 pkg create --build-type ament_python yolo_detector </span><span class="token parameter variable" style="color:#36acaa">--dependencies</span><span class="token plain"> rclpy sensor_msgs std_msgs</span><br></span></code></pre></div></div>
<p>This will create a <code>yolo_detector</code> folder with a Python package structure.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="34-implement-the-yolo-detection-node">3.4. Implement the YOLO Detection Node<a href="https://www.reduct.store/blog/tutorial-store-ros-data#34-implement-the-yolo-detection-node" class="hash-link" aria-label="Direct link to 3.4. Implement the YOLO Detection Node" title="Direct link to 3.4. Implement the YOLO Detection Node" translate="no">​</a></h3>
<p>Create a file <code>yolo_detector/yolo_detector/yolo_node.py</code> with the following content:</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary><strong>YoloDetectorNode (Python code)</strong></summary><div><div class="collapsibleContent_i85q"><div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> rclpy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> rclpy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">node </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Node</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> sensor_msgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">msg </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> Image</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> std_msgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">msg </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Float32</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> numpy </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> np</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> onnxruntime </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> ort</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">YoloDetectorNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token builtin">super</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'yolo_detector'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Load the YOLOv5n ONNX model</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        model_path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/path/to/yolov5n.onnx'</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># TODO: update to actual path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">session </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ort</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">InferenceSession</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">model_path</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> providers</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'CPUExecutionProvider'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_logger</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"Loaded model </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">model_path</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Get model input details for preprocessing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        model_inputs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_inputs</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model_inputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_shape </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> model_inputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shape  </span><span class="token comment" style="color:#999988;font-style:italic"># e.g., [1, 3, 640, 640]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">img_height </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">img_width </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_shape</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Subscribers and publishers</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">subscription </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">create_subscription</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Image</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'/image_raw'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">image_callback</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pub_object </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">create_publisher</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'object_detected'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pub_conf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">create_publisher</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Float32</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'confidence_score'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># If the model requires normalization factors or specific transformations, define them:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">mean </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># YOLOv5 models assume 0-255 input, no mean subtraction</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">std </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">array</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">255.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255.0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># we'll scale 0-1 later by dividing by 255</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">image_callback</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> msg</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Image</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Convert ROS Image message to OpenCV format (BGR array)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Assuming msg.encoding is 'rgb8' as provided by v4l2_camera default output</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">frombuffer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">msg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">data</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> dtype</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">uint8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">reshape</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">msg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">height</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> msg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">width</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Convert RGB to BGR as YOLO model might expect BGR input (depending on training)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        img_bgr </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">cvtColor</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">img</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">COLOR_RGB2BGR</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Resize and pad image to model input shape (letterboxing if needed)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        input_img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> cv2</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">resize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">img_bgr</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">img_width</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">img_height</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Convert to float32 and normalize 0-1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        input_img </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> input_img</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">astype</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'float32'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">255.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># transpose to [channels, height, width]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        input_blob </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">transpose</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input_img</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        input_blob </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">expand_dims</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">input_blob</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> axis</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># shape [1,3,H,W]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Run inference</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        outputs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">session</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">run</span><span class="token punctuation" style="color:#393A34">(</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">input_name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> input_blob</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Parse outputs to find the highest confidence detection (for simplicity)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># YOLOv5 ONNX output typically includes [1, num_boxes, 85] array (for COCO: 4 box coords, 1 objness, 80 class scores)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        detections </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> outputs</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Filter by confidence threshold (e.g., 0.5)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        conf_threshold </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.5</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        best_label </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"none"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        best_conf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> detections </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> det </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> detections</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                obj_conf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> det</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                class_conf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> det</span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">5</span><span class="token punctuation" style="color:#393A34">:</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># class confidences</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                score </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> obj_conf </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token builtin">max</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">class_conf</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                class_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> np</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">argmax</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">class_conf</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> score </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> conf_threshold </span><span class="token keyword" style="color:#00009f">and</span><span class="token plain"> score </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> best_conf</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    best_conf </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">float</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">score</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                    best_label </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">str</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">class_id</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># or use a class id-&gt;name mapping</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic"># Publish results</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pub_object</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">publish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">String</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">best_label</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">pub_conf</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">publish</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Float32</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">data</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">best_conf</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_logger</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">info</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-interpolation string" style="color:#e3116c">f"Detected: </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">best_label</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c"> (</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">best_conf</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">:</span><span class="token string-interpolation interpolation format-spec">.2f</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">}</span><span class="token string-interpolation string" style="color:#e3116c">)"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rclpy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">init</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">args</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">args</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    node </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> YoloDetectorNode</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rclpy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">spin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">node</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    node</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">destroy_node</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    rclpy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">shutdown</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div></div></div></details>
<p><strong>Explanation:</strong> This node subscribes to the camera images (<code>/image_raw</code>), processes each frame through the YOLOv5n model, and publishes two topics:</p>
<ul>
<li class=""><code>object_detected</code> (std_msgs/String): the class label (or ID) of the primary detected object (or <code>"none"</code> if none above threshold).</li>
<li class=""><code>confidence_score</code> (std_msgs/Float32): the confidence score of that detection.</li>
</ul>
<p>For simplicity, we took the detection with highest confidence above a threshold. In a real scenario, you probably output multiple detections or more info (bounding boxes, etc.), but we only need metadata for this tutorial.</p>
<p>Make sure to adjust the <code>model_path</code> to the actual location of your <code>yolov5n.onnx</code>. Also note that without class name mapping, <code>best_label</code> is currently the class index (as string). You can map this index to an actual label (e.g., using the COCO class list below).</p>
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary><strong>COCO Class List</strong></summary><div><div class="collapsibleContent_i85q"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">names</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> person</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bicycle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">2</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> car</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">3</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> motorcycle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">4</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> airplane</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">5</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bus</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">6</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> train</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">7</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> truck</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">8</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> boat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">9</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> traffic light</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">10</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> fire hydrant</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">11</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> stop sign</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">12</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> parking meter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">13</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bench</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">14</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bird</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">15</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">16</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">17</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> horse</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">18</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sheep</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">19</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cow</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">20</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> elephant</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">21</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bear</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">22</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> zebra</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">23</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> giraffe</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">24</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> backpack</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">25</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> umbrella</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">26</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> handbag</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">27</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tie</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">28</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> suitcase</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">29</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> frisbee</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">30</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> skis</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">31</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> snowboard</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">32</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sports ball</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">33</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> kite</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">34</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> baseball bat</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">35</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> baseball glove</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">36</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> skateboard</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">37</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> surfboard</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">38</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tennis racket</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">39</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bottle</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">40</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> wine glass</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">41</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">42</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> fork</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">43</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> knife</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">44</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> spoon</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">45</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bowl</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">46</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> banana</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">47</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> apple</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">48</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sandwich</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">49</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> orange</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">50</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> broccoli</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">51</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> carrot</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">52</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hot dog</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">53</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> pizza</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">54</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> donut</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">55</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cake</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">56</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> chair</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">57</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> couch</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">58</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> potted plant</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">59</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> bed</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">60</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> dining table</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">61</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> toilet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">62</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> tv</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">63</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> laptop</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">64</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> mouse</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">65</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> remote</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">66</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> keyboard</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">67</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cell phone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">68</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> microwave</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">69</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> oven</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">70</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> toaster</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">71</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> sink</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">72</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> refrigerator</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">73</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> book</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">74</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> clock</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">75</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> vase</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">76</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> scissors</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">77</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> teddy bear</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">78</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> hair drier</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">79</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> toothbrush</span><br></span></code></pre></div></div></div></div></details>
<p>Also, update <code>setup.py</code> entry points to include our node script:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">setup</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    entry_points</span><span class="token operator" style="color:#393A34">=</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token string" style="color:#e3116c">'console_scripts'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">'yolo_node = yolo_detector.yolo_node:main'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="35-build-and-run-the-yolo-node">3.5. Build and Run the YOLO Node<a href="https://www.reduct.store/blog/tutorial-store-ros-data#35-build-and-run-the-yolo-node" class="hash-link" aria-label="Direct link to 3.5. Build and Run the YOLO Node" title="Direct link to 3.5. Build and Run the YOLO Node" translate="no">​</a></h3>
<p>Add <code>onnxruntime</code> and <code>opencv-python</code> to your workspace's requirements (you might include them in a <code>requirements.txt</code> for the package and use <code>pip</code> to install, since they are pip packages). For now, ensure they are installed via pip as done earlier.</p>
<p>Build the workspace:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">cd</span><span class="token plain"> ~/ros2_ws</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">colcon build --packages-select yolo_detector</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">source</span><span class="token plain"> install/setup.bash</span><br></span></code></pre></div></div>
<p>Run the YOLO detection node in a new terminal on the Pi:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run yolo_detector yolo_node.py</span><br></span></code></pre></div></div>
<p>You should see log output from the node whenever it processes an image (every frame or at least when something is detected, depending on your logging). The node will publish messages on <code>object_detected</code> and <code>confidence_score</code> topics.</p>
<p>You can echo these topics in another terminal to verify:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">ros2 topic </span><span class="token builtin class-name">echo</span><span class="token plain"> /object_detected</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 topic </span><span class="token builtin class-name">echo</span><span class="token plain"> /confidence_score</span><br></span></code></pre></div></div>
<p>For example, if a person is detected with 85% confidence. You should see messages like:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">object</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">confidence</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.85</span><br></span></code></pre></div></div>
<p>Now we have a camera streaming images and a detector publishing YOLOv5 detections as ROS 2 topics. The next step is to record this data and upload it to ReductStore.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-installing-and-configuring-reductstore-on-the-raspberry-pi">4. Installing and Configuring ReductStore on the Raspberry Pi<a href="https://www.reduct.store/blog/tutorial-store-ros-data#4-installing-and-configuring-reductstore-on-the-raspberry-pi" class="hash-link" aria-label="Direct link to 4. Installing and Configuring ReductStore on the Raspberry Pi" title="Direct link to 4. Installing and Configuring ReductStore on the Raspberry Pi" translate="no">​</a></h2>
<p>Before running the recorder node, we need a ReductStore server running on the Pi to accept uploads. ReductStore is a lightweight time-series object storage, perfect for edge devices. We will install it on the Pi and create a bucket for our MCAP files.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-install-reductstore-on-raspberry-pi">4.1. Install ReductStore on Raspberry Pi<a href="https://www.reduct.store/blog/tutorial-store-ros-data#41-install-reductstore-on-raspberry-pi" class="hash-link" aria-label="Direct link to 4.1. Install ReductStore on Raspberry Pi" title="Direct link to 4.1. Install ReductStore on Raspberry Pi" translate="no">​</a></h3>
<p>The easiest way on Ubuntu is to use <strong>snap</strong> or <strong>Docker</strong>. For example with snap:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># On Raspberry Pi</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> update</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">apt</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> snapd </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain">   </span><span class="token comment" style="color:#999988;font-style:italic"># if snapd is not already installed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> snap </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> reductstore</span><br></span></code></pre></div></div>
<p>This will install ReductStore from the Snap Store. The snap should set up ReductStore as a service listening on port 8383 by default. If using a different OS or if snap isn't desired, you can use Docker (e.g., <code>docker run -d -p 8383:8383 -v ~/reduct_data:/data reduct/store:latest</code>) to run ReductStore in a container.</p>
<blockquote>
<p><strong>Note:</strong> The database will listen on <code>http://0.0.0.0:8383</code> (accessible to the LAN). Ensure this port is allowed through any firewall if you want external access.</p>
</blockquote>
<p>Check that ReductStore is running:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> snap services reductstore  </span><span class="token comment" style="color:#999988;font-style:italic"># should show active</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> http://127.0.0.1:8383/api/v1/info</span><br></span></code></pre></div></div>
<p>The <code>curl</code> command should return JSON info about the instance (like version, uptime, etc.).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-configure-reductstore">4.2. Configure ReductStore<a href="https://www.reduct.store/blog/tutorial-store-ros-data#42-configure-reductstore" class="hash-link" aria-label="Direct link to 4.2. Configure ReductStore" title="Direct link to 4.2. Configure ReductStore" translate="no">​</a></h3>
<p>By default, ReductStore doesn't require authentication for local use (anonymous access is allowed). This is fine for our edge scenario on a local network. If you want to set up access tokens or adjust storage quotas, you can do so by following the <a class="" href="https://www.reduct.store/docs/guides/access-control"><strong>Access Control guide</strong></a>. For now, we'll use defaults.</p>
<p>We will use the <strong>Web Console</strong> to verify data and to set up replication later. The web console is accessible from a browser at the server's address (it's the same as the API endpoint). For example, on the Pi, open <code>http://&lt;raspberrypi_ip&gt;:8383</code> in a browser - you should see the ReductStore Web Console interface (a simple GUI).</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="43-create-a-bucket-for-ros-bag-data">4.3. Create a Bucket for ROS bag data<a href="https://www.reduct.store/blog/tutorial-store-ros-data#43-create-a-bucket-for-ros-bag-data" class="hash-link" aria-label="Direct link to 4.3. Create a Bucket for ROS bag data" title="Direct link to 4.3. Create a Bucket for ROS bag data" translate="no">​</a></h3>
<p>The <code>reductstore_agent</code> will upload to a bucket named <code>pi_robot</code> (we will specify that in a config file). If the bucket does not exist, you can create it manually via the web console:</p>
<ul>
<li class="">Navigate to <strong>Buckets</strong> and create a new bucket named <code>pi_robot</code>. (You can set a quota, e.g., a FIFO quota to avoid filling up the disk on the Pi.)</li>
</ul>
<p><img decoding="async" loading="lazy" alt="ReductStore Web Console Bucket" src="https://www.reduct.store/assets/images/create_bucket_webconsole-932e386a63037948289235e1ab8d0e7f.png" width="2378" height="1504" class="img_ev3q"></p>
<small style="display:block;margin-top:-20px"><p>ReductStore Web Console: Creating a bucket named "pi_robot" for storing MCAP
files.</p></small>
<p>Now, ReductStore is set up on the Pi and ready to accept data. We can run the complete system to record ROS data, detect objects, and upload to ReductStore.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-recording-and-uploading-data-with-reductstore_agent">4. Recording and Uploading Data with <code>reductstore_agent</code><a href="https://www.reduct.store/blog/tutorial-store-ros-data#4-recording-and-uploading-data-with-reductstore_agent" class="hash-link" aria-label="Direct link to 4-recording-and-uploading-data-with-reductstore_agent" title="Direct link to 4-recording-and-uploading-data-with-reductstore_agent" translate="no">​</a></h2>
<p><code>reductstore_agent</code> is a ROS 2 node that records selected topics, splits them into MCAP segments and uploads each segment to ReductStore. It is configured through a YAML file and can be run on the Raspberry Pi to capture data from the camera and YOLO detection nodes.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="41-clone-install-dependencies-and-build">4.1. Clone, install dependencies, and build<a href="https://www.reduct.store/blog/tutorial-store-ros-data#41-clone-install-dependencies-and-build" class="hash-link" aria-label="Direct link to 4.1. Clone, install dependencies, and build" title="Direct link to 4.1. Clone, install dependencies, and build" translate="no">​</a></h3>
<p>To install <code>reductstore_agent</code>, we will clone the repository and build it in our ROS 2 workspace.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> ~/ros2_ws/src</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> ~/ros2_ws/src</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">git</span><span class="token plain"> clone https://github.com/reductstore/reductstore_agent.git</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">cd</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">..</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">rosdep </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> --from-paths src --ignore-src </span><span class="token parameter variable" style="color:#36acaa">-r</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-y</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">colcon build --packages-select reductstore_agent</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token builtin class-name">source</span><span class="token plain"> install/setup.bash</span><br></span></code></pre></div></div>
<p>You also need to install additional dependencies like <code>mcap</code>, <code>mcap-ros2-support</code>, and <code>reduct-py</code> for MCAP support and ReductStore integration. You can do this with pip:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">pip </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> mcap mcap-ros2-support reduct-py</span><br></span></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="42-create-configyaml">4.2. Create <code>config.yaml</code><a href="https://www.reduct.store/blog/tutorial-store-ros-data#42-create-configyaml" class="hash-link" aria-label="Direct link to 42-create-configyaml" title="Direct link to 42-create-configyaml" translate="no">​</a></h3>
<p>This configuration defines how <code>reductstore_agent</code> records topics and uploads them to ReductStore.</p>
<ul>
<li class=""><code>url</code>: ReductStore endpoint.</li>
<li class=""><code>api_token</code>: Access token (can be empty if anonymous access is allowed).</li>
<li class=""><code>bucket</code>: Target bucket in ReductStore.</li>
<li class=""><code>include_topics</code>: List of ROS 2 topics to record.</li>
<li class=""><code>split.max_duration_s</code>: Maximum duration of each MCAP file in seconds.</li>
<li class=""><code>max_size_bytes</code>: Maximum size of each MCAP file (e.g., "1MB" or "10MB").</li>
<li class=""><code>compression</code>: Compression algorithm (<code>none</code>, <code>lz4</code>, or <code>zstd</code>).</li>
<li class=""><code>filename_mode</code>: Naming mode for output files. <code>timestamp</code> will name the MCAP files with the timestamp of the first message in the segment, and <code>incremental</code> will use a counter (0, 1, 2, ...).</li>
</ul>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">/**/*</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ros__parameters</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">storage</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">url</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://127.0.0.1:8383"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">api_token</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">""</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># Leave empty for anonymous access</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">bucket</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"pi_robot"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">pipelines</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">camera_stream</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">include_topics</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /image_raw</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /object_detected</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /confidence_score</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> /rosout</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">split</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">max_duration_s</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token key atrule" style="color:#00a4db">max_size_bytes</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1MB"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">compression</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"zstd"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">filename_mode</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"timestamp"</span><br></span></code></pre></div></div>
<p>As a best practice for production, you might want to:</p>
<ul>
<li class="">Include <code>/rosout</code> to capture logs for debugging.</li>
<li class="">Separate lightweight telemetry from raw sensor data into different pipelines for better organization and performance.</li>
<li class="">Limit the size of each segment to avoid large files that are hard to manage.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-running-the-complete-system">6. Running the Complete System<a href="https://www.reduct.store/blog/tutorial-store-ros-data#6-running-the-complete-system" class="hash-link" aria-label="Direct link to 6. Running the Complete System" title="Direct link to 6. Running the Complete System" translate="no">​</a></h2>
<p>We have three ROS 2 nodes to run on the Raspberry Pi:</p>
<ul>
<li class="">The camera driver (<code>v4l2_camera_node</code>)</li>
<li class="">The YOLO detection node (<code>YoloDetectorNode</code>)</li>
<li class="">The <code>reductstore_agent</code> recorder/uploader node</li>
</ul>
<p>It's best to run each in its own terminal (or use a launch file to launch them together). For clarity, we'll do it step-by-step:</p>
<p><strong>Terminal 1:</strong> Camera node</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Terminal 1 on Pi (source ROS 2 and workspace)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run v4l2_camera v4l2_camera_node</span><br></span></code></pre></div></div>
<p><strong>Terminal 2:</strong> YOLO detection node</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Terminal 2 on Pi (source ROS 2 and workspace)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run yolo_detector yolo_node.py</span><br></span></code></pre></div></div>
<p>(If you set up the entry point, you could do <code>ros2 run yolo_detector yolo_detector</code> or similar, but here we assume running the script directly.)</p>
<p><strong>Terminal 3:</strong> reductstore_agent recorder node</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># Terminal 3 on Pi (source ROS 2 and workspace)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ros2 run reductstore_agent recorder </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  --ros-args --params-file ~/ros2_ws/config.yaml</span><br></span></code></pre></div></div>
<p>Now monitor the outputs:</p>
<ul>
<li class="">The camera node should just stream (no text output unless error or warning).</li>
<li class="">The YOLO node will log detections (as we coded with <code>get_logger().info</code> on each detection).</li>
<li class="">The <code>reductstore_agent</code> node will save MCAPs into the specified bucket in ReductStore.</li>
</ul>
<p>Let this run for a while. By default, every 60 seconds it will finalize a bag and upload it. If you want to trigger a rotation sooner (for testing), you could reduce <code>max_duration_s</code> in the config.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="61-verify-data-in-reductstore">6.1. Verify Data in ReductStore<a href="https://www.reduct.store/blog/tutorial-store-ros-data#61-verify-data-in-reductstore" class="hash-link" aria-label="Direct link to 6.1. Verify Data in ReductStore" title="Direct link to 6.1. Verify Data in ReductStore" translate="no">​</a></h3>
<p>On the Pi (or from any machine that can access the Pi's port 8383), open the ReductStore Web Console in a browser: <strong><code>http://&lt;raspberrypi_ip&gt;:8383</code></strong>. You should see the bucket <code>pi_robot</code>. Naviguate to it, and you should see the uploaded MCAP files listed under the <code>camera_stream</code> entry.</p>
<p>You have successfully set up the edge device to capture ROS data and push it to ReductStore!</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-setting-up-replication-tasks">7. Setting up Replication Tasks<a href="https://www.reduct.store/blog/tutorial-store-ros-data#7-setting-up-replication-tasks" class="hash-link" aria-label="Direct link to 7. Setting up Replication Tasks" title="Direct link to 7. Setting up Replication Tasks" translate="no">​</a></h2>
<p>With data being collected on the Pi, we likely want to aggregate it on a central server (e.g., a cloud instance or your laptop for this tutorial) for analysis or long-term storage.</p>
<p>ReductStore's replication feature allows the Pi (source) to <strong>push</strong> new records to another ReductStore instance (destination) in real-time.</p>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>Replication lets you stream only the data you need from the edge to the cloud or between edge devices. You can filter by label and push data without constant polling. If the device is offline or the destination is down, the data waits and replicates later.</p></div></div>
<p>In this section, we'll:</p>
<ul>
<li class="">Run ReductStore on the laptop.</li>
<li class="">Use the ReductStore Web Console to create a replication task on the Pi's instance that filters and forwards data to the laptop's instance.</li>
<li class="">Verify that replication works.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="71-installrun-reductstore-on-laptop">7.1. Install/Run ReductStore on Laptop<a href="https://www.reduct.store/blog/tutorial-store-ros-data#71-installrun-reductstore-on-laptop" class="hash-link" aria-label="Direct link to 7.1. Install/Run ReductStore on Laptop" title="Direct link to 7.1. Install/Run ReductStore on Laptop" translate="no">​</a></h3>
<p>On your laptop (assuming Ubuntu 22.04+ or any system with Docker or Snap):</p>
<ul>
<li class="">
<p><strong>Option A: Docker</strong> - run ReductStore in a container:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> ~/reduct_data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">chown</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-R</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10001</span><span class="token plain">:10001 ~/reduct_data</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#36acaa">-d</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">-p</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8383</span><span class="token plain">:8383 </span><span class="token parameter variable" style="color:#36acaa">-v</span><span class="token plain"> ~/reduct_data:/data reduct/store:latest</span><br></span></code></pre></div></div>
<p>This runs ReductStore locally on port 8383 and stores data in <code>~/reduct_data</code> on your laptop.</p>
</li>
<li class="">
<p><strong>Option B: Native</strong> - you could similarly install via Snap (<code>sudo snap install reductstore</code>) or use a binary.</p>
</li>
</ul>
<p>After starting it, ensure you can access it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> http://127.0.0.1:8383/api/v1/info</span><br></span></code></pre></div></div>
<p>should return info as before (but for the laptop's instance).</p>
<p>Open the web console on the laptop: <code>http://localhost:8383</code> and keep it open for monitoring.</p>
<blockquote>
<p>Create a bucket named <code>pi_robot</code> on the laptop as well, otherwise the replication task will fail (it needs the destination bucket to exist).</p>
</blockquote>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>tip</div><div class="admonitionContent_BuS1"><p>Use provisioning to automate bucket creation and other setup steps in a real deployment. See <a class="" href="https://www.reduct.store/docs/configuration/provisioning"><strong>Configuration/Provisioning Documentation</strong></a> for more.</p></div></div>
<p><strong>Networking:</strong> Make sure your laptop is accessible from the Pi. If both are on the same LAN, you might use the laptop's IP (e.g., 192.168.x.x). If the laptop's ReductStore is in Docker, ensure the port 8383 is open (it is published in the run command above). For testing, you might temporarily disable firewall or ensure port 8383 is allowed.</p>
<p>Find your laptop's IP address (e.g., <code>hostname -I</code> on Linux), let's say it's <code>192.168.1.100</code> for example.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="72-configure-replication-on-the-pi-via-web-console">7.2. Configure Replication on the Pi via Web Console<a href="https://www.reduct.store/blog/tutorial-store-ros-data#72-configure-replication-on-the-pi-via-web-console" class="hash-link" aria-label="Direct link to 7.2. Configure Replication on the Pi via Web Console" title="Direct link to 7.2. Configure Replication on the Pi via Web Console" translate="no">​</a></h3>
<p>On the Pi's web console (<strong><code>http://&lt;raspberrypi_ip&gt;:8383</code></strong>), look for “Replications” and the “+” to add replication). We want to create a replication task that sends data to the laptop.</p>
<p>Fill in the replication settings as:</p>
<ul>
<li class=""><strong>Source Bucket:</strong> pi_robot (on Pi)</li>
<li class=""><strong>Destination URL:</strong> <code>http://&lt;laptop_ip&gt;:8383</code> (e.g., <code>http://192.168.1.100:8383</code>)</li>
<li class=""><strong>Destination Bucket:</strong> pi_robot (on laptop)</li>
<li class=""><strong>Replication Name:</strong> (give it a name like “to_laptop”)</li>
<li class=""><strong>Entries:</strong> <code>camera_stream</code> (name of the pipeline we set up in <code>reductstore_agent</code> config)</li>
</ul>
<p>Start the replication task. The Pi's ReductStore will now start forwarding new records that meet the criteria to the laptop, in real-time. It's a push model from Pi to laptop, so the laptop doesn't need to know about the Pi or poll it - the Pi will push new records as they arrive.</p>
<p><img decoding="async" loading="lazy" alt="ReductStore Web Console Replication" src="https://www.reduct.store/assets/images/replication_webconsole-a69987342bda01cac2f9891d96d6e350.png" width="3328" height="1870" class="img_ev3q"></p>
<small style="display:block;margin-top:-20px"><p>ReductStore Web Console: Setting up a replication task to forward data to a
central storage.</p></small>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="73-test-replication">7.3. Test Replication<a href="https://www.reduct.store/blog/tutorial-store-ros-data#73-test-replication" class="hash-link" aria-label="Direct link to 7.3. Test Replication" title="Direct link to 7.3. Test Replication" translate="no">​</a></h3>
<p>Back on the Pi, ensure the <code>reductstore_agent</code> node is still running and creating new records. When the next bag file is uploaded on the Pi, the replication task will immediately send it to the laptop.</p>
<p>On the <strong>laptop's web console</strong>, open the <code>pi_robot</code> bucket. You should start seeing records appear that correspond to those on the Pi (with a slight delay for transfer).</p>
<p>You can also check the replication status on the Pi's web console; it may show last replicated record timestamp etc., indicating it's working.</p>
<p>At this point, we have a full end-to-end pipeline:</p>
<ul>
<li class="">Raspberry Pi captures images and detects objects with YOLOv5.</li>
<li class=""><code>reductstore_agent</code> continuously records this data into MCAP files.</li>
<li class="">ReductStore on Pi replicates the files to another ReductStore instance.</li>
<li class="">The laptop accumulates the replicated files in its own ReductStore bucket.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-production-best-practices-and-considerations">8. Production Best Practices and Considerations<a href="https://www.reduct.store/blog/tutorial-store-ros-data#8-production-best-practices-and-considerations" class="hash-link" aria-label="Direct link to 8. Production Best Practices and Considerations" title="Direct link to 8. Production Best Practices and Considerations" translate="no">​</a></h2>
<p>This tutorial only scratches the surface of building a robust data acquisition and storage pipeline for robotics. Here are some best practices to consider:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="81-separate-storage-topics-by-types">8.1 Separate Storage Topics by Types<a href="https://www.reduct.store/blog/tutorial-store-ros-data#81-separate-storage-topics-by-types" class="hash-link" aria-label="Direct link to 8.1 Separate Storage Topics by Types" title="Direct link to 8.1 Separate Storage Topics by Types" translate="no">​</a></h3>
<p>Separate storage streams for different topic categories (e.g., lightweight telemetry, downsampled sensor data, full-resolution data). This allows you to apply different retention policies, access controls, or replication rules to each category.</p>
<ul>
<li class="">See <a class="" href="https://www.reduct.store/blog/store-ros-topics"><strong>3 Ways to Store ROS Topics</strong></a></li>
<li class="">Example: <a class="" href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision"><strong>How to Store Images in ROS 2</strong></a></li>
</ul>
<p>Here are some common data categories and their characteristics to consider:</p>
<table><thead><tr><th>Category</th><th>Examples</th><th>Characteristics</th></tr></thead><tbody><tr><td><strong>Lightweight telemetry</strong></td><td>GPS, IMU, joint states, system status</td><td>Low bandwidth, near real-time, useful for business analytics</td></tr><tr><td><strong>Downsampled sensor data</strong></td><td>Lower framerate/resolution camera or lidar data</td><td>Mid-size, great for monitoring and incident triage</td></tr><tr><td><strong>Full-resolution data</strong></td><td>Raw camera frames, high-fps lidar, depth maps</td><td>High volume (up to 1TB/hour), needed for debugging or model retraining</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="82-combine-downsampling-with-replication">8.2 Combine Downsampling with Replication<a href="https://www.reduct.store/blog/tutorial-store-ros-data#82-combine-downsampling-with-replication" class="hash-link" aria-label="Direct link to 8.2 Combine Downsampling with Replication" title="Direct link to 8.2 Combine Downsampling with Replication" translate="no">​</a></h3>
<p>This is typical in ELT (Extract-Load-Transform) pipelines. The idea is to save everything locally (on the robot) at high resolution and framerate, then stream part of the data to the cloud or a central server at a lower resolution or lower frequency.</p>
<ul>
<li class="">Example: Store 1 FPS video in cloud, keep 30 FPS original on robot SSD</li>
<li class="">See example in: <a class="" href="https://www.reduct.store/blog/daq-manufacture-system"><strong>Building a Data Acquisition System for Manufacturing</strong></a></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="83-offload-data-to-cold-storage">8.3 Offload Data to Cold Storage<a href="https://www.reduct.store/blog/tutorial-store-ros-data#83-offload-data-to-cold-storage" class="hash-link" aria-label="Direct link to 8.3 Offload Data to Cold Storage" title="Direct link to 8.3 Offload Data to Cold Storage" translate="no">​</a></h3>
<p>For long-term archiving, consider offloading data to cold storage to reduce costs while keeping data accessible.</p>
<ul>
<li class="">Example: Keep 30 days of data locally, archive older data to Cloud Storage (e.g., AWS S3, Google Cloud Storage)</li>
<li class="">Connect us if you need a trial instance of <a class="" href="https://www.reduct.store/solutions/cloud"><strong>ReductStore Cloud</strong></a> for this purpose.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="9-conclusion">9. Conclusion<a href="https://www.reduct.store/blog/tutorial-store-ros-data#9-conclusion" class="hash-link" aria-label="Direct link to 9. Conclusion" title="Direct link to 9. Conclusion" translate="no">​</a></h2>
<p>This tutorial showed how to build a complete ROS 2 data logging pipeline on a Raspberry Pi. We captured camera data and YOLOv5 detections, recorded selected topics into segmented MCAP files using <code>reductstore_agent</code>, and stored them in ReductStore. We then configured a replication task to automatically stream data to another ReductStore instance.</p>
<p>This setup is highly adaptable: you can group topics by type, filter out unnecessary data, and enrich records with metadata labels for easier filtering. It provides a solid foundation for scalable and efficient data acquisition—whether you're working with a single robot or managing a distributed fleet.</p>
<p>We hope this tutorial helps you build your own ROS 2 data acquisition system! Happy hacking!</p>
<hr>
<p>Thanks for reading, I hope this article will help you choose the right storage strategy for your vibration data.
If you have any questions or comments, feel free to use the <a href="https://community.reduct.store/" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>ros</category>
            <category>robotics</category>
        </item>
    </channel>
</rss>