Bota Control Toolkit C++
Definitions
BotaControlBlock
BotaControlBlock is defined as a parent class from which all control blocks inherit.
-
class BotaControlBlock
Abstract base class providing the interface for all control processing blocks.
Constructor and Destructor
-
BotaControlBlock(const std::string &config_path) = default
Default constructor with path to JSON configuration file. Prefered method for instantiating blocks.
-
BotaControlBlock(const nlohmann::json &config_json) = default
Default constructor with a JSON object containing the configuration data.
-
BotaControlBlock(const BotaControlConfig &config_object) = default
Default constructor with a configuration object populated somewhere else in the code.
-
virtual ~BotaControlBlock() = default
Virtual destructor for proper inheritance handling.
Runtime Update Methods
-
virtual BotaControlReturnCode update(std::span<const double> input_signal, std::span<double> output_signal) = 0
Reads input data and performs processing.
- Parameters:
input_signal – Reference to a span containing the input signal data.
output_signal – Reference to a span where the processed output signal should be stored.
- Returns:
A return code indicating status of the update operation.
-
virtual BotaControlReturnCode updateInplace(std::span<double> signal) = 0
Reads data and performs processing in place.
- Parameters:
signal – Reference to a span containing the signal data to be processed in place.
- Returns:
A return code indicating status of the update operation.
-
virtual BotaControlReturnCode updateObserver(std::span<const double> measurement, std::span<double> state_estimate) = 0
Reads input data and performs processing.
- Parameters:
measurement – Reference to a span containing the measurement data.
state_estimate – Reference to a span where the state estimate should be stored.
- Returns:
A return code indicating status of the update operation.
-
virtual BotaControlReturnCode updateController(std::span<const double> reference, std::span<const double> state_estimate, std::span<double> control_action) = 0
Reads input data and performs processing.
- Parameters:
reference – Reference to a span containing the reference data.
state_estimate – Reference to a span containing the state estimate data.
control_action – Reference to a span where the control action should be stored.
- Returns:
A return code indicating status of the update operation.
In-line configuration methods
The specific implementations of the control blocks may include additional in-line configuration methods that allow users to modify block parameters at configuration or runtime without needing to re-instantiate the block. These methods are specific to each block and are not part of the base class interface.
-
BotaControlBlock(const std::string &config_path) = default
BotaControlReturnCode
Pipeline Wiring
Here’s how to connect multiple blocks to create a processing pipeline:
////////////////////////////////////////////////////////////////////////////////////
// EXAMPLE OF A BUNDLED CONTROL PIPELINE USING THE BOTA CONTROL BLOCKS //
///////////////////////////////////////////////////////////////////////////////////
#include <chrono>
#include <thread>
// Define signals - they can be std::array or std::vector as long as they are std::span compatible
std::array<double, N> signal_1;
std::array<double, M> signal_2;
std::vector<double> signal_3;
// Path to the JSON configuration file for the blocks
std::string config_1 = "path/to/config_1.json";
std::string config_2 = "path/to/config_2.json";
std::string config_3 = "path/to/config_3.json";
// Instantiation & Configuration at initialization time
std::unique_ptr<Block1> block_1 = std::make_unique<Block1>(config_1);
std::unique_ptr<Block2> block_2 = std::make_unique<Block2>(config_2);
std::unique_ptr<Block3> block_3 = std::make_unique<Block3>(config_3);
// Block in-line static configuration
block_1->setParameter(value_1);
block_2->setParameter(value_2);
block_3->setParameter(value_3);
// Set the desired processing rate
const double rate_hz = 500.0; // 500 Hz
const auto dt = std::chrono::duration<double>(1.0 / rate_hz);
bool running = true;
// loop reactor lamda function to handle return codes and control flow
auto loop_reactor = [&](BotaControlReturnCode code) {
switch (code) {
case BotaControlReturnCode::OK:
// Normal operation, continue processing
break;
{...other cases...}
case BotaControlReturnCode::FatalError:
// Handle fatal errors (e.g., log the error, shut down the system safely, etc.)
running = false; // Example: stop the loop on fatal error
break;
}
while (running) {
auto start_time = std::chrono::steady_clock::now();
// Update signals with new data here (e.g., from sensors, reference generators, etc.)
signal_1 = getNewDataForSignal1();
// Process data through the pipeline
loop_reactor(block_1->update(signal_1, signal_2));
//=> signal_2 freshly updated by block_1 algorithm can be used HERE
loop_reactor(block_2->updateInplace(signal_2));
//=> signal_2 freshly updated by block_2 algorithm can be used HERE
loop_reactor(block_3->update(signal_2, signal_3));
//=> signal_3 freshly updated by block_3 algorithm can be used HERE
////////////////////////
// Control logic here //
////////////////////////
// Rate control - sleep to maintain desired frequency
auto elapsed_time = std::chrono::steady_clock::now() - start_time;
auto sleep_time = dt - elapsed_time;
if (sleep_time > std::chrono::duration<double>::zero()) {
std::this_thread::sleep_for(sleep_time);
}
}
Distribution
The Bota Control Toolkit definitions are distributed as a collection of header files, available here.
Important
They must be included in every C++ project using any of the Bota Control Toolkit blocks.