Beam Bots Logo

BB.Parameter.Store.CubDB

CI License: Apache 2.0 Hex version badge Hexdocs badge REUSE status

CubDB-backed parameter persistence for the Beam Bots robotics framework.

Tune a gain with BB.Parameter.set/3, and it's still there after a reboot.

Why CubDB

bb ships BB.Parameter.Store.Dets, which is fine when the machine shuts down politely. Robots don't always. CubDB stores its data in an append-only, immutable B-tree, so an unexpected shutdown can't corrupt the database or leave it needing a repair pass — which matters when the off switch is "it fell over". Keys and values are arbitrary terms, so parameter paths and BB.Unit values are stored as-is, and writes are flushed to disk as they happen.

Installation

mix igniter.install bb_parameter_store_cubdb

That adds the extension to your robot and declares a parameter_store_cubdb section, pointing at /root on a Nerves project and the build directory otherwise.

Or add it to mix.exs by hand:

def deps do
[
{:bb_parameter_store_cubdb, "~> 0.1.0"}
]
end

Usage

Add the extension to your robot and say where the database lives:

defmodule MyRobot.Robot do
use BB, extensions: [BB.Parameter.Store.CubDB.Dsl]
parameter_store_cubdb do
data_dir "/root/my_robot_params"
end
parameters do
group :balance do
param :kp, type: :float, default: 180.0, min: 0.0, max: 500.0
param :kd, type: :float, default: 4.0, min: 0.0, max: 50.0
end
end
topology do
link :base_link
end
end

Then tune away, and the values will be there next boot:

iex> BB.Parameter.set(MyRobot.Robot, [:balance, :kp], 200.0)
:ok

Declaring the section is all that's needed — it sets the robot's parameter_store setting for you. If you'd rather configure the store directly and skip the extension, that works too:

settings do
parameter_store {BB.Parameter.Store.CubDB, data_dir: "/root/my_robot_params"}
end

Options

Option Default Description
data_dir required Directory to store the database in, created if it doesn't exist
auto_compact true Compact automatically. true, false, or {min_write_operations, min_dirt_factor}
auto_file_sync true Flush the disk buffer on every write. Turning this off trades durability for write throughput

Where to put the data directory

On Nerves, use a path under /root — the application data partition. The firmware's root filesystem is read-only, and bb only logs a warning when a store fails to open, so a robot pointed at an unwritable path will boot with persistence quietly disabled.

Which is a problem if the same robot module has to run on your laptop and on the device, because /root isn't writable on the one and _build doesn't exist on the other. Take the directory from the application environment instead:

parameter_store_cubdb do
data_dir Application.compile_env(:my_app, :params_dir)
end

Then give each environment and target its own value. In a Nerves project, config/config.exs ends with import_config "#{Mix.target()}.exs", so the per-target files are where the paths go:

# config/host.exs
config :my_app, params_dir: Path.expand("_build/params")
# config/rpi0_2.exs
config :my_app, params_dir: "/root/params"

For a plain (non-Nerves) project the same trick works per Mix.env(), with config/dev.exs and config/test.exs setting their own directories — handy for keeping a test run from inheriting the gains you tuned in dev.

Two things to know about this:

Bounds aren't rechecked on load

bb applies persisted values without revalidating them against each parameter's min/max. A value that was in bounds when it was written will still be in bounds when it's read, so this only bites if something edits the database directly.

Licence

Apache-2.0. See LICENSE.txt.