osrf / rmf_core

Provides the centralized functions of RMF: scheduling, etc.
Apache License 2.0
102 stars 41 forks source link

Enrolment of fleets on the fly #273

Closed txlei closed 3 years ago

txlei commented 3 years ago

how do I "enroll" more fleets on the fly even after all the rmf core packages had been launched? assume an API app is built to CRUD fleet adapters as the user does not know which fleets are required on startup. Could you advise whether it is possible to launch fleet adapters using the fleet adapter launch file using the rclpy library instead of using ros2 run or ros2 launch terminal commands? This is related to spinning and destroying nodes during runtime.

mxgrey commented 3 years ago

assume an API app is built to CRUD fleet adapters as the user does not know which fleets are required on startup

This will depend entirely on the requirements of any given deployment. In production environments it's normal for all fleets to be known ahead of time. If an entire new fleet is introduced into production, I generally expect system integrators will be involved in launching it into the environment and that they will likely spin up the fleet adapter for their new fleet manually.

All that being said, RMF core does not impose any limits that require you to launch every new fleet adapter using ros2 launch. The fleet adapter API is flexible by design and could support runtime launching of new fleet adapters if that's desirable. However, you'd have to use the C++ API to do this, and all those dynamically generated fleet adapters would be running in the same process and using the same ROS2 nodes, which might not always be desirable.

Would you be able to elaborate on your need to dynamically add new fleets into the system at runtime? Are there certain constraints or requirements that prevent you from knowing all of the participating fleets beforehand, or that require the addition of new fleets to be done by some kind of lifecycle management script instead of by a human operator? Maybe if I understand the requirements more clearly, I can provide a better recommendation.

txlei commented 3 years ago

the goal is to build an adapter(s) for a new type of traffic participants, i.e. cones, road blocks, wheelchair pedestrians that would be detected by cameras within building infra. A simple use case would be to automatically identify road blocks ahead of time to prevent robot from entering the blocked lane, without needing operator to manually block out that area temporarily. I briefly understand from @codebot that rmf traffic map is designed to load statically for now, and the design for dynamic map (closer to real world) is currently in progress.

Hence, for now my plan is to utilise the read-only fleet adapter as reference and modify from there because it is already interfaced with the traffic module and the read-only adapter means it would not participant in traffic negotiation. I would also try to utilise the slotcar plugin to add the road block model in the traffic map. Currently, I need help with the above issue mentioned, how to spin and destroy nodes during runtime. eg. how does a python/ cpp application spins fleet_adapter nodes?

However, you'd have to use the C++ API to do this, and all those dynamically generated fleet adapters would be running in the same process and using the same ROS2 nodes, which might not always be desirable.

could you let me know which library or method to use? and also elaborate more on the implications on using the same ROS2 nodes dynamically generated?

mxgrey commented 3 years ago

Thanks for elaborating on the goals; that gives me a much more clear picture of the intentions. I suggest that this problem should be tackled in two phases:

  1. The first phase can just leverage the traffic management system as it currently exists and shove the obstacles into the schedule as "read-only" traffic participants. This should be a good way to quickly get a proof of concept finished with minimal effort and no changes to RMF as it currently exists.

  2. In the second phase I would recommend having a completely different pathway for dealing with static obstacles that are identified by external sensors. The traffic management system makes certain assumptions about the behavior of traffic "participants", and those assumptions are largely violated by static obstacles in the environment. For example, it's assumed that traffic participants are generally going to be moving around in a predictable way; not indefinitely stationed in a single location. It's also assumed that (most) traffic participants can engage in a "negotiation" so that they can give way to each other when necessary (although the "read-only" category of participant is an exception to this). From the perspective of a dynamic motion planner, it would be very useful to know that these are static obstacles and not dynamic traffic participants. The planning could be significantly optimized if it were provided with that distinction. However, this would involve having a separate path (i.e. a different server node, topics, and message definitions) for communicating about these obstacles. That will be a significant development effort, although a worthwhile one.

Circling back on how to approach this problem for the first phase, I do not recommend implementing your system as a "fleet". Instead I would make a custom ROS2 node that uses rmf_traffic_ros2::schedule::Writer directly and uses make_participant to create traffic schedule participants. After that you can use the rmf_traffic::schedule::Participant API to tell the schedule where the obstacles are located. You'll need to construct trajectories for the obstacles, but it's very easy to create stationary trajectories, e.g.:

rmf_traffic::Trajectory trajectory;
const auto now = get_current_time();
const auto p = get_obstacle_position();
trajectory.insert(now, p, {0, 0, 0});
trajectory.insert(now + std::chrono::hours(5), p, {0, 0, 0});

I recommend constructing the trajectory to say that the obstacle will be there for some number of hours to reflect the belief that the obstacle will be there for a very long time. If it's possible to predict when the obstacle will go away, then the second waypoint in the trajectory should have that predicted timestamp instead.

One last detail to make phase 1 work: Since the obstacles are going to be "traffic participants" they need to "participate" in the traffic negotiation. You'll need to have a rmf_traffic_ros2::schedule::Negotiation instance that you register your obstacle negotiators with. Since the obstacles are in the "read-only" category, you can simply use the premade StubbornNegotiator implementation for their negotiators. The implementation of that negotiator effectively just says "No, I won't move anywhere; you just need to deal with it," every time it gets involved in a traffic negotiation. Since the StubbornNegotiator requires a view of the schedule, you will also need to create a MirrorManager to get (and maintain) a local copy of the traffic schedule.

You can refer to the legacy read_only fleet adapter implementation as a reference on how to construct and use some of these classes, but I don't recommend using that node directly because it uses the legacy Fleet Driver API which is considered deprecated.

mxgrey commented 3 years ago

Closing as this is not an issue. To continue discussing this topic, please open a discussion.