扩展 Genesis World#

构建在 Genesis World 之上的软件包往往有自己的资源需要初始化与释放:GPU 缓冲区、渲染器、后台服务。把这一生命周期挂到 Genesis 上。注册一对回调,Genesis 就会在 gs.init() 时运行你的初始化、在 gs.destroy() 时运行你的清理,于是你的扩展与引擎同步启动、同步关闭。

注册模块#

gs.register_external_module(init_fun, destroy_fun) 接受两个无参数的可调用对象:

import genesis as gs

def my_init():
    # allocate resources that depend on the active backend and device
    ...

def my_destroy():
    # release them
    ...

gs.register_external_module(my_init, my_destroy)

gs.init(backend=gs.gpu)  # my_init() runs here, after core initialization
# ... use Genesis and your extension ...
gs.destroy()             # my_destroy() runs here
  • init_fun 只运行一次,紧接在 Genesis 完成初始化之后。如果你注册时 Genesis 已经 初始化完成,init_fun 会立即运行。

  • destroy_fun 在调用 gs.destroy() 时运行。Genesis 还会把 destroy() 注册到 atexit,所以即使你不主动调用,正常退出解释器时也会执行清理。

尽量在 gs.init() 之前注册,这样初始化会作为一次统一且有序的初始化过程的一部分完成。

注销#

gs.unregister_external_module(init_fun, destroy_fun) 移除这一对回调。注册表以你传入的函数对象本身为键,所以注销时要使用与注册时相同的两个可调用对象:

gs.unregister_external_module(my_init, my_destroy)

Note

由于这两个可调用对象标识了注册项,请传入具名函数(或保留引用),不要用一次性的 lambda,否则之后无法注销。

编写自定义记录器#

内置的文件写入器和绘图器覆盖了大多数需求;若要以其他方式采集数据,请继承 genesis.recorders.Recorder 并把你的选项传给 scene.add_recorder。场景通过五个方法驱动记录器,你只需实现它们,永远不要自己调用:

  1. __init__ 根据选项配置记录器。

  2. build() 初始化资源(在 scene.build() 期间调用)。

  3. process(data, cur_time) 在记录期间处理每个采样值。

  4. cleanup() 完成收尾并释放资源(在记录停止时调用)。

  5. reset(envs_idx=None) 为新回合重置状态。

import genesis as gs
from genesis.recorders import Recorder

class MyRecorder(Recorder):
    def __init__(self, manager, options, data_func):
        super().__init__(manager, options, data_func)
        self.data_buffer = []

    def build(self):
        super().build()
        self.data_buffer = []

    def process(self, data, cur_time):
        self.data_buffer.append({"time": cur_time, "data": data})

    def cleanup(self):
        print(f"Recorded {len(self.data_buffer)} samples")
        self.data_buffer = []

    def reset(self, envs_idx=None):
        self.data_buffer = []

记录工作流与内置记录器的用法见 记录数据。

另见#