Skip to content

Commit 091582c

Browse files
Clean implementation of mixed-play (self-play with adjustable replay population) (#306)
* Add mixed play (self-play + log-replay interaction). * Include mixed play.
1 parent 579e5ba commit 091582c

5 files changed

Lines changed: 45 additions & 15 deletions

File tree

pufferlib/config/ocean/drive.ini

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,12 @@ map_dir = "resources/drive/binaries/training"
5353
num_maps = 10000
5454
; Determines which step of the trajectory to initialize the agents at upon reset
5555
init_steps = 0
56-
; Options: "control_vehicles", "control_agents", "control_wosac", "control_sdc_only"
56+
; Options: "control_vehicles", "control_agents", "control_wosac", "control_sdc_only", "control_mixed_play"
5757
control_mode = "control_vehicles"
5858
; Options: "created_all_valid", "create_only_controlled"
5959
init_mode = "create_all_valid"
60+
; Sets the maximum number of controllable agents per scene, ONLY used if control_mode is "control_mixed_play"
61+
max_controlled_agents = 32
6062

6163
[train]
6264
seed=42

pufferlib/ocean/drive/binding.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ static PyObject *my_shared(PyObject *self, PyObject *args, PyObject *kwargs) {
7676
int init_steps = unpack(kwargs, "init_steps");
7777
int goal_behavior = unpack(kwargs, "goal_behavior");
7878
float goal_target_distance = unpack(kwargs, "goal_target_distance");
79+
int max_controlled_agents = unpack(kwargs, "max_controlled_agents");
7980

8081
clock_gettime(CLOCK_REALTIME, &ts);
8182
srand(ts.tv_nsec); // Always use random sampling with replacement
@@ -104,8 +105,10 @@ static PyObject *my_shared(PyObject *self, PyObject *args, PyObject *kwargs) {
104105
env->init_steps = init_steps;
105106
env->goal_behavior = goal_behavior;
106107
env->goal_target_distance = goal_target_distance;
108+
env->max_controlled_agents = max_controlled_agents;
107109
snprintf(map_file, sizeof(map_file), "%s/map_%03d.bin", map_dir, map_id);
108110
env->entities = load_map_binary(map_file, env);
111+
// Count the number of controllable agents in map
109112
set_active_agents(env);
110113

111114
// Skip map if it doesn't contain any controllable agents
@@ -218,6 +221,7 @@ static int my_init(Env *env, PyObject *args, PyObject *kwargs) {
218221
OVERRIDE_FLOAT(goal_target_distance);
219222
OVERRIDE_FLOAT(goal_radius);
220223
OVERRIDE_FLOAT(goal_speed);
224+
OVERRIDE_INT(max_controlled_agents);
221225

222226
#undef OVERRIDE_INT
223227
#undef OVERRIDE_FLOAT
@@ -264,6 +268,8 @@ static int my_log(PyObject *dict, Log *log) {
264268
assign_to_dict(dict, "dnf_rate", log->dnf_rate);
265269
assign_to_dict(dict, "completion_rate", log->completion_rate);
266270
assign_to_dict(dict, "lane_alignment_rate", log->lane_alignment_rate);
271+
assign_to_dict(dict, "perc_controlled", log->perc_controlled);
272+
assign_to_dict(dict, "perc_other", log->perc_other);
267273
assign_to_dict(dict, "offroad_per_agent", log->offroad_per_agent);
268274
assign_to_dict(dict, "collisions_per_agent", log->collisions_per_agent);
269275
assign_to_dict(dict, "goals_sampled_this_episode", log->goals_sampled_this_episode);

pufferlib/ocean/drive/drive.h

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#define CONTROL_AGENTS 1
4040
#define CONTROL_WOSAC 2
4141
#define CONTROL_SDC_ONLY 3
42+
#define CONTROL_MIXED_PLAY 4
4243

4344
// Minimum distance to goal position
4445
#define MIN_DISTANCE_TO_GOAL 2.0f
@@ -163,6 +164,8 @@ struct Log {
163164
float active_agent_count;
164165
float expert_static_agent_count;
165166
float static_agent_count;
167+
float perc_controlled;
168+
float perc_other;
166169
};
167170

168171
typedef struct Entity Entity;
@@ -317,7 +320,6 @@ struct Drive {
317320
float reward_goal_post_respawn;
318321
float goal_radius;
319322
float goal_speed;
320-
int max_controlled_agents;
321323
int logs_capacity;
322324
int goal_behavior;
323325
float goal_target_distance;
@@ -330,6 +332,7 @@ struct Drive {
330332
int *tracks_to_predict_indices;
331333
int init_mode;
332334
int control_mode;
335+
int max_controlled_agents;
333336
};
334337

335338
void add_log(Drive *env) {
@@ -374,6 +377,9 @@ void add_log(Drive *env) {
374377
env->log.active_agent_count += env->active_agent_count;
375378
env->log.expert_static_agent_count += env->expert_static_agent_count;
376379
env->log.static_agent_count += env->static_agent_count;
380+
int total = env->active_agent_count + env->static_agent_count;
381+
env->log.perc_controlled += (float)env->active_agent_count / (float)total;
382+
env->log.perc_other += (float)env->static_agent_count / (float)total;
377383
env->log.n += 1;
378384
}
379385
}
@@ -1201,10 +1207,9 @@ void compute_agent_metrics(Drive *env, int agent_idx) {
12011207
return;
12021208
}
12031209

1204-
bool should_control_agent(Drive *env, int agent_idx) {
1205-
1210+
bool should_control_agent(Drive *env, int agent_idx, int control_limit) {
12061211
// Check if we have room for more agents or are already at capacity
1207-
if (env->active_agent_count >= env->num_agents) {
1212+
if (env->active_agent_count >= control_limit) {
12081213
return false;
12091214
}
12101215

@@ -1267,6 +1272,13 @@ void set_active_agents(Drive *env) {
12671272
env->num_agents = MAX_AGENTS;
12681273
}
12691274

1275+
int control_limit;
1276+
if (env->control_mode == CONTROL_MIXED_PLAY) {
1277+
control_limit = (env->max_controlled_agents < env->num_agents) ? env->max_controlled_agents : env->num_agents;
1278+
} else {
1279+
control_limit = env->num_agents;
1280+
}
1281+
12701282
// If we have a SDC index (WOMD), initialize it first:
12711283
int sdc_index = env->sdc_track_index;
12721284

@@ -1310,17 +1322,17 @@ void set_active_agents(Drive *env) {
13101322
// Determine if this agent should be policy-controlled
13111323
bool is_controlled = false;
13121324

1313-
is_controlled = should_control_agent(env, i);
1325+
is_controlled = should_control_agent(env, i, control_limit);
13141326

13151327
if (is_controlled) {
13161328
active_agent_indices[env->active_agent_count] = i;
13171329
env->active_agent_count++;
13181330
env->entities[i].active_agent = 1;
13191331
} else if (env->init_mode != INIT_ONLY_CONTROLLABLE_AGENTS) {
13201332
static_agent_indices[env->static_agent_count] = i;
1321-
env->static_agent_count++;
1333+
env->static_agent_count++; // Includes expert replay and static agents
13221334
env->entities[i].active_agent = 0;
1323-
if (env->entities[i].mark_as_expert == 1 || env->active_agent_count == env->num_agents) {
1335+
if (env->entities[i].mark_as_expert == 1 || env->active_agent_count == control_limit) {
13241336
expert_static_agent_indices[env->expert_static_agent_count] = i;
13251337
env->expert_static_agent_count++;
13261338
env->entities[i].mark_as_expert = 1;
@@ -1341,6 +1353,9 @@ void set_active_agents(Drive *env) {
13411353
for (int i = 0; i < env->expert_static_agent_count; i++) {
13421354
env->expert_static_agent_indices[i] = expert_static_agent_indices[i];
13431355
}
1356+
// printf("Total actors: %d, Active agents: %d, Static agents: %d, Expert static agents: %d\n", env->num_actors,
1357+
// env->active_agent_count, env->static_agent_count, env->expert_static_agent_count);
1358+
// printf("Control mode: %d, max controlled agents: %d\n", env->control_mode, env->max_controlled_agents);
13441359

13451360
return;
13461361
}

pufferlib/ocean/drive/drive.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,12 @@ def __init__(
3535
num_agents=512,
3636
action_type="discrete",
3737
dynamics_model="classic",
38-
max_controlled_agents=-1,
3938
buf=None,
4039
seed=1,
4140
init_steps=0,
4241
init_mode="create_all_valid",
4342
control_mode="control_vehicles",
43+
max_controlled_agents=32,
4444
map_dir="resources/drive/binaries/training",
4545
):
4646
# env
@@ -63,6 +63,7 @@ def __init__(
6363
self.termination_mode = termination_mode
6464
self.resample_frequency = resample_frequency
6565
self.dynamics_model = dynamics_model
66+
self.max_controlled_agents = max_controlled_agents
6667

6768
# Observation space calculation
6869
self.ego_features = {"classic": binding.EGO_FEATURES_CLASSIC, "jerk": binding.EGO_FEATURES_JERK}.get(
@@ -96,9 +97,11 @@ def __init__(
9697
self.control_mode = 2
9798
elif self.control_mode_str == "control_sdc_only":
9899
self.control_mode = 3
100+
elif self.control_mode_str == "control_mixed_play":
101+
self.control_mode = 4
99102
else:
100103
raise ValueError(
101-
f"control_mode must be one of 'control_vehicles', 'control_wosac', or 'control_agents'. Got: {self.control_mode_str}"
104+
f"control_mode must be one of 'control_vehicles', 'control_wosac', 'control_agents' or 'control_mixed_play'. Got: {self.control_mode_str}"
102105
)
103106
if self.init_mode_str == "create_all_valid":
104107
self.init_mode = 0
@@ -140,7 +143,6 @@ def __init__(
140143
raise ValueError(
141144
f"num_maps ({num_maps}) exceeds available maps in directory ({available_maps}). Please reduce num_maps or add more maps to resources/drive/binaries."
142145
)
143-
self.max_controlled_agents = int(max_controlled_agents)
144146

145147
# Iterate through all maps to count total agents that can be initialized for each map
146148
agent_offsets, map_ids, num_envs = binding.shared(
@@ -150,9 +152,9 @@ def __init__(
150152
init_mode=self.init_mode,
151153
control_mode=self.control_mode,
152154
init_steps=self.init_steps,
153-
max_controlled_agents=self.max_controlled_agents,
154155
goal_behavior=self.goal_behavior,
155156
goal_target_distance=self.goal_target_distance,
157+
max_controlled_agents=self.max_controlled_agents,
156158
)
157159

158160
self.num_agents = agent_offsets[-1]
@@ -186,14 +188,14 @@ def __init__(
186188
dt=dt,
187189
episode_length=(int(episode_length) if episode_length is not None else None),
188190
termination_mode=(int(self.termination_mode) if self.termination_mode is not None else 0),
189-
max_controlled_agents=self.max_controlled_agents,
190191
map_id=map_ids[i],
191192
max_agents=nxt - cur,
192193
ini_file="pufferlib/config/ocean/drive.ini",
193194
init_steps=init_steps,
194195
init_mode=self.init_mode,
195196
control_mode=self.control_mode,
196197
map_dir=map_dir,
198+
max_controlled_agents=self.max_controlled_agents,
197199
)
198200
env_ids.append(env_id)
199201

@@ -218,11 +220,11 @@ def resample_maps(self):
218220
init_mode=self.init_mode,
219221
control_mode=self.control_mode,
220222
init_steps=self.init_steps,
221-
max_controlled_agents=self.max_controlled_agents,
222223
goal_behavior=self.goal_behavior,
223224
goal_target_distance=self.goal_target_distance,
224225
goal_speed=self.goal_speed,
225226
map_dir=self.map_dir,
227+
max_controlled_agents=self.max_controlled_agents,
226228
)
227229
self.agent_offsets = agent_offsets
228230
self.map_ids = map_ids
@@ -253,7 +255,6 @@ def resample_maps(self):
253255
offroad_behavior=self.offroad_behavior,
254256
dt=self.dt,
255257
episode_length=(int(self.episode_length) if self.episode_length is not None else None),
256-
max_controlled_agents=self.max_controlled_agents,
257258
map_id=map_ids[i],
258259
max_agents=nxt - cur,
259260
ini_file="pufferlib/config/ocean/drive.ini",
@@ -262,6 +263,7 @@ def resample_maps(self):
262263
control_mode=self.control_mode,
263264
map_dir=self.map_dir,
264265
termination_mode=(int(self.termination_mode) if self.termination_mode is not None else 0),
266+
max_controlled_agents=self.max_controlled_agents,
265267
)
266268
env_ids.append(env_id)
267269
self.c_envs = binding.vectorize(*env_ids)

pufferlib/ocean/env_config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ typedef struct {
2828
int init_steps;
2929
int init_mode;
3030
int control_mode;
31+
int max_controlled_agents;
3132
char map_dir[256];
3233
} env_init_config;
3334

@@ -104,6 +105,8 @@ static int handler(void *config, const char *section, const char *name, const ch
104105
env_config->control_mode = 2;
105106
} else if (strcmp(value, "\"control_sdc_only\"") == 0 || strcmp(value, "control_sdc_only") == 0) {
106107
env_config->control_mode = 3;
108+
} else if (strcmp(value, "\"control_mixed_play\"") == 0 || strcmp(value, "control_mixed_play") == 0) {
109+
env_config->control_mode = 4;
107110
} else {
108111
printf("Warning: Unknown control_mode value '%s', defaulting to CONTROL_VEHICLES\n", value);
109112
env_config->control_mode = 0; // Default to CONTROL_VEHICLES
@@ -114,6 +117,8 @@ static int handler(void *config, const char *section, const char *name, const ch
114117
env_config->map_dir[sizeof(env_config->map_dir) - 1] = '\0';
115118
}
116119
// printf("Parsed map_dir: '%s'\n", env_config->map_dir);
120+
} else if (MATCH("env", "max_controlled_agents")) {
121+
env_config->max_controlled_agents = atoi(value);
117122
} else {
118123
return 0; // Unknown section/name, indicate failure to handle
119124
}

0 commit comments

Comments
 (0)