Skip to content
This repository was archived by the owner on Jul 22, 2021. It is now read-only.

Commit b50b5cd

Browse files
authored
Allow Traffic Light APIs to update the location of a robot while it is idle (#270)
1 parent 163d700 commit b50b5cd

File tree

8 files changed

+145
-29
lines changed

8 files changed

+145
-29
lines changed

rmf_fleet_adapter/CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Changelog for package rmf_fleet_adapter
44

55
1.3.0 (2021-XX-XX)
66
------------------
7+
* Allow Traffic Light APIs to update the location of a robot while it is idle: [#270](https://github.com/osrf/rmf_core/pull/270)
78
* Allow TrafficLight and EasyTrafficLight API to update battery level: [#263](https://github.com/osrf/rmf_core/pull/263)
89
* Migrating to a task dispatcher framework: [#217](https://github.com/osrf/rmf_core/pull/217)
910
* The `rmf_fleet_adapter::agv` component interacts with a dispatcher node over topics with `rmf_task` prefix as specified in `rmf_fleet_adapter/StandardNames.hpp`

rmf_fleet_adapter/include/rmf_fleet_adapter/agv/EasyTrafficLight.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class EasyTrafficLight : public std::enable_shared_from_this<EasyTrafficLight>
3030
public:
3131

3232
/// Update the traffic light with a new path for your robot.
33+
///
34+
/// \warning This function will throw an exception if there are less than 2
35+
/// waypoints in the path.
3336
void follow_new_path(const std::vector<Waypoint>& new_path);
3437

3538
/// This instruction is given for moving updates. It
@@ -124,6 +127,18 @@ class EasyTrafficLight : public std::enable_shared_from_this<EasyTrafficLight>
124127
/// Get the last checkpoint that the traffic light knows has been reached.
125128
std::size_t last_reached() const;
126129

130+
/// Update the location of the robot while it is idle. This means the robot is
131+
/// sitting somewhere without the intention of going anywhere.
132+
///
133+
/// \param[in] map_name
134+
/// The name of the reference map where the robot is located.
135+
///
136+
/// \param[in] position
137+
/// The (x, y, yaw) coordinates of the robot.
138+
EasyTrafficLight& update_idle_location(
139+
std::string map_name,
140+
Eigen::Vector3d position);
141+
127142
/// Update the current battery level of the robot by specifying its state of
128143
/// charge as a fraction of its total charge capacity.
129144
EasyTrafficLight& update_battery_soc(double battery_soc);

rmf_fleet_adapter/include/rmf_fleet_adapter/agv/TrafficLight.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,25 @@ class TrafficLight
5858
/// This function should be called any time the robot changes the path that
5959
/// it intends to follow.
6060
///
61+
/// \warning This function will throw an exception if there are less than 2
62+
/// waypoints in the path.
63+
///
6164
/// \param[in] new_path
6265
/// Submit a new path that the robot intends to follow.
6366
std::size_t follow_new_path(const std::vector<Waypoint>& new_path);
6467

68+
/// Update the location of the robot while it is idle. Calling this function
69+
/// will clear the last path that was given to follow_new_path.
70+
///
71+
/// \param[in] map_name
72+
/// The name of the reference map where the robot is located.
73+
///
74+
/// \param[in] position
75+
/// The (x, y, yaw) coordinates of the robot.
76+
UpdateHandle& update_idle_location(
77+
std::string map_name,
78+
Eigen::Vector3d position);
79+
6580
/// Update the current battery level of the robot by specifying its state of
6681
/// charge as a fraction of its total charge capacity.
6782
UpdateHandle& update_battery_soc(double battery_soc);

rmf_fleet_adapter/src/rmf_fleet_adapter/agv/EasyTrafficLight.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,17 @@ void EasyTrafficLight::Implementation::deadlock(std::vector<Blocker> blockers)
230230
void EasyTrafficLight::Implementation::follow_new_path(
231231
const std::vector<Waypoint>& new_path)
232232
{
233+
clear();
233234
current_path = new_path;
235+
current_checkpoints.resize(new_path.size()-1);
236+
237+
current_version = update_handle->follow_new_path(new_path);
238+
}
239+
240+
//==============================================================================
241+
void EasyTrafficLight::Implementation::clear()
242+
{
243+
current_path.clear();
234244
last_received_checkpoints.reset();
235245
last_received_stop_info.reset();
236246
wait_until->reset();
@@ -239,11 +249,7 @@ void EasyTrafficLight::Implementation::follow_new_path(
239249
on_standby = nullptr;
240250
last_departed_checkpoint.reset();
241251
last_reached = 0;
242-
243252
current_checkpoints.clear();
244-
current_checkpoints.resize(new_path.size()-1);
245-
246-
current_version = update_handle->follow_new_path(new_path);
247253
}
248254

249255
//==============================================================================
@@ -515,6 +521,13 @@ auto EasyTrafficLight::Implementation::waiting_after(
515521
//==============================================================================
516522
void EasyTrafficLight::follow_new_path(const std::vector<Waypoint>& new_path)
517523
{
524+
if (new_path.size() < 2)
525+
{
526+
throw std::runtime_error(
527+
"[EasyTrafficLight::follow_new_path] Invalid number of waypoints given ["
528+
+ std::to_string(new_path.size()) + "]. Must be at least 2.");
529+
}
530+
518531
auto lock = _pimpl->lock();
519532
_pimpl->follow_new_path(new_path);
520533
}
@@ -552,6 +565,19 @@ std::size_t EasyTrafficLight::last_reached() const
552565
return _pimpl->last_reached;
553566
}
554567

568+
//==============================================================================
569+
EasyTrafficLight& EasyTrafficLight::update_idle_location(
570+
std::string map_name,
571+
Eigen::Vector3d position)
572+
{
573+
auto lock = _pimpl->lock();
574+
if (!_pimpl->current_path.empty())
575+
_pimpl->clear();
576+
577+
_pimpl->update_handle->update_idle_location(std::move(map_name), position);
578+
return *this;
579+
}
580+
555581
//==============================================================================
556582
EasyTrafficLight& EasyTrafficLight::update_battery_soc(double battery_soc)
557583
{

rmf_fleet_adapter/src/rmf_fleet_adapter/agv/TrafficLight.cpp

Lines changed: 70 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,15 @@ class TrafficLight::UpdateHandle::Implementation::Data
7878
std::vector<rmf_traffic::Route> plan_itinerary;
7979
std::vector<rmf_traffic::agv::Plan::Waypoint> pending_waypoints;
8080

81+
struct Location
82+
{
83+
std::string map;
84+
Eigen::Vector3d position;
85+
};
86+
8187
// Remember which checkpoint we need to depart from next
8288
std::optional<std::size_t> last_departed_waypoint;
83-
std::optional<Eigen::Vector3d> latest_location;
89+
std::optional<Location> last_known_location;
8490
bool awaiting_confirmation = false;
8591
bool rejected = false;
8692
bool approved = false;
@@ -244,21 +250,21 @@ void TrafficLight::UpdateHandle::Implementation::Data::update_path(
244250
ready_check_timer = nullptr;
245251
waiting_timer = nullptr;
246252
last_departed_waypoint.reset();
247-
latest_location.reset();
248253

249254
if (new_path.empty())
250255
{
251256
// Clear out this robot's itinerary because it is done moving
252257
RCLCPP_INFO(
253258
node->get_logger(),
254259
"Traffic light controlled robot [%s] owned by [%s] is being taken off "
255-
"the schedule after being given a null path.",
260+
"the schedule because it is idle.",
256261
itinerary.description().name().c_str(),
257262
itinerary.description().owner().c_str());
258263

259264
planner = nullptr;
260265
path.clear();
261266
departure_timing.clear();
267+
blockade.cancel();
262268
return;
263269
}
264270
else if (new_path.size() == 1)
@@ -330,7 +336,11 @@ void TrafficLight::UpdateHandle::Implementation::Data::update_path(
330336
rmf_traffic::agv::Plan::Configuration(graph, traits),
331337
rmf_traffic::agv::Plan::Options(nullptr));
332338

333-
latest_location = new_path.front().position();
339+
last_known_location = Location{
340+
new_planner->get_configuration().graph().get_waypoint(0).get_map_name(),
341+
new_path.front().position()
342+
};
343+
334344
rmf_traffic::agv::Plan::Start start{now, 0, new_path.front().position()[2]};
335345

336346
auto approval_cb =
@@ -757,15 +767,15 @@ rmf_traffic::schedule::Negotiation::Alternatives make_reservation_alts(
757767

758768
alts.emplace_back(std::move(preferred_itinerary));
759769

760-
if (data->latest_location.has_value())
770+
if (data->last_known_location.has_value())
761771
{
762772
// TODO(MXG): Should we do something about the situation where we don't know
763773
// the latest location? Maybe just add a stop_and_sit on the first waypoint?
764774
rmf_traffic::Trajectory stop_and_sit;
765775
stop_and_sit.insert(
766-
now, data->latest_location.value(), Eigen::Vector3d::Zero());
776+
now, data->last_known_location->position, Eigen::Vector3d::Zero());
767777
stop_and_sit.insert(
768-
now + 10s, data->latest_location.value(), Eigen::Vector3d::Zero());
778+
now + 10s, data->last_known_location->position, Eigen::Vector3d::Zero());
769779

770780
const auto& graph = data->planner->get_configuration().graph();
771781
const auto& wp = graph.get_waypoint(data->blockade.last_reached());
@@ -1124,7 +1134,11 @@ void TrafficLight::UpdateHandle::Implementation::Data::update_location(
11241134
data->last_departed_waypoint = checkpoint_index;
11251135
}
11261136

1127-
data->latest_location = location;
1137+
data->last_known_location = Location{
1138+
data->planner->get_configuration().graph()
1139+
.get_waypoint(checkpoint_index).get_map_name(),
1140+
location
1141+
};
11281142
data->blockade.reached(checkpoint_index);
11291143

11301144
if (plan_version != data->current_plan_version)
@@ -1651,7 +1665,11 @@ void TrafficLight::UpdateHandle::Implementation::Data::reject(
16511665
// Even if the plan version is wrong, it may still be useful to update this
16521666
// information.
16531667
data->last_departed_waypoint = actual_last_departed;
1654-
data->latest_location = stopped_location;
1668+
data->last_known_location = Location{
1669+
data->planner->get_configuration().graph()
1670+
.get_waypoint(actual_last_departed).get_map_name(),
1671+
stopped_location
1672+
};
16551673

16561674
if (plan_version != data->current_plan_version)
16571675
return;
@@ -1674,13 +1692,13 @@ TrafficLight::UpdateHandle::Implementation::Data::current_location() const
16741692
{
16751693
const auto now = rmf_traffic_ros2::convert(node->now());
16761694

1677-
if (last_departed_waypoint.has_value() && latest_location.has_value())
1695+
if (last_departed_waypoint.has_value() && last_known_location.has_value())
16781696
{
16791697
const auto* lane = planner->get_configuration().graph().lane_from(
16801698
*last_departed_waypoint, *last_departed_waypoint+1);
16811699
assert(lane);
16821700

1683-
const Eigen::Vector3d p = *latest_location;
1701+
const Eigen::Vector3d p = last_known_location->position;
16841702

16851703
return rmf_traffic::agv::Plan::Start(
16861704
now,
@@ -1700,6 +1718,9 @@ TrafficLight::UpdateHandle::Implementation::Data::current_location() const
17001718
void TrafficLight::UpdateHandle::Implementation::Data
17011719
::publish_fleet_state() const
17021720
{
1721+
if (!last_known_location.has_value())
1722+
return;
1723+
17031724
auto robot_mode = [&]()
17041725
{
17051726
if (current_range.begin == current_range.end)
@@ -1718,23 +1739,15 @@ ::publish_fleet_state() const
17181739
.mode_request_id(0);
17191740
}();
17201741

1721-
auto location = [&]() -> rmf_fleet_msgs::msg::Location
1722-
{
1723-
const auto& graph = planner->get_configuration().graph();
1724-
const auto& l = current_location();
1725-
const auto& wp = graph.get_waypoint(l.waypoint());
1726-
const Eigen::Vector2d p = l.location().value_or(wp.get_location());
1727-
1728-
return rmf_fleet_msgs::build<rmf_fleet_msgs::msg::Location>()
1729-
.t(rmf_traffic_ros2::convert(l.time()))
1742+
const auto& map = last_known_location->map;
1743+
const auto p = last_known_location->position;
1744+
auto location = rmf_fleet_msgs::build<rmf_fleet_msgs::msg::Location>()
1745+
.t(node->now())
17301746
.x(p.x())
17311747
.y(p.y())
1732-
.yaw(l.orientation())
1733-
.level_name(wp.get_map_name())
1734-
// NOTE(MXG): This field is only used by the fleet drivers. For now, we
1735-
// will just fill it with a zero.
1748+
.yaw(p[2])
1749+
.level_name(map)
17361750
.index(0);
1737-
}();
17381751

17391752
const auto& fleet_name = itinerary.description().owner();
17401753
auto robot_state = rmf_fleet_msgs::build<rmf_fleet_msgs::msg::RobotState>()
@@ -2005,6 +2018,13 @@ TrafficLight::UpdateHandle::Implementation::make(
20052018
std::size_t TrafficLight::UpdateHandle::follow_new_path(
20062019
const std::vector<Waypoint>& new_path)
20072020
{
2021+
if (new_path.size() < 2)
2022+
{
2023+
throw std::runtime_error(
2024+
"[TrafficLight::follow_new_path] Invalid number of waypoints given ["
2025+
+ std::to_string(new_path.size()) + "]. Must be at least 2.");
2026+
}
2027+
20082028
const std::size_t version = ++_pimpl->received_version;
20092029
_pimpl->data->worker.schedule(
20102030
[version, new_path, data = _pimpl->data](const auto&)
@@ -2015,6 +2035,31 @@ std::size_t TrafficLight::UpdateHandle::follow_new_path(
20152035
return version;
20162036
}
20172037

2038+
//==============================================================================
2039+
auto TrafficLight::UpdateHandle::update_idle_location(
2040+
std::string map,
2041+
Eigen::Vector3d position) -> UpdateHandle&
2042+
{
2043+
const std::size_t version = ++_pimpl->received_version;
2044+
_pimpl->data->worker.schedule(
2045+
[version, map = std::move(map), position, data = _pimpl->data](const auto&)
2046+
{
2047+
if (rmf_utils::modular(version).less_than(data->processing_version))
2048+
return;
2049+
2050+
data->last_known_location =
2051+
Implementation::Data::Location{
2052+
map,
2053+
position
2054+
};
2055+
2056+
if (!data->path.empty())
2057+
data->update_path(version, {});
2058+
});
2059+
2060+
return *this;
2061+
}
2062+
20182063
//==============================================================================
20192064
auto TrafficLight::UpdateHandle::update_battery_soc(double battery_soc)
20202065
-> UpdateHandle&

rmf_fleet_adapter/src/rmf_fleet_adapter/agv/internal_EasyTrafficLight.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class EasyTrafficLight::Implementation
108108

109109
void follow_new_path(const std::vector<Waypoint>& new_path);
110110

111+
void clear();
112+
111113
void accept_new_checkpoints();
112114

113115
std::optional<MovingInstruction> handle_new_checkpoints_moving(

rmf_traffic/include/rmf_traffic/blockade/Participant.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ class Participant
6161
/// checkpoint.
6262
void reached(CheckpointId checkpoint);
6363

64+
/// Cancel the current path entirely. Note that if a path is canceled while
65+
/// the robot is in space that it needs to share with other robots, a
66+
/// permanent deadlock could result.
67+
void cancel();
68+
6469
/// Get the last checkpoint that this participant said it has reached.
6570
CheckpointId last_reached() const;
6671

rmf_traffic/src/rmf_traffic/blockade/Participant.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,13 @@ void Participant::reached(CheckpointId checkpoint)
233233
_pimpl->_send_reached();
234234
}
235235

236+
//==============================================================================
237+
void Participant::cancel()
238+
{
239+
if (_pimpl->_reservation_id)
240+
_pimpl->_writer->cancel(_pimpl->_id, *_pimpl->_reservation_id);
241+
}
242+
236243
//==============================================================================
237244
CheckpointId Participant::last_reached() const
238245
{

0 commit comments

Comments
 (0)