Data Collection and Processing

In the process of sensor data collection and post-processing, exportRuntimeConfig and createSolver are two key interfaces, which are respectively responsible for the core functions of configuration export and data parsing. The following is the interface description and best practices.

1. Core Interface Description

1. exportRuntimeConfig Interface

Sensor.exportRuntimeConfig(self, save_dir="", binary=False) is used to export the sensor runtime configuration. It persists the current sensor’s runtime parameters (such as calibration data, hardware characteristics, etc.) to a specified directory, providing a configuration basis for subsequent offline data parsing.

  • Parameters:

  • save_dir (Union[str, Path]): Directory for saving configuration files, default is the current working directory.

  • binary (bool): Whether to return data in binary encrypted format (instead of saving to a file), default is False.

  • Return Value: None

  • Function: Ensure that the collected raw data can use the same sensor configuration as during collection when parsed offline, thus guaranteeing the accuracy of processing results.

2. createSolver Interface

Sensor.createSolver(runtime_path) is a factory class method. It is used to create a SensorSolver instance from a specified runtime configuration path, and this instance is a core tool for offline parsing of sensor data.

  • Parameter:

  • runtime_path (Union[str, Path]): Path pointing to the runtime configuration file (generated by exportRuntimeConfig).

  • Return Value: Returns a SensorSolver instance on success, and returns False on failure.

  • Function: Load the configuration exported during collection, calculate derived data such as depth maps and force values based on raw data, and realize offline post-processing.

2. Complete Example of Data Collection and Processing

The following is a complete process of “data collection - configuration export - offline processing” combining the two interfaces, including code implementation and key step explanations.

1. Data Collection and Configuration Export (save_data Function)

from pathlib import Path
SCRIPT_DIR = Path(__file__).resolve().parent
SAVE_DIR = Path(SCRIPT_DIR / "test_dir")  # Directory for saving data
SAVE_DIR.mkdir(parents=True, exist_ok=True)
import cv2
import time
import numpy as np
from xensesdk import Sensor
sensor_id = 'OG000232'  # Sensor ID

def save_data():
    fps = 30  # Data collection frame rate
    duration = 3  # Duration in seconds
    frame_interval = 1.0 / fps  # Time interval between frames
    total_frames = fps * duration  # Total number of collected frames

    # Create a sensor instance
    sensor_0 = Sensor.create(sensor_id)

    for i in range(total_frames):
        start_time = time.time()

        # Collect a frame of rectify type image (raw data)
        rec = sensor_0.selectSensorInfo(Sensor.OutputType.Rectify)

        # Generate file name
        filename = SAVE_DIR / f"{sensor_id}_{i:03d}.png"

        # Save the image
        cv2.imwrite(str(filename), rec)
        print(f"Saved {filename}")

        # Control frame rate (ensure stable 30Hz)
        elapsed = time.time() - start_time
        sleep_time = frame_interval - elapsed
        if sleep_time > 0:
            time.sleep(sleep_time)

    # Export runtime configuration
    sensor_0.exportRuntimeConfig(SAVE_DIR)

    # Release sensor resources
    sensor_0.release()

2. Offline Data Parsing and Post-Processing (replay_data Function)

def replay_data():
    # Create a solver instance by loading the runtime configuration
    sensor_solver = Sensor.createSolver(SAVE_DIR / f"runtime_{sensor_id}")

    # Traverse all collected PNG files and process them one by one
    for png_file in sorted(SAVE_DIR.glob("*.png")):
        # Skip the already generated depth maps to avoid duplicate processing
        if not png_file.name.endswith("_depth.png"):
            # Read the raw collected image
            img = cv2.imread(str(png_file), cv2.IMREAD_UNCHANGED)

            # Parse depth map, force value, and difference map based on raw image
            depth, force, diff = sensor_solver.selectSensorInfo(
                Sensor.OutputType.Depth,
                Sensor.OutputType.Force,
                Sensor.OutputType.Difference,
                rectify_image=img  # Pass the raw rectify image as input
            )

            # Normalize the depth map for visualization
            depth_norm = cv2.normalize(depth, None, 0, 255, cv2.NORM_MINMAX)
            # Convert the normalized depth map to 8-bit image format
            depth_vis = np.uint8(depth_norm)
            # Save the visualized depth map
            cv2.imwrite(SAVE_DIR / f"{png_file.stem}_depth.png", depth_vis)

    # Release solver resources
    sensor_solver.release()

3. Main Process Execution

if __name__ == '__main__':
    save_data()    # Execute data collection and configuration export
    replay_data()  # Execute offline data parsing and post-processing
    print("Data saved and replayed successfully.")

3. Process Description

  • Collection Phase: Initialize the sensor through Sensor.create, collect raw images at a fixed frame rate, and call exportRuntimeConfig to export the configuration after collection is completed, ensuring the correspondence between “data and configuration”.

  • Post-Processing Phase: Load the exported configuration through createSolver, create a parser instance, perform processing such as depth calculation on the raw images, and finally generate derived data (e.g., depth maps).

This process ensures the consistency of data collection and parsing, and is applicable to scenarios that require offline analysis of sensor data (such as algorithm verification, data visualization, etc.).