OpenDuckMini强化学习框架入门教程-工具类
纠错,疑问,交流: 请进入讨论区或 请点击进入页面,扫码加入微信群或Q群进行交流
获取最新文章: 扫一扫加入“创客智造”公众号
欢迎加入我们的openduckmini交流群,微信扫描右侧二维码立即进群交流
工具类
- 理解和使用相关便利工具
概述
工具类模块(playground/common/utils.py)提供了一些辅助功能,主要用于动作后处理和信号滤波。目前主要包含一个 LowPassActionFilter 类,用于平滑策略输出的动作。
LowPassActionFilter
作用
对策略输出的动作信号进行低通滤波,消除高频抖动,使动作更平滑、更自然。
原理
使用一阶低通滤波器(也称为指数平滑):
动作_filtered(t) = alpha * 动作_filtered(t-1) + (1 - alpha) * 动作_raw(t)
其中:
alpha = cutoff_freq / (cutoff_freq + control_freq)
类定义
class LowPassActionFilter:
def __init__(self, control_freq, cutoff_frequency=30.0):
self.last_action = 0
self.current_action = 0
self.control_freq = float(control_freq)
self.cutoff_frequency = float(cutoff_frequency)
self.alpha = self.compute_alpha()
参数
| 参数 | 默认值 | 说明 |
|---|---|---|
control_freq |
(构造时传入) | 控制频率 (Hz) |
cutoff_frequency |
30.0 Hz | 截止频率,高于此的频率分量被衰减 |
方法
def compute_alpha(self):
"""计算滤波系数 alpha"""
return (1.0 / self.cutoff_frequency) / (
1.0 / self.control_freq + 1.0 / self.cutoff_frequency
)
def push(self, action):
"""推送新的原始动作"""
self.current_action = action
def get_filtered_action(self):
"""获取滤波后的动作"""
self.last_action = (
self.alpha * self.last_action +
(1 - self.alpha) * self.current_action
)
return self.last_action
使用示例
from playground.common.utils import LowPassActionFilter
# 创建滤波器 (50 Hz 控制频率, 37.5 Hz 截止频率)
action_filter = LowPassActionFilter(50, cutoff_frequency=37.5)
# 在控制循环中使用
action_filter.push(raw_action)
smoothed_action = action_filter.get_filtered_action()
频率响应
| 控制频率 | 截止频率 | alpha | 效果 |
|---|---|---|---|
| 50 Hz | 37.5 Hz | 0.429 | 中度平滑 |
| 50 Hz | 25 Hz | 0.333 | 较强平滑 |
| 50 Hz | 10 Hz | 0.167 | 强力平滑 |
其他辅助功能
plot_saved_obs.py
用于分析推理过程中保存的观测数据:
# 推理后保存观测 (由 mujoco_infer.py 自动完成)
pickle.dump(self.saved_obs, open("mujoco_saved_obs.pkl", "wb"))
# 使用 plot_saved_obs.py 分析
动作处理策略
Open Duck Playground 提供了三种动作处理方式:
1. 低通滤波
使用 LowPassActionFilter 平滑动作:
# 在 joystick.py 中 (已注释)
self.action_filter = LowPassActionFilter(1/0.02, cutoff_frequency=37.5)
action_filter.push(action_w_delay)
action = action_filter.get_filtered_action()
2. 电机速度限制
限制每步关节位置的最大变化量(当前默认方式):
motor_targets = clip(
motor_targets,
prev_motor_targets - max_motor_velocity * dt,
prev_motor_targets + max_motor_velocity * dt,
)
3. 动作变化率代价
通过奖励函数 cost_action_rate 间接鼓励平滑动作:
cost_action_rate = sum(square(action - last_action))
三种方式的对比
| 方式 | 训练中 | 推理中 | 效果 |
|---|---|---|---|
| 低通滤波 | 已注释 | 已注释 | 直接平滑,可能引入延迟 |
| 速度限制 | 启用 (USE_MOTOR_SPEED_LIMITS=True) |
启用 | 限制最大变化率,无延迟 |
| 动作代价 | 始终启用 | N/A | 间接鼓励,但不强制 |
常量定义
playground/open_duck_mini_v2/constants.py 提供机器人特定的常量和辅助函数:
# 路径
ROOT_PATH = epath.Path(__file__).parent
FLAT_TERRAIN_XML = ROOT_PATH / "xmls" / "scene_flat_terrain.xml"
# ...
# 任务到 XML 的映射
def task_to_xml(task_name):
return {"flat_terrain": ..., "rough_terrain": ...}[task_name]
# 关节信息
JOINTS_ORDER_NO_HEAD = [
"left_hip_yaw", "left_hip_roll", "left_hip_pitch",
"left_knee", "left_ankle",
"right_hip_yaw", "right_hip_roll", "right_hip_pitch",
"right_knee", "right_ankle",
]
# 传感器名称
GRAVITY_SENSOR = "upvector"
GYRO_SENSOR = "gyro"
# ...
JAX 编译缓存
在 BaseRunner 初始化时配置了 JAX 编译缓存:
os.makedirs(".tmp", exist_ok=True)
jax.config.update("jax_compilation_cache_dir", ".tmp/jax_cache")
jax.config.update("jax_persistent_cache_min_entry_size_bytes", -1)
jax.config.update("jax_persistent_cache_min_compile_time_secs", 0)
这显著加速了重复训练,因为 JAX 的 XLA 编译结果会被缓存到磁盘。
纠错,疑问,交流: 请进入讨论区或 请点击进入页面,扫码加入微信群或Q群进行交流
获取最新文章: 扫一扫加入“创客智造”公众号
欢迎加入我们的openduckmini交流群,微信扫描右侧二维码立即进群交流


















