多传感器数据采集系统技术架构

多传感器数据采集系统技术架构

1. 概述

本系统用于同时采集多个传感器的数据(如力觉、触觉、视觉等),并将数据按时间对齐后统一保存,以便后续用于遥操作、模型训练或数据分析。系统采用多线程架构,每个传感器拥有独立的采集线程,通过共享数据缓冲区和锁机制保证线程安全,并提供统一的启动、停止和保存接口。

2. 核心设计要点

2.1 类结构设计
  • 采集控制器类 (例如 DataCollector)负责管理所有传感器的生命周期、数据汇聚与持久化。
  • __init__ 方法中:
    • 创建各传感器实例(如 self.force_sensor = ForceSensor())。
    • 初始化共享数据缓冲区 self.data_buffer = {}(字典结构,键为传感器名称,值为该传感器的数据列表)。
    • 初始化线程锁 self.lock = threading.Lock()
    • 初始化运行标志 self.running = False
    • 为每个传感器创建独立线程,线程目标为对应的数据采集方法。
2.2 数据采集方法

为每个传感器定义一个采集方法(如 _collect_force_data),其结构为:

python 复制代码
def _collect_force_data(self):
    while self.running:
        data = self.force_sensor.get_data()   # 获取一帧数据
        with self.lock:
            self.data_buffer["force"].append(data)  # 加锁写入
        time.sleep(1.0 / freq)               # 控制采样频率
  • 每个采集方法独立运行,互不阻塞。
  • 使用 while self.running 控制循环,便于外部优雅停止。
  • 写入共享缓冲区时必须加锁,避免数据损坏。
2.3 启动流程(start 方法)
  • 清空或初始化 self.data_buffer,例如:self.data_buffer = {"force": [], "tactile": [], ...}
  • 设置 self.running = True
  • 启动所有传感器采集线程(线程已在 __init__ 中创建,只需确保线程未启动或使用标志控制)。
2.4 停止流程(stop 方法)
  • 设置 self.running = False,各采集线程自然退出循环。
  • 记录采集结束时间,计算总采集时长。
  • 可选:等待所有线程结束(thread.join())。
2.5 数据保存(save 方法)
  • 复制一份当前 self.data_buffer 的深拷贝(避免保存过程中缓冲区被修改)。
  • 将拷贝的数据写入磁盘,常用格式:.npz.pkl.csv 或按传感器分开存储。
  • 可同时保存采集参数(如时间戳、采样频率、传感器配置等)。

3. 关键注意事项

  • 线程安全 :所有对 self.data_buffer 的写操作必须使用锁;读操作(如保存时的拷贝)也建议加锁。
  • 采样频率控制 :使用 time.sleep 控制循环间隔,避免占用过高 CPU。
  • 异常处理 :在采集循环中应捕获传感器 get_data 可能抛出的异常,避免线程意外退出。
  • 资源释放:停止采集后应合理关闭传感器连接(如串口、网络套接字)。

示例代码模板

python 复制代码
import threading
import time
import copy
import pickle
from datetime import datetime

# 假设存在以下传感器类(用户需根据实际情况替换)
# from sensors import ForceSensor, TactileSensor, CameraSensor

class DataCollector:
    """
    多传感器数据采集控制器
    """
    def __init__(self, sensor_configs, freq=100):
        """
        参数:
            sensor_configs: dict, 例如 {'force': ForceSensor(port=6001), 'tactile': TactileSensor(port=6002)}
            freq: int, 全局采集频率 (Hz),每个传感器按此频率独立采集
        """
        self.sensor_configs = sensor_configs
        self.freq = freq
        self.sleep_interval = 1.0 / freq

        # 共享数据缓冲区: { sensor_name: list_of_data_frames }
        self.data_buffer = {}
        self.lock = threading.Lock()
        self.running = False

        # 存储线程对象
        self.threads = []

        # 为每个传感器创建采集线程
        for name, sensor in self.sensor_configs.items():
            # 初始化缓冲区中的列表
            self.data_buffer[name] = []
            # 创建线程,target 使用 lambda 或 partial 传递传感器名
            t = threading.Thread(target=self._collect_loop, args=(name, sensor))
            t.daemon = True   # 主程序退出时自动终止
            self.threads.append(t)

    def _collect_loop(self, sensor_name, sensor):
        """单个传感器的采集循环"""
        while self.running:
            try:
                # 调用传感器的获取数据方法(需自行实现)
                data = sensor.get_data()
                # 可以添加时间戳:data['timestamp'] = time.time()
                with self.lock:
                    self.data_buffer[sensor_name].append(data)
            except Exception as e:
                print(f"[{sensor_name}] 采集异常: {e}")
            time.sleep(self.sleep_interval) # 不同的传感器频率是不同的,这里应该做一个 map 使用 name 去查就好了

    def start(self):
        """启动所有传感器采集"""
        if self.running:
            print("采集已在运行中")
            return

        # 清空缓冲区(保留结构)
        with self.lock:
            for name in self.sensor_configs.keys():
                self.data_buffer[name] = []

        self.running = True
        self.start_time = time.time()

        # 启动所有线程
        for t in self.threads:
            if not t.is_alive():
                t.start()
        print(f"采集已启动,共 {len(self.threads)} 个传感器线程,采样频率 {self.freq} Hz")

    def stop(self):
        """停止采集"""
        if not self.running:
            return
        self.running = False
        # 等待所有线程结束(可选)
        for t in self.threads:
            t.join(timeout=2.0)
        self.end_time = time.time()
        duration = self.end_time - self.start_time
        print(f"采集已停止,总时长 {duration:.2f} 秒")

    def save(self, filepath=None):
        """
        将采集的数据保存到磁盘
        参数:
            filepath: 保存路径,若为 None 则自动生成
        """
        if filepath is None:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filepath = f"collected_data_{timestamp}.pkl"

        # 加锁复制数据(深拷贝)
        with self.lock:
            data_to_save = copy.deepcopy(self.data_buffer)

        # 附加元数据
        metadata = {
            "start_time": self.start_time,
            "end_time": self.end_time,
            "duration": self.end_time - self.start_time,
            "frequency": self.freq,
            "sensors": list(self.sensor_configs.keys())
        }
        save_obj = {
            "data": data_to_save,
            "metadata": metadata
        }

        # 保存为 pickle 文件
        with open(filepath, "wb") as f:
            pickle.dump(save_obj, f)
        print(f"数据已保存至: {filepath} (大小: {len(str(save_obj))} bytes)")

    # 可选:添加上下文管理器支持
    def __enter__(self):
        self.start()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.stop()


# ========== 使用示例 ==========
if __name__ == "__main__":
    # 模拟传感器类(实际使用时替换为真实传感器驱动)
    class MockSensor:
        def __init__(self, name):
            self.name = name
            self.counter = 0
        def get_data(self):
            self.counter += 1
            return {"value": self.counter, "name": self.name}

    # 配置传感器
    sensors = {
        "force": MockSensor("force_sensor"),
        "tactile": MockSensor("tactile_sensor"),
        # "camera": CameraSensor()  # 实际使用时添加
    }

    # 创建采集器 (频率 50 Hz)
    collector = DataCollector(sensors, freq=50)

    # 启动采集
    collector.start()
    # 模拟采集 3 秒
    time.sleep(3)
    collector.stop()
    # 保存数据
    collector.save()

    # 或者使用上下文管理器
    # with DataCollector(sensors, freq=50) as collector:
    #     time.sleep(3)
    #     collector.save()

说明

  • 传感器接口 :示例中使用了 MockSensor 模拟真实传感器,实际使用时请替换为真实的传感器驱动类,并确保其具有 get_data() 方法(返回一帧数据,可以是字典或列表)。
  • 频率控制 :每个传感器按相同频率独立采集。若某些传感器频率需要单独设置,可扩展配置字典为 {"name": sensor, "freq": 100}
  • 数据格式 :每个传感器的数据列表可包含任意 Python 对象,推荐每帧数据包含 timestamp 字段以便后续时间对齐。
  • 扩展性 :可轻松添加新的传感器,只需在 sensor_configs 中增加条目即可。

如果需要针对特定传感器(如力觉、触觉、相机)的详细实现,请提供相关接口文档,我可以进一步补充。

相关推荐
传说故事2 小时前
【论文阅读】RADAR:通过语义规划与自主因果环境重置的闭环机器人数据生成
论文阅读·人工智能·机器人·具身智能
VBsemi-专注于MOSFET研发定制2 小时前
面向AI管道检测机器人的功率MOSFET选型分析——以高集成度、高可靠电源与驱动系统为例
人工智能·单片机·机器人
踩着两条虫2 小时前
VTJ:架构设计模式
前端·架构·ai编程
小谢小哥2 小时前
49-缓存一致性详解
java·后端·架构
ZPC82102 小时前
nmtui
人工智能·算法·机器人
AI服务老曹2 小时前
【架构深评】打通 X86/ARM 异构屏障:基于 GB28181/RTSP 的企业级 AI 视频管理平台架构解析
arm开发·人工智能·架构
szxinmai主板定制专家2 小时前
基于ARM+FPGA高性能MPSOC 多轴伺服设计方案
arm开发·人工智能·嵌入式硬件·fpga开发·架构
禅思院3 小时前
中篇:构建弹性的异步组件
前端·架构·前端框架
企业架构师老王3 小时前
2026电网与发电企业巡检数据智能分析工具选型指南:从AI模型到实在Agent的架构实战
人工智能·ai·架构