Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RST-2186] Added a FixedLagSmootherParams struct #68

Merged
merged 2 commits into from
Jun 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions fuse_optimizers/include/fuse_optimizers/fixed_lag_smoother.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

#include <fuse_core/graph.h>
#include <fuse_core/transaction.h>
#include <fuse_optimizers/fixed_lag_smoother_params.h>
#include <fuse_optimizers/optimizer.h>
#include <fuse_optimizers/variable_stamp_index.h>
#include <fuse_optimizers/vector_queue.h>
Expand Down Expand Up @@ -112,6 +113,7 @@ class FixedLagSmoother : public Optimizer
{
public:
SMART_PTR_DEFINITIONS(FixedLagSmoother);
using ParameterType = FixedLagSmootherParams;

/**
* @brief Constructor
Expand Down Expand Up @@ -153,30 +155,23 @@ class FixedLagSmoother : public Optimizer
*/
using TransactionQueue = VectorQueue<ros::Time, TransactionQueueElement, std::greater<ros::Time>>;

std::vector<std::string> ignition_sensors_; //!< The set of sensors whose transactions will trigger the optimizer
//!< thread to start running. This is designed to keep the system idle
//!< until the origin constraint has been received.
ros::Duration lag_duration_; //!< Parameter that controls the time window of the fixed lag smoother. Variables
//!< older than the lag duration will be marginalized out
fuse_core::Transaction marginal_transaction_; //!< The marginals to add during the next optimization cycle
ros::Time optimization_deadline_; //!< The deadline for the optimization to complete. Triggers a warning if exceeded.
std::mutex optimization_mutex_; //!< Mutex held while the graph is begin optimized
ros::Duration optimization_period_; //!< The expected time between optimization cycles
std::atomic<bool> optimization_request_; //!< Flag to trigger a new optimization
std::condition_variable optimization_requested_; //!< Condition variable used by the optimization thread to wait
//!< until a new optimization is requested by the main thread
std::mutex optimization_requested_mutex_; //!< Required condition variable mutex
std::atomic<bool> optimization_running_; //!< Flag indicating the optimization thread should be running
std::thread optimization_thread_; //!< Thread used to run the optimizer as a background process
ParameterType params_; //!< Configuration settings for this fixed-lag smoother
TransactionQueue pending_transactions_; //!< The set of received transactions that have not been added to the
//!< optimizer yet. Transactions are added by the main thread, and removed
//!< and processed by the optimization thread.
std::mutex pending_transactions_mutex_; //!< Synchronize modification of the pending_transactions_ container
ros::Time start_time_; //!< The timestamp of the first ignition sensor transaction
bool started_; //!< Flag indicating the optimizer is ready/has received a transaction from an ignition sensor
VariableStampIndex timestamp_tracking_; //!< Object that tracks the timestamp associated with each variable
ros::Duration transaction_timeout_; //!< Parameter that controls how long to wait for a transaction to be processed
//!< successfully before kicking it out of the queue.

// Ordering ROS objects with callbacks last
ros::Timer optimize_timer_; //!< Trigger an optimization operation at a fixed frequency
Expand Down
116 changes: 116 additions & 0 deletions fuse_optimizers/include/fuse_optimizers/fixed_lag_smoother_params.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/***************************************************************************
* Copyright (C) 2019 Locus Robotics. All rights reserved.
* Unauthorized copying of this file, via any medium, is strictly prohibited
* Proprietary and confidential
***************************************************************************/
#ifndef FUSE_OPTIMIZERS_FIXED_LAG_SMOOTHER_PARAMS_H
#define FUSE_OPTIMIZERS_FIXED_LAG_SMOOTHER_PARAMS_H

#include <ros/console.h>
#include <ros/duration.h>
#include <ros/node_handle.h>

#include <algorithm>
#include <string>
#include <vector>


namespace fuse_optimizers
{

/**
* @brief Defines the set of parameters required by the fuse_optimizers::FixedLagSmoother class
*/
struct FixedLagSmootherParams
{
public:
/**
* @brief The set of sensors whose transactions will trigger the optimizer thread to start running
*
* This is designed to keep the system idle until the origin constraint has been received.
*/
std::vector<std::string> ignition_sensors;

/**
* @brief The duration of the smoothing window in seconds
*/
ros::Duration lag_duration { 5.0 };

/**
* @brief The target duration for optimization cycles
*
* If an optimization takes longer than expected, an optimization cycle may be skipped. The optimization period
* may be specified in either the "optimization_period" parameter in seconds, or in the "optimization_frequency"
* parameter in Hz.
*/
ros::Duration optimization_period { 0.1 };

/**
* @brief The topic name of the advertised reset service
*/
std::string reset_service { "reset" };

/**
* @brief The maximum time to wait for motion models to be generated for a received transaction.
*
* Transactions are processed sequentially, so no new transactions will be added to the graph while waiting for
* motion models to be generated. Once the timeout expires, that transaction will be deleted from the queue.
*/
ros::Duration transaction_timeout { 0.1 };

/**
* @brief Helper function that loads strictly positive floating point values from the parameter server
*
* @param[in] node_handle - The node handle used to load the parameter
* @param[in] parameter_name - The parameter name to load
* @param[in] default_value - A default value to use if the provided parameter name does not exist
* @return The loaded (or default) value
*/
double getPositiveParam(const ros::NodeHandle& node_handle, const std::string& parameter_name, double default_value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but if we can't release some of our internal utility methods like this, we should consider adding some of them to a utility header in this package.

{
double value;
node_handle.param(parameter_name, value, default_value);
if (value <= 0)
{
ROS_WARN_STREAM("The requested " << parameter_name << " is <= 0. Using the default value (" <<
default_value << ") instead.");
value = default_value;
}
return value;
}

/**
* @brief Method for loading parameter values from ROS.
*
* @param[in] nh - The ROS node handle with which to load parameters
*/
void loadFromROS(const ros::NodeHandle& nh)
{
// Read settings from the parameter server
nh.getParam("ignition_sensors", ignition_sensors);
std::sort(ignition_sensors.begin(), ignition_sensors.end());

auto lag_duration_sec = getPositiveParam(nh, "lag_duration", lag_duration.toSec());
lag_duration.fromSec(lag_duration_sec);

if (nh.hasParam("optimization_frequency"))
{
auto optimization_frequency = getPositiveParam(nh, "optimization_frequency", 1.0 / optimization_period.toSec());
optimization_period.fromSec(1.0 / optimization_frequency);
}
else
{
auto optimization_period_sec = getPositiveParam(nh, "optimization_period", optimization_period.toSec());
optimization_period.fromSec(optimization_period_sec);
}

nh.getParam("reset_service", reset_service);

auto transaction_timeout_sec = getPositiveParam(nh, "transaction_timeout", transaction_timeout.toSec());
transaction_timeout.fromSec(transaction_timeout_sec);
}
};

} // namespace fuse_optimizers

#endif // FUSE_OPTIMIZERS_FIXED_LAG_SMOOTHER_PARAMS_H
60 changes: 19 additions & 41 deletions fuse_optimizers/src/fixed_lag_smoother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <ros/ros.h>

#include <algorithm>
#include <iterator>
#include <mutex>
#include <string>
#include <thread>
Expand All @@ -50,24 +51,6 @@
namespace fuse_optimizers
{

namespace
{

double getPositiveParam(const ros::NodeHandle& node_handle, const std::string& parameter_name, double default_value)
{
double value;
node_handle.param(parameter_name, value, default_value);
if (value <= 0)
{
ROS_WARN_STREAM("The requested " << parameter_name << " is <= 0. Using the default value (" <<
default_value << ") instead.");
value = default_value;
}
return value;
}

} // namespace

FixedLagSmoother::FixedLagSmoother(
fuse_core::Graph::UniquePtr graph,
const ros::NodeHandle& node_handle,
Expand All @@ -78,27 +61,19 @@ FixedLagSmoother::FixedLagSmoother(
start_time_(ros::TIME_MAX),
started_(false)
{
auto lag_duration = getPositiveParam(private_node_handle_, "lag_duration", 5.0);
lag_duration_.fromSec(lag_duration);

auto optimization_frequency = getPositiveParam(private_node_handle_, "optimization_frequency", 10.0);
optimization_period_ = ros::Duration(1.0 / optimization_frequency);
params_.loadFromROS(private_node_handle);

auto transaction_timeout = getPositiveParam(private_node_handle_, "transaction_timeout", 0.1);
transaction_timeout_.fromSec(transaction_timeout);

private_node_handle_.getParam("ignition_sensors", ignition_sensors_);
// Sort the sensors for efficient lookups
std::sort(ignition_sensors_.begin(), ignition_sensors_.end());
// Warn about possible configuration errors
for (const auto& sensor_model_name : ignition_sensors_)
// TODO(swilliams) Move this warning to the Parameter loadFromROS() method once all parameters are loaded there.
for (const auto& sensor_model_name : params_.ignition_sensors)
{
if (sensor_models_.find(sensor_model_name) == sensor_models_.end())
{
ROS_WARN_STREAM("Sensor '" << sensor_model_name << "' is configured as an ignition sensor, but no sensor "
"model with that name currently exists. This is likely a configuration error.");
}
}

// Test for auto-start
autostart();

Expand All @@ -107,12 +82,15 @@ FixedLagSmoother::FixedLagSmoother(

// Configure a timer to trigger optimizations
optimize_timer_ = node_handle_.createTimer(
optimization_period_,
params_.optimization_period,
&FixedLagSmoother::optimizerTimerCallback,
this);

// Advertise a service that resets the optimizer to its initial state
reset_service_server_ = private_node_handle_.advertiseService("reset", &FixedLagSmoother::resetServiceCallback, this);
reset_service_server_ = node_handle_.advertiseService(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This switch from private_node_handle_ to node_handle_ is intentional. It is in the spirit of locusrobotics/fuse_rl#10

ros::names::resolve(params_.reset_service),
&FixedLagSmoother::resetServiceCallback,
this);
}

FixedLagSmoother::~FixedLagSmoother()
Expand All @@ -129,7 +107,7 @@ FixedLagSmoother::~FixedLagSmoother()

void FixedLagSmoother::autostart()
{
if (ignition_sensors_.empty())
if (params_.ignition_sensors.empty())
{
// No ignition sensors were provided. Auto-start.
started_ = true;
Expand All @@ -147,9 +125,9 @@ std::vector<fuse_core::UUID> FixedLagSmoother::computeVariablesToMarginalize()
{
auto current_stamp = timestamp_tracking_.currentStamp();
auto lag_stamp = ros::Time(0, 0);
if (current_stamp > ros::Time(0, 0) + lag_duration_)
if (current_stamp > ros::Time(0, 0) + params_.lag_duration)
{
lag_stamp = current_stamp - lag_duration_;
lag_stamp = current_stamp - params_.lag_duration;
}
auto old_variables = std::vector<fuse_core::UUID>();
timestamp_tracking_.query(lag_stamp, std::back_inserter(old_variables));
Expand Down Expand Up @@ -234,7 +212,7 @@ void FixedLagSmoother::optimizerTimerCallback(const ros::TimerEvent& event)
{
{
std::lock_guard<std::mutex> lock(optimization_requested_mutex_);
optimization_deadline_ = event.current_expected + optimization_period_;
optimization_deadline_ = event.current_expected + params_.optimization_period;
}
optimization_requested_.notify_one();
}
Expand All @@ -257,13 +235,13 @@ void FixedLagSmoother::processQueue(fuse_core::Transaction& transaction)
// Apply the motion models to the transaction
if (!applyMotionModels(element.sensor_name, *element.transaction))
{
if (element.transaction->stamp() + transaction_timeout_ < current_time)
if (element.transaction->stamp() + params_.transaction_timeout < current_time)
{
// Warn that this transaction has expired, then skip it.
ROS_ERROR_STREAM("The queued transaction with timestamp " << element.transaction->stamp()
<< " could not be processed after " << (current_time - element.transaction->stamp())
<< " seconds, which is greater than the 'transaction_timeout' value of "
<< transaction_timeout_ << ". Ignoring this transaction.");
<< params_.transaction_timeout << ". Ignoring this transaction.");
pending_transactions_.pop_back();
continue;
}
Expand Down Expand Up @@ -324,7 +302,7 @@ void FixedLagSmoother::transactionCallback(
if (!started_)
{
// ...check if we should
if (std::binary_search(ignition_sensors_.begin(), ignition_sensors_.end(), sensor_name))
if (std::binary_search(params_.ignition_sensors.begin(), params_.ignition_sensors.end(), sensor_name))
{
started_ = true;
start_time_ = transaction_time;
Expand All @@ -338,9 +316,9 @@ void FixedLagSmoother::transactionCallback(
{
purge_time = start_time_;
}
else if (ros::Time(0, 0) + transaction_timeout_ < last_pending_time) // ros::Time doesn't allow negatives
else if (ros::Time(0, 0) + params_.transaction_timeout < last_pending_time) // ros::Time doesn't allow negatives
{
purge_time = last_pending_time - transaction_timeout_;
purge_time = last_pending_time - params_.transaction_timeout;
}
while (!pending_transactions_.empty() && pending_transactions_.back_key() < purge_time)
{
Expand Down