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

EasyFullControl fleet_adapter_mir and cart delivery PerformAction plugin #36

Merged
merged 50 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
b3106d6
EFC fleet adapter
xiyuoh Dec 27, 2023
118be99
Create standard RMF missions from fleet adapter
xiyuoh Jan 10, 2024
aceb401
Set up plugin system for custom MiR perform actions
xiyuoh Jan 10, 2024
6e2dd98
Cart delivery plugin fixes
xiyuoh Jan 11, 2024
47db7f1
Add cart pickup task script
xiyuoh Jan 12, 2024
9d92896
Footprint and marker type things and update readme
xiyuoh Jan 12, 2024
946d98c
Restore mir/mirfm comparison and add note that mirfm is not actively …
xiyuoh Jan 15, 2024
725fd0d
Make marker type a variable when posting missions
xiyuoh Jan 18, 2024
8aeb014
Update dispatch pickup cancellation behavior and configs
xiyuoh Jan 31, 2024
b7f3770
Remove limits on charging mission
xiyuoh Feb 5, 2024
7b747f0
Fixes bugs for cancelled pickup states, refactor mission info stored …
xiyuoh Feb 22, 2024
6b256df
Use ActionFactory to create MirAction objects
xiyuoh Feb 26, 2024
a68026a
Remove fleet_adapter_mir_actions as dependency in fleet_adapter_mir
xiyuoh Mar 4, 2024
ee6fbdd
Move MirAction to fleet_adapter_mir pkg
xiyuoh Mar 4, 2024
bdc0218
Working plugin module via importlib and modified dispatch_delivery task
xiyuoh Mar 28, 2024
bc241bb
Rename actions arg parsed to fleet_adapter_mir to rmf_missions
xiyuoh Mar 28, 2024
3d855a3
Add module key to config
xiyuoh Mar 28, 2024
598a2ee
Remove footprint update from cart delivery plugin and cancel task if
xiyuoh Apr 16, 2024
5158596
Configurable transformation params
xiyuoh Apr 18, 2024
6063200
Add in retry mechanism for navigate cmds
xiyuoh May 10, 2024
51d7fba
Add retry mechanism for dock and go to known position
xiyuoh May 15, 2024
27baec2
Fix wrong input argument for task cancellation
xiyuoh May 21, 2024
01ad9cc
Separate cart detection related functions into another plugin
xiyuoh Jun 26, 2024
c48e0af
Add example for module config and minor cleanup
xiyuoh Jul 17, 2024
9c25969
Codestyle cleanup
xiyuoh Jul 30, 2024
933358f
Update README with instruction to write own CartDetection module
xiyuoh Jul 30, 2024
52c4fbd
Remove position_put logic when localizing to prevent overwriting exis…
xiyuoh Aug 2, 2024
47d5488
Add disconnect mutex
xiyuoh Aug 2, 2024
d3bf9fe
Check config for lift or localize positions before requesting for loc…
xiyuoh Aug 2, 2024
c2ecf17
Style
xiyuoh Aug 2, 2024
561044d
Cleanup and add license declaration
xiyuoh Aug 2, 2024
fc2e769
Cleanup
xiyuoh Aug 7, 2024
a153c8d
Replace execution.identifier with activity
xiyuoh Oct 4, 2024
cecab1e
Create nav_issue_ticket if no mission_queue_id received for missions
xiyuoh Oct 4, 2024
3cf3740
Import modules at init and store action factories with plugin configs
xiyuoh Oct 7, 2024
55b8877
Raise errors if action config is missing required keys
xiyuoh Oct 7, 2024
0f0e835
Add in ActionContext and refactor perform action calls
xiyuoh Oct 7, 2024
15ee84a
Clean up details for pickup/dropoff actions and add cancellation beha…
xiyuoh Oct 8, 2024
6d8d3e6
Move execution out of ActionContext and add supports_action
xiyuoh Oct 8, 2024
fd45e28
Cleanup and codestyle
xiyuoh Oct 8, 2024
30d58ff
Add and refine documentation
xiyuoh Oct 8, 2024
9610e97
Accept new action if there's an ongoing action
xiyuoh Oct 8, 2024
99491fe
Fix sample config
xiyuoh Oct 8, 2024
2530e29
Robot adapter to be responsible for marking execution as finished
xiyuoh Oct 9, 2024
b87a704
Protect MirAction task cancellation from race conditions
xiyuoh Nov 13, 2024
49724d8
Add performable actions for plugins
xiyuoh Nov 13, 2024
8239b87
Map action name to plugin name
xiyuoh Nov 13, 2024
a7aac27
Do supports_action() check right after loading the action factory
xiyuoh Nov 13, 2024
45ac09a
Iterate through factories to try matching unconfigured actions
xiyuoh Nov 13, 2024
3045bcc
Append action to ActionFactory
xiyuoh Nov 13, 2024
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: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# fleet_adapter_mir
MiR100 and MirFM Fleet Adapter using the https://github.com/open-rmf/rmf_ros2/tree/main/rmf_fleet_adapter_python
MiR100, 200 and 250 using the https://github.com/open-rmf/rmf_ros2/tree/main/rmf_fleet_adapter_python



Expand Down Expand Up @@ -30,7 +30,7 @@ colcon build

## Description

These packages implement a MiR robot/MiR Fleet command handle that is managed by a fleet adapter in Python. (Along with some helpers.) It can be used to command and manage a fleet of MiR robots using RMF!
These packages implement a MiR command handle that is managed by a fleet adapter in Python. (Along with some helpers.) It can be used to command and manage a fleet of MiR robots using RMF!

It uses the `rmf_fleet_adapter_python` bindings, which allows for communication with `open-rmf` libraries and ROS2 nodes.

Expand All @@ -40,12 +40,14 @@ In effect, it interfaces the MiR REST API with `open-rmf`, all without needing t

### MiR vs. MiR Fleet

Since the MiR robots and MiR Fleet work with different sets of endpoints that serve different functions, both `fleet_adapter_mir` and `fleet_adapter_mirfm` packages are availble to demonstrate RMF integration between MiR100 and MiR Fleet respectively. In addition, the `mir_fleet_client` package provides additional API needed for the MiR Fleet + RMF integration.
Since the MiR robots and MiR Fleet work with different sets of endpoints that serve different functions, both `fleet_adapter_mir` and `fleet_adapter_mirfm` packages are availble to demonstrate RMF integration between MiR100/200/250 and MiR Fleet respectively. In addition, the `mir_fleet_client` package provides additional API needed for the MiR Fleet + RMF integration.

For users who wish to take advantage of MiR Fleet's centralized control system/interface or do not have access to individual MiR robots, the `fleet_adapter_mirfm` package enables RMF integration with MiR Fleet by obtaining individual MiR robot positions and mission GUIDs via the MiR Fleet API and dispatching commands directly to the robots themselves. The current MiR Fleet API does not provide all the necessary endpoints for RMF to perform its task allocation and traffic deconfliction to the full extent, hence the implementation contains direct communication between RMF and MiR robots as well.

As such, it is recommended to implement the fleet adapter directly with individual MiR robots using `fleet_adapter_mir`.

**NOTE**: `fleet_adapter_mirfm` is currently not being actively supported.



## Additional Notes
Expand Down Expand Up @@ -83,7 +85,8 @@ We have to deal with the `rmf_traffic` navgraph and the MiR map, with their own

Luckily, we can leverage the `nudged` Python library to compute coordinate transforms between the two sets of coordinates. You just have to make sure to provide equivalent reference points within the two maps so transforms can be computed.

The transform objects are stored in the robot command handle's `self.transforms` member.
The transform objects are stored in the EasyFullControl fleet adapter handle.




Expand Down
142 changes: 77 additions & 65 deletions configs/mir_config.yaml
Original file line number Diff line number Diff line change
@@ -1,73 +1,57 @@
# ROBOT CONFIG =================================================================
# To init MiR robot REST APIs and RMF states

robots:
# NOTE(CH3): This robot name is different from the MiR robot name
# for some reason
#
# I don't have access to the MiR REST server to test it out
# Doesn't seem very configurable from the code side?
#
# So for now, this name is the name used in the script only.
my_test_robot:
mir_config:
base_url: "http://some.ip.address/api/v2.0.0/"
user: "application/json"
password: "Basic HASH_OF_PASSWORD_OBTAINED_AT_API_DOCUMENTATION_PAGE"
rmf_move_mission: "rmf_default_move_mission" # This mission must be predefined
dock_and_charge_mission: "dock_to_charger" # This mission must be predefined

rmf_config:
robot_state_update_frequency: 1
start:
map_name: "L1"
max_merge_waypoint_distance: 3.0
max_merge_lane_distance: 3.0
# NOTE(CH3):
# If you leave these empty, the robot will try to figure it out on init
# from the MiR reported location.
#
# Otherwise, fill BOTH of these! And make sure that the robot starts
# ON the waypoint!
waypoint_index: 0 # Optional
orientation: 0.0 # Optional, radians
charger:
waypoint: "charger_waypoint"


# NODE CONFIG ==================================================================
# To init the ROS2 node names used in the script

node_names:
robot_command_handle: "rmf_mir_fleet_command_handler"
fleet_state_publisher: "rmf_mir_fleet_state_publisher"
rmf_fleet_adapter: "TestDeliveryAdapter"
rmf_fleet_adapter: "DeliveryAdapter"


# TRANSFORM CONFIG =============================================================
# For computing transforms between MiR and RMF coordinate systems
#
# NOTE(CH3): I am assuming that the transform is the same across maps
# Otherwise... Future me or future someone else, PLEASE KEY THIS PER MAP
# And also remember to edit the robot.transforms dictionary accordingly!!
# Conversions between RMF data and MiR data
conversions:
reference_coordinates:
L1: # Map name
rmf: [[0.0, 0.0],
[0.0, 1.0],
[1.0, 1.0],
[1.0, 0.0]]
mir: [[0.0, 0.0],
[0.0, 1.0],
[1.0, 1.0],
[1.0, 0.0]]
L2:
rmf: [[0.0, 0.0],
[0.0, 1.0],
[1.0, 1.0],
[1.0, 0.0]]
mir: [[0.0, 0.0],
[0.0, 1.0],
[1.0, 1.0],
[1.0, 0.0]]

reference_coordinates:
rmf: [[26.95, -20.23],
[29.26, -22.38],
[11.4, -16.48],
[12.46, -16.99]]
maps:
# "RMF map name": "Map name stored on MiR"
L1: "Level 1"
L2: "Level 2"

mir: [[7.2, 16.6],
[5.15, 18.35],
[23, 12.35],
[22.05, 12.95]]
missions:
move: "rmf_move" # This mission must be predefined
dock_and_charge: "rmf_dock_and_charge" # This mission must be predefined
localize: "rmf_localize"
go_to: "rmf_move_to_position"

lift_positions:
lift_1: # Name of lift matching the navigation graph
L1:
name: "Level_1_Lift" # Name of lift position stored on the robot on this map
# tolerance: 0.3
L2:
name: "Level_2_Lift"

# FLEET CONFIG =================================================================
# RMF Fleet parameters
marker_types:
"charger": "Narrow asymmetric MiR500/1000 shelf"

# RMF Fleet parameters
rmf_fleet:
name: "test_fleet"
name: "mir"
limits:
linear: [0.7, 0.3] # velocity, acceleration
angular: [1.0, 0.45]
Expand All @@ -85,17 +69,45 @@ rmf_fleet:
friction_coefficient: 0.20
ambient_system:
power: 20.0
cleaning_system:
tool_system:
power: 650.0
recharge_threshold: 0.01
recharge_soc: 1.0
account_for_battery_drain: True
publish_fleet_state: True
fleet_state_topic: "fleet_states"
fleet_state_publish_frequency: 1
publish_fleet_state: 1.0 # Publish frequency (Hz) for the fleet state
task_capabilities: # Specify the types of RMF Tasks that robots in this fleet are capable of performing
loop: True
delivery: False
delivery: True
clean: False
finishing_request: "nothing" # [park, charge, nothing]
action_categories: ["custom_mission_1"]
finishing_request: "charge" # [park, charge, nothing]
actions: ["delivery_pickup", "delivery_dropoff"]
robots:
mir_1:
charger: "Charger_1"
max_merge_waypoint_distance: 0.25
max_merge_lane_distance: 1.0
mir_config:
base_url: "http://some.ip.address/api/v2.0.0/"
user: "application/json"
password: "Basic HASH_OF_PASSWORD_OBTAINED_AT_API_DOCUMENTATION_PAGE"

robot_state_update_frequency: 1
debug: False # whether to enable debug printouts from EasyFullControl

plugins:
rmf_cart_delivery:
module: "fleet_adapter_mir_actions.rmf_cart_delivery"
actions: ["delivery_pickup", "delivery_dropoff"]
xiyuoh marked this conversation as resolved.
Show resolved Hide resolved
cart_detection_module: "fleet_adapter_mir_actions.rmf_cart_detection"
missions:
dock_to_cart: "rmf_dock_to_cart" # Name of MiR mission for robot to dock under a cart - to be created by fleet adapter upon launch
pickup: "rmf_pickup_cart" # Name of MiR mission for robot to latch onto a cart - to be created and filled in by user
dropoff: "rmf_dropoff_cart" # Name of MiR mission for robot to unlatch and exit from under a cart - to be created and filled in by user
exit_lot: "rmf_exit_lot" # Name of MiR mission for robot to exit from under a cart - to be created by fleet adapter upon launch
marker_types:
cart: "AsymmetricShelf" # Name of cart marker type
missions_json: "/home/some_user/mir_ws/src/fleet_adapter_mir/missions/rmf_cart_missions.json" # Filepath to RMF cart delivery missions
search_timeout: 60 # Dock to cart timeout in seconds
# Examples of additional fields for cart detection module
io_name: "MiR internal IOs"
latch_io: 2
56 changes: 56 additions & 0 deletions docs/mir_actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
## MiR Action Plugins

This section lists and elaborates on the MiR actions provided out-of-the-box in this repo.

## Available Action Plugins

* [rmf_cart_delivery](#rmf_cart_delivery)


## rmf_cart_delivery

### Overview

The `rmf_cart_delivery` plugin allows users to submit pickup and dropoff tasks for MiR from point A to point B via RMF. The intended workflow of a delivery task is as follows:
1. RMF will send the robot to the pickup lot.
2. The robot will attempt to dock under a cart in the pickup lot. If the cart is missing or is not the desired cart, RMF will cancel the task.
3. If the robot successfully docks under the correct cart, it will latch onto the cart.
4. With the cart attached, the robot will move to the designated dropoff point.
5. Upon reaching the dropoff point, the robot will release and exit from under the cart. The task ends once the robot has safely exited.

### Setup

Some relevant MiR missions are pre-defined and can be automatically created on the MiR on startup. These missions are used to facilitate the pickup and dropoff activities. They are defined and stored in the `rmf_cart_missions.json` file and do not require any further configuration.

However, since there are various types of latching methods available for different MiR models, users will need to set up their custom pickup and dropoff missions on the MiR, as well as implement their own `CartDetection` plugin module with the appropriate APIs to detect latching states.

Before setting up, you may want to refer to the [list of RMF missions](https://github.com/open-rmf/fleet_adapter_mir/blob/main/docs/mir_missions.md#RMF-missions-for-rmf_cart_delivery) required for this MiR Action.

Steps for setup:

1. Create 2 missions on the MiR:
- `rmf_pickup_cart`: Triggers the robot's latching module to open
- `rmf_dropoff_cart`: Triggers the robot's latching module to close and release the cart, then exit from under the cart (relative move in the negative X-direction)
2. Fill in the MiR mission names in the plugin config under `missions`.
- This helps the fleet adapter identify and map the action to the missions you have created earlier.
- The recommended mission names are `rmf_pickup_cart` and `rmf_dropoff_cart`, per the instructions in Step 1. However, it is possible to use a different mission name as long as it is indicated accordingly under `missions`.
3. Fill in the appropriate cart marker type in the plugin config under `marker_type`.
4. Create your own `CartDetection` plugin.
- You are encouraged to use the `CartDetection` class in `rmf_cart_detection.py` as a base for your own module implementation. The class methods will be used by the `CartPickup` and `CartDropoff` Mir Actions. Some API calls to check the MiR's PLC registers and IO modules are provided in case you may want to use them.
- In the plugin config, update the `cart_detection_module` field to point to your own written module.
6. Enable RMF cart missions creation.
- If this is your first time setting up the action, and the pre-defined RMF cart missions have not been created on your robot, you will need to provide the filepath to `rmf_cart_missions.json` under `missions_json`.

You can refer to `mir_config.yaml` under the `configs` folder for an example of a filled-in plugin configuration.


### Usage

To submit a cart delivery task, you may use the `dispatch_delivery` task script found in the `fleet_adapter_mir_tasks` package:
```bash
ros2 run fleet_adapter_mir_tasks dispatch_delivery -g go_to_waypoint -p pickup_lot -d dropoff_lot -c some_cart_id
```
- `-g`: Takes in an existing waypoint name for the robot to travel to before performing the pickup. The robot will begin docking into the pickup lot from this waypoint.
- `-p`: Name of the pickup lot. This name should be identical to the shelf position configured on the MiR.
- `-d`: Name of the dropoff lot. This name should be identical to the robot or shelf position configured on the MiR.
- `-c`: Optional cart identifier for the fleet adapter to assess whether the cart is correct for pickup.
39 changes: 39 additions & 0 deletions docs/mir_missions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## MiR Missions for RMF

RMF relies on MiR missions to command MiR robots to move and perform various actions. To standardize the missions used for RMF, users have the option to automatically create these missions required by RMF via the fleet adapter by parsing in the JSON filepath. This section lists and explains what these missions do.

Missions may be used for default behavior, which mostly involves the robot patrolling across the same or different maps. Additional missions can be optionally created for custom actions, such as pickup and dropoff missions for delivery tasks.

For more information on how to set up missions, actions and variables, please refer to [official guide documents](https://www.manualslib.com/manual/1941073/Mir-Mir250.html?page=150#manual) (this is for the MiR250).


### List of standardized RMF missions

* [Default RMF missions](#Default-RMF-missions)
* [RMF missions for `rmf_cart_delivery`](#RMF-missions-for-rmf_cart_delivery)


### Default RMF missions

Definition can be found in `rmf_missions.json`.

| Mission name | Use case | Created by RMF |
| -------------- | ----------- | :--------------: |
| `rmf_dock_and_charge` | Enables the robot to dock into its charger and begin charging | Yes |
| `rmf_localize` | Switches MiR map once the robot reaches another level | Yes |
| `rmf_move` | Commands the robot to move to the MiR coordinates `[X, Y, Orientation]` | Yes |
| `rmf_move_to_position` | Commands the robot to move to the specified MiR Position | Yes |
| `rmf_follow_line` | Commands the robot to move to two specified MiR Positions in sequence | Yes |


### RMF missions for `rmf_cart_delivery`

| Mission name | Use case | Created by RMF |
| -------------- | ----------- | :--------------: |
| `rmf_dock_to_cart` | Enables the robot to dock under a cart | Yes |
| `rmf_exit_lot` | Commands the robot to move backwards by 1 metre, for exiting from under a cart | Yes |
| `rmf_pickup_cart` | Commands the robot open its latch to attach to a cart | No |
| `rmf_dropoff_cart` | Commands the robot unlatch from a cart and move backwards to exit from under the cart | No |

**Important note:**
In order to ensure that your robot has the correct footprint after pickup/dropoff, do remember to add in the appropriate MiR actions to update robot footprint in your `rmf_pickup_cart` and `rmf_dropoff_cart` missions.
Loading