task_generator modules¶
TM_Module and its shipped subclasses. Modules run cross-cutting logic before
and after every episode reset without owning the robot or obstacle axes.
TM_Module ABC¶
class TM_Module(TaskMode):
_task: "Task"
def before_reset(self): ...
def after_reset(self): ...
Both hooks default to no-ops. Subclasses override only the ones they need.
_task is the live Task instance, giving modules access to
world_manager, robots_manager, and force_reset().
Modules are instantiated in Task.__init__ from the tm_modules ROS param
(a comma-separated list of Constants.TaskMode.TM_Module values). Each is
registered on MODULE_MODES in
tasks/registry.py.
Package structure¶
Each TM_Module subclass is a package:
__init__.py(eager): declares_NSand (optionally)_declare_schema, then registers the mode onMODULE_MODES(aTaskModeRegistryfromtasks/registry.py) withnamespace=_NSandschema=_declare_schema. Imported at node startup.impl.py(lazy): contains the class body. Imported only on first activation.
Parameters live under task.<mode>.<leaf>.
Shipped modules¶
| Enum value | Class | File | before_reset |
after_reset |
|---|---|---|---|---|
clear_forbidden_zones |
Mod_ClearForbiddenZones |
clear_forbidden_zones/ |
calls world_manager.forbid_clear() |
- |
sounds |
Mod_Sounds |
sounds/ |
- | renders world-, scenario- and launch-declared sound semantic entities |
rviz_ui |
Mod_OverrideRobot |
rviz_ui/ |
- | puts the robot and goal handles |
zone_edit |
Mod_ZoneEdit |
zone_edit/ |
- | puts one drag handle per zone corner |
staged |
Mod_Staged |
staged/ |
loads new stage config when stage index changes; publishes goal_radius and obstacle counts |
- |
Mod_ClearForbiddenZones¶
clear_forbidden_zones/impl.py:4
Clears all dynamically forbidden map cells before each reset so obstacles from the previous episode do not pollute free-cell sampling.
Limitation (multi-level worlds): forbid_clear() with no argument only clears the
global (single-level) map. Per-level forbidden zones written via forbid(..., level_id=X)
accumulate across resets and are not cleared by this module. This is a known gap for
multi-level worlds; a follow-up should extend forbid_clear() to accept level_id="*"
semantics that clears all per-level forbidden zones.
Mod_Sounds¶
A pure renderer, not an owner of state. After each reset it resolves every
sound entity from the loaded world, the active scenario's episode-scoped
sounds: and the world-independent static_sounds launch configuration
through the task generator realizer against the sound catalog
(arena_simulation_setup.tree.assets.sound_catalog), and every 0.1 s hands
one SoundEmission per resolved entity, built from the live
sounding/volume_db semantics the engine already tracks, to the acoustics
simulator's emit_sounds. The wire format and retired-source repeats belong
to the acoustics backend, a removed sound is simply no longer listed. With
acoustics:=none the noop backend drops them. It serves
runtime/spawn_sound and runtime/remove_sound (task_generator_msgs/srv)
for RViz-driven runtime sources. Toggling a declared sound is a SetSemantic
write, not a module service. A runtime sound not attached to a TF frame gets a
drag handle with a Remove menu. Dragging moves the source in place while the
handle moves, so it keeps its entity name and keeps playing.
Mod_OverrideRobot¶
Subscribes to <task_generator_node>/initialpose (PoseWithCovarianceStamped),
<task_generator_node>/goal_pose (PoseStamped), and
<task_generator_node>/clicked_point (PointStamped), all namespaced under
the task_generator node so multiple instances do not cross-talk. Forwards
set-position and set-goal calls to Task.set_robot_position /
set_robot_goal; a clicked point calls task.force_reset(). Provides
interactive RViz-based control without modifying the active task mode.
It also puts drag handles on the node's marker server (the Handles display).
Every robot gets a robot/<robot> handle in its base TF frame, so rviz
carries it along with the robot. Dropping it teleports the robot there and the
handle snaps back onto it. Robots whose task mode has no goal editor of its own
(see TM_Robots.goal_editing_robots) also get a goal/<robot> handle at the
first go-to of their current task. On release it sends that robot alone a
single go-to through Task.submit_task. The goal handle moves through
RobotManager.watch, which fires after every teleport and every dispatched
task. Handles are rebuilt after every reset and on every fleet change. Each
robot keeps one palette color (interactive/colors.py)
for its robot and goal handles, its guided waypoints and its Plan and Trail
displays.
Mod_ZoneEdit¶
Off unless listed in task.modules. After every reset it puts one handle per
zone corner of every loaded level. Dragging a corner rewrites it in the world
description the world manager holds, so placement by zone name follows at
once, and republishes the world overlay (the World display, off by default).
Floors are not respawned, they catch up when the world reloads. Each corner's
menu has "Save world to
world.yaml and leaves every other byte
untouched (utils/zone_corners.py). The next
reset that applies the world reloads it, because the world manager compares
world.yaml mtimes. A world resolved from a download cache is saved into
that cache.
Mod_Staged¶
Reads a curriculum YAML (list of stages, each with static, dynamic, goal_radius, and optional dynamic_map fields). Stage index is
advanced via next_stage / previous_stage ROS topics. before_reset
publishes the stage's goal_radius and obstacle counts as ROS params when
the stage index changes.
Adding a module¶
- Create
tasks/modules/<name>/as a package. - In
__init__.py: declare_NS = _REGISTRY_NAMESPACE("<name>"), define_declare_schema(node, ns)if the module has tunable parameters (using helpers fromarena_rclpy_mixins.declarations), then register the loader with@MODULE_MODES.register(Constants.TaskMode.TM_Module.<NAME>, namespace=_NS, schema=_declare_schema). - In
impl.py: define the class extendingTM_Module; overridebefore_resetand/orafter_reset. - Add
<NAME> = "<name>"toConstants.TaskMode.TM_Moduleinconstants/__init__.py. walk_schemas(module-level intasks/registry.py) picks up your schema automatically at node init.