bb_pid_controller provides BB.PID.Controller, a general-purpose PID
implementation of the BB.Controller behaviour for
Beam Bots. For BB framework basics, see bb's rules
(mix usage_rules.sync <file> bb:all); this file covers only what's specific to
the controller.
- It is a declared, supervised controller — not a solver you pass. Wire it
into the
controllersblock as{BB.PID.Controller, opts}. BB wraps it in a supervised process; you never write achild_spec. - It is entirely message-driven. The controller subscribes to a setpoint topic and a measurement topic, and publishes its output to a third topic. It does not touch actuators, joints, or the topology directly — everything is PubSub. One controller instance is exactly one PID loop; declare several for several loops.
- The control law lives in
BB.PID.Kernel, not in the controller. It is anNx.Defnkernel written elementwise over its tensors, so it drives one loop or a batch of them; the controller is the single-loop case. Reach for the kernel directly if you need PID inside a larger Nx computation, or many loops advanced in one call. - It runs its own tick loop at
rateHz, viaBB.Loop. Each tick it publishes only once both a setpoint and a measurement have arrived; until then it stays quiet. The loop schedules against an absolute deadline and drops whole missed periods rather than firing catch-up ticks, and reports its achieved interval and skipped-period count on[:bb, :loop, :tick]. - The integral and derivative terms use the measured elapsed time between
steps, not the nominal
rate. Gains are therefore per-second:kiis integral gain per second of accumulated error,kdis derivative gain per second of error change.
controllers do
controller :shoulder_pid, {BB.PID.Controller,
kp: 2.0, ki: 0.1, kd: 0.05,
output_min: -1.0, output_max: 1.0,
setpoint_topic: [:actuator, :base_link, :shoulder, :pid],
setpoint_message: BB.Message.Actuator.Command.Position,
setpoint_path: [:position],
measurement_topic: [:sensor, :base_link, :shoulder, :encoder],
measurement_message: BB.Message.Sensor.JointState,
measurement_path: [:positions, 0],
output_topic: [:actuator, :base_link, :shoulder, :servo],
output_message: BB.Message.Actuator.Command.Velocity,
output_field: :velocity,
output_frame_id: :shoulder,
rate: 100}
endmix igniter.install bb_pid_controller scaffolds this entry with kp/ki/kd
wired to tunable parameters and the topic paths left as [:TODO] to fill in.
| Option | Default | Meaning |
|---|---|---|
kp |
required | Proportional gain |
ki |
0.0 |
Integral gain |
kd |
0.0 |
Derivative gain |
tau |
1.0 |
Derivative low-pass filter (0–1, 1 = no filter) |
output_min / output_max |
-1.0 / 1.0 |
Output clamp bounds |
setpoint_topic / setpoint_message / setpoint_path |
required | Where the target comes from, the message type to match, and the path to the value in its payload |
measurement_topic / measurement_message / measurement_path |
required | Same three for the feedback signal |
output_topic / output_message / output_field / output_frame_id |
required | Topic to publish to, message type to build, numeric field for the output, and its frame_id |
rate |
100 |
Loop frequency in Hz. The loop self-corrects for drift; watch :skipped on [:bb, :loop, :tick] to see whether the machine is actually keeping up |
A *_path is a list of atoms (field names) and integers (list indices):
[:position], [:positions, 0], [:data, :readings, 0, :value].
The gains are plain floats, but you can point them at robot parameters —
kp: param([:config, :shoulder_pid, :kp]) — to make them tunable at runtime;
handle_options/2 rebuilds the PID state when a parameter changes.
- Don't expect it to run in simulation by default. DSL-declared controllers
default to
simulation: :omit, so a robot started in simulation mode does not start this controller. Addsimulation: :mockorsimulation: :startto the entry if you need it under simulation. - Don't give the setpoint and measurement the same source. Init fails if
{setpoint_topic, setpoint_message}equals{measurement_topic, measurement_message}; paths must be non-empty; andoutput_fieldmust name a numeric field onoutput_messageor init rejects the config. - Don't rely on it stopping when the robot disarms. This controller does not
implement
disarm/1and does not register withBB.Safety— it keeps ticking and publishing regardless of safety state. If its output must cease on disarm, that has to come from the downstream actuator, not from here.
- bb_pid_controller docs
bb's controller and safety rules (bb:safety-and-commands) and Reactive Controllers