Tutorial 7: Parameters Deep Dive (C++)
Parameters let you change robot behavior at runtime without recompiling. Tune PID gains, adjust speed limits, enable/disable features — all while the robot is running.
What You'll Learn
horus::Paramsfor typed key-value storageget<T>()with defaults for safe access- Live tuning from
horus paramCLI - Organizing parameters by subsystem
Basic Usage
#include <horus/horus.hpp>
using namespace horus::literals;
class TunableController : public horus::Node {
public:
TunableController(horus::Params& params)
: Node("tunable_ctrl"), params_(params)
{
cmd_sub_ = subscribe<horus::msg::CmdVel>("cmd_vel");
out_pub_ = advertise<horus::msg::CmdVel>("motor.cmd");
}
void tick() override {
// Read parameters every tick — picks up live changes
double max_speed = params_.get<double>("max_speed", 0.5);
double gain = params_.get<double>("controller_gain", 0.8);
bool enabled = params_.get<bool>("motor_enabled", true);
if (!enabled) {
horus::msg::CmdVel stop{};
out_pub_->send(stop);
return;
}
auto cmd = cmd_sub_->recv();
if (!cmd) return;
double scaled = cmd->get()->linear * gain;
scaled = std::min(scaled, max_speed);
horus::msg::CmdVel out{};
out.linear = static_cast<float>(scaled);
out_pub_->send(out);
}
private:
horus::Params& params_;
horus::Subscriber<horus::msg::CmdVel>* cmd_sub_;
horus::Publisher<horus::msg::CmdVel>* out_pub_;
};
int main() {
horus::Params params;
// Set defaults
params.set("max_speed", 0.5);
params.set("controller_gain", 0.8);
params.set("motor_enabled", true);
params.set("robot_name", "atlas");
horus::Scheduler sched;
sched.tick_rate(100_hz);
TunableController ctrl(params);
sched.add(ctrl).order(10).build();
sched.spin();
}
Supported Types
| Type | set() | get<T>() | get_*() |
|---|---|---|---|
double | set("key", 1.5) | get<double>("key", 0.0) | get_f64("key") → optional<double> |
int64_t | set("key", int64_t(42)) | get<int64_t>("key", 0) | get_i64("key") → optional<int64_t> |
bool | set("key", true) | get<bool>("key", false) | get_bool("key") → optional<bool> |
string | set("key", "value") | get<std::string>("key", "") | get_string("key") → optional<string> |
Organizing Parameters
Group by subsystem:
// Locomotion
params.set("loco.max_speed", 0.5);
params.set("loco.max_angular", 1.0);
params.set("loco.wheel_base", 0.3);
// PID
params.set("pid.kp", 2.0);
params.set("pid.ki", 0.1);
params.set("pid.kd", 0.05);
// Safety
params.set("safety.min_distance", 0.3);
params.set("safety.estop_enabled", true);
Pattern: Parameter Validation
void init() override {
double kp = params_.get<double>("pid.kp", -1.0);
if (kp < 0) {
horus::log::error("pid", "pid.kp must be >= 0");
return;
}
double max_speed = params_.get<double>("loco.max_speed", 0.5);
if (max_speed > 2.0) {
horus::log::warn("pid", "max_speed > 2.0 m/s — are you sure?");
}
}
Key Takeaways
- Read params every tick — captures live changes
- Always provide defaults:
get<double>("key", 0.0)never fails - Use dotted naming (
pid.kp,loco.max_speed) to organize - Validate in
init()— log warnings for dangerous values horus::Paramsis thread-safe for concurrent read/write