Querying ROS data with SDKs
This guide explains how to query and decode ROS data with the official ReductStore client SDKs. It expands on the SDK workflow introduced in the ROS applications guide.
This guide uses Python only as a representative example; the same workflow and ReductROS options are available with the other official SDKs.
Server-side ROS decoding requires the ReductROS extension, which is available with ReductStore Pro. The public Play server used in this guide already provides the extension.
The Play server and its reductstore token are shared demo resources. Do not
upload private data to this server or use the demo token in production.
Query and save images
The example queries filtered infrared images from the public ROS dataset, samples one image every 30 seconds, and saves the results as JPEG files. It uses the ReductROS extension to decode ROS messages and encode raw image data as JPEG.
The script performs the following steps:
- Connect to the public Play server with the
reductstoretoken and open theorionbucket. - Query the
/right_ir/rotated/image_rawentry for the previous hour. - Apply the strict range
100 < gps_z < 120to the storedgps_zlabel before decoding payloads. - Use
$each_tto keep at most one matching image every 30 seconds, reducing decoding work and network traffic. - Ask ReductROS through
#ext.ros.extractto decode each CDR image using its$schemaattachment and encode the rawdatafield as JPEG. - Base64-decode each result and save it as
orion-images/<timestamp>.jpg.
import asyncio
import base64
import json
import os
from datetime import datetime, timedelta, timezone
from pathlib import Path
from reduct import Client
SERVER_URL = 'https://play.reduct.store/'
API_TOKEN = 'reductstore'
BUCKET_NAME = 'orion'
ENTRY_NAME = '/right_ir/rotated/image_raw'
OUTPUT_DIR = Path(os.environ.get('OUTPUT_DIR', 'orion-images'))
def last_hour_query(now=None):
stop = now or datetime.now(timezone.utc)
start = stop - timedelta(hours=1)
condition = {
'$and': [
{'&gps_z': {'$gt': 100}},
{'&gps_z': {'$lt': 120}},
],
'$each_t': '30s',
'#ext': {
'ros': {
'extract': {
'encode': {
'data': 'jpeg',
}
},
}
},
}
return start, stop, condition
def save_jpeg(message, output_path):
image = message[0]
jpeg = base64.decodebytes(image['data'].encode('ascii'))
output_path.write_bytes(jpeg)
return {key: value for key, value in image.items() if key != 'data'}
async def main():
start, stop, condition = last_hour_query()
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
async with Client(SERVER_URL, api_token=API_TOKEN) as client:
bucket = await client.get_bucket(BUCKET_NAME)
print(f"Run query: {start} to {stop} condition: {json.dumps(condition)}")
async for record in bucket.query(
ENTRY_NAME,
start=start,
stop=stop,
when=condition,
):
message = json.loads((await record.read_all()).decode('utf-8'))
output_path = OUTPUT_DIR / f'{record.timestamp}.jpg'
image_info = save_jpeg(message, output_path)
print(record.entry, record.timestamp, record.labels['gps_z'])
print(f'Saved JPEG to {output_path}')
print(f'Image parameters: {json.dumps(image_info, sort_keys=True)}')
if __name__ == '__main__':
asyncio.run(main())
To run this implementation, clone the documentation repository, enter the example directory, install the Python SDK, and execute the script:
git clone https://github.com/reductstore/website.git
cd website/docs/ros/example
pip install reduct-py
python query_orion.py
See Python bucket queries for the Python API used in this implementation, comparison operators for additional label conditions, and the ReductROS examples for more query patterns.