VDA5050 Messages with Meili FMS #
VDA5050 denfines specific json-schema for messages that can be sent or received by the robot.
Meili FMS is designed to use the information contained in the state messages to update position, battery and progress of the mission assigned to the robot. Whenever a robot attempts to alter the status of an assigned mission, we check the “orderId” to see if it’s valid, only then we will update the status of its mission. A subtask in the Meili FMS is a sub-part of a Mission, it includes an action or movement. When converting to VDA5050 order message, Meili performs a distinction between actions and movements, meaning that there are different ways of progressing a move-to-point subtask and an action subtask. These are described below.
Setting a Move-to-point Subtask in Progress #
To set a Move-to-point subtask as “in progress” we use the “nodeStates” sent from the robot and the “lastNodeId”. We check where the robot is using the “lastNodeId” and check the next nodes if they include the node which would complete the move-to-point subtask. In case the robot is manually paused, the “paused” field will be checked and the mission will appear as paused in the UI.
Orders sent from Meili include the whole path to the final node of that move-to-point subtask. Nodes that are intermediate between the starting station and the final station are denominated “intermediate” while the final node will have a unique UUID as “nodeID” that Meili uses to process the specific subtask. Once the robot starts sending the state message containing the “nodeStates” and the “lastNodeId” with “intermediate” prefix then the subtask will be set in progress.
Completing a Move-to-point Subtask #
The Meili system uses the “orderId” and “lastNodeId” to complete a move-to-point subtask: when the robot is on the final station, we expect to receive the “lastNodeId” that matches the “NodeId” of the last node in the “nodeStates” list.
As an example, here is part of an order message that contains the “orderId” and one node in the “nodes” list:
{
"headerId": 0,
"timestamp": "2019-04-30T15:07:02.698931656Z",
"version": "2.0.0",
"manufacturer": "meili",
"serialNumber": "serial_number",
"orderId": "04576bad742140f7bc76fa3a709a3674",
"orderUpdateId": 0,
"nodes": [
{
"nodeId": "66a03c63d294461d94a3b11a49189cca",
"description": "",
"sequenceId": 0,
"released": true,
"nodePosition": {
"x": 0.0,
"y": 0.0,
"theta": 0.0,
"allowedDeviationXY": 0.1,
"allowedDeviationTheta": 1,
"mapId": ""
},
"actions": []
}
]
}
The information contained in the order message can then be used by the robot to update the state message to set the progress of task in the state message sent by the robot to the FMS. When the robot is in the last node of the mission, we check that the “lastNodeId” sent by the robot matches the “nodeId” sent in the order. Impotrtant fields in the state message sent by the robot that have to match the order message are eg:
"orderId":"04576bad742140f7bc76fa3a709a3674"
and
"lastNodeId":"66a03c63d294461d94a3b11a49189cca"
Completing Action and Setting Action in Progress #
Everything other than a “move-to-point” action in the Meili system, will be sent to the robot as an action on a node. Currently a robot has to be on a station to perform an action. The status of this action is then sent from the robot in the “actionStates” field of the state message. Below is the conversion table from VDA5050 action states into Meili subtask status:
VDA5050 action state | Meili status |
---|---|
WAITING | PENDING |
INITIALIZING | IN_PROGRESS |
RUNNING | IN_PROGRESS |
PAUSED | PAUSED |
FINISHED | COMPLETED |
FAILED | FAILED |
Errors from the Robot #
The robot can also send errors which can send notifications to the user or fail a task that has been sent to the robot. The errors are sent in the “Errors” field of the state message from the robot.
The behaviour in the Meili FMS depends on the level of the error.
- WARNING: it will send a notification to the user with the information included in the error.
- FATAL: will send a notification, and fail the current task that is assigned to the robot.
Messages details #
Meili FMS uses three types of messages: “order”, “state” and “instantActions” to manage missions, robot states and traffic. Meili also provides the “factsheet” message to directly import from the robot essential information and “connection” topic to notify of an unexpected robot disconnection.
State message #
The state message is sent at a frequency of 1Hz from the robot to the broker. An example state message can be found at the bottom of this document.
Required information in the State Message:
Field | Required subfield | Required by Meili | Notes |
---|---|---|---|
agvPosition | x, y, theta, positionInitialized | YES | mapId can be empty |
batteryState | batteryCharge, charging | YES | |
driving | YES | ||
operatingMode | YES | one of: [ “AUTOMATIC”, “SEMIAUTOMATIC”, “MANUAL”, “SERVICE”, “TEACHIN” ] | |
serialNumber | YES | must be the vehicle serialNumber as in Meili system | |
manufacturer | YES | must be the manufacturer as in Meili system | |
headerId | YES | ||
newBasedRequest | NO | ||
paused | YES | ||
timestamp | NO | system time will be used if not provided by the robot | |
version | NO | ||
velocity | vx, vy, omega | YES | |
orderID | YES | Pass empty when idle | |
orderUpdateID | YES | Pass empty when idle | |
lastNodeId | YES | Pass empty when idle | |
lastNodeSequenceId | YES | Pass empty when idle | |
nodeStates [nodeState] | nodeId, sequenceId | YES | Pass [] when idle |
edgeStates [edgeState] | edgeId, sequenceId | YES | Pass [] when idle |
actionStates [actionState] | actionId, actionStatus | YES | Pass [] when idle, actionStatus has to be one of: {WAITING; INITIALIZING; RUNNING; PAUSED; FINISHED; FAILED} |
errors | errorType, errorLevel | YES | Pass [] when no errors are detected. ErrorLevel can be: {WARNING, FATAL} |
safetyState | eStop, fieldViolation | YES | eStop has to be one of: {AUTOACK,MANUAL,REMOTE,NONE} |
information [info] | NO | ||
distanceSinceLastNode | NO | ||
loads [load] | NO |
It is important to know that agvPosition, paused, driving and safetyState are essential parameters that are used by the system to check the state of the robot: when the robot is in emergency-stop no mission will be sent and the current mission will be paused. When a mission is cancelled or paused by the user directly from the UI, Meili FMS checks if the robot is responding to the command, specifically if it is driving or paused and if the actionState is updated coorectly. If the conditions are not met, the system will send the instantAction message again to make sure that it has been received by the robot.
Order message #
The order message is sent from Meili FMS to the MQTT-client and includes an orderId, nodes, actions, and edges. The orderId is used in the state message to track and update the progress of the task. It utilizes the lastNodeId from the state message to reflect which node has been reached.
Auto-confirmation is a setting available for each mission, allowing you to control whether subtasks (move-to-point or action) require user confirmation.
- With auto-confirmation off, users must manually confirm the subtask in the UI. In addition if an action is present in the mission it will be separated from the move-to-point order message and send as a standalone message consisting of a node with a single action.
- When auto-confirmation on, all nodes are released upon the initial order. If actions one or more actions have to be performed on the same node, a single message with all the actions is sent to the robot together with the
instantActions message #
Meili FMS currently supports three types of messages published on the instantAction topic that are used to control the status of tasks and robot:
- cancelOrder: can be triggered by the user to cancel a mission or automatically by the FMS when an error from the robot is FATAL.
- startPause: can be triggered by the user by pressing the icons on the mission tab next to the mission name or is automatically triggered by FMS for traffic control.
- stopPause: same as above.
Moreover, there are three instantActions that can be triggered by the user:
- initPosition: it is used to pass an initial position to the robot
- forbiddenZones: passes the forbidden zones to the robot as a set of 4 points corresponding to the forbidden zone corners.
- factsheetRequest: instantAction to request the robot’s factsheet.
Factsheet message #
The factsheet provides basic information about a specific AGV type series. The JSON structure of the factsheet message is defined here: VDA5050 factsheet documentation.
This file contains information that can be directly imported from the robot into Meili FMS, such as physical dimensions and supported actions.
By clicking the “factsheet request” button on the vehicle page (via the edit icon for the vehicle, then the factsheet tab), a factsheetRequest instant action message is sent to the robot.
The FMS expects a factsheet message to be published on the topic: meili/v2/{manufacturer}/{serial_numer}/factsheet
. Once the message is received and elaborated, the relevant fields and properties will be automatically updated in Meili FMS. This feature facilitates the process of integrating a robot in a new team/organisation.
Currently Meili imports only dimensions and action definitions which are defined in the JSON schema as:
-
"physicalParameters"
and specifically"heightMax"
,"width"
and"length"
. -
"protocolFeatures"
, specifically"agvActions"
which is a list containing theactionType
(name),actionDescription
and a list ofactionParameters
that will be used by Meili to define the actions.
Note: Actions that have the same name (actionType
) as the preset ones or as already created custom actions, will not be imported. We suggest to define theactionType
as “Robot series/category - Action name” or “Action name - Robot series/category” to avoid conflicts.
An example of factsheet message is shown in the example message. Fields that are not necessary can be left empty.
Connection message #
The connection topic is used to inform Meili FMS of an unexpected disconnection of a vehicle. During the connection of a vehicle to the Meili broker, a “Last Will Topic” and “Last Will Message” can be set up which will be published by the broker upon unexpected disconnection of the AGV. This functionality is described in the VDA5050 standard, the JSON encapsulated message fields are described here: VDA5050 connection message spec
When an unexpected disconnection happens, the FMS will cancel the current task and will set it offline. The robot, depending on its capabilities, could continue to perform the task offline on it’s own. It won’t receive any new orders as long as it is not back online. The connection topic and message can be set using different MQTT libraries. Here is an example using Eclipse paho-mqtt library in python and it has to be set before connecting to the broker.
client.will_clear()
message = {
"headerId": 0,
"timestamp": datetime.now(),
"version": "2.0",
"manufacturer": manufacturer,
"serialNumber": vehicle_uuid,
"connectionState": "CONNECTIONBROKEN"
}
client.will_set(
topic = f"meili/v2/{manufacturer}/{vehicle_uuid}/connection",
payload = (json.dumps(message, default=str)).encode('utf-8'),
qos=1,
retain=True
)
Message examples #
State message #
{
"agvPosition": {
"positionInitialized": true,
"theta": 30,
"x": 0.7,
"y": -0.5
},
"batteryState": {
"batteryCharge": 10,
"batteryHealth": 1,
"batteryVoltage": 12,
"charging": true
},
"driving": true,
"headerId": 189,
"manufacturer": "Meili",
"newBaseRequested": false,
"lastNodeId": "intermediate_node__0_1",
"manufacturer": "meili",
"nodeStates": [
{
"nodeId": "intermediate_node__0_1",
"description": "intermediate point 1 of task subtask index 0 ",
"nodePosition": {
"theta": 1.5707964,
"x": 0.7,
"y": -0.5,
"allowedDeviationXY": 0.1,
"mapId": "floor 0"
},
"released": true,
"sequenceId": 1,
"actions": []
},
{
"nodeId": "66a03c63d294461d94a3b11a49189cca",
"description": "we are in 2 Subtask of 20240801_0001 at index 0 ",
"nodePosition": {
"theta": 1.5707964,
"x": 0.8,
"y": -0.6,
"allowedDeviationXY": 0.1,
"mapId": "floor 0"
},
"released": false,
"sequenceId": 2,
"actions": []
}
],
"operatingMode": "AUTOMATIC",
"orderId": "0d9961951d3a48d4b762b6959219ff86",
"paused": false,
"safetyState": {
"eStop": "NONE",
"fieldViolation": false
},
"serialNumber": "serial_number",
"timestamp": "2019-04-30T15:07:02.698931656Z",
"velocity": {
"omega": 0,
"vx": 0.05,
"vy": 0.75
},
"version": "2.0.0"
}
Order message #
{
"headerId": 3,
"timestamp": "2019-04-30T15:07:02.698931656Z",
"version": "2.0.0",
"manufacturer": "meili",
"serialNumber": "serial_number",
"orderId": "217368b19ece4317a2abeba81ebe4937",
"orderUpdateId": 0,
"nodes": [
{
"nodeId": "intermediate_node_0_0",
"description": "intermediate point 0 of task wed subtask index 0 ",
"sequenceId": 0,
"released": true,
"nodePosition": {
"x": 4.3,
"y": -5.15,
"theta": 2.3561945,
"allowedDeviationXY": 0.1,
"allowedDeviationTheta": 1
}
},
{
"nodeId": "89bd6a888a8947e2a5fa07e697c2cc83",
"description": "we are in 2 Subtask of wed at index 0",
"sequenceId": 2,
"released": true,
"nodePosition": {
"x": 4.05,
"y": -1.75,
"theta": 0,
"allowedDeviationXY": 0.1,
"allowedDeviationTheta": 1
},
"actions": [
{
"actionType": "Pick shelf",
"actionId": "c2c78d6286fb4df5bd814a2e6ca57588",
"actionDescription": "Start detection routine and pick a shelf",
"blockingType": "NONE",
"actionParameters": [
{
"key": "shelf_id",
"value": "3"
},
{
"key": "sub_area",
"value": "4"
}
]
}
]
}
],
"edges": [
{
"edgeId": "edge_0",
"sequenceId": 1,
"released": true,
"startNodeId": "intermediate_node_0_0",
"endNodeId": "89bd6a888a8947e2a5fa07e697c2cc83",
"maxSpeed": 5
}
]
}
instantActions message #
{
"headerId": 26,
"timestamp": "2019-04-30T15:07:02.698931656Z",
"version": "2.0.0",
"manufacturer": "meili",
"serialNumber": "serial_number",
"actions": [
{
"actionType": "stopPause",
"actionId": "ea5615a50019477f9c53a88a2f90f881",
"blockingType": "HARD",
"actionParameters": []
}
]
}
factsheet message #
{
"headerId": 0,
"timestamp": "",
"version": "2.0.0",
"manufacturer": "meili",
"serialNumber": "serial_number",
"typeSpecification": {
"seriesName": "Robot series",
"seriesDescription": "This is a description of the robot series",
"agvKinematics": "DIFF",
"agvClass": "CARRIER",
"maxLoadMass": 100,
"localizationTypes": ["NATURAL"],
"navigationTypes": ["AUTONOMOUS"]
},
"physicalParameters": {
"speedMin": 0.1,
"speedMax": 3,
"accelerationMax": 1,
"decelerationMax": 2,
"heightMin": 0.1,
"heightMax": 0.2,
"width": 0.8,
"length": 0.8
},
"protocolLimits": {},
"protocolFeatures": {
"optionalParameters": [],
"agvActions": [
{
"actionType": "Robot New - Test factsheet 1",
"actionDescription": "This is a description of the action",
"actionScopes": ["NODE"],
"actionParameters": [
{
"key": "variable name",
"valueDataType": "INTEGER",
"description": "This is a description of the parameter",
"isOptional": false
}
],
"resultDescription": "This is a description of the result"
},
{
"actionType": "Robot New - Test factsheet 2",
"actionDescription": "This is a description of the action",
"actionScopes": ["NODE"],
"actionParameters": [
{
"key": "param_1",
"valueDataType": "FLOAT",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_2",
"valueDataType": "BOOL",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_3",
"valueDataType": "STRING",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_4",
"valueDataType": "INTEGER",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_5",
"valueDataType": "NUMBER",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_6",
"valueDataType": "ARRAY",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_7",
"valueDataType": "TIME",
"description": "This is a description of the parameter",
"isOptional": false
},
{
"key": "param_8",
"valueDataType": "DATETIME",
"description": "This is a description of the parameter",
"isOptional": false
}
],
"resultDescription": "This is a description of the result"
}
]
},
"agvGeometry": {}
}