Skip to content

beam-bots/bb_pid_controller

Beam Bots Logo

Beam Bots PID Controller

CI License: Apache 2.0 Hex version badge REUSE status

A general-purpose PID controller for the Beam Bots robotics framework.

This library provides a BB.Controller implementation that subscribes to configurable topics for setpoint and measurement values, runs a periodic PID loop, and publishes output to a configurable topic. One controller instance = one PID loop.

Features

  • Configurable subscriptions - subscribe to any topic/message combination for setpoint and measurement
  • Flexible path extraction - extract values from nested message fields or list indices
  • Configurable output - publish to any topic with any numeric message field
  • Runtime parameter updates - PID gains can be changed at runtime via BB parameters
  • Validation - validates configuration at init time with clear error messages

Installation

Add bb_pid_controller to your list of dependencies in mix.exs:

def deps do
  [
    {:bb_pid_controller, "~> 0.2.0"}
  ]
end

Requirements

  • BB framework (~> 0.13)

Usage

Define a PID controller in your robot DSL:

defmodule MyRobot do
  use BB

  controller :shoulder_pid, {BB.PID.Controller,
    kp: 2.0, ki: 0.1, kd: 0.05,
    output_min: -1.0, output_max: 1.0,

    # Subscribe to position commands
    setpoint_topic: [:actuator, :base_link, :shoulder, :pid],
    setpoint_message: BB.Message.Actuator.Command.Position,
    setpoint_path: [:position],

    # Subscribe to encoder feedback
    measurement_topic: [:sensor, :base_link, :shoulder, :encoder],
    measurement_message: BB.Message.Sensor.JointState,
    measurement_path: [:positions, 0],

    # Publish velocity commands to servo
    output_topic: [:actuator, :base_link, :shoulder, :servo],
    output_message: BB.Message.Actuator.Command.Velocity,
    output_field: :velocity,
    output_frame_id: :shoulder,

    rate: 100
  }

  topology do
    link :base_link do
      joint :shoulder, type: :revolute do
        link :upper_arm do
        end
      end
    end
  end
end

Sending Setpoints

Send position setpoints to the PID controller via pubsub:

{:ok, msg} = BB.Message.new(
  BB.Message.Actuator.Command.Position,
  :shoulder,
  position: 1.57
)

BB.PubSub.publish(MyRobot, [:actuator, :base_link, :shoulder, :pid], msg)

The controller will compute the PID output and publish velocity commands to the servo.

Configuration Options

PID Gains

Option Type Default Description
kp float required Proportional gain
ki float 0.0 Integral gain
kd float 0.0 Derivative gain
tau float 1.0 Derivative low-pass filter (0-1, 1=no filter)
output_min float -1.0 Output clamp minimum
output_max float 1.0 Output clamp maximum

Setpoint Subscription

Option Type Description
setpoint_topic [atom] Topic path to subscribe to
setpoint_message module Message module to filter for
setpoint_path [atom | integer] Path to value in payload

Measurement Subscription

Option Type Description
measurement_topic [atom] Topic path to subscribe to
measurement_message module Message module to filter for
measurement_path [atom | integer] Path to value in payload

Output Publication

Option Type Description
output_topic [atom] Topic path to publish to
output_message module Message module to construct
output_field atom Field name for output value
output_frame_id atom frame_id for constructed messages

Control Loop

Option Type Default Description
rate pos_integer 100 Control loop frequency (Hz)

Path Extraction

The *_path options support atoms (field names) and integers (list indices):

# Simple field access
setpoint_path: [:position]           # payload.position

# List index access
measurement_path: [:positions, 0]    # payload.positions |> Enum.at(0)

# Nested access
path: [:data, :readings, 0, :value]  # payload.data.readings[0].value

How It Works

Architecture

Setpoint Topic ─────────────────────────┐
(configurable message/field)            │
                                        ▼
                               ┌─────────────────┐
Measurement Topic ────────────►│ BB.PID.Controller│
(configurable message/field)   │                  │
                               │   PIDControl     │
                               │   .step()        │
                               └────────┬─────────┘
                                        │
                                        ▼
                               Output Topic
                               (configurable message/field)

Message Flow

  1. Controller subscribes to setpoint_topic and measurement_topic at init
  2. When a message arrives on setpoint_topic with matching setpoint_message type, the setpoint value is extracted and stored
  3. When a message arrives on measurement_topic with matching measurement_message type, the measurement value is extracted and stored
  4. Every 1000/rate ms, if both setpoint and measurement exist:
    • PID step is computed: output = Kp*error + Ki*integral + Kd*derivative
    • Output is clamped to [output_min, output_max]
    • Output message is constructed and published to output_topic

Validation

The controller validates configuration at init time:

  1. Unique sources - {setpoint_topic, setpoint_message} must differ from {measurement_topic, measurement_message}
  2. Non-empty paths - setpoint_path and measurement_path cannot be empty
  3. Valid output field - output_field must exist in output_message schema and be numeric

Common Use Cases

Position Control with Velocity Output

Use encoder feedback to control position, outputting velocity commands:

controller :joint_pid, {BB.PID.Controller,
  kp: 5.0, ki: 0.5, kd: 0.1,
  setpoint_topic: [:actuator, :joint, :pid],
  setpoint_message: BB.Message.Actuator.Command.Position,
  setpoint_path: [:position],
  measurement_topic: [:sensor, :joint, :encoder],
  measurement_message: BB.Message.Sensor.JointState,
  measurement_path: [:positions, 0],
  output_topic: [:actuator, :joint, :motor],
  output_message: BB.Message.Actuator.Command.Velocity,
  output_field: :velocity,
  output_frame_id: :joint
}

Velocity Control with Effort Output

Use velocity feedback to control velocity, outputting effort commands:

controller :velocity_pid, {BB.PID.Controller,
  kp: 1.0, ki: 0.1,
  setpoint_topic: [:actuator, :wheel, :velocity_cmd],
  setpoint_message: BB.Message.Actuator.Command.Velocity,
  setpoint_path: [:velocity],
  measurement_topic: [:sensor, :wheel, :encoder],
  measurement_message: BB.Message.Sensor.JointState,
  measurement_path: [:velocities, 0],
  output_topic: [:actuator, :wheel, :motor],
  output_message: BB.Message.Actuator.Command.Effort,
  output_field: :effort,
  output_frame_id: :wheel
}

Documentation

Full documentation is available at HexDocs.

About

PID controller for Beam Bots

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Languages