Dynamic Charging (#489)

Posted by @DDDWY:

[New release of Open-RMF on iron and rolling]

Dynamic Charging: You can dynamically update which chargers each robot should use while idle (or provide a location for them to park). We also provide a charger schedule node that lets you pass in a schedule configuration (.yaml) and will automatically send out charger updates according to the schedule.

Could you please provide an example of dynamic charging?

Additionally, I have a scenario where a robot needs to determine its next action based on its battery level after completing a task. If the battery level is sufficient, the robot should go to a parking spot; if the battery level is insufficient, it should go to charge. How can this be implemented?

The prerequisite is that I hope RMF can help me choose the appropriate charging station or parking spot.

If you could reply, I would be very grateful.


Edited by @DDDWY at 2024-07-05T06:23:10Z

Chosen answer

Answer chosen by @DDDWY at 2024-07-16T08:15:47Z.
Answered by @xiyuoh:

I’ve tried out your config with rmf_demos Office world, and I found that the robot will still go to the charger if the recharge_soc is set to 1.0. Meaning that whenever the robot is detected to fall below the recharge_soc, it will head to the charger. You could try tweaking this value and bringing it down to an acceptable SOC.

Posted by @xiyuoh:

Hi there! Dynamic charging is a feature that allows robots to switch between their designated chargers at different periods throughout the day. Prior to this, each robot in the fleet is typically assigned to a specific charger in its fleet config (example for Office demos).

Could you please provide an example of dynamic charging?

To use the dynamic charging feature, you would have to bring up the ROS 2 node and provide a charging schedule yaml config. For example,

ros2 run rmf_charging_schedule charging_schedule schedule.yaml

The schedule.yaml config may look something like this:

tinyRobot:
  "00:00": { tinyRobot1: tinyRobot1_charger, tinyRobot2: tinyRobot2_charger }
  "08:00": { tinyRobot1: tinyRobot2_charger, tinyRobot2: pantry }
  "16:00": { tinyRobot2: tinyRobot1_charger }

parking: ["pantry"]

This would mean tinyRobots 1 and 2 will head to chargers 1 and 2 respectively at 00:00 when they are assigned a charging task (by RMF). From 08:00 to 15:59, whenever given a charging task, tinyRobot1 would head to tinyRobot2_charger for charging, and tinyRobot2 would head to the pantry waypoint. Since there are no chargers at pantry, the robot would simply park there and not charge. From 16:00 to 23:59, tinyRobot2 would head to tinyRobot1_charger whenever a charging task is assigned, and tinyRobot1 would remain at tinyRobot2_charger.

By adding the last line parking: ["pantry"], we are indicating that the pantry waypoint is a parking spot. This helps to ensure that the correct robot status is reflected. When the robot is charging, the status would be CHARGING. When the robot is parking, the status would be IDLE

For more details about formatting the schedule yaml file, you may refer to the package README.

Additionally, I have a scenario where a robot needs to determine its next action based on its battery level after completing a task. If the battery level is sufficient, the robot should go to a parking spot; if the battery level is insufficient, it should go to charge. How can this be implemented?

To achieve this, we won’t need to use the dynamic charging feature. You can configure your robot fleet’s idle behavior using the finishing_request field in the fleet config to park, so that whenever the robot is free, it will park at any waypoint that is indicated to be a parking spot instead of charging. Do also make sure that account_for_battery_drain is set to True.

If the robot’s current battery SOC falls below a certain level, a charging task would automatically be assigned by RMF. This value is derived from the recharge_threshold configured, after adding some safety factor to make sure that the robot has enough battery to reach the charger, so you might want to modify this value too. It might also be helpful to point out that you can set the battery level that the robot should charge up to using recharge_soc.

This automatic retreat to charger can be configured or disabled by setting a retreat_to_charger_interval parameter in the fleet config. If a null value is provided, the fleet adapter will not trigger a retreat even if the robot’s battery level falls below the recharge threshold. By providing an integer X, the fleet adapter would check the robot’s battery level every X seconds to determine whether a retreat is necessary.

Let me know if this works for your use case or if you have any other questions.

Posted by @DDDWY:

Here is my configuration:

rmf_fleet:
  name: "tinyRobot"
  limits:
    linear: [0.5, 0.75] # velocity, acceleration
    angular: [0.6, 2.0] # velocity, acceleration
  profile: # Robot profile is modelled as a circle
    footprint: 0.5 # radius in m
    vicinity: 0.6 # radius in m
  reversible: False # whether robots in this fleet can reverse
  battery_system:
    voltage: 12.0 # V
    capacity: 24.0 # Ahr
    charging_current: 5.0 # A
  mechanical_system:
    mass: 20.0 # kg
    moment_of_inertia: 10.0 #kgm^2
    friction_coefficient: 0.22
  ambient_system:
    power: 20.0 # W
  tool_system:
    power: 0.0 # W
  recharge_threshold: 0.2 # Battery level below which robots in this fleet will not operate
  retreat_to_charger_interval: 30
  recharge_soc: 1.0 # Battery level to which robots in this fleet should be charged up to during recharging tasks
  publish_fleet_state: 5.0 # Publish frequency for fleet state, ensure that it is same as robot_state_update_frequency
  account_for_battery_drain: True
  task_capabilities: # Specify the types of RMF Tasks that robots in this fleet are capable of performing
    loop: False
    delivery: True
  actions: []
  finishing_request: "park" # [park, charge, nothing]
  responsive_wait: True # Should responsive wait be on/off for the whole fleet by default? False if not specified.
  max_merge_waypoint_distance: 0.05
  robots:
    robot_1:
      charger: "drop_1"
      responsive_wait: True # Should responsive wait be on/off for this specific robot? Overrides the fleet-wide setting.
    robot_2:
      charger: "charger100"
      responsive_wait: True
    robot_3:
      charger: "drop_2"
      responsive_wait: True




fleet_manager:
  prefix: "http://127.0.0.1:22012"
  ip: "127.0.0.1"
  port: 22011
  user: "some_user"
  password: "some_password"
  reference_coordinates:
    L1:
      rmf: [ [ 8.39490032196045, -23.9205265045166 ],
             [ 11.03225040435791, -22.222421646118164 ],
             [ 12.202396392822266, -23.672346115112305 ],
             [ 13.883111000061035, -22.44031524658203 ] ]
      robot: [ [ 0.013485236508403442, -1.429341977285164 ],
               [ 2.834536304992918, 0.4730012547396587 ],
               [ 3.9351977179900075, -0.8007824072673576 ],
               [ 5.625444128776892, 0.35160153388809356 ] ]





The image above shows my robot’s battery level, which I have set to 40%, and the waypoint information.

I noticed that when the battery level is sufficient, after completing a task, the robot returns to the point configured as the charger in the YAML file, even though this point is not set as a parking spot. What could be causing this? I look forward to your response.

Posted by @xiyuoh:

I’ve tried out your config with rmf_demos Office world, and I found that the robot will still go to the charger if the recharge_soc is set to 1.0. Meaning that whenever the robot is detected to fall below the recharge_soc, it will head to the charger. You could try tweaking this value and bringing it down to an acceptable SOC.


This is the chosen answer.

Posted by @DDDWY:

Here’s my understanding of recharge_threshold and recharge_soc:

  • recharge_soc is understood as the idle charging threshold. When the robot’s battery level falls below this level, it can still accept new tasks, and after completing the tasks, it will automatically go to a designated charging point.
  • recharge_threshold is understood as the forced charging threshold. When the robot’s battery level falls below this level, and account_for_battery_drain is set to true, the robot will not be assigned new tasks and can only perform charging tasks.

“Could you please check if my understanding is correct?”

Posted by @xiyuoh:

Your understanding is pretty much correct.

The recharge_soc is the battery level that we would like our robot to charge up to. If a robot is given a charging task, the task will only be considered complete when the robot’s battery level reaches the configured recharge_soc.

The recharge_threshold is the battery level below which the robot will not perform any tasks until its battery is charged up sufficiently. For example, for a fleet with a single robot and its recharge_threshold is 0.5, when its battery falls to 0.4 and a task is submitted, the robot will not perform the assigned task until its battery level is charged up to/above the recharge_soc.

Upon more experimentation with your use case, I also realized that the robot’s configured charger matters even when the finishing request is park. I made a mistake in my initial explanation: Whether the finishing task is park or charge, the robot will always go back to its dedicated charging waypoint. It will not go to a waypoint with the attribute is_parking_spot: True.
There is currently no support for a designated parking waypoint for each robot, but we can certainly look into implementing that.


Edited by @xiyuoh at 2024-07-17T08:17:56Z

Posted by @anandharaj-dotworld:

@xiyuoh I have a few questions regarding a charging request:

Is it possible to use a dynamic charger without integrating Easy Fleet?
If I have 2 charging points and 4 robots, will the robots wait until a charging point is free before they can charge?
I want to manually trigger the charging point via an API. Is it possible to trigger the charger like a fire alarm?

Posted by @xiyuoh:

Hey @anandharaj-dotworld ! Yes you can use dynamic charging without Easy Full Control fleet adapter. The feature is built into the core RMF fleet adapter libraries. Do remember to add the rmf_charging_schedule node to your launch file.

Dynamic charging is managed based on the schedule you provide. You may refer to this README for more details about setting it up. RMF does not automatically check whether a charging point is free before rotating the robots to the chargers. Users will have to ensure that the schedule they provide does not result in any clash in assignments.
I’d also recommend giving the robots some time to swap around, for example if robot_A and robot_B are sharing the charger:

"some_robot_fleet":
    "00:00": { "robot_A": "charger", "robot_B": "holding_point_1" }
    "00:50": { "robot_A": "holding_point_2" }
    "01:00": { "robot_B": "charger" }

This example gives a 10-minute buffer for robot_A to move out of the charger before sending robot_B in.

You may trigger the charging point based on time by parsing in the desired schedule to your rmf_charging_schedule node. I suggest looking into the hardware specs of your robots (time taken for 0 to 100% charge, time taken for 100 to 0% depleted, etc.) to determine an ideal schedule that ensures all robots get to charge sufficiently throughout the day.

You can also trigger non-scheduled charger assignments using this msg on the /charging_assignments topic. This way you won’t need the charging schedule node anymore.

Posted by @anandharaj-dotworld:

Hey @xiyuoh !

I’m encountering an issue with auto-selecting charging points. When two robots are in the same lane and close to a charging point, they both end up selecting the same charging point, even though there are multiple charging points available. This happens even though I’ve added a time delay for each robot joining the fleet.

Is it possible to check if a charging waypoint has already been acquired by another robot before assigning it. Without mentioning to a fleet config.

Posted by @arjo129:

Hi @anandharaj-dotworld,
This seems like a problem that open-rmf/rmf_ros2#325 will address. The current work around would be to use mutex groups on the lanes near the charger.


Edited by @arjo129 at 2024-08-27T08:10:26Z

Posted by @xiyuoh:

To add on to @arjo129 , we’ll need to understand how you are “auto-selecting” these charging points. The PR he mentioned will help to ensure that the nearest charger is unoccupied before heading over (and in the meantime temporarily park at the nearest parking spot), but it is important to note that the robot will wait until this particular charger is vacant instead of looking for an alternate empty charger. Same for mutex groups, the second robot will wait until the mutex group is released by the first robot before heading to this charger instead of searching for another vacant charger. You could give that a try if that’s the behavior you’d like to achieve :grinning_face_with_smiling_eyes:

Perhaps you can provide an example setup (how many robots and chargers), how you are assigning chargers to the robots, and what is the expected behavior you’re looking for. Lastly, are you using dynamic charging at all in this scenario?

Posted by @anandharaj-dotworld:

@xiyuoh ,

I was hoping to have the charging point chosen dynamically based on the robot’s need to charge.

Here’s the current situation:

When I don’t specify a waypoint for self.update_handle.set_charger_waypoint, RMF automatically selects the nearest charger waypoint. However, if two or more robots are near the same charger point, they both end up selecting the same charger. Don’t wait at nearest parking spot, if the multiple charging waypoint available in a nav graph. I would like to ensure that if a charging point is already occupied, the system should automatically choose a different charging point, both for immediate assignments and scheduling.

That means, i need a dynamic choose a charging point at all the cases.


Edited by @anandharaj-dotworld at 2024-08-27T08:31:58Z

Posted by @xiyuoh:

Thanks for clarifying! Unfortunately the term “dynamic” refers to assigning chargers based on the time of the day, and not assigning chargers based on the current charger occupancy. In the current state of RMF, users will have to assume the responsibility of ensuring that the assigned charger is vacant.

If you have a 1:1 charger to robot ratio, I suggest not using dynamic charging at all, and assign a unique charger to each robot. If you have less chargers than robots, I suggest using dynamic charging to rotate the robots around the chargers every few hours. You will have to make sure that your schedule provides enough time for robots to charge up sufficiently, while also not be too long such that the non-charging robots would have their batteries depleted before their turn to charge arrives.

I should also correct my comment above: the PR @arjo129 mentioned aims to enable the behavior you’re looking for, i.e. robot goes to another free charger if the nearest one is taken, but this particular use case has not been tested extensively yet.

Posted by @anandharaj-dotworld:

Could you consider incorporating this into future development plans? I believe this suggestion could significantly enhance the performance and coordination of multi-robot systems

Posted by @xiyuoh:

I agree, this would definitely come in helpful for many users! We’ll keep it in mind, in the meantime you may want to keep an eye out on the progress of Adds a simple parking spot management system. by arjo129 · Pull Request #325 · open-rmf/rmf_ros2 · GitHub since this feature is one of the PR’s goal.