22
33import json
44import logging
5- from multiprocessing import Pipe , Process
5+ import multiprocessing
66from multiprocessing .connection import Connection
77from pathlib import Path
88from typing import Any , Callable , Tuple
@@ -52,16 +52,23 @@ def __init__(
5252 env (ManipulatorBaseEnv): The environment in which the policy will be applied.
5353 overrides (dict | None): Optional overrides for the policy configuration.
5454 """
55- self .parent_conn , self .child_conn = Pipe ()
5655 self .env = env
5756 self .overrides = overrides if overrides is not None else {}
5857
59- self .inf_proc = Process (
58+ ctx = multiprocessing .get_context ("spawn" )
59+ self .parent_conn , self .child_conn = ctx .Pipe ()
60+
61+ # Extract env data before spawning (env may not be picklable — ROS2 handles)
62+ observation_space = env .observation_space
63+ env_metadata = env .get_metadata ()
64+
65+ self .inf_proc = ctx .Process (
6066 target = inference_worker ,
6167 kwargs = {
6268 "conn" : self .child_conn ,
6369 "pretrained_path" : pretrained_path ,
64- "env" : env ,
70+ "observation_space" : observation_space ,
71+ "env_metadata" : env_metadata ,
6572 "overrides" : self .overrides ,
6673 },
6774 daemon = True ,
@@ -114,15 +121,17 @@ def shutdown(self):
114121def inference_worker (
115122 conn : Connection ,
116123 pretrained_path : str ,
117- env : ManipulatorBaseEnv ,
124+ observation_space ,
125+ env_metadata : dict ,
118126 overrides : dict | None = None ,
119127): # noqa: ANN001
120128 """Policy inference process: loads policy on GPU, receives observations via conn, returns actions, and exits on None.
121129
122130 Args:
123131 conn (Connection): The connection to the parent process for sending and receiving data.
124132 pretrained_path (str): Path to the pretrained policy model.
125- env (ManipulatorBaseEnv): The environment in which the policy will be applied.
133+ observation_space: The environment's observation space (pre-extracted for spawn compatibility).
134+ env_metadata (dict): The environment metadata (pre-extracted for spawn compatibility).
126135 overrides (dict | None): Optional overrides for the policy configuration.
127136 """
128137 setup_logging ()
@@ -145,7 +154,7 @@ def inference_worker(
145154
146155 train_config = TrainPipelineConfig .from_pretrained (pretrained_path )
147156
148- _check_dataset_metadata (train_config , env , logger )
157+ _check_dataset_metadata (train_config , env_metadata , logger )
149158
150159 logger .info ("[Inference] Loaded training config." )
151160
@@ -176,7 +185,7 @@ def inference_worker(
176185 if USE_LEROBOT_PROCESSORS :
177186 preprocessor , postprocessor = make_pre_post_processors (policy_cfg = policy .config , pretrained_path = pretrained_path )
178187
179- warmup_obs_raw = env . observation_space .sample ()
188+ warmup_obs_raw = observation_space .sample ()
180189 warmup_obs_raw ["observation.state" ] = concatenate_state_features (warmup_obs_raw )
181190 warmup_obs = numpy_obs_to_torch (warmup_obs_raw )
182191 if USE_LEROBOT_PROCESSORS :
@@ -238,15 +247,15 @@ def inference_worker(
238247
239248def _check_dataset_metadata (
240249 train_config : TrainPipelineConfig ,
241- env : ManipulatorBaseEnv ,
250+ env_metadata : dict ,
242251 logger : logging .Logger ,
243252 keys_to_skip : list [str ] | None = None ,
244253):
245254 """Check if the dataset metadata matches the environment configuration.
246255
247256 Args:
248257 train_config (TrainPipelineConfig): The training pipeline configuration.
249- env (ManipulatorBaseEnv ): The environment to compare against.
258+ env_metadata (dict ): The environment metadata dict to compare against.
250259 logger (logging.Logger): Logger for logging information.
251260 keys_to_skip (list[str] | None): List of metadata keys to skip during comparison.
252261 """
@@ -272,7 +281,6 @@ def _warn_if_missing(key: str):
272281 logger .info (
273282 "[Inference] Found crisp_meta.json in dataset, comparing environment and policy configs..."
274283 )
275- env_metadata = env .get_metadata ()
276284 with open (path_to_metadata , "r" ) as f :
277285 dataset_metadata = json .load (f )
278286 for key , value in dataset_metadata .items ():
0 commit comments