Commands
Commands makes it simple to publish to ROS topics, call services or send goals to action servers on your robots over the internet through a REST API.
How it works
-
Clients call the
POST /robots/:id/commands
endpoint on the Airbotics API. In the request body they provide the ROS interface (topic, service or action), the type of interface (e.g.std_msgs/msg/String
), its name (e.g./chatter
), and a payload in JSON format. -
The command will then be created in the database, in a
created
state. -
If the robot is online the command will be immediately sent to the Airbotics agent on the robot and the state changed to
sent
. If the robot is not online the command will not be sent, moved into anerror
state, and an error will be returned to the client. -
If the command is received by the agent, it will be converted from JSON formant to ROS format and executed (i.e. published to a topic, service called, or goal sent to an action server).
-
If the agent is instructed to call a service or send a goal to an action server that aren’t ready, the agent will report an error to the backend and the command will be moved into an
error
state. However, if the command was successfully executed, the agent will report this to the cloud and move the comannd to anexecuted
state. -
The client can query state of a command with the
GET /commands/:id
endpoint.
Publishing to a topic
The request body to publish a message to a ROS topic looks like this:
{
"interface": "topic",
"name": "/chatter",
"type": "std_msgs/msg/String",
"payload": {
"data": "Hello world"
}
}
Below is an explanation of each part:
Parameter | Description | Example |
---|---|---|
interface | The ROS interface, this must be topic . | topic |
name | The name of the topic. | /chatter |
type | The type of ROS interface the topic expects. | std_msgs/msg/String |
payload | The valid ROS interface payload in JSON format . | { "data": "Hello world" } |
Airbotics currently supports all message types defined in ROS common_interfaces. It is also possible to send custom messages that may be specific to your application.
See JSON to ROS conversion for more information about how to provide the correct payload for each ROS message type.
Calling a service
The request body to call a ROS service looks like this:
{
"interface": "service",
"name": "/add_two_ints",
"type": "example_interfaces/srv/AddTwoInts",
"payload": {
"a": 1,
"b": 3
}
}
Parameter | Description | Example |
---|---|---|
interface | The ROS interface, this must be service . | service |
name | The name of the service to call. | /add_two_ints |
type | The type of ROS interface that the service expects. | example_interfaces/srv/AddTwoInts |
payload | The valid ROS interface payload in JSON format. | { "a": 2, "b": 3 } |
If the service is not available when the agent receives the instruction the agent will report this to the backend and the command will enter an error
state.
Airbotics considers the command to be executed successfully if the service has been called, even if the logic in your service failed to perform its function. The agent discards the response of the service.
Sending a goal to an action
The request body to send a goal to a ROS action looks like this:
{
"interface": "action_send_goal",
"name": "/turtle1/rotate_absolute",
"type": "turtlesim/action/RotateAbsolute",
"payload": {
"theta": 90.0,
}
}
Parameter | Description | Example |
---|---|---|
interface | The ROS interface, this must be action_send_goal . | action_send_goal |
name | The name of the action to call. | /turtle1/rotate_absolute |
type | The type of ROS interface that the service expects. | turtlesim/action/RotateAbsolute |
payload | The valid ROS interface payload in JSON format. | { "theta": 90.0} |
If the action server is not ready to accept a goal the agent will report this to the backend and the command will enter an error
state.
Airbotics considers the command to be executed successfully if the goal has been successfully sent to the action server, even if the logic in your action failed to perform its function. The agent discards any feedback or result from the action server.
JSON to ROS conversion
When you send an API request to execute a command, you must provide data specific to ROS in JSON format. For the agent to be able to make the conversion from JSON to ROS, you must provide valid and corresponding values for type
and payload
, discussed below:
Providing a valid type
The type
parameter must match the ROS convention for defining interfaces. Examples:
std_msgs/msg/String
std_srvs/srv/Empty
custom_msgs/msg/MyMessage
turtlesim/action/RotateAbsolute
If the agent cannot find the type an error will be produced, the agent will report this to the backend and move the command to an error
state.
Providing a valid payload
A valid value for payload
will depend on the value you provided for type
. In general, you should supply the same arguments and types that ROS uses. This applies to messages, services and actions. You can inspect the fields and types of an interface using the following command:
ros2 interface show <interface_type>
If the agent cannot convert JSON to ROS format an error will be produced, the agent will report this to the backend and move the command to an error
state.
See below for examples on how ROS types can be converted to JSON:
Example 1
We can examine the String
message type in ROS with:
ros2 interface show std_msgs/msg/String
# Returns
String
string data
We can see it has a single child property data
with a type string
. The JSON format for this is therefore:
"type": "std_msgs/msg/String"
---
"payload": {
"data": "hello world"
}
Just like the the ROS message, the JSON has a single child with the same name (data
) and a value that matches the expected type (string
).
Example 2
Looking at a slightly more complex example:
ros2 interface show geometry_msgs/msg/Twist
# Returns
Twist
Vector3 linear
float64 x
float64 y
float64 z
Vector3 angular
float64 x
float64 y
float64 z
This time there are several child properties (linear
and angular
) and those children have children of their own (x
, y
and z
). To convert this to JSON we follow the same convention as before:
"type": "geometry_msgs/msg/Twist"
---
"payload": {
"linear": {
"x": 1.0,
"y": 1.0,
"z": 1.0
},
"angular": {
"x" :1.0,
"y": 1.0,
"z": 1.0
}
}
Example 3
Finally, we take an even more complex example:
ros2 interface show std_msgs/msg/Float64MultiArray
# Returns
Float64MultiArray
MultiArrayLayout layout
MultiArrayDimension[] dim
string label
uint32 size
uint32 stride
uint32 data_offset
float64[] data
Here there are several child properties whose children have children and where some of the children are arrays. But again, following the same convention, it can be converted to JSON like so:
"type": "std_msgs/msg/Float64MultiArray"
---
"payload": {
"layout": {
"dim": [
{
"label": "sensor-dim",
"size": 3,
"stride": 1
}
],
"data_offset": 0
},
"data": [
1.0,
2.0,
3.0
]
}
States
A command can be in one of the following states:
State | Description |
---|---|
created | The request to send a command has been received by the backend but has not yet been attempted to be sent. |
sent | The command has been attempted to be sent to the robot and should be received by the agent. |
error | The command has failed, e.g. because the robot is not online. |
executed | The command has been received by the agent and has been executed. |
Error codes
Commands can fail for various reasons, which are outlined below:
Error code | Description |
---|---|
robot_not_online | The robot is not online. |
invalid_type | The command type cannot be found or is invalid. |
invalid_payload | The command payload does not match the expected payload. |
service_not_ready | The service is not ready. |
action_server_not_ready | The action server is not ready. |
unknown_error | Something has gone wrong with the agent. |