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

Disable automatic retreat #330

Merged
merged 13 commits into from
Apr 18, 2024
Merged

Disable automatic retreat #330

merged 13 commits into from
Apr 18, 2024

Conversation

xiyuoh
Copy link
Member

@xiyuoh xiyuoh commented Mar 12, 2024

This PR aims to

  • Make retreat_to_charger configurable from the fleet config, and
  • Prevents retreat_to_charger from being triggered if the robot's finishing task is already a Charge task

We ran into scenarios where the robot is on its way back to the charger (as part of the finishing task) while its battery level is nearing the recharge threshold. This sometimes triggers a retreat to charger that overrides the finishing task. As a result, the robot is unable to perform other tasks queued later on until its current battery SOC reaches the recharge SOC.

In the retreat_to_charger callback, we now check the fleet's configured behavior before initiating automatic retreat.

This depends on open-rmf/rmf_task#114

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>
Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>
Copy link
Member

@Yadunund Yadunund left a comment

Choose a reason for hiding this comment

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

Thanks for working on the fix but I feel the implementation should be as follows:

  1. FleetUpdateHandle::set_task_planner_params() should accept an std::optional<rmf_traffic::Duration> param.
  2. Add a TaskManager::retreat_to_charger(std::optional<rmf_traffic::Duration> duration) method. If a value is provided this method will execute
    mgr->_retreat_timer = mgr->context()->node()->try_create_wall_timer(
    std::chrono::seconds(10),
    [w = mgr->weak_from_this()]()
    {
    if (auto mgr = w.lock())
    {
    mgr->retreat_to_charger();
    }
    });
    . If std::nullopt, it will call mgr->_retreat_timer.reset() to disable the timer.
  3. Setting the task planner params or using the new setter in FleetUpdateHandle will invoke the method in 2. This would allow for more efficient processing as if the check is not needed, the timer callback will not execute thereby saving some cycles for the ROS 2 executor.

Comment on lines 715 to 720
/// Get whether or not to trigger automatic retreat to charger.
bool retreat_to_charger() const;

/// Set whether or not to trigger automatic retreat to charger.
void set_retreat_to_charger(bool value);

Copy link
Member

Choose a reason for hiding this comment

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

I don't think these APIs should be added here. Instead they should be present in FleetUpdateHandle instead. Users who rely on EasyFullControl API can get a FleetUpdateHandler handler using the EasyFullControl::more() API and set this config.

Copy link
Member

Choose a reason for hiding this comment

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

See comment below on setting/getting this value as an std::optional<rmf_traffic::Duration>.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see, what is stopping us from allowing this to be parsed in automatically via FleetConfiguration? This API is used within set_task_planner_params to retrieve the configured values. Without it, users will have to modify the default value from the fleet adapter implementation itself.

@@ -1628,6 +1628,10 @@ void TaskManager::retreat_to_charger()

if (!task_planner->configuration().constraints().drain_battery())
return;
if (!task_planner->configuration().constraints().retreat_to_charger())
return;
if (_idle_task && _idle_task->request_type() == "Charge")
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't have comparison to magic stings like this. The rmf_task::Request class should have a virtual static member function that should return the request type. Then you can call _idle_task && _idle_task->request_type() == ParkRobotIndefinitely::request_type()

Copy link
Member

Choose a reason for hiding this comment

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

With the proposed refactor, we no longer rely on checking request types.

@@ -308,7 +313,8 @@ class FleetUpdateHandle : public std::enable_shared_from_this<FleetUpdateHandle>
double recharge_threshold,
double recharge_soc,
bool account_for_battery_drain,
rmf_task::ConstRequestFactoryPtr finishing_request = nullptr);
rmf_task::ConstRequestFactoryPtr finishing_request = nullptr,
bool retreat_to_charger = true);
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this should be a bool. Instead it should be the timer period. If this is set to std::nullopt, we reset the timer thereby disabling the automatic retreat to charger behavior. This way we not only expose the ability to enable/disable this feature but also allow users to configure the rate at which the check happens. Else we will need to add another parameter for that.

The docstring above should also be updated and it's worth mentioning the timer there so users know how this check is performed.

Suggested change
bool retreat_to_charger = true);
std::optional<rmf_traffic::Duration> check_retreat_to_charger_interval = rmf_traffic::time::from_seconds(10));

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>
@xiyuoh
Copy link
Member Author

xiyuoh commented Apr 15, 2024

Updated according to the changes requested, without the check for the fleet's configured idle task before deciding whether to proceed with the automatic retreat to charger. I'll work on that in a separate PR.

@xiyuoh xiyuoh requested a review from Yadunund April 15, 2024 07:32
Signed-off-by: Michael X. Grey <mxgrey@intrinsic.ai>
mxgrey
mxgrey previously approved these changes Apr 16, 2024
Copy link
Contributor

@mxgrey mxgrey left a comment

Choose a reason for hiding this comment

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

I've made some changes to prevent the API from breaking and for a few other nitpicky reasons.

I've left in-line comments as part of this review to explain the changes that are being made, but no further action is needed.

@@ -568,6 +574,7 @@ class EasyFullControl::FleetConfiguration
double recharge_threshold,
double recharge_soc,
bool account_for_battery_drain,
std::optional<rmf_traffic::Duration> retreat_to_charger_interval,
Copy link
Contributor

Choose a reason for hiding this comment

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

Adding a field to the constructor breaks the API in a way that isn't backwards compatible.

We should leave this argument out of the constructor and instead provide a set_retreat_to_charger_interval(std::optional<rmf_traffic::Duration>) member function.

We could also consider putting this as the last argument in the constructor and giving it a default value. That would probably be an ABI break, but we can probably have some flexibility on that, as opposed to breaking the API which we should not do for anything less serious than a security vulnerability.

@@ -1753,7 +1743,33 @@ std::function<void()> TaskManager::_make_resume_from_waiting()
}

//==============================================================================
void TaskManager::retreat_to_charger()
void TaskManager::retreat_to_charger(
Copy link
Contributor

Choose a reason for hiding this comment

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

This is somewhat of a nitpick, but I don't like that we're changing the meaning of the retreat_to_charger function. I think I'd prefer we keep the current retreat_to_charger function as it was, and introduce a new function called configure_retreat_to_charger(std::optional<rmf_traffic::Duration>).

}
else
{
const auto retreat_to_charger_interval_sec =
Copy link
Contributor

Choose a reason for hiding this comment

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

This parsing logic doesn't seem very sound to me.

This assumes that if a value is given for retreat_to_charger_interval then it will definitely be a double. I believe this line would throw an exception if it's not a double. We should endeavor to return std::nullopt from this function in cases where things cannot be parsed correctly instead of throwing an exception.

Also this treats values of 0.0 as null, which is ... not an unusual convention historically speaking, but I think it's a practice that should be left behind. Instead we should support the user providing a yaml null value for cases where they want to turn off this behavior.

@@ -45,6 +45,7 @@

#include <rmf_task_sequence/phases/SimplePhase.hpp>

#include <iostream>
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like this was put in for debugging and not needed anymore.

@@ -308,7 +314,8 @@ class FleetUpdateHandle : public std::enable_shared_from_this<FleetUpdateHandle>
double recharge_threshold,
double recharge_soc,
bool account_for_battery_drain,
rmf_task::ConstRequestFactoryPtr finishing_request = nullptr);
rmf_task::ConstRequestFactoryPtr finishing_request = nullptr,
std::optional<rmf_traffic::Duration> retreat_to_charger_interval = std::nullopt);
Copy link
Contributor

Choose a reason for hiding this comment

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

This default value isn't backwards compatible. An old fleet adapter that was using the API as it used to be will unintentionally turn off the retreat behavior after updating to this version of the library.

Since we have a separate function for modifying the interval, let's just keep this fully separate and remove this argument.

if (_retreat_timer)
_retreat_timer->reset();
_retreat_timer = _context->node()->try_create_wall_timer(
duration.value(),
Copy link
Contributor

Choose a reason for hiding this comment

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

We're not checking if duration <= 0, which could cause the timer to behave in ways we don't actually want. We should issue an error message in that situation.


/// Set whether or not to trigger automatic retreat to charger with a
/// duration between checks for automatic retreat.
FleetUpdateHandle& retreat_to_charger_interval(
Copy link
Contributor

Choose a reason for hiding this comment

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

Overloading the function name makes it messy to produce python bindings, so let's add set_ in front of this function.

Signed-off-by: Michael X. Grey <mxgrey@intrinsic.ai>
@xiyuoh xiyuoh merged commit 6dfafa0 into main Apr 18, 2024
3 of 4 checks passed
@xiyuoh xiyuoh deleted the xiyu/disable_automatic_retreat branch April 18, 2024 02:12
arjo129 pushed a commit that referenced this pull request Jun 7, 2024
* Add retreat_to_charger param

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* If idle task is charge, do not retreat to charger

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* Use duration instead of boolean for retreat to charger

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* Undo adding request type

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* Change retreat to timer from bool to optional duration

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* Store param in FleetUpdateHandle

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* Uncrustify and remove request_type from ChargeBattery

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>

* Tweak some details and remove API breakages

Signed-off-by: Michael X. Grey <mxgrey@intrinsic.ai>

* Fix style

Signed-off-by: Michael X. Grey <mxgrey@intrinsic.ai>

---------

Signed-off-by: Xiyu Oh <xiyu@openrobotics.org>
Signed-off-by: Michael X. Grey <mxgrey@intrinsic.ai>
Co-authored-by: Michael X. Grey <mxgrey@intrinsic.ai>
Co-authored-by: Grey <grey@openrobotics.org>
Signed-off-by: Arjo Chakravarty <arjoc@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants