Controllers¶
A controller maps the current situation to a velocity command. In the low-level engine it is any callable with this signature:
robot— the B-bot being controlled (has.x,.y,.theta,.position,.max_speed,.max_omega, …).world— the environment (.obstacles,.grid_map,.recharge_places).goal— the current waypoint[x, y, theta]orNone.config— the full configuration dictionary.dt— timestep in seconds.lidar_readings— list of(angle, distance, hit)tuples.
The engine also accepts 5-argument controllers (without lidar_readings) and
falls back automatically.
High-level API
With HiveBrAgent you implement loop(state, neighbors) instead, where the
goal arrives inside state.goal. See the OpenLab API.
Controllers shipped in examples/controllers¶
| Controller | Import | Idea |
|---|---|---|
potential_field_controller |
examples.controllers.potential_field |
Artificial potential fields with collision-recovery FSM, local-minima detection, virtual subgoals and tangential repulsion. |
potential_field_controller |
examples.controllers.potential_field2 |
Lighter potential-field variant. |
vortex_apf_controller |
examples.controllers.vortex_apf |
Vortex artificial potential fields — circulating flux around obstacles to avoid local minima. |
waypoint_follower_controller |
examples.controllers.waypoint_follower_controller |
Pure pursuit / heading tracking to the current waypoint. |
simple_ackermann_controller |
examples.controllers.simple_ackermann_controller |
Steering controller for Ackermann B-bots. |
PlannerController |
examples.controllers.planner_controller |
Universal adapter that turns any path planner into an engine controller, per robot type. |
Using a controller¶
from hivebr.engine import Simulator, load_config
from examples.controllers.potential_field import potential_field_controller
config = load_config("config/default.yaml")
sim = Simulator(config=config, controller=potential_field_controller)
sim.run()
Writing one from scratch¶
import numpy as np
def go_to_goal(robot, world, goal, config, dt, lidar_readings=None):
if goal is None:
return {"v": 0.0, "omega": 0.0}
dx, dy = goal[0] - robot.x, goal[1] - robot.y
desired = np.arctan2(dy, dx)
error = np.arctan2(np.sin(desired - robot.theta),
np.cos(desired - robot.theta))
return {"v": min(0.6, np.hypot(dx, dy)), "omega": 2.0 * error}
Per-robot dispatch in a fleet¶
When several B-bots share one controller, the engine calls the controller once per B-bot, passing that B-bot, its own goal and its own LiDAR readings. This is how examples/multi_robot_differential.py reuses a single Kanayama controller across the fleet.
For global, fleet-wide logic (optimization, task allocation), use
SwarmController instead — see the OpenLab API.