扩展 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。场景通过五个方法驱动记录器,你只需实现它们,永远不要自己调用:
__init__根据选项配置记录器。build()初始化资源(在scene.build()期间调用)。process(data, cur_time)在记录期间处理每个采样值。cleanup()完成收尾并释放资源(在记录停止时调用)。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 = []
记录工作流与内置记录器的用法见 记录数据。