<?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>Sat, 09 May 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[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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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/signup" 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>
        <item>
            <title><![CDATA[Getting Started with LeRobot]]></title>
            <link>https://www.reduct.store/blog/hugging-face-lerobot</link>
            <guid>https://www.reduct.store/blog/hugging-face-lerobot</guid>
            <pubDate>Tue, 27 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A beginner’s guide to exploring robotics with LeRobot in Google Colab, covering setup, running models, training, and managing data.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Intro image" src="https://www.reduct.store/assets/images/lerobot-50cdf9154ef2ad3d4aabaccc35ffdafa.png" width="3794" height="1234" class="img_ev3q"></p>
<p><a href="https://huggingface.co/lerobot" target="_blank" rel="noopener noreferrer" class=""><strong>LeRobot is an open-source project by Hugging Face</strong></a> that makes it easy to explore the world of robotics with machine learning, even if you’ve never done anything like this before. It gives you pre-trained models, real-world data, and simple tools built with PyTorch, a popular machine learning framework. Whether you're just curious or ready to try your first robotics project, LeRobot is a great place to start.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-you-need-to-get-started">What You Need to Get Started<a href="https://www.reduct.store/blog/hugging-face-lerobot#what-you-need-to-get-started" class="hash-link" aria-label="Direct link to What You Need to Get Started" title="Direct link to What You Need to Get Started" translate="no">​</a></h2>
<p>You can run everything in a simulation right from your browser — no robot, no installations, and no powerful computer needed. We’ll be using Google Colab, a free cloud-based coding environment.</p>
<p>Here’s what you’ll need:</p>
<ul>
<li class="">
<p><strong>Google Account:</strong> To use Colab, you need a Google account. If you use Gmail, you already have one. If not, you can <a href="https://colab.research.google.com/" target="_blank" rel="noopener noreferrer" class=""><strong>create a Google account</strong></a>.</p>
</li>
<li class="">
<p><strong>Hugging Face Account:</strong> LeRobot uses models and datasets hosted on Hugging Face. To access all features, you'll need to <a href="https://huggingface.co/" target="_blank" rel="noopener noreferrer" class=""><strong>create a Hugging Face account</strong></a>.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="preparation">Preparation<a href="https://www.reduct.store/blog/hugging-face-lerobot#preparation" class="hash-link" aria-label="Direct link to Preparation" title="Direct link to Preparation" translate="no">​</a></h3>
<p>To get started with LeRobot in Google Colab, first open Google Colab and sign in with your Google account. Once you're signed in, click the <code>New Notebook</code> button to create a blank notebook — this is where you’ll run all your code.</p>
<blockquote>
<p><strong>Note:</strong> All the commands below are already written out in a <a href="https://colab.research.google.com/gist/AnthonyCvn/f02f12ce113f0e2fcd773fd39d0e1dfa/getting-started-with-lerobot.ipynb" target="_blank" rel="noopener noreferrer" class=""><strong>ready-made Google Colab notebook</strong></a> you can use to follow along.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-switch-to-gpu">Step 1: Switch to GPU<a href="https://www.reduct.store/blog/hugging-face-lerobot#step-1-switch-to-gpu" class="hash-link" aria-label="Direct link to Step 1: Switch to GPU" title="Direct link to Step 1: Switch to GPU" translate="no">​</a></h4>
<p>LeRobot can use a GPU (Graphics Processing Unit), which makes things run faster, especially for simulation and machine learning tasks:</p>
<ul>
<li class="">
<p>In the Colab menu, click <code>Runtime</code> &gt; <code>Change runtime type</code>.</p>
</li>
<li class="">
<p>Under the <code>Hardware accelerator</code>, select <code>GPU</code>.</p>
</li>
<li class="">
<p>Click <code>Save</code>.</p>
</li>
</ul>
<p>Now your notebook is using a free GPU provided by Google. Note that GPU access in Colab is limited in time and resources, depending on whether you’re using the free or PRO version.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-clone-the-lerobot-repository">Step 2: Clone the LeRobot Repository<a href="https://www.reduct.store/blog/hugging-face-lerobot#step-2-clone-the-lerobot-repository" class="hash-link" aria-label="Direct link to Step 2: Clone the LeRobot Repository" title="Direct link to Step 2: Clone the LeRobot Repository" translate="no">​</a></h4>
<p>Run this command in a Colab code cell to download LeRobot from GitHub. This repository is public, so you don’t need a GitHub account to clone it.</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">!git clone https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token operator" style="color:#393A34">//</span><span class="token plain">github</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">com</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">huggingface</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">lerobot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">git</span><br></span></code></pre></div></div>
<p>A new folder named <code>lerobot</code> will appear in the file browser on the left (click the folder icon to open it).</p>
<p>For now, you can simply start with the <code>lerobot/examples</code> folder. It contains ready-to-use scripts that let you try out real robot tasks using pre-trained models — no setup or deep knowledge needed.</p>
<blockquote>
<p><strong>Note:</strong> Colab’s environment is temporary. If you restart the runtime, the files will be deleted and you’ll need to run the setup steps again. It’s best to keep these commands handy at the top of your notebook.</p>
</blockquote>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3-move-into-the-lerobot-folder">Step 3: Move into the LeRobot folder<a href="https://www.reduct.store/blog/hugging-face-lerobot#step-3-move-into-the-lerobot-folder" class="hash-link" aria-label="Direct link to Step 3: Move into the LeRobot folder" title="Direct link to Step 3: Move into the LeRobot folder" translate="no">​</a></h4>
<p>Now that the LeRobot files are downloaded, we need to tell Python to work inside that folder. Run this command in a new cell:</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 operator" style="color:#393A34">%</span><span class="token plain">cd lerobot</span><br></span></code></pre></div></div>
<p>This changes the current working directory to the <code>lerobot</code> folder, where all the code and scripts are located.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-4-install-lerobot-and-its-dependencies">Step 4: Install LeRobot and Its Dependencies<a href="https://www.reduct.store/blog/hugging-face-lerobot#step-4-install-lerobot-and-its-dependencies" class="hash-link" aria-label="Direct link to Step 4: Install LeRobot and Its Dependencies" title="Direct link to Step 4: Install LeRobot and Its Dependencies" translate="no">​</a></h4>
<p>After cloning the repository and switching to the <code>lerobot</code> folder, the next step is to install everything LeRobot needs to work. Run this command in a new cell:</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">!pip install </span><span class="token string" style="color:#e3116c">".[pusht]"</span><br></span></code></pre></div></div>
<p>After running it, LeRobot will be ready to use in your notebook. All necessary tools and libraries will be installed automatically.</p>
<blockquote>
<p><strong>Note:</strong> If you see any errors during installation, you may just need to install a missing libraries.</p>
</blockquote>
<p>We recommend installing the <code>hf_xet</code> library for faster and more reliable downloads from Hugging Face:</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">!pip install hf_xet</span><br></span></code></pre></div></div>
<p>This tool helps speed up access to models and datasets, especially when loading large files in Colab.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="running-a-pre-trained-model">Running a Pre-Trained Model<a href="https://www.reduct.store/blog/hugging-face-lerobot#running-a-pre-trained-model" class="hash-link" aria-label="Direct link to Running a Pre-Trained Model" title="Direct link to Running a Pre-Trained Model" translate="no">​</a></h2>
<p>LeRobot includes several pre-trained models, so you can try robot tasks without needing to train anything yourself. These models are already trained on specific tasks and ready to go.</p>
<ul>
<li class="">
<p><strong>π<sub>0</sub>:</strong> A powerful model that combines vision, language, and action. It’s designed for general robot tasks, for example, following instructions or reacting to what it sees.</p>
</li>
<li class="">
<p><strong>π<sub>0</sub> FAST:</strong> A faster, optimized version of the π<sub>0</sub> model.</p>
</li>
<li class="">
<p><strong>Diffusion Policy:</strong> A model trained on the Push-T dataset, where a robot learns to push a T-shaped object toward a target.</p>
</li>
<li class="">
<p><strong>VQ-BeT:</strong> Another model trained on the same Push-T task, but it uses a different architecture. You can run both and compare how they perform.</p>
</li>
<li class="">
<p><strong>ACT:</strong> A model trained for fine manipulation tasks that require high precision, like inserting objects or handling small parts.</p>
</li>
</ul>
<p>By default, the example script runs the Diffusion Policy model on the Push-T task. To try it out, run this command in a code cell:</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">!python examples</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">2_evaluate_pretrained_policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">py</span><br></span></code></pre></div></div>
<p>When you run the command, LeRobot will automatically download the pre-trained model, set up a simulation environment, and run the robot as it tries to complete the task. Throughout the process, you’ll see messages showing what’s happening step-by-step. A short video will also be saved so you can see how the robot performed.</p>
<blockquote>
<p><strong>Note:</strong> When running dataset downloads or model loading multiple times in a row, you might occasionally encounter temporary access restrictions from Hugging Face. This is normal and part of their rate limiting to prevent abuse.</p>
</blockquote>
<p><strong>What You’ll See in the Output</strong></p>
<p>As the model runs, Colab will print some logs in the output below the code:</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">{'observation.image': PolicyFeature(type=&lt;FeatureType.VISUAL: 'VISUAL'&gt;, shape=(3, 96, 96)), 'observation.state': PolicyFeature(type=&lt;FeatureType.STATE: 'STATE'&gt;, shape=(2,))}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Dict('agent_pos': Box(0.0, 512.0, (2,), float64), 'pixels': Box(0, 255, (96, 96, 3), uint8))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{'action': PolicyFeature(type=&lt;FeatureType.ACTION: 'ACTION'&gt;, shape=(2,))}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Box(0.0, 512.0, (2,), float32)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=0 reward=np.float64(0.0) terminated=False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=1 reward=np.float64(0.0) terminated=False</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">step=108 reward=np.float64(0.9727550736734778) terminated=False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=109 reward=np.float64(0.9969248691240408) terminated=False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=110 reward=np.float64(1.0) terminated=True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Success!</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">IMAGEIO FFMPEG_WRITER WARNING: input image is not divisible by macro_block_size=16, resizing from (680, 680) to (688, 688) to ensure video compatibility with most codecs and players. To prevent resizing, make your input image divisible by the macro_block_size or set the macro_block_size to 1 (risking incompatibility).</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Video of the evaluation is available in 'outputs/eval/example_pusht_diffusion/rollout.mp4'.</span><br></span></code></pre></div></div>
<p>Here’s what they mean:</p>
<ul>
<li class="">
<p><strong>Observations:</strong> What kind of data the robot receives, like the shape and type of images or sensor readings it expects.</p>
</li>
<li class="">
<p><strong>Actions:</strong> The format of the commands the robot will output to control its movements.</p>
</li>
<li class="">
<p><strong>Reward:</strong> A number that shows how well the robot is doing (higher = better).</p>
</li>
<li class="">
<p><strong>Step-by-step info:</strong> Shows progress, like step 108, reward 0.97, etc.</p>
</li>
<li class="">
<p><strong>Success or Failure:</strong> Whether the robot completed the task. In our experiments, the same pre-trained model produced different results. It didn’t always complete the task successfully.</p>
</li>
</ul>
<p>You may also see a <strong>warning</strong> about video resizing. It’s normal and doesn’t affect how the robot runs.</p>
<p><strong>Where’s the Video?</strong></p>
<p>The video is saved in <code>lerobot/outputs/eval/example_pusht_diffusion/rollout.mp4</code>.</p>
<p>It shows the robot pushing the T-shaped object in simulation using the actions generated by the model. To download it, find the file in the file browser, click the three dots to the right of the filename, and select <code>Download</code>. Then you can watch it with any video player.</p>
<p><img decoding="async" loading="lazy" alt="GIF" src="https://www.reduct.store/assets/images/rollout-f3e1a107499fb3f20fc9145034457905.gif" width="688" height="688" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="want-to-try-a-different-model">Want to Try a Different Model?<a href="https://www.reduct.store/blog/hugging-face-lerobot#want-to-try-a-different-model" class="hash-link" aria-label="Direct link to Want to Try a Different Model?" title="Direct link to Want to Try a Different Model?" translate="no">​</a></h3>
<p>You can switch from Diffusion Policy to VQ-BeT, which is trained on the same task. It’s a good way to explore how different models perform.</p>
<p>Here’s how you can do it:</p>
<ol>
<li class="">
<p>In the file browser, open the file <code>lerobot/examples/2_evaluate_pretrained_policy.py</code>.</p>
</li>
<li class="">
<p>Double-click the file to open it in the editor pane on the right.</p>
</li>
<li class="">
<p>Update the following lines in the script:</p>
</li>
</ol>
<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 number" style="color:#36acaa">33</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> lerobot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">common</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">policies</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">vqbet</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">modeling_vqbet </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> VQBeTPolicy</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"># Optional: change output path to avoid overwriting results</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">36</span><span class="token plain"> output_directory </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">"outputs/eval/example_vqbet_pusht"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">43</span><span class="token plain"> pretrained_policy_path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lerobot/vqbet_pusht"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">47</span><span class="token plain"> policy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> VQBeTPolicy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pretrained_policy_path</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<ol start="4">
<li class="">
<p>Save the file by pressing <code>Ctrl+S</code> (or <code>Cmd+S</code> on Mac).</p>
</li>
<li class="">
<p>After saving, re-run the code cell that runs the script:</p>
</li>
</ol>
<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">!python examples</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">2_evaluate_pretrained_policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">py</span><br></span></code></pre></div></div>
<p>This will now evaluate the VQ-BeT model instead of the Diffusion Policy.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="training-a-model">Training a Model<a href="https://www.reduct.store/blog/hugging-face-lerobot#training-a-model" class="hash-link" aria-label="Direct link to Training a Model" title="Direct link to Training a Model" translate="no">​</a></h2>
<p>LeRobot isn’t just for running pre-trained models, it also lets you try training one yourself. You can train the same type of model used by the official LeRobot team: the Diffusion Policy on the Push-T task.</p>
<p>Since we’re using Google Colab, you have access to a free GPU, which is important because training on other systems without a CUDA-enabled GPU can be very slow. For example, in our tests on a Mac with Apple Silicon (using the MPS backend), training took significantly longer — in one case, up to two hours just to complete just 20 steps.</p>
<p>By default, the training script runs for 5000 steps, which takes some time. In our case, the run took about an hour on Colab’s GPU. If you want to try it faster, you can reduce the steps to, say, 100. This will still give you a good idea of how training works.</p>
<p>In the file <code>lerobot/examples/3_train_policy.py</code>, find and change these line:</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 number" style="color:#36acaa">42</span><span class="token plain"> training_steps </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">5000</span><br></span></code></pre></div></div>
<p>Now run the training 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">!python examples</span><span class="token operator" style="color:#393A34">/</span><span class="token plain">3_train_policy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">py</span><br></span></code></pre></div></div>
<p>This will start training the Diffusion Policy model on the Push-T task using the <code>lerobot/pusht</code> dataset.</p>
<p>As the script runs, you’ll see lines like this in the terminal:</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">step: 0 loss: 1.161</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step: 1 loss: 5.978</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">step: 4998 loss: 0.048</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step: 4999 loss: 0.037</span><br></span></code></pre></div></div>
<p>Each line shows the current training step and the corresponding loss value. A decreasing loss generally means the model is learning.</p>
<p><strong>Where the Trained Model is Saved</strong></p>
<p>LeRobot will save your trained model in <code>lerobot/outputs/train/example_pusht_diffusion</code>. Inside the folder, you’ll find two files represent your trained Diffusion Policy: one with the model’s weights and one with its settings. They will be used automatically when you run the model.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="evaluating-your-trained-model">Evaluating Your Trained Model<a href="https://www.reduct.store/blog/hugging-face-lerobot#evaluating-your-trained-model" class="hash-link" aria-label="Direct link to Evaluating Your Trained Model" title="Direct link to Evaluating Your Trained Model" translate="no">​</a></h3>
<p>Now let’s see your model in action.</p>
<p>Open the file <code>lerobot/examples/2_evaluate_pretrained_policy.py</code> and change the code so it loads your trained model instead of the pre-trained one:</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 number" style="color:#36acaa">33</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> lerobot</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">common</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">policies</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">diffusion</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">modeling_diffusion </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> DiffusionPolicy</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">36</span><span class="token plain"> output_directory </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">"outputs/eval/example_pusht_diffusion"</span><span class="token punctuation" style="color:#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"># Comment out the old pretrained model path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">43</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># pretrained_policy_path = "lerobot/diffusion_pusht"</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 your newly trained model path instead</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">45</span><span class="token plain"> pretrained_policy_path </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">"outputs/train/example_pusht_diffusion"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">47</span><span class="token plain"> policy </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> DiffusionPolicy</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">from_pretrained</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">pretrained_policy_path</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>To avoid overwriting the previous video, give your video a new name:</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 number" style="color:#36acaa">136</span><span class="token plain"> video_path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> output_directory </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"rollout_our_model.mp4"</span><br></span></code></pre></div></div>
<p>Now run the evaluation script:</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">!python examples/2_evaluate_pretrained_policy.py</span><br></span></code></pre></div></div>
<p><strong>What You’ll See</strong></p>
<p>The script will run your model in simulation and save a video you can later open to see how your model behaved <code>lerobot/outputs/eval/example_pusht_diffusion/rollout_our_model.mp4</code>.</p>
<p>You’ll also see logs like:</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">...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=297 reward=np.float64(0.0) terminated=False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=298 reward=np.float64(0.0) terminated=False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">step=299 reward=np.float64(0.0) terminated=False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Failure!</span><br></span></code></pre></div></div>
<p>This means the robot didn’t complete the task successfully. Even if you trained for 5000 steps, your model may still perform noticeably worse than the official pre-trained model. That’s normal, the LeRobot team trained their models with much more compute and fine-tuning. In comparison, your version might show less precise or more random movements. It’s a good first step, though, and shows the entire training and evaluation pipeline working end-to-end.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="downloading-a-dataset">Downloading a Dataset<a href="https://www.reduct.store/blog/hugging-face-lerobot#downloading-a-dataset" class="hash-link" aria-label="Direct link to Downloading a Dataset" title="Direct link to Downloading a Dataset" translate="no">​</a></h2>
<p>To train a model we need one key ingredient: data. These include video from the robot’s cameras, joint positions, and the actions it took over time.</p>
<p>LeRobot makes this part easy. It comes with a growing collection of high-quality robot learning datasets you can download and explore with just a few lines of code.</p>
<p><a href="https://huggingface.co/datasets?other=LeRobot" target="_blank" rel="noopener noreferrer" class=""><strong>Browse all available datasets here</strong></a>.</p>
<p>To download and inspect a dataset, run this example script:</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">!python examples/1_load_lerobot_dataset.py</span><br></span></code></pre></div></div>
<p>By default, this will download a dataset <code>lerobot/aloha_mobile_cabinet</code>.</p>
<p>But you’re not limited to just one. If you’d like to try the dataset used by the models in the previous section (DiffusionPolicy and VQ-BeT), open the script and change the <code>repo_id</code> variable like this:</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 number" style="color:#36acaa">50</span><span class="token plain"> repo_id </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"lerobot/pusht"</span><br></span></code></pre></div></div>
<p>Then re-run the script. This will download the <a href="https://huggingface.co/datasets/lerobot/pusht" target="_blank" rel="noopener noreferrer" class=""><strong>Push-T dataset</strong></a>, the same one used to train both models you just ran earlier. You’ll now have access to the raw data they were trained on.</p>
<p><strong>Tip: Clean Up the Output</strong></p>
<p>The dataset script prints a lot of information, overwhelming for beginners. To make things easier, you can comment out some of the verbose print lines.</p>
<blockquote>
<p>To comment out multiple lines quickly:</p>
<ul>
<li class=""><strong>Windows/Linux:</strong> Press <code>Ctrl + /</code>.</li>
<li class=""><strong>macOS:</strong> Press <code>Cmd + /</code>.</li>
</ul>
</blockquote>
<p>Suggested lines to comment out include:</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 number" style="color:#36acaa">38</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print("List of available datasets:")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">39</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># pprint(lerobot.available_datasets)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">42</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># hub_api = HfApi()</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">43</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># repo_ids = [info.id for info in hub_api.list_datasets(task_categories="robotics", tags=["LeRobot"])]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">44</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># pprint(repo_ids)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">65</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print("Features:")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">66</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># pprint(ds_meta.features)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">69</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(ds_meta)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">73</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># dataset = LeRobotDataset(repo_id, episodes=[0, 10, 11, 23])</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">76</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(f"Selected episodes: {dataset.episodes}")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">77</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(f"Number of episodes selected: {dataset.num_episodes}")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">78</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(f"Number of frames selected: {dataset.num_frames}")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">82</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(f"Number of episodes selected: {dataset.num_episodes}")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">83</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(f"Number of frames selected: {dataset.num_frames}")</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">86</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(dataset.meta)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">90</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic"># print(dataset.hf_dataset)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">111</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># pprint(dataset.features[camera_key])</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">113</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># pprint(dataset.features[camera_key])</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">119</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># delta_timestamps = {</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">#... all lines</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token number" style="color:#36acaa">148</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># break</span><br></span></code></pre></div></div>
<p>You can always uncomment them later if you want a deeper look into the dataset structure.</p>
<p><strong>What’s Inside the Push-T Dataset?</strong></p>
<p>Once downloaded, you’ll see a summary like this:</p>
<ul>
<li class="">
<p><strong>Number of episodes:</strong> 206. An episode is like one full attempt by the robot to complete a task, one round of practice.</p>
</li>
<li class="">
<p><strong>Frames per episode (avg.):</strong> ~124. Each episode is made up of about 124 images (or frames), showing what the robot saw over time as it moved and acted.</p>
</li>
<li class="">
<p><strong>Recording speed:</strong> 10 FPS. These images were recorded at 10 frames per second, like a slow-motion video. It lets you see how the robot moved step by step.</p>
</li>
<li class="">
<p><strong>Camera views:</strong> <code>observation.image</code>. Each frame is taken from the robot’s camera, and labeled as <code>observation.image</code> in the data. It’s what the robot sees.</p>
</li>
<li class="">
<p><strong>Task description:</strong> Push the T-shaped block onto the T-shaped target.</p>
</li>
<li class="">
<p><strong>Image format:</strong> Each image is stored as a PyTorch tensor (a data structure used in machine learning):</p>
<ul>
<li class="">3: color channels (red, green, blue)</li>
<li class="">96 x 96: image height and width in pixels</li>
</ul>
</li>
</ul>
<p>LeRobot downloads the dataset into a special hidden cache folder inside the Colab environment <code>/root/.cache/huggingface/lerobot/lerobot/pusht/</code>.</p>
<p>This folder contains all the data files: observations, actions, metadata, and even video recordings. Since it’s hidden by default, follow these steps to access it:</p>
<ol>
<li class="">
<p>Click the eye icon at the top of the file browser to show hidden folders like <code>.cache.</code></p>
</li>
<li class="">
<p>Click the folder icon with two dots just above the <code>lerobot</code> folder.</p>
</li>
</ol>
<p><img decoding="async" loading="lazy" alt="Folders" src="https://www.reduct.store/assets/images/lerobot_folder-f6e2ad81097e3e745be5e425bd35c372.png" width="937" height="529" class="img_ev3q"></p>
<ol start="3">
<li class="">
<p>Now navigate through the folders like this: <code>root</code> &gt; <code>.cache</code> &gt; <code>huggingface</code> &gt; <code>lerobot</code> &gt; <code>lerobot</code> &gt; <code>pusht</code>.</p>
</li>
<li class="">
<p>To go back to the <code>lerobot</code> folder, look for the <code>content</code> folder, it's at the same level as the <code>root</code>, and go inside.</p>
</li>
</ol>
<p><strong>Dataset Folder Structure</strong></p>
<p>Here's what the folder structure typically looks like:</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">lerobot/pusht</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── README.md</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── .cache/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── data/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── chunk-000/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       ├── episode_000000.parquet</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       └── ...  # More episodes</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── meta/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">├── videos/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│   └── chunk-000/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│       └── observation.image/</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│           ├── episode_000000.mp4</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│           └── ...  # More videos</span><br></span></code></pre></div></div>
<ul>
<li class="">
<p><code>README.md</code>: A short file that explains what’s inside the dataset and what it’s for.</p>
</li>
<li class="">
<p><code>data/</code>: This folder contains one file per episode <code>.parquet</code>, where the robot logs everything it experienced.</p>
</li>
<li class="">
<p><code>meta/</code>: This folder contains helpful background info, like the episode’s descriptions, task goals, and performance stats, that LeRobot uses to organize and analyze the data.</p>
</li>
<li class="">
<p><code>videos/</code>: Short <code>.mp4</code> videos showing the robot’s camera view during each episode. These are great if you want to see what the robot was doing.</p>
</li>
<li class="">
<p><code>.cache/</code>: A hidden folder used by LeRobot internally.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="visualize-a-dataset">Visualize a Dataset<a href="https://www.reduct.store/blog/hugging-face-lerobot#visualize-a-dataset" class="hash-link" aria-label="Direct link to Visualize a Dataset" title="Direct link to Visualize a Dataset" translate="no">​</a></h2>
<p>Once your dataset is loaded, it’s super helpful to see what the robot actually experienced. LeRobot comes with an easy-to-use, interactive visualization tool that runs right in your browser.</p>
<p><strong>Try the Built-in Viewer</strong></p>
<p>You can open it here: <a href="https://huggingface.co/spaces/lerobot/visualize_dataset" target="_blank" rel="noopener noreferrer" class=""><strong>Visualize Dataset (v2.0+ latest dataset format)</strong></a> or use the older version: <a href="https://huggingface.co/spaces/lerobot/visualize_dataset_v1.6" target="_blank" rel="noopener noreferrer" class=""><strong>Visualize Dataset (v1.6 old dataset format)</strong></a>.</p>
<p>In the viewer, just enter the name of a dataset, like <code>lerobot/pusht</code>.</p>
<p><strong>What Can You See?</strong></p>
<ul>
<li class="">
<p>Watch each episode like a video from the robot’s point of view.</p>
</li>
<li class="">
<p>Explore graphs showing how the robot moved and what actions it took. For example, in the <code>lerobot/pusht</code> dataset, the viewer displays Motor 0 and Motor 1 — both state and action — as four curves plotted over time. This allows you to see how the robot's decisions changed from frame to frame during each episode.</p>
</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Motors" src="https://www.reduct.store/assets/images/motors-b0e01d756d6e181832c1efeb44d20e62.png" width="774" height="375" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="next-steps">Next Steps<a href="https://www.reduct.store/blog/hugging-face-lerobot#next-steps" class="hash-link" aria-label="Direct link to Next Steps" title="Direct link to Next Steps" translate="no">​</a></h2>
<p>You’ve just taken your first steps into robotics and machine learning with LeRobot, so what can you do next?</p>
<ul>
<li class="">
<p><strong>Try different models and tasks:</strong> LeRobot supports several models and scenarios. For more challenging examples, check out the <code>lerobot/examples/advanced</code> folder.</p>
</li>
<li class="">
<p><strong>Run your own experiment:</strong> Once you’re familiar with the basic workflow, you can try a simple experiment: change the dataset slightly or load a new one. Even a small change, such as selecting a different set of episodes, will help you see how data affects the model’s behavior.</p>
</li>
<li class="">
<p><strong>Grow your projects further:</strong> As you work more with LeRobot and collect larger amounts of data, organizing and managing that data becomes important. This can feel overwhelming at first, but understanding the basics of data management will save you time and frustration later. We recommend checking out this beginner-friendly guide, <a class="" href="https://www.reduct.store/blog/store-robotic-data"><strong>How to Store and Manage Robotics Data</strong></a>. It explains simple strategies for handling robot data efficiently. You don’t need to master this now, but keeping these ideas in mind will help you scale your experiments smoothly when you’re ready.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/hugging-face-lerobot#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>In this tutorial, we saw how LeRobot lets you explore robotics and machine learning without needing a physical robot. You ran pre-trained models in simulation, worked with real robot data, and even trained a simple model — all within Colab.</p>
<p>What many find surprising is how accessible this has become. Tasks that once required expensive hardware and deep skills can now be done with just a browser and a few lines of code. Seeing a robot act based on what it sees is exciting, and you can go further by modifying, training, and evaluating models yourself. LeRobot is a great way to start new projects and dive into robotics.</p>
<hr>
<p>We hope this tutorial inspires you to keep exploring. If you have any questions or ideas to share, feel free to use the <a href="https://community.reduct.store/signup" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>robotics</category>
            <category>lerobot</category>
        </item>
        <item>
            <title><![CDATA[How to Store Images in ROS2]]></title>
            <link>https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision</link>
            <guid>https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision</guid>
            <pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to capture and store images from ROS2 using ReductStore, a high-performance storage and streaming solution for unstructured, time-series data.]]></description>
            <content:encoded><![CDATA[<div class="videoWrapper_VWB9"><div class="responsiveIframe__DxY"></div></div>
<p>&nbsp;</p>
<p>ROS2 is widely used for building robotic systems with sensors like cameras, LiDAR, and IMUs. While it's great for communication (e.g., publishing and subscribing to topics), it lacks a built-in solution for storing large amounts of unstructured data, such as images.</p>
<p>Bag files are commonly used to store data in ROS2, but they aren't a good fit for long-term storage or real-time streaming. They're mainly meant for recording and replaying mission data or episodes, not for managing large volumes of unstructured data.</p>
<p>Addressing this challenge, this blog post will guide you through setting up ROS2 with ReductStore a high-performance storage and streaming solution optimized for unstructured, time-series data.</p>
<p>We will focus specifically on image data, but if you are interested in a more general overview you can read <a class="" href="https://www.reduct.store/blog/store-robotic-data"><strong>How to Store and Manage Robotic Data</strong></a> which covers the challenges and strategies for storing and managing robotic data in general.</p>
<p>For the full code example, we will be using the <a href="https://github.com/reductstore/reduct-ros-example" target="_blank" rel="noopener noreferrer" class=""><strong>reduct-ros-example repository</strong></a>, which provides a complete implementation of the concepts discussed in this article.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ros2-distributions">ROS2 distributions<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#ros2-distributions" class="hash-link" aria-label="Direct link to ROS2 distributions" title="Direct link to ROS2 distributions" translate="no">​</a></h2>
<p>To install ROS2, you'll need to select a distribution that aligns with your project requirements and system compatibility. As of May 2025, two distributions are currently supported:</p>
<ul>
<li class=""><strong>Jazzy Jalisco</strong> (<code>jazzy</code>) (Release date: May 23rd, 2024; End of life: May 2029)</li>
<li class=""><strong>Humble Hawksbill</strong> (<code>humble</code>) (Release date: May 23rd, 2022; End of life: May 2027)</li>
</ul>
<p>If you are starting today, the <code>jazzy</code> distribution is recommended for its long-term support until May 2029. This is the thenth release of ROS2 and supports the following platforms:</p>
<ul>
<li class="">Tier 1: Ubuntu 24.04, Windows 10</li>
<li class="">Tier 2: RHEL 9 (Red Hat Enterprise Linux)</li>
<li class="">Tier 3: macOS and Debian Bookworm</li>
</ul>
<p>To install the Jazzy Jalisco distribution, you can follow the instructions provided in <a href="https://docs.ros.org/en/jazzy/Installation.html" target="_blank" rel="noopener noreferrer" class=""><strong>Jazzy's installation guide</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-the-advantages-of-using-reductstore-with-ros2">Understanding the advantages of using ReductStore with ROS2<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#understanding-the-advantages-of-using-reductstore-with-ros2" class="hash-link" aria-label="Direct link to Understanding the advantages of using ReductStore with ROS2" title="Direct link to Understanding the advantages of using ReductStore with ROS2" translate="no">​</a></h2>
<p>Integrating ReductStore with ROS provides many benefits for robotic applications:</p>
<ul>
<li class=""><strong>Best performance</strong>: ReductStore's time-series design is tailored to the sequential nature of robotic applications and optimized for unstructured data (such as images).</li>
<li class=""><strong>Real-time data management</strong>: You can set a real-time <a class="" href="https://www.reduct.store/docs/glossary#fifo-quota"><strong>FIFO quota</strong></a> policy based on storage volume, which is critical for managing large amounts of data in real time. This ensures that the system doesn't run out of space and can handle continuous data streams.</li>
<li class=""><strong>Metadata association</strong>: It supports the association of labels (key-value pairs) directly with each record, allowing you to add context to your data. This is particularly useful for filtering and searching through large datasets.</li>
<li class=""><strong>Replication</strong>: With a replication task, you can stream data to another ReductStore instance, either on the same machine or a different one. This is useful for backup or to automatically filter and save data to a cloud instance.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-to-capture-and-store-raw-camera-images">Example to capture and store raw camera images<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#example-to-capture-and-store-raw-camera-images" class="hash-link" aria-label="Direct link to Example to capture and store raw camera images" title="Direct link to Example to capture and store raw camera images" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" alt="ROS with ReductStore" src="https://www.reduct.store/assets/images/ros-reductstore-example-6baee92a61de9f27842a9d6009e7bb3b.png" width="2406" height="747" class="img_ev3q"></p>
<p>To capture and store raw camera images with ROS2, you need to create a node that subscribes to an image topic, serialize the image, and stores it in ReductStore.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-a-custom-ros2-node">Create a custom ROS2 Node<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#create-a-custom-ros2-node" class="hash-link" aria-label="Direct link to Create a custom ROS2 Node" title="Direct link to Create a custom ROS2 Node" translate="no">​</a></h3>
<p>To set it up, you will need to create a custom ROS2 Node that listens to image messages and uses ReductStore client for data storage.</p>
<p>Below is an example demonstrating this integration within a Python class (<code>ImageListener</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"> 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"> 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"> 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"> Bucket</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"> CompressedImage</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">ImageListener</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 triple-quoted-string string" style="color:#e3116c">"""Node for listening to image messages and storing them 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 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 plain"> reduct_client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Client</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> loop</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AbstractEventLoop</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 triple-quoted-string string" style="color:#e3116c">"""</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        Initialize the image listener node.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :param reduct_client: Client instance for interacting with ReductStore.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :param loop: The asyncio event loop.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string 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 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">"image_listener"</span><span class="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">reduct_client</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Client </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> reduct_client</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">loop</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> asyncio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">AbstractEventLoop </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> loop</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">bucket</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> Bucket </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</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"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            CompressedImage</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/image_raw/compressed"</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 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>In this example <code>ImageListener</code> is a subclass of <code>Node</code>, which is part of ROS2's client library (<code>rclpy</code>). It sets up a subscription to listen for incoming images from the <code>/image_raw/compressed</code> topic. When an image message is received by the node via <code>self.subscription</code>, it triggers <code>image_callback</code>.</p>
<p>In this callback function, each received frame is stored in the designated bucket in ReductStore using Python's built-in <code>asyncio</code> module, more on this later.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="initialize-a-new-reductstore-bucket">Initialize a new ReductStore bucket<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#initialize-a-new-reductstore-bucket" class="hash-link" aria-label="Direct link to Initialize a new ReductStore bucket" title="Direct link to Initialize a new ReductStore bucket" translate="no">​</a></h3>
<p>This process involves setting up configuration parameters such as the bucket name and its storage quota settings. In our example, we create a bucket named <code>ros-bucket</code> with a FIFO quota type to ensure that the disk does not run out of space. The <code>exist_ok</code> parameter reuses the existing bucket if it already exists instead of raising an exception.</p>
<p>Here's how we can define this initialization within our Python class:</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">from</span><span class="token plain"> reduct_py </span><span class="token keyword" style="color:#00009f">import</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">class</span><span class="token plain"> </span><span class="token class-name">ImageListener</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 comment" style="color:#999988;font-style:italic"># ... [other parts of ImageListener class] ...</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">init_bucket</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"> </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 triple-quoted-string string" style="color:#e3116c">"""Asynchronously initialize the Reduct bucket for storing images."""</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" style="color:#e3116c">"Initializing Reduct 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">        self</span><span class="token punctuation" style="color:#393A34">.</span><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"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">reduct_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 plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token string" style="color:#e3116c">"ros-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">            BucketSettings</span><span class="token punctuation" style="color:#393A34">(</span><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"> quota_size</span><span class="token operator" style="color:#393A34">=</span><span class="token number" style="color:#36acaa">1_000_000_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">            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">        </span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>This code snippet should be called within our existing <code>ImageListener</code> class during the node's initialization or before storing the first image. This guarantees that the storage bucket is ready.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="handling-images-in-callbacks">Handling images in callbacks<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#handling-images-in-callbacks" class="hash-link" aria-label="Direct link to Handling images in callbacks" title="Direct link to Handling images in callbacks" translate="no">​</a></h3>
<p>When an image message from a ROS topic is received, it triggers the <code>image_callback</code> method. This method's role is to serialize the image data and organize its storage without blocking the main thread.</p>
<p>Serialization converts the ROS message data into a binary format that can be stored in ReductStore. This step is necessary because ReductStore is designed to handle binary data, i.e Blobs or byte streams. Here's an example code snippet demonstrating how to handle images in callbacks for storing them in ReductStore:</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">class</span><span class="token plain"> </span><span class="token class-name">ImageListener</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 comment" style="color:#999988;font-style:italic"># ... [previous parts of ImageListener class] ...</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 decorator annotation punctuation" style="color:#393A34">@staticmethod</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">get_timestamp</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 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">int</span><span class="token punctuation" style="color:#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">"""</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        Extract the timestamp from a ROS message.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :param msg: The ROS message.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :return: The timestamp in microseconds.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string 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">return</span><span class="token plain"> </span><span class="token builtin">int</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">header</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stamp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">sec </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1e6</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> msg</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">header</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">stamp</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">nanosec </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1e3</span><span class="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 decorator annotation punctuation" style="color:#393A34">@staticmethod</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">serialize_message</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"> CompressedImage</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">"""</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        Serialize a ROS message to bytes.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :param msg: The ROS message.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :return: The serialized message.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string 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">return</span><span class="token plain"> </span><span class="token builtin">bytes</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"></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"> CompressedImage</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 triple-quoted-string string" style="color:#e3116c">"""</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        Handle incoming image messages by scheduling storage.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        This callback is triggered by ROS message processing. It schedules</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        the image storage coroutine to be executed in the asyncio event loop.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        """</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'Received image, storing to database'</span><span class="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"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">get_timestamp</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"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        binary_data </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">serialize_message</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"></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_coroutine_threadsafe</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">store_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"> binary_data</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 punctuation" style="color:#393A34">)</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">loop</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre></div></div>
<p>In this context, <code>serialize_message</code> is used to convert the <code>CompressedImage</code> message data (in our case JPEG) to bytes that can then be passed along for storage.</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>The serialization format determines how data is stored and retrieved, so it should be cross-platform to be usable on different operating systems. In this example, we use the JPEG format, which is widely supported and efficient for image data.</p></div></div>
<p>Following serialization, an asynchronous coroutine (<code>store_data</code>) is scheduled on the event loop (<code>self.loop</code>) using <code>asyncio.run_coroutine_threadsafe</code>.</p>
<p>This function is useful for integrating asynchronous operations into primarily synchronous ROS2 callback handlers. This way, processing doesn't block the executor.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="store-images-in-a-reductstore-bucket-entry">Store images in a ReductStore bucket entry<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#store-images-in-a-reductstore-bucket-entry" class="hash-link" aria-label="Direct link to Store images in a ReductStore bucket entry" title="Direct link to Store images in a ReductStore bucket entry" translate="no">​</a></h3>
<p>The <code>store_data</code> method is responsible for storing the serialized images to ReductStore with the following parameters:</p>
<ul>
<li class="">The <code>timestamp</code> in microseconds that will be used as an index for the image.</li>
<li class="">The <code>data</code>, which is the serialized image data (in bytes).</li>
<li class="">The <code>content_type</code>, which specifies the format of the data being stored (e.g., "image/jpeg").</li>
</ul>
<p>Moreover, it allows you to associate metadata with the stored data, such as labels (key-value pairs) that can be used for filtering and searching. In this example, we generate a random value to demonstrate how it works:</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">class</span><span class="token plain"> </span><span class="token class-name">ImageListener</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 comment" style="color:#999988;font-style:italic"># ... [previous parts of ImageListener class] ...</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">store_data</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"> timestamp</span><span class="token punctuation" 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"> data</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">bytes</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 builtin">str</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 triple-quoted-string string" style="color:#e3116c">"""</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        Store unstructured data in the Reduct bucket.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="display:inline-block;color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :param timestamp: The timestamp for the data.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string string" style="color:#e3116c">        :param data: The serialized data.</span><br></span><span class="token-line" style="color:#393A34"><span class="token triple-quoted-string 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">if</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">not</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">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"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">init_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" 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"># Example of a label (key-value pair) to associate with the data record</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">"random_value"</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">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" style="display:inline-block"></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"Storing data at </span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">{</span><span class="token string-interpolation interpolation">self</span><span class="token string-interpolation interpolation punctuation" style="color:#393A34">.</span><span class="token string-interpolation interpolation">display_timestamp</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 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">        </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</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">"images"</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"> 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"> content_type</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">content_type</span><span class="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></code></pre></div></div>
<p>When the <code>store_data</code> method is called, it first checks if the bucket has been initialized. If not, it calls the <code>init_bucket</code> method to create the bucket. Then, it writes the serialized data to the bucket entry <code>images</code> with the provided timestamp and labels. The <code>content_type</code> parameter is also passed to specify the format of the data being stored.</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>The <code>store_data</code> method is designed to be non-blocking, which allows the main thread to continue processing other tasks while the data is stored.</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="testing-the-image-storage-system">Testing the image storage system<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#testing-the-image-storage-system" class="hash-link" aria-label="Direct link to Testing the image storage system" title="Direct link to Testing the image storage system" translate="no">​</a></h2>
<p>To test the image storage system, you can run the ROS2 node and publish image messages to the <code>/image_raw/compressed</code> topic.</p>
<p>For example, we can use the <code>usb_cam</code> package to capture images from a USB camera (such as a web camera) and publish them to the <code>/image_raw/compressed</code> topic.</p>
<p>To install the <code>usb_cam</code> package, you can use the following command:</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-get</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">install</span><span class="token plain"> ros-</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ros2-distro</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain">-usb-cam</span><br></span></code></pre></div></div>
<p>Replace <code>&lt;ros2-distro&gt;</code> with the ROS2 distribution you are using, such as <code>jazzy</code> or <code>humble</code>.</p>
<p>To configure the <code>usb_cam</code> node, you can create a YAML configuration file (e.g., <code>usb_cam_params.yaml</code>). If your camera supports MJPEG, you can set the <code>pixel_format</code> parameter to <code>raw_mjpeg</code> to publish JPEG images:</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">usb_cam</span><span class="token punctuation" style="color:#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">video_device</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/dev/video0"</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_width</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">640</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_height</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">480</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">pixel_format</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"raw_mjpeg"</span><br></span></code></pre></div></div>
<p>MotionJPEG (MJPEG) is a common format for video compression and is often used for video streaming. This format compresses each frame of video as a separate JPEG image, which is the format we want to use in our example.</p>
<p>We can then run the <code>usb_cam</code> node to start capturing images and publishing them to the <code>/image_raw/compressed</code> topic using the following command:</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 usb_cam usb_cam_node_exe --ros-args --params-file ./usb_cam_params.yaml</span><br></span></code></pre></div></div>
<p>After the <code>usb_cam</code> node is running, we can start our custom ROS2 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 plain">ros2 run reduct_camera capture_and_store</span><br></span></code></pre></div></div>
<p>The <code>capture_and_store</code> node will start listening to the <code>/image_raw/compressed</code> topic and store the images in ReductStore as they are received.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="inspect-stored-images">Inspect stored images<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#inspect-stored-images" class="hash-link" aria-label="Direct link to Inspect stored images" title="Direct link to Inspect stored images" translate="no">​</a></h3>
<p>Once the images are stored in ReductStore, you can access them using any supported SDK (Python, C++, etc.), the Web Console, or the CLI client.</p>
<p>For a quick visual check, open <code>http://localhost:8383</code> in your browser to access the ReductStore Web Console. There, you can browse your buckets, view stored images, and check timestamps and labels.</p>
<p>If you want to copy the images to your local machine, you can also use the <a href="https://github.com/reductstore/reduct-cli" target="_blank" rel="noopener noreferrer" class=""><strong>CLI Client</strong></a>. After installing it, add an alias for your 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 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 parameter variable" style="color:#36acaa">-L</span><span class="token plain"> http://localhost:8383 </span><span class="token builtin class-name">local</span><br></span></code></pre></div></div>
<p>Then export all images from the ros-bucket bucket to a local folder:</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 function" style="color:#d73a49">cp</span><span class="token plain"> local/ros-bucket ./exported-data</span><br></span></code></pre></div></div>
<p>This command copies all images from the <code>ros-bucket</code> bucket to the <code>./exported-data</code> folder.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="best-practices">Best practices<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#best-practices" class="hash-link" aria-label="Direct link to Best practices" title="Direct link to Best practices" translate="no">​</a></h2>
<p>There are several best practices to consider when integrating ReductStore. Here are a few to keep in mind:</p>
<ul>
<li class="">Use non-blocking operations to prevent the main thread of the ROS2 node from being blocked. This allows the node to continue processing other tasks while waiting for data to be stored.</li>
<li class="">Serialize data before storing it in ReductStore in a cross-platform binary format to ensure compatibility with different systems and programming languages.</li>
<li class="">Create a ReductStore bucket with a FIFO quota policy to prevent disk space issues.</li>
<li class="">Use token authentication to the bucket to ensure secure access to the data.</li>
<li class="">Use labels (key-value pairs) to associate metadata with stored data. This allows you to filter and search through large datasets easily.</li>
<li class="">Set up data replication tasks to stream data to another ReductStore instance for backup or filtering purposes (e.g., to a cloud instance).</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/tutorials/ros/optimal-image-storage-solutions-for-ros-based-computer-vision#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Storing images from ROS2 in ReductStore is an effective and straightforward method for managing large volumes of unstructured data. This tutorial demonstrates how to create a ROS2 node that captures camera images, serializes them, and stores them in a bucket. Using such a system provides better performance than traditional bag files and offers the ability to filter and search through images, manage quotas, and stream data to a central storage or cloud infrastructure.</p>
<hr>
<p>I hope this tutorial has been helpful. If you have any questions or feedback, don't hesitate to use the <a href="https://community.reduct.store/signup" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community</strong></a> forum.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>ros</category>
            <category>computer vision</category>
            <category>robotics</category>
        </item>
        <item>
            <title><![CDATA[Getting Started with MetriCal]]></title>
            <link>https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar</link>
            <guid>https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar</guid>
            <pubDate>Tue, 13 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This tutorial explains sensor calibration, covering intrinsic and extrinsic parameters, and how tools like ReductStore and MetriCal enable automated, scalable calibration workflows for robotics and autonomous systems.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="Intro image" src="https://www.reduct.store/assets/images/intro_metrical-2229f713bec7a5720000c1b7e932ebe4.png" width="1486" height="1007" class="img_ev3q"></p>
<p><strong>Sensor calibration</strong> is the process of determining the precise mathematical parameters that describe how a sensor perceives or measures the physical world. By comparing sensor outputs to known reference values, we can correct measurement errors and ensure data from different sensors align accurately.</p>
<p>There are two main categories of calibration parameters:</p>
<ul>
<li class="">
<p><strong>Intrinsic parameters (Intrinsics):</strong> These capture the internal characteristics of a sensor, such as lens distortion in cameras or bias and scaling errors in IMUs. Calibrating intrinsics helps eliminate built-in measurement errors.</p>
</li>
<li class="">
<p><strong>Extrinsic parameters (Extrinsics):</strong> These define a sensor's position and orientation relative to another sensor or the environment. Accurate extrinsics are essential for transforming and combining data from multiple sensors into a shared coordinate system.</p>
</li>
</ul>
<!-- -->
<p>High-quality calibration is key to getting reliable, consistent data, which is critical for mapping, perception, and decision-making in robotics and autonomous systems. Recognizing this need, <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a> can be used to manage the entire calibration data pipeline — from raw inputs such as LiDAR scans and calibration images to the output files produced during processing (e.g., intrinsic/extrinsic parameters, transformation matrices). When used together with tools like <a href="https://www.tangramvision.com/products/calibration/metrical" target="_blank" rel="noopener noreferrer" class=""><strong>MetriCal</strong></a>, which streamline the calibration of multimodal sensor data, ReductStore can help enable scalable, automated workflows across distributed systems by making it easy to collect, store, and manage sensor data directly at the edge. Calibration results can then be saved back to ReductStore for persistent access and reuse.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-metrical">What is MetriCal?<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#what-is-metrical" class="hash-link" aria-label="Direct link to What is MetriCal?" title="Direct link to What is MetriCal?" translate="no">​</a></h2>
<p><a href="https://docs.tangramvision.com/metrical/intro/" target="_blank" rel="noopener noreferrer" class=""><strong>MetriCal is a calibration tool developed by Tangram Vision</strong></a> for systems that include diverse types of sensors. It’s designed to handle real-world calibration scenarios and supports the simultaneous processing of data from cameras, LiDARs, and IMUs. MetriCal is suitable for both small-scale setups and larger, production-level environments, providing tools for precise and consistent multi-sensor calibration.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="key-features">Key Features<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#key-features" class="hash-link" aria-label="Direct link to Key Features" title="Direct link to Key Features" translate="no">​</a></h3>
<ul>
<li class="">
<p><strong>ROS Data Input:</strong> Supports <code>.bag</code> and <code>.mcap</code> files (recommended)</p>
</li>
<li class="">
<p><strong>Automatic Extrinsics Estimation:</strong> Computes sensor and target poses without requiring CAD models or manual setup</p>
</li>
<li class="">
<p><strong>Unlimited Sensor Streams:</strong> Supports an arbitrary number of input streams</p>
</li>
<li class="">
<p><strong>Broad Target Support:</strong> Compatible with both 2D and 3D targets; includes a library of premade targets and supports multiple targets at once</p>
</li>
<li class="">
<p><strong>Modular Calibration Workflow:</strong> Allows splitting the calibration process into multiple datasets and stages</p>
</li>
<li class="">
<p><strong>Detailed Diagnostics:</strong> Provides visual and numerical feedback on data quality and calibration performance</p>
</li>
<li class="">
<p><strong>ROS Integration:</strong> Outputs calibration results as an URDF file</p>
</li>
<li class="">
<p><strong>Pixel-Level Corrections:</strong> Generates lookup tables for single-camera undistortion and stereo rectification</p>
</li>
<li class="">
<p><strong>Lightweight Deployment:</strong> CPU-only operation; runs efficiently on compact devices like Intel NUCs or in the cloud</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-metrical-works">How MetriCal Works<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#how-metrical-works" class="hash-link" aria-label="Direct link to How MetriCal Works" title="Direct link to How MetriCal Works" translate="no">​</a></h2>
<p>MetriCal is structured as a CLI-based, fully scriptable pipeline designed to support reproducible workflows and automation. The core calibration process can be divided into the following stages:</p>
<p><strong>1. Preparation</strong></p>
<p>The quality of calibration strongly depends on the choice of targets and the quality of the input data. It's important to select or build targets suited to your use case and follow MetriCal’s data capture guidelines to ensure the collected data meets the required quality standards.</p>
<p>At this stage, you'll also prepare an <strong>object space file</strong>, which describes all calibration targets and their properties.</p>
<p><strong>2. Initialization</strong></p>
<p>Once the dataset and configuration files are ready, MetriCal’s <code>init mode</code> analyzes sensor observations to infer a raw input <strong>plex</strong> — a description of the spatial, temporal, and semantic relationships within your perception system. It represents the physical system being calibrated and serves as the starting point for all further calibration steps.</p>
<blockquote>
<p>If you already have a plex with existing calibration results that you want to preserve, it can be used as a seed for an init plex.</p>
</blockquote>
<p><strong>3. Calibration</strong></p>
<p>In <code>calibrate mode</code>, MetriCal performs a full bundle adjustment to refine both the initial plex and the object space. It applies motion filtering to remove features affected by motion blur, rolling shutter, false detections, and other artifacts in images or point clouds.</p>
<p>A <code>.json</code> cache file is created at this step. This file stores detected objects, allowing future runs to skip the detection process and complete faster.</p>
<blockquote>
<p>The calibration data capture and detection process can also be visualized during this step.</p>
</blockquote>
<p><strong>4. Diagnostics</strong></p>
<p>MetriCal generates a detailed diagnostic report with color-coded charts summarizing calibration quality:</p>
<ul>
<li class=""><strong>Cyan</strong> – spectacular</li>
<li class=""><strong>Green</strong> – good</li>
<li class=""><strong>Orange</strong> – okay, but generally poor</li>
<li class=""><strong>Red</strong> – bad</li>
</ul>
<p><strong>5. Visualization</strong></p>
<p>In <code>display mode</code>, calibration results are visualized using <a href="https://rerun.io/" target="_blank" rel="noopener noreferrer" class=""><strong>Rerun, an open-source tool for multimodal data visualization</strong></a>. It allows you to quickly verify the calibration quality before exporting.</p>
<blockquote>
<p>Typically, the same dataset is used for visualization, but you can also use a different one if it has the same topic names.</p>
</blockquote>
<p><strong>6. Export</strong></p>
<p>In <code>shape mode</code>, the optimized plex can be transformed into various configurations for use in deployed systems, for example, ROS URDFs or pixel-wise lookup tables.</p>
<blockquote>
<p>MetriCal also includes several additional modes to support advanced workflows: <code>completion mode</code>, <code>consolidate object spaces mode</code>, <code>pipeline mode</code>, and <code>pretty print mode</code>.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="metrical-example">MetriCal Example<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#metrical-example" class="hash-link" aria-label="Direct link to MetriCal Example" title="Direct link to MetriCal Example" translate="no">​</a></h2>
<p>To test MetriCal’s multi-sensor capabilities, we use the <a href="https://gitlab.com/tangram-vision/platform/metrical/-/tree/main/examples/camera_lidar" target="_blank" rel="noopener noreferrer" class=""><strong>official example featuring two cameras and a LiDAR</strong></a>. The dataset contains synchronized observations from all three sensors, capturing a LiDAR circle target from different angles. This allows MetriCal to calculate:</p>
<ul>
<li class="">
<p>Intrinsics and poses for both cameras</p>
</li>
<li class="">
<p>Extrinsics between each camera and the LiDAR</p>
</li>
<li class="">
<p>Target geometry and consistency across different views</p>
</li>
</ul>
<p><strong>Installation</strong></p>
<p>We installed MetriCal via Docker. Make sure to set up an alias for convenient access during installation:</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">~/.zshrc</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-name function" style="color:#d73a49">metrical</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><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">--rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--tty</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--init</span><span class="token plain"> </span><span class="token parameter variable" style="color:#36acaa">--user</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">id</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-u</span><span class="token string variable" style="color:#36acaa">)</span><span class="token string" style="color:#e3116c">:</span><span class="token string variable" style="color:#36acaa">$(</span><span class="token string variable function" style="color:#d73a49">id</span><span class="token string variable" style="color:#36acaa"> </span><span class="token string variable parameter variable" style="color:#36acaa">-g</span><span class="token string variable" style="color:#36acaa">)</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 plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token parameter variable" style="color:#36acaa">--volume</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"</span><span class="token string environment constant" style="color:#36acaa">$PATH</span><span class="token string" style="color:#e3116c">/metrical/"</span><span class="token builtin class-name">:</span><span class="token string" style="color:#e3116c">"/datasets"</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">--volume</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">metrical-license-cache:/.cache/tangram-vision </span><span class="token punctuation" style="color:#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">--workdir</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"/datasets"</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">    --add-host</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">host.docker.internal:host-gateway </span><span class="token punctuation" style="color:#393A34">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    tangramvision/cli:latest </span><span class="token punctuation" style="color:#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">--license</span><span class="token operator" style="color:#393A34">=</span><span class="token string" style="color:#e3116c">"LICENSE KEY"</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">"</span><span class="token string variable" style="color:#36acaa">$@</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"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre></div></div>
<blockquote>
<p>MetriCal requires a license key.</p>
</blockquote>
<p>You can also install MetriCal natively on <code>Ubuntu</code> or <code>Pop!_OS</code>.</p>
<p><strong>Calibration</strong></p>
<p>After cloning the repository, download and unzip the <code>.zip</code> file. Place the observations folder into:</p>
<div class="language-zsh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-zsh 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">$PATH/metrical/examples/camera_lidar</span><br></span></code></pre></div></div>
<p>Next, set the <code>LICENSE</code> variable inside <code>metrical_alias.sh</code>, located in the same directory.</p>
<p>Once everything is configured, you can run the full calibration pipeline using the provided shell script:</p>
<div class="language-zsh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-zsh 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">$PATH/metrical/examples/camera_lidar/camera_lidar_runner.sh</span><br></span></code></pre></div></div>
<p><strong>Visualization</strong></p>
<p>To visualize the results, install Rerun via <code>pip</code> and launch the Rerun server in a separate terminal tab.</p>
<p>Then, run the following command to display calibration results in <code>display mode</code> and view the data in real time:</p>
<div class="language-zsh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-zsh 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">metrical display /datasets/examples/camera_lidar/observations /datasets/examples/camera_lidar/results.json</span><br></span></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="correction" src="https://www.reduct.store/assets/images/correction-54f2d6dfea7e66187f0293e1abbe6355.png" width="701" height="646" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-results">Understanding Results<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#understanding-results" class="hash-link" aria-label="Direct link to Understanding Results" title="Direct link to Understanding Results" translate="no">​</a></h3>
<p>During calibration, MetriCal produces charts and diagnostics that show the quality of the process and highlight areas that may need improvement.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-inputs-di-section">Data Inputs (DI Section)<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#data-inputs-di-section" class="hash-link" aria-label="Direct link to Data Inputs (DI Section)" title="Direct link to Data Inputs (DI Section)" translate="no">​</a></h4>
<p>The Data Inputs section provides an overview of the input data and ensures that the dataset is appropriate for a successful calibration.</p>
<p><strong>Key Metrics:</strong></p>
<ul>
<li class=""><strong>Calibration Inputs (DI-1):</strong> Displays basic configuration parameters.</li>
</ul>
<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">+--------------------------------------+----------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| Calibration Parameter                | Value    |</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">| MetriCal Version                     | 13.2.1   |</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">| Optimization Profile                 | Standard |</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">| Camera Motion Threshold              | Disabled |</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">| Lidar Motion Threshold               | Disabled |</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">| Preserve Input Constraints           | Disabled |</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">| Object Relative Extrinsics Inference | Enabled  |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+--------------------------------------+----------+</span><br></span></code></pre></div></div>
<ul>
<li class=""><strong>Object Space Descriptions(DI-2):</strong> Describes the calibration targets (object spaces).</li>
</ul>
<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">+-------------+-------------------------+---------------------------------------+------------------------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| Type        | UUID                    | Detector                              | Variance               |</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">| Circle      | 34e6df7b...45d796bf     | - 0.6m radius                         | 1e-8, 1e-8, 1e-8       |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             |                         | - 0.375m x offset                     |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             | Mutual Group A          | - 0.375m y offset                     |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             | |-- 24e6df7b...45d796bf | - 0m z offset                         |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             |                         | - 0.05m reflective tape width         |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             |                         | - Detect interior points: true        |                        |</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">| Markerboard | 24e6df7b...45d796bf     | - 7x7 grid                            | 0.0002, 0.0002, 0.0002 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             |                         | - 0.097m markers                      |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             | Mutual Group A          | - 0.125m checkers (aka solid squares) |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             | |-- 34e6df7b...45d796bf | - Dictionary: Aruco4x4_1000           |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             |                         | - Marker IDs start at 0               |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|             |                         | - Top-left corner is a Marker         |                        |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+-------------+-------------------------+---------------------------------------+------------------------+</span><br></span></code></pre></div></div>
<ul>
<li class="">
<p><strong>Processed Observation Count (DI-3):</strong> Shows how many observations were processed from the dataset.</p>
<p>If you observe that a significant number of detections were filtered out, verify the quality of the input data or settings (e.g., sensor noise).</p>
</li>
</ul>
<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">█ DI-3 █ Processed Observation Count</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">| Component                        | # read | # with detections | # after quality filter | # after motion filter |</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">| infra1_image_rect_raw (f7df04cc) |    283 |               276 |                    273 |                   273 |</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">| infra2_image_rect_raw (34ed8934) |    284 |               282 |                    278 |                   278 |</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">|      velodyne_points1 (38140838) |   2750 |              2026 |                   2026 |                  2026 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------------------------------+--------+-------------------+------------------------+-----------------------+</span><br></span></code></pre></div></div>
<ul>
<li class="">
<p><strong>Camera FOV Coverage (DI-4):</strong> Displays how well the calibration data covers the field of view (FOV) of each camera. Ideal coverage is characterized by minimal red cells, which represent areas without detected features.</p>
<p>For FOV coverage, ensure that the cameras are capturing data from all parts of the scene. Gaps in coverage can lead to poor calibration, especially for intrinsic parameters.</p>
</li>
</ul>
<p><img decoding="async" loading="lazy" alt="DI-4" src="https://www.reduct.store/assets/images/DI-4-d180be0f0b6a6559ecf3b39feac4a876.png" width="761" height="768" class="img_ev3q"></p>
<ul>
<li class=""><strong>Detection Timeline(DI-5):</strong> Displays when detections occurred across the dataset timeline. Each row corresponds to a different sensor, making it easier to check synchronization.</li>
</ul>
<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">+----------------------------------+-------------------------------------------------------------------------------------------------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|            Components            |          Detection Timeline (x axis is seconds elapsed since first observation)                 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |          Every point on the timeline represents an observation with detected features.          |</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">|                                  | ⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 4.0 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| infra1_image_rect_raw (f7df04cc) | ⠄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| infra2_image_rect_raw (34ed8934) | ⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠁⠉⠉⠉⠁⠉⠀⠉⠉⠉⠉⠁⠁⠉⠀⠈⠉⠉⠉⠉⠉⠈⠉⠁⠀⠀⠈⠁⠈⠈⠉⠉⠉⠁⠁⠀⠉⠀⠈⠁⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠈⠈⠀⠉⠉⠉⠁⠉⠉⠈⠉⠉⠈⠉⠉⠈⠉⠁⠉⠉⠈⠉⠉⠀⠉⠁     |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| velodyne_points1 (38140838)      | ⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀     |</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">|                                  | ⡉⠀⠀⠈⠀⠉⠈⠈⠀⠀⠈⠁⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠈⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠁⠉⠁     |</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">|                                  | ⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁⠈⠀⠁ 0.0 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  | 0.0                                                                                  269.1      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------------------------------+-------------------------------------------------------------------------------------------------+</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="camera-modeling-cm-section">Camera Modeling (CM Section)<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#camera-modeling-cm-section" class="hash-link" aria-label="Direct link to Camera Modeling (CM Section)" title="Direct link to Camera Modeling (CM Section)" translate="no">​</a></h4>
<p>This section shows how well the camera models fit the actual calibration data — that is, how accurately the system understood the camera’s behavior based on the collected data.</p>
<p><strong>Key Metrics:</strong></p>
<ul>
<li class=""><strong>Binned Reprojection Errors (CM-1):</strong> A heatmap showing reprojection errors across the camera’s FOV. If certain areas show high error (orange or red), it could indicate problems with the camera model or lens distortion that isn't being captured correctly.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="CM-1" src="https://www.reduct.store/assets/images/CM-1-cfeb45b8944a7a3f6937de8e2c12fbd1.png" width="758" height="696" class="img_ev3q"></p>
<ul>
<li class=""><strong>Stereo Pair Rectification Error (CM-2):</strong> For multi-camera setups, this shows the stereo rectification error between camera pairs, indicating how well the cameras are aligned for stereo vision.</li>
</ul>
<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">█ CM-2 █ Stereo Pair Rectification Error</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">| Stereo Pair                           | # Mutual Obs | RMSE  | Binned rectified error (px)                                                         |</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">| Dominant eye:  infra1_image_rect_raw  | 155          | 0.742 | ⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 3202.0 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| Secondary eye: infra2_image_rect_raw  |              |       | ⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀        |</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">|                                       |              |       | ⡇⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀        |</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">|                                       |              |       | ⠇⠸⠀⠏⠹⠒⠖⠲⠒⠖⠲⠒⠦⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠤⠀⠀ 0.0    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                       |              |       | 0.0                                                                     7.0         |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+---------------------------------------+--------------+-------+-------------------------------------------------------------------------------------+</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="extrinsics-info-ei-section">Extrinsics Info (EI Section)<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#extrinsics-info-ei-section" class="hash-link" aria-label="Direct link to Extrinsics Info (EI Section)" title="Direct link to Extrinsics Info (EI Section)" translate="no">​</a></h4>
<p>This section focuses on the spatial relationships between components in the calibration setup. Accurate extrinsic calibration ensures that the relative positions and orientations of the sensors are well understood.</p>
<p><strong>Key Metrics:</strong></p>
<ul>
<li class=""><strong>Component Extrinsics Errors (EI-1):</strong> Displays the extrinsic errors between each pair of components. If the errors are large, check whether all components are positioned and oriented correctly.</li>
</ul>
<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">█ EI-1 █ Component Extrinsics Errors</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">| Weighted Component Relative Extrinsic RMSE | X (m)    | Y (m)    | Z (m)    | Roll (°) | Pitch (°) | Yaw (°) |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| Rotation is Euler XYZ ext                  |          |          |          |          |           |         |</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">| To: infra1_image_rect_raw (f7df04cc),      | 2.254e-3 | 1.802e-3 | 3.780e-3 |    0.077 |     0.100 |   0.148 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|    From: infra2_image_rect_raw (34ed8934)  |          |          |          |          |           |         |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+--------------------------------------------+----------+----------+----------+----------+-----------+---------+</span><br></span></code></pre></div></div>
<ul>
<li class="">
<p><strong>IMU Preintegration Errors (EI-2):</strong> Displays a summary of all IMU preintegration errors from the system. In this example, IMUs were not calibrated.</p>
</li>
<li class="">
<p><strong>Observed Camera Range of Motion (EI-3):</strong> Shows how much motion was observed for each camera during the data collection. Sufficient motion is necessary to avoid projective compensation errors.</p>
</li>
</ul>
<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">█ EI-3 █ Observed Camera Range of Motion</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">| Camera                           | Z (m)  | Horizontal angle (°) | Vertical angle (°) |</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">| infra1_image_rect_raw (f7df04cc) | 6.308  | 127.081              | 63.801             |</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">| infra2_image_rect_raw (34ed8934) | 6.434  | 144.606              | 126.280            |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------------------------------+--------+----------------------+--------------------+</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="calibrated-plex-cp-section">Calibrated Plex (CP Section)<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#calibrated-plex-cp-section" class="hash-link" aria-label="Direct link to Calibrated Plex (CP Section)" title="Direct link to Calibrated Plex (CP Section)" translate="no">​</a></h4>
<p>This section displays the final results of the calibration, including the intrinsic and extrinsic parameters that can be used for updating the system configuration.</p>
<p><strong>Key Metrics:</strong></p>
<ul>
<li class=""><strong>Camera Metrics (CP-1):</strong> Contains the intrinsic parameters of each camera, such as focal length, principal point, and distortion parameters. Standard deviations indicate the uncertainty of each parameter.</li>
</ul>
<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">█ CP-1 █ Camera Metrics</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">| Camera                           | Specs                   | Projection Model                        | Distortion Model                     |</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">| infra1_image_rect_raw (f7df04cc) |  width (px)        848  |  Pinhole                                |      OpenCV Distortion               |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |  height (px)       480  |  f (px)      431.914 ±      0.224 (1σ)  |  k1   -1.574e-3 ±   8.715e-4 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |  pixel pitch (um)  1    |  cx (px)     421.938 ±      0.395 (1σ)  |  k2       0.011 ±   1.876e-3 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |                         |  cy (px)     230.592 ±      0.465 (1σ)  |  k3   -6.171e-3 ±   1.241e-3 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |                         |                                         |  p1   -2.037e-3 ±   2.680e-4 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |                         |                                         |  p2   -1.479e-3 ±   2.443e-4 (1σ)    |</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">| infra2_image_rect_raw (34ed8934) |  width (px)        848  |  Pinhole                                |      OpenCV Distortion               |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |  height (px)       480  |  f (px)      429.085 ±      0.215 (1σ)  |  k1   -3.050e-4 ±   8.638e-4 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |  pixel pitch (um)  1    |  cx (px)     421.203 ±      0.387 (1σ)  |  k2    1.517e-3 ±   1.809e-3 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |                         |  cy (px)     230.821 ±      0.436 (1σ)  |  k3   -6.881e-4 ±   1.170e-3 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |                         |                                         |  p1   -1.887e-3 ±   2.510e-4 (1σ)    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                                  |                         |                                         |  p2   -1.630e-3 ±   2.358e-4 (1σ)    |</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>
<ul>
<li class="">
<p><strong>Optimized IMU Metrics (CP-2):</strong> In this example, IMUs were not calibrated.</p>
</li>
<li class="">
<p><strong>Calibrated Extrinsics (CP-3):</strong> Shows the minimum spanning tree of spatial constraints in the plex, highlighting only the most critical constraints needed to keep the structure intact.</p>
<p>Large differences between the initial and final values may indicate that the starting guesses for positions or orientations were incorrect, highlighting the extent of corrections made during the calibration process.</p>
</li>
</ul>
<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">█ CP-3 █  Calibrated Extrinsics</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">| Final Extrinsics          | Subplex ID | Translation (m) | Diff from input (mm) | Rotation (°)  | Diff from input (°) |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| 'To' component is Origin  |            |                 |                      |               |                     |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| Rotation is Euler XYZ ext |            |                 |                      |               |                     |</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">| To: infra1_image_rect_raw | A          | X: 0.360        | ΔX: 359.862          | Roll: -85.208 | ΔRoll: -85.208      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|     f7df04cc, RDF         |            | Y: 0.083        | ΔY: 82.722           | Pitch: -2.812 | ΔPitch: -2.812      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| From: velodyne_points1    |            | Z: 0.048        | ΔZ: 48.451           | Yaw: 171.579  | ΔYaw: 171.579       |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|     38140838, Unknown     |            |                 |                      |               |                     |</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">| To: infra2_image_rect_raw | A          | X: 0.319        | ΔX: 318.513          | Roll: -85.317 | ΔRoll: -85.317      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|     34ed8934, RDF         |            | Y: 0.086        | ΔY: 85.533           | Pitch: -2.717 | ΔPitch: -2.717      |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| From: velodyne_points1    |            | Z: 0.033        | ΔZ: 33.454           | Yaw: 171.470  | ΔYaw: 171.470       |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|     38140838, Unknown     |            |                 |                      |               |                     |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+---------------------------+------------+-----------------+----------------------+---------------+---------------------+</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary-statistics-ss-section">Summary Statistics (SS Section)<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#summary-statistics-ss-section" class="hash-link" aria-label="Direct link to Summary Statistics (SS Section)" title="Direct link to Summary Statistics (SS Section)" translate="no">​</a></h4>
<p>This section provides a high-level overview of the optimization process and the overall calibration quality.</p>
<ul>
<li class=""><strong>Optimization Summary Statistics (SS-1):</strong> Includes overall reprojection error and posterior variance, which indicates the calibration’s uncertainty.</li>
</ul>
<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">█ SS-1 █ Optimization Summary Statistics</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">| Optimized Object RMSE, | 0.206 px |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| based on all cameras   |          |</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">| Posterior Variance     | 0.731    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+------------------------+----------+</span><br></span></code></pre></div></div>
<ul>
<li class="">
<p><strong>Camera Summary Statistics (SS-2):</strong> Summarizes the reprojection errors for each camera. An RMSE under 0.5 pixels is typically acceptable, and under 0.2 pixels is excellent.</p>
<blockquote>
<p>Use <code>pixel_pitch</code> in the <code>Plex API</code> to fairly compare RMSEs if your cameras have different pixel sizes.</p>
</blockquote>
</li>
</ul>
<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">█ SS-2 █ Camera Summary Statistics</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">| Camera                           | Reproj. RMSE, outliers downweighted (px) |</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">| infra1_image_rect_raw (f7df04cc) | 0.209 px                                 |</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">| infra2_image_rect_raw (34ed8934) | 0.204 px                                 |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+----------------------------------+------------------------------------------+</span><br></span></code></pre></div></div>
<ul>
<li class=""><strong>LiDAR Summary Statistics (SS-3):</strong> Shows the RMSE of various residual metrics: circle misalignment, interior points to plane error, paired 3D point error, and paired plane normal error.</li>
</ul>
<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">█ SS-3 █ LiDAR Summary Statistics</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">| LiDAR                       | Circle misalignment RMSE with | Circle edge misalignment RMSE with | Interior point RMSE with | Plane normal difference, |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                             | all cameras, outliers         | all cameras, outliers              | all cameras, outliers    | lidar-lidar, outliers    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">|                             | downweighted (m)              | downweighted (m)                   | downweighted (m)         | downweighted (deg)       |</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">| velodyne_points1 (38140838) | 0.020 m                       | 0.028 m                            | 0.018 m                  | (n/a)                    |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+-----------------------------+-------------------------------+------------------------------------+--------------------------+--------------------------+</span><br></span></code></pre></div></div>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-diagnostics">Data Diagnostics<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#data-diagnostics" class="hash-link" aria-label="Direct link to Data Diagnostics" title="Direct link to Data Diagnostics" translate="no">​</a></h4>
<p>This section highlights potential issues with the calibration setup, data, or process.</p>
<p><img decoding="async" loading="lazy" alt="diagnostics" src="https://www.reduct.store/assets/images/diagnostics-b0ab4dbd185b7c3947c4504491d5aac2.png" width="748" height="387" class="img_ev3q"></p>
<p><strong>High-Risk Diagnostics:</strong> Critical issues such as insufficient camera motion or missing required components must be addressed for successful calibration.</p>
<p><strong>Medium and Low-Risk Diagnostics:</strong> Less critical issues, such as poor feature coverage, should still be monitored and corrected when possible to improve calibration quality.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="output-summary">Output Summary<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#output-summary" class="hash-link" aria-label="Direct link to Output Summary" title="Direct link to Output Summary" translate="no">​</a></h4>
<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">+------------------------+------------------------------------------------------------+</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">| Results JSON           | /datasets/camera_lidar/results.json                        |</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">| Calibrated Plex        | Run `jq .plex [results.json] &gt; optimized_plex.json`        |</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">| Optimized Object Space | Run `jq .object_space [results.json] &gt; optimized_obj.json` |</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">| Cached Detections JSON | /datasets/camera_lidar/observations.detections.json        |</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">| Report Path            | /datasets/camera_lidar/report.html                         |</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">+------------------------+------------------------------------------------------------+</span><br></span></code></pre></div></div>
<p>The calibration process generates several output files, located in the <code>$PATH/metrical/examples/camera_lidar</code> directory.</p>
<ul>
<li class="">
<p><strong>init_plex.json:</strong> A raw input plex from the <code>init mode</code>.</p>
</li>
<li class="">
<p><strong>observations.detections.json:</strong> Cached detections for faster reruns in <code>calibrate mode</code>.</p>
</li>
<li class="">
<p><strong>results.json:</strong> The main output file, containing calibrated plex and object space.</p>
<ul>
<li class="">
<p>Extract the optimized plex:</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">jq .plex $PATH/metrical/examples/camera_lidar/results.json &gt; $PATH/metrical/examples/camera_lidar/optimized_plex.json</span><br></span></code></pre></div></div>
</li>
<li class="">
<p>Extract the optimized object space:</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">jq .object_space $PATH/metrical/examples/camera_lidar/results.json &gt; $PATH/metrical/examples/camera_lidar/optimized_obj.json</span><br></span></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong>report.html:</strong> An HTML report summarizing calibration performance visually.</p>
</li>
<li class="">
<p><strong>results_urdf.xml:</strong> A ROS-compatible URDF file that describes the spatial relationships between the two calibrated cameras and the LiDAR, enabling tools like <code>robot_state_publisher</code> to publish real-time TF transforms based on these relationships.</p>
<p>This is especially useful when multiple sensors are calibrated together, as the URDF allows you to see the pose of one sensor relative to another within a unified coordinate frame. Such a structure is essential for precise sensor fusion, including depth-aware perception and advanced tasks like object localization and 3D scene understanding.</p>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/metrical-calibrate-camera-and-lidar#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>MetriCal simplifies multimodal sensor calibration by offering a fully scriptable, CLI-based workflow with detailed diagnostics and seamless ROS integration. One of the key takeaways from working with this tool is that successful calibration depends heavily on the quality of the captured data. Carefully choosing calibration targets, ensuring sufficient sensor motion, and achieving full field-of-view coverage all have a major impact on the results. For those just starting out, prioritizing high-quality data capture and closely following the recommended guidelines is essential for obtaining reliable outcomes.</p>
<hr>
<p>We hope this tutorial provided a clear and practical introduction to using MetriCal for multi-sensor calibration. If you have any questions or comments, feel free to use the <a href="https://community.reduct.store/signup" target="_blank" rel="noopener noreferrer" class=""><strong>ReductStore Community Forum</strong></a>.</p>]]></content:encoded>
            <category>tutorials</category>
            <category>robotics</category>
            <category>ROS</category>
        </item>
        <item>
            <title><![CDATA[How to Choose the Right MQTT Database]]></title>
            <link>https://www.reduct.store/blog/advice/database/mqtt-data-storage</link>
            <guid>https://www.reduct.store/blog/advice/database/mqtt-data-storage</guid>
            <pubDate>Thu, 08 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A guide to select the most suitable database for IoT or IIoT projects using MQTT. This article explores the factors to consider when choosing a database and the solutions available for storing MQTT data.]]></description>
            <content:encoded><![CDATA[<p><img decoding="async" loading="lazy" alt="MQTT Data Storage" src="https://www.reduct.store/assets/images/mqtt-data-storage-42053a68fed390977fb606d89f3c7cfe.png" width="1584" height="888" class="img_ev3q"></p>
<p>At a previous company, we used MQTT to send industrial data, such as vibration readings, images and log files. However, maintaining a history of this data proved challenging. Initially, we used a combination of a time-series database and an object store, but we struggled to ingest blob data quickly enough, and the system was difficult to maintain.</p>
<p>To help you avoid a similar experience, this article will recommend the most suitable database for your IoT or Industrial IoT (IIoT) project. We will look at different ways of storing data from IoT devices that communicate with each other via MQTT.</p>
<p>MQTT stands for <em>Message Queuing Telemetry Transport</em> and is a lightweight messaging protocol designed to be efficient, reliable, and scalable, making it ideal for collecting and transmitting data from sensors in real time.</p>
<p>Why is this important when choosing a database?</p>
<p>Well, MQTT is format-agnostic, but it works in a specific way. We should therefore be aware of its architecture, how it works, and its limitations to make the right choice. This is what this article is about, we will try to cut through the fog and explore some key factors to consider when selecting the right option.</p>
<p>Let's get started!</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tldr">TL;DR<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#tldr" class="hash-link" aria-label="Direct link to TL;DR" title="Direct link to TL;DR" translate="no">​</a></h2>
<table><thead><tr><th>Project Requirement</th><th>Recommended Database Types</th><th>Examples</th></tr></thead><tbody><tr><td>Real-time sensor data with limited record size</td><td>Time-series</td><td>InfluxDB, TimescaleDB, Prometheus, QuestDB</td></tr><tr><td>High frequency sensor, MQTT payload, or camera data</td><td>Time-series object storage</td><td>ReductStore</td></tr><tr><td>Large volumes of unstructured or semi-structured data</td><td>NoSQL</td><td>MongoDB, Amazon DynamoDB, Apache Cassandra</td></tr><tr><td>Large amounts of blob data (multimedia files)</td><td>Blob storage</td><td>Azure Blob Storage, Google Cloud Storage, Amazon, MinIO S3</td></tr><tr><td>Strong data consistency and complex relationships</td><td>Relational</td><td>MySQL, PostgreSQL, Oracle Database</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="brief-explanation-of-mqtt-and-its-use-in-iot-projects">Brief explanation of MQTT and its use in IoT projects<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#brief-explanation-of-mqtt-and-its-use-in-iot-projects" class="hash-link" aria-label="Direct link to Brief explanation of MQTT and its use in IoT projects" title="Direct link to Brief explanation of MQTT and its use in IoT projects" translate="no">​</a></h2>
<p>MQTT is a publish-subscribe messaging protocol that allows devices to send and receive messages over a network. It is not a database, but a transport mechanism. MQTT does not store data itself. It is particularly well-suited for IoT projects due to its lightweight nature, low power consumption, and support for unreliable networks. Because MQTT is just a protocol, it can be used with any programming language that has a client library (e.g. Python, JavaScript, Java, C/C++, and so on).</p>
<p>If you are new to MQTT, you can check out our tutorials <a class="" href="https://www.reduct.store/blog/tutorials/iot/how-to-keep-mqtt-data-node"><strong>Keeping MQTT Data in Node.js</strong></a>, <a class="" href="https://www.reduct.store/blog/tutorials/iot/how-to-keep-mqtt-data-python"><strong>Keeping MQTT Data in Python</strong></a>, or <a class="" href="https://www.reduct.store/blog/tutorials/iot/how-to-keep-mqtt-data-rust"><strong>Keeping MQTT Data History with Rust</strong></a> to get started with MQTT and learn how to subscribe to, publish, and store MQTT data.</p>
<p>In an MQTT-based system, devices (known as publishers) publish messages to a central broker, which then distributes these messages to other devices (known as subscribers) that have subscribed to specific topics. The subscriber can then process the message and take appropriate action. For example, a sensor may publish a message containing its current temperature, which is then received by a subscriber that has subscribed to the topic <code>temperature</code>.</p>
<p><img decoding="async" loading="lazy" alt="Example of Pub/Sub architecture" src="https://www.reduct.store/assets/images/example-pub-sub-428c732a81150f4641a351869963fbe9.webp" title="Example of Pub/Sub architecture" width="1800" height="619" class="img_ev3q"><small>Example of Pub/Sub architecture (image by authors)</small></p>
<p>MQTT is easy to use and keeps subscription and publishing tasks separate. To get info from a device, you don't need to know all its details like its address or password. You just need to connect to a <em>middleman</em> called a broker and know the topic's name. This pattern offers many advantages for IoT over other protocols, such as HTTP, which requires servers and clients to be aware of each other's details and communicate directly.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data-storage-for-mqtt">Data storage for MQTT<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#data-storage-for-mqtt" class="hash-link" aria-label="Direct link to Data storage for MQTT" title="Direct link to Data storage for MQTT" translate="no">​</a></h3>
<p>An important aspect to consider is the real-time aspect of IoT projects. Note that MQTT messages are not directly time-stamped, but you will often set the time information in the payload. This is done by the publisher, and you may decide to use the time you want. For example, one can transmit the time of the edge device when the message is created or the time of the sensor when the data is collected.</p>
<blockquote>
<p>A time-series database is an obvious choice to store data in chronological order as it allows for efficient storage and retrieval of messages indexed by time.</p>
</blockquote>
<p>Another aspect to consider is the type of data that can be transmitted via MQTT (which is pretty much anything). The MQTT protocol is format-agnostic, meaning that it does not specify how data should be formatted. This allows for a wide variety of data types to be transmitted via MQTT, including text, images, or audio. The only requirements are that the data must be formatted either as a string (UTF-8 encoded) or as a byte stream, and that the payload size does not exceed 256MB-which is pretty large, like a 2K video for 35 seconds.</p>
<p>In the context of computer vision, MQTT can be used to transmit images captured by cameras. This is particularly useful in surveillance systems, smart homes, or industrial applications where cameras are used to monitor and analyze visual data. The images can be transmitted as binary streams and stored in a database for further processing, analysis, or review.
If you are interested in learning more about storing data for computer vision applications, you can check out our tutorial <a class="" href="https://www.reduct.store/blog/computer-vision-applications"><strong>3 Ways to Store Data for Computer Vision Applications</strong></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="factors-to-consider-when-choosing-a-mqtt-data-store">Factors to consider when choosing a MQTT data store<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#factors-to-consider-when-choosing-a-mqtt-data-store" class="hash-link" aria-label="Direct link to Factors to consider when choosing a MQTT data store" title="Direct link to Factors to consider when choosing a MQTT data store" translate="no">​</a></h2>
<p>When selecting an MQTT data storage solution, there are several factors to consider:</p>
<ul>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#performance-speed-and-efficiency-in-processing-and-retrieving-data" class=""><strong>Performance</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#scalability-ability-to-handle-large-amounts-of-data-and-increasing-workload" class=""><strong>Scalability</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#reliability-ensuring-data-integrity-and-availability-without-loss-or-corruption" class=""><strong>Reliability</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#security-protection-against-unauthorized-access-attacks-and-breaches" class=""><strong>Security</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#compatibility-integration-with-other-systems-protocols-or-analytics-tools" class=""><strong>Compatibility</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#cost-affordability-and-cost-effectiveness" class=""><strong>Cost</strong></a></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="performance-speed-and-efficiency-in-processing-and-retrieving-data">Performance: speed and efficiency in processing and retrieving data<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#performance-speed-and-efficiency-in-processing-and-retrieving-data" class="hash-link" aria-label="Direct link to Performance: speed and efficiency in processing and retrieving data" title="Direct link to Performance: speed and efficiency in processing and retrieving data" translate="no">​</a></h3>
<p>Performance is an obvious one. The storage should be able to process and retrieve data efficiently and quickly with a low response time which implies that the database's read and write speeds, along with the network latency, are minimized.
If you are intested in learning more about how we benchmark databases in a real-world scenario, you can check out our article about <a class="" href="https://www.reduct.store/blog/comparisons/computer-vision/iot/performance-comparison-reductstore-vs-minio"><strong>ReductStore vs. MinIO &amp; InfluxDB</strong></a>.</p>
<p>When storing pictures from a camera, or high-frequency sensor data (e.g. accelerometers) performance becomes even more critical. Cameras often generate high-resolution images that can be pretty large, and an average accelerometer can easily produce around 4,000 measurements per second (4kHz).</p>
<p>For example, let's consider a smart surveillance system that uses MQTT to transmit images captured by security cameras. In this scenario, the storage solution needs to be able to handle a continuous stream of time-stamped images in real time. This requires not only fast write speeds but also efficient compression techniques to reduce the size of each image without compromising its quality.</p>
<p>When it comes to retrieving these pictures for analysis or review purposes, speed is crucial. You should be able to easily access and fetch images from any specified time interval from the database installed on the edge device and from the database deployed on the cloud.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="scalability-ability-to-handle-large-amounts-of-data-and-increasing-workload">Scalability: ability to handle large amounts of data and increasing workload<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#scalability-ability-to-handle-large-amounts-of-data-and-increasing-workload" class="hash-link" aria-label="Direct link to Scalability: ability to handle large amounts of data and increasing workload" title="Direct link to Scalability: ability to handle large amounts of data and increasing workload" translate="no">​</a></h3>
<p>While this might be expected for cloud databases, how does it apply to edge databases?</p>
<blockquote>
<p>Since MQTT applications frequently produce a significant amount of data from various devices and sensors, the edge storage should be capable of managing this high throughput and having a solid reduction strategy (e.g. downsampling, retention policies, etc.), and replication methods.</p>
</blockquote>
<p>For additional context, you can check out our article about <a class="" href="https://www.reduct.store/blog/edge-computing/data-reduction-on-edge"><strong>Data Reduction on Edge</strong></a> to learn more about how to reduce data on the edge device and optimize storage space.</p>
<p>A scalable system should also include considerations such as handling increasing workloads, accommodating additional devices or sensors, and supporting horizontal scaling by adding more edge devices or storage nodes when needed.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="reliability-ensuring-data-integrity-and-availability-without-loss-or-corruption">Reliability: ensuring data integrity and availability without loss or corruption<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#reliability-ensuring-data-integrity-and-availability-without-loss-or-corruption" class="hash-link" aria-label="Direct link to Reliability: ensuring data integrity and availability without loss or corruption" title="Direct link to Reliability: ensuring data integrity and availability without loss or corruption" translate="no">​</a></h3>
<p>To guarantee the reliability and accessibility of the stored data without any loss or damage, it is crucial to select a storage solution that incorporates appropriate measures. These measures should be capable of addressing possible failures, such as power outages or network interruptions, and safeguarding against data loss or compromise.</p>
<p>One popular solution is to replicate data. Replication involves creating duplicate copies of the data and storing them in multiple locations or servers. This redundancy ensures that even if one server fails the data can still be accessed from another one, minimizing the risk of data loss or corruption.</p>
<p>MQTT brokers like Mosquitto support persistence by writing session data and queued messages to disk. This allows recovery after a restart but isn't suitable for long-term storage or querying. For that, you'll need to forward the data to an external database.</p>
<p>You can check out our guide about <a class="" href="https://www.reduct.store/docs/guides/data-replication"><strong>Data Replication</strong></a> to learn more about the technique used by ReductStore to replicate data across multiple nodes and ensure high availability and data integrity.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="security-protection-against-unauthorized-access-attacks-and-breaches">Security: protection against unauthorized access, attacks, and breaches<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#security-protection-against-unauthorized-access-attacks-and-breaches" class="hash-link" aria-label="Direct link to Security: protection against unauthorized access, attacks, and breaches" title="Direct link to Security: protection against unauthorized access, attacks, and breaches" translate="no">​</a></h3>
<p>MQTT data can contain sensitive information such as device telemetry, user behavior, or images, making it essential to protect against unauthorized access, attacks, and breaches.</p>
<p>The open nature of many databases allows them to be more transparent about vulnerabilities and fixes because the code is available for anyone to review. This means that the community can quickly identify and address potential security issues.</p>
<p>Moreover, open-source databases often have a large user base and community support. This means that many eyes are looking out for potential threats or vulnerabilities, leading to quicker detection and resolution of any issues.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="compatibility-integration-with-other-systems-protocols-or-analytics-tools">Compatibility: integration with other systems, protocols, or analytics tools<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#compatibility-integration-with-other-systems-protocols-or-analytics-tools" class="hash-link" aria-label="Direct link to Compatibility: integration with other systems, protocols, or analytics tools" title="Direct link to Compatibility: integration with other systems, protocols, or analytics tools" translate="no">​</a></h3>
<p>When considering the compatibility factor, you can think about the other systems or protocols that your database needs to integrate with.</p>
<p>For example, if you are also using a cloud platform like AWS or Azure, you will want to ensure that your chosen edge database can integrate with these platforms.</p>
<p>In addition, if you plan on performing analytics on your MQTT data, you will need a solution that can easily integrate with popular tools such as Grafana, Apache Kafka, or Apache Spark.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cost-affordability-and-cost-effectiveness">Cost: affordability and cost-effectiveness<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#cost-affordability-and-cost-effectiveness" class="hash-link" aria-label="Direct link to Cost: affordability and cost-effectiveness" title="Direct link to Cost: affordability and cost-effectiveness" translate="no">​</a></h3>
<p>Cost is another important factor to consider when selecting a storage solution. Overall, the cost of a database can be broken down into two categories:</p>
<ul>
<li class="">Upfront costs include the initial purchase price of the database, along with any additional hardware or software required to run it.</li>
<li class="">Ongoing costs include maintenance fees, support fees, and any other recurring expenses associated with using the database.</li>
</ul>
<p>It is essential to consider both the upfront and ongoing costs when evaluating different storage solutions to ensure that the chosen database is affordable and cost-effective in the long run.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="types-of-database-options-available-for-mqtt-data-storage">Types of database options available for MQTT data storage<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#types-of-database-options-available-for-mqtt-data-storage" class="hash-link" aria-label="Direct link to Types of database options available for MQTT data storage" title="Direct link to Types of database options available for MQTT data storage" translate="no">​</a></h2>
<p>There are several types of popular database options available for IoT, such as time-series, NoSQL, or relational SQL databases.</p>
<ul>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#time-series-databases" class=""><strong>Time-series databases</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#nosql-databases" class=""><strong>NoSQL databases</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#blob-storage" class=""><strong>Blob storage</strong></a></li>
<li class=""><a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#relational-databases" class=""><strong>Relational databases</strong></a></li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="time-series-databases">Time-series databases<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#time-series-databases" class="hash-link" aria-label="Direct link to Time-series databases" title="Direct link to Time-series databases" translate="no">​</a></h3>
<p>Time-series databases are specifically designed to handle time-stamped data, making them an ideal choice for storing MQTT data.</p>
<blockquote>
<p>Time-series databases optimize storage and retrieval performance for time-series data by efficiently indexing the data.</p>
</blockquote>
<p>They provide built-in functions and query capabilities that are well-suited for analyzing and visualizing information stored in chronological order. They often offer features like downsampling and retention policies to manage large volumes of historical data efficiently.</p>
<p>Some popular time-series databases that are commonly used for storing MQTT data include:</p>
<ul>
<li class=""><strong>ReductStore</strong>: a high-performance storage and streaming solution for unstructured time-series data. It stores blobs with timestamps and labels (key-value pairs) making it ideal for MQTT payloads like images, audio, vibration data or logs.</li>
<li class=""><strong>QuestDB</strong>: a high-performance time-series database with fast ingestion and SQL support. Best for structured, high-frequency sensor data. It has strict limitations on blob size and isn't suitable for storing images or other large binary payloads.</li>
<li class=""><strong>InfluxDB</strong>: an open-source database that provides high-performance storage and retrieval of time-stamped data with a SQL-like query language. You can store numbers (integer or floating point values), boolean values, or text strings-with a limit of 64KB-that's 4000 times less than MQTT's payload capacity.</li>
<li class=""><strong>TimescaleDB</strong>: an extension of PostgreSQL that adds time-series capabilities to the relational database model. It provides scalability and performance optimizations for handling large volumes of time-stamped data while maintaining the flexibility of a relational database. If you are interested in learning more about the differences between ReductStore and TimescaleDB, you can check out our article <a class="" href="https://www.reduct.store/blog/comparisons/iot/reductstore-vs-timescaledb"><strong>ReductStore vs. TimescaleDB</strong></a>.</li>
<li class=""><strong>Prometheus</strong>: another widely used open-source monitoring system and time-series database designed for collecting metrics from various sources. It offers powerful querying capabilities, alerting functionalities, and easy integration with Grafana (an open-source visualization and monitoring tool).</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="nosql-databases">NoSQL databases<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#nosql-databases" class="hash-link" aria-label="Direct link to NoSQL databases" title="Direct link to NoSQL databases" translate="no">​</a></h3>
<p>NoSQL (which stands for <em>not only SQL</em>) represents a broad category of databases that are not limited to traditional <a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#relational-databases" class=""><strong>Relational databases</strong></a>. Those databases are often used for storing MQTT data due to their flexibility and scalability. Some popular NoSQL databases commonly used for storing MQTT data include:</p>
<ul>
<li class=""><strong>MongoDB</strong>: a document-oriented database with high scalability and flexibility for handling unstructured or semi-structured data. It offers rich querying capabilities, indexing options, and support for distributed data storage with a technique called <em>sharding</em>. Some of the differences between ReductStore and MongoDB are discussed in our article <a class="" href="https://www.reduct.store/blog/comparisons/iot/reductstore-vs-mongodb"><strong>ReductStore vs. MongoDB</strong></a>.</li>
<li class=""><strong>Apache Cassandra</strong>: a highly scalable and fault-tolerant database that can handle large volumes of data across multiple nodes or clusters. It provides fast read and write operations, making it suitable for real-time analytics or applications with high throughput requirements.</li>
<li class=""><strong>Amazon DynamoDB</strong>: a NoSQL database which is fully managed. Meaning that it offers automatic scaling, low latency access to data, durability guarantees, and integration with other AWS services.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="blob-storage">Blob storage<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#blob-storage" class="hash-link" aria-label="Direct link to Blob storage" title="Direct link to Blob storage" translate="no">​</a></h3>
<p>Blob stands for <em>Binary Large Object</em> and can be stored in specific services specialized in storing unstructured binary data such as files, images, videos, and backups.</p>
<p>Blob Storage is not typically considered part of the NoSQL database category, as it does not provide advanced querying capabilities or data modeling options in traditional NoSQL databases.</p>
<p>Some popular blob storage options that can be used for storing unstructured binary data include MinIO, Google Cloud Storage, Azure Blob Storage, and Amazon S3 (Simple Storage Service).</p>
<p>If you need to install a blob storage on your edge device, you should consider MinIO. It is an open-source, high-performance object storage system designed to store any kind of unstructured data–usually heavy in memory–such as photos, videos, backups, and container images.</p>
<p>Then, there are well-known cloud service providers:</p>
<ul>
<li class=""><strong>Azure Blob Storage</strong>: a scalable and highly available object storage service provided by Microsoft Azure. They offer various storage tiers, so you can optimize cost and performance based on your requirements. They also provides features like lifecycle management, versioning, and data encryption.</li>
<li class=""><strong>Google Cloud Storage</strong>: a globally distributed object storage service offered by Google Cloud Platform. They provide trustworthy and scalable databases for storing large amounts of blob data. They also provide a way to optimize cost and performance with different storage classes and pricing options.</li>
<li class=""><strong>Amazon S3</strong>: a widely used object storage service thanks to its global infrastructure and integration with other AWS services. They also provide features like lifecycle policies, versioning, and server-side encryption.</li>
<li class=""><strong>MinIO</strong>: an open-source object storage system that is compatible with Amazon S3 and provides high performance and scalability for storing large amounts of unstructured data. It is designed to be lightweight and easy to deploy on edge devices or in the cloud. For more information about MinIO, you can check out our article <a class="" href="https://www.reduct.store/blog/comparisons/computer-vision/iot/performance-comparison-reductstore-vs-minio"><strong>Performance Comparison: ReductStore vs. MinIO</strong></a>.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="relational-databases">Relational databases<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#relational-databases" class="hash-link" aria-label="Direct link to Relational databases" title="Direct link to Relational databases" translate="no">​</a></h3>
<p>Relational databases, such as MySQL, are another option to consider for storing MQTT data in IoT projects. These databases provide a structured and organized approach to data storage, making them suitable for projects that require strong data consistency and complex relationships between different entities.</p>
<p>Relational databases use tables with predefined schemas to store data, allowing for efficient querying and indexing. They offer features like transactions, which means that you can perform multiple operations on the database as a single unit of work, and each transaction follows a set of properties known as ACID (Atomicity, Consistency, Isolation, Durability) to ensure that the data remains consistent even if there is a failure or a crash.</p>
<p>Some popular relational database management systems (RDBMS) that are commonly used include MySQL, PostgreSQL, or Oracle Database.</p>
<ul>
<li class=""><strong>MySQL</strong>: an open-source RDBMS that is widely used in IoT projects due to its simplicity, reliability, and scalability. It offers strong data consistency and supports efficient querying using SQL.</li>
<li class=""><strong>PostgreSQL</strong>: another popular open-source RDBMS known for its robustness, extensibility, and support for advanced features like JSON data types and spatial indexing.</li>
<li class=""><strong>Oracle Database</strong>: a commercial RDBMS with a proven track record in handling multiple databases. It offers advanced security features and analytics capabilities.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://www.reduct.store/blog/advice/database/mqtt-data-storage#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>Choosing the right storage option for your project is crucial for ensuring efficient data management and analysis. Factors such as the type of data, scalability needs, and project requirements should be aligned with your choice of database.</p>
<p>For example, if your project involves collecting real-time sensor data at low frequencies from various devices spread across different locations. In this scenario, you would require a time-series database like InfluxDB, TimescaleDB, or Prometheus that can handle high volumes of time-stamped data.</p>
<p>On the other hand, if your project involves gathering real-time sensor data at a high frequency or capturing time-stamped images from a camera, you would probably need a solution such as <a class="" href="https://www.reduct.store/"><strong>ReductStore</strong></a>.</p>
<p>NoSQL databases like MongoDB, Amazon DynamoDB, or Apache Cassandra are ideal for handling large volumes of unstructured or semi-structured data with high scalability and real-time processing capabilities.</p>
<p>Blob storage options, such as MinIO, Azure Blob Storage, Google Cloud Storage, and Amazon S3, are good options for storing large amounts of blob data, including multimedia files like audio, images, or videos.</p>
<p>Finally, relational databases like MySQL, Oracle Database, or PostgreSQL suit projects requiring strong data consistency, complex relationships between entities, and advanced querying capabilities.</p>
<hr>
<p>Thanks for reading, I hope this article will help you choose the most appropriate database for your IoT project. If you have any questions or comments, please feel free to reach out.</p>]]></content:encoded>
            <category>advice</category>
            <category>database</category>
            <category>mqtt</category>
        </item>
    </channel>
</rss>