UMI复现——SharedMemoryManager初识(持续更新)

SharedMemoryManager属于multiprocessing.managers模块,用于管理共享内存,允许多个进程之间高效地共享数据,而不需要通过管道或队列传递数据的开销

基本概念

1.目的:

- 在多进程环境下,利用共享内存避免数据复制

- 提供线程安全的共享对象,允许多个进程访问

2.主要功能:

- 分配共享内存 (SharedMemory) 

- 创建共享的队列或环形缓冲区,作为共享数据结构的基础

3.典型场景:

- 生产者-消费者模型

 - 数据流处理系统

在代码中的使用方式

创建并启动共享内存管理器

通过 SharedMemoryManager 实例化的,用来管理共享内存资源

from multiprocessing.managers import SharedMemoryManager


with SharedMemoryManager() as shm_manager:
    # 使用共享内存进行后续操作
    ...

创建共享队列:存储独立的离散命令

SharedMemoryQueue 被用作存储命令的队列,存储的是一组离散的命令,每个命令是独立的,彼此没有直接关系。example需要明确队列中每条命令的格式

example = {
    'cmd': Command.SCHEDULE_WAYPOINT.value,
    'target_pos': 0.0,
    'target_time': 0.0
}

input_queue = SharedMemoryQueue.create_from_examples(
    shm_manager=shm_manager,      # 创建共享内存队列
    examples=example,             # 指定队列中数据结构和大小
    buffer_size=command_queue_size# 指定整个输入队列的容量,example的数量
)

# 实时监控队列占用率
# queue_length = input_queue.length()
# print(f"Queue length: {queue_length}/{command_queue_size}")
# 如果生产者尝试 put 数据时队列已满,可能会引发异常或数据被丢弃,这时需要增大 command_queue_size
  • 这里的 example 定义了队列中每条命令的基本字段和数据类型
  • 包含:
    • cmd:命令的类型(例如调度路径点或关闭命令)
    • target_postarget_time:每个命令的具体参数
  • 队列中的每个元素代表单一任务,直接被消费者(run 方法)提取并执行
  • 数据量较小,通常只有几个字段

创建共享内存环形缓冲区:存储连续的数据流

共享内存环形缓冲区被用于存储机械手的状态信息

example = {
    'gripper_state': 0,
    'gripper_position': 0.0,
    'gripper_velocity': 0.0,
    'gripper_force': 0.0,
    'gripper_measure_timestamp': time.time(),
    'gripper_receive_timestamp': time.time(),
    'gripper_timestamp': time.time()
}                                       # 定义存储数据的结构
ring_buffer = SharedMemoryRingBuffer.create_from_examples(
    shm_manager=shm_manager,
    examples=example,
    get_max_k=get_max_k,                # 设置缓冲区最多存储的历史数据量
    get_time_budget=0.2,
    put_desired_frequency=frequency     # 定义写入频率,帮助环形缓冲区管理数据吞吐
)
  • 这里的 example 定义了状态数据的基本字段和类型,共同组成每次测量的完整状态
  • 包含:
    • 机械手的状态、位置、速度、力、时间戳等。
  • 环形缓冲区存储的数据往往更复杂,可能包含多个浮点数或其他结构化数据。
  • 数据必须按照固定的格式(由 example 定义)组织,以便后续插入和读取操作

注意:

队列的数据由外部生产者(主进程)生成,FIFO(先进先出),每次取出一个完整命令

缓冲区的数据由内部逻辑(如机械手反馈)生成,环形存储,覆盖旧数据

生产者-消费者模型

生产者run 方法中的代码将机械手状态数据写入环形缓冲区

state = {
    'gripper_state': info['state'],
    'gripper_position': info['position'] / self.scale,
    ...
}
self.ring_buffer.put(state)

消费者:其他进程通过 get_state 方法读取环形缓冲区数据

def get_state(self, k=None, out=None):
    if k is None:
        return self.ring_buffer.get(out=out)
    else:
        return self.ring_buffer.get_last_k(k=k, out=out)

UMI代码中的重要设计

多进程控制

WSGController 继承自 multiprocessing.Process,用于在单独的子进程中运行机械手控制器逻辑

启动和停止:

controller = WSGController(shm_manager, hostname="robot-host", port=5556)
controller.start()
controller.stop()

上下文管理器:

with WSGController(shm_manager, hostname="robot-host", port=5556) as controller:
    controller.schedule_waypoint(10.0, target_time=5.0)

命令队列

通过 SharedMemoryQueue 实现命令的异步传递:

  • input_queue.put() 用于生产命令
  • input_queue.get_all() 用于消费命令

实时调度和数据流处理

  • PoseTrajectoryInterpolator 被用来插值机械手的运动轨迹。
  • 使用 precise_wait 调节循环频率,确保在设定的时间步长内完成任务

总结

SharedMemoryManager 是 Python 中多进程间共享数据的高效工具。在你的代码中,它被用来管理共享队列和环形缓冲区,构建了一个高性能的实时控制系统,主要特点包括:

  1. 使用 SharedMemoryQueue 处理异步命令。
  2. 使用 SharedMemoryRingBuffer 存储机械手状态数据。
  3. 通过多进程架构和上下文管理器提升代码的可维护性。
  4. 实现生产者-消费者模型,支持实时控制和数据流处理。

在高频控制和数据传递的应用中非常有效,避免进程间数据拷贝的开销,同时保证高效和实时性

问题分析

  • 1.决策层发布频率高于控制层读取频率:

    • 队列中的命令会堆积,导致控制层总是延迟处理决策层发布的命令。
    • 例如,决策层在时间 t=1t=10 发布了 10 条命令,但控制层只能处理其中一部分,可能导致行为不连贯或延迟响应。
  • 2.决策层命令不稳定:

    • 如果决策层频繁更改目标,例如在 t=9s 设置了一个目标后又发布了早于 t=9s 的目标(例如 t=8s),控制层可能会执行过时的命令。
    • 如果控制层以队列的顺序执行命令,这种反复切换目标的行为会导致控制不稳定。
  • 3.控制行为异常:

    • 控制层可能连续处理不一致的目标状态,例如执行完 t=10s 的控制后,又回到了过时的 t=9s 状态。
    • 控制器的输出会呈现随机或混乱的运动,不符合预期。

解决方案

1. 使用最新命令覆盖队列中的旧命令

2. 引入时间戳验证机制,只执行时间戳晚于当前状态时间的命令

3. 限制队列长度

4. 插值平滑控制目标

5. 决策层与控制层频率同步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值