In this guide we will use Airbotics to send commands to our robot.

Goals

  • Publish to a topic, Call a service, Send a goal to an action.
  • Verifying command states
  • Understand when and why commands fail.

Prerequisites

  • An account with Airbotics.
  • A valid API key with the commands:read and commands:write permissions.
  • Device running Ubuntu 22.04 with ROS 2 installed.
  • Physical or SSH access to your device.

Prepare environment

Ensure you have the following two environment variables set before you get started:

export AIR_API_KEY=<your_api_key>
export AIR_ROBOT_ID=<your_robot_id>

Run the agent and the application nodes

ros2 run turtlesim turtlesim_node
airbotics

Publish to a topic

Now we will publish to a ROS topic through the REST API. We will send a Twist message on the /turtle1/cmd_vel topic, which will cause the robot to turn.

curl --request POST https://api.airbotics.io/robots/$AIR_ROBOT_ID/commands \
    --header "content-type: application/json" \
    --header "air-api-key: $AIR_API_KEY" \
    --data '{
        "interface": "topic",
        "name": "/turtle1/cmd_vel",
        "type": "geometry_msgs/msg/Twist",
        "payload": {
            "linear": {
                "x": 2.0,
                "y": 0.0,
                "z": 0.0
            },
            "angular": {
                "x": 0.0,
                "y": 0.0,
                "z": 2.0
            }
        }
    }'

You should now see your turtle start to turn in a circle.

Call a ROS service

You can also use Commands to call ROS services on your robots. Let’s send another API request to call a service, we will call the /turtle1/teleport_absolute service to move the turtle.

curl --request POST https://api.airbotics.io/robots/$AIR_ROBOT_ID/commands \
    --header "content-type: application/json" \
    --header "air-api-key: $AIR_API_KEY" \
    --data '{
        "interface": "service",
        "name": "/turtle1/teleport_absolute",
        "type": "turtlesim/srv/TeleportAbsolute",
        "payload": {
            "x": 10.0, 
            "y": 5.0,
            "theta": 1.57
        }
    }'

You should now see the turtle move.

Send a goal to an action server

Finally, we will send a goal to the /turtle1/rotate_absolute action to turn the turtle around with the below command:

curl --request POST https://api.airbotics.io/robots/$AIR_ROBOT_ID/commands \
    --header "content-type: application/json" \
    --header "air-api-key: $AIR_API_KEY" \
    --data '{
        "interface": "action_send_goal",
        "name": "/turtle1/rotate_absolute",
        "type": "turtlesim/action/RotateAbsolute",
        "payload": {
            "theta": 0.0
        }
    }'

Verifying command states

In the previous examples, if the agent was running and connected to the network, the commands should have executed almost immediately.

Each command you try to send has an associated state and you can make another API call to check command state:

curl --request GET https://api.airbotics.io/robots/$AIR_ROBOT_ID/commands \
    --header 'content-type: application/json' \
    --header "air-api-key: $AIR_API_KEY" 

The response shows a list of previous commands sent to the robot, and each command has a state and error_code field. If the command was executed successfully, all the command states should be executed and the error_code null.

Why commands fail

However under certain circumstances, commands will either fail to reach the robot or will reach the robot but fail to execute as expected. Thankfully the state and error_code fields can let you know if and why a command was not executed.

To see a complete list of error codes and what them, refer to the error codes table in the commands docs.

To show you a failed command in action let’s send a command with an invalid payload;

curl --request POST https://api.airbotics.io/robots/$AIR_ROBOT_ID/commands \
    --header "content-type: application/json" \
    --header "air-api-key: $AIR_API_KEY" \
    --data '{
        "interface": "topic",
        "name": "/chatter",
        "type": "std_msgs/msg/String",
        "payload": {
            "x": 10.0,
        }
    }'

In the above command we’re asking to send a command to publish a message to the chatter topic. The ROS message type is std_msgs/msg/String, but the payload we’re sending contains {"x": 10} which does not match the datatype that std_msgs/msg/String expects. If you query the command state again you’ll see the following:

{
    "uuid": "5807bb36-aa37-47cd-8701-9722b672e60c",
    "state": "error",
    "error_code": "invalid_payload",
    "interface": "topic",
    "name": "/chatter",
    "type": "std_msgs/msg/String",
    "payload": {
      "x": 10.0
    },
    "created_at": "2023-07-28T08:14:30.133Z"
}

Another failure reason is that a robot is not online when you attempt to send a command. Airbotics adopts a fire and forget approach to commands, there is no queueing or resending logic. This is to avoid unexpected behaviour of executing commands at a time other than when they were sent.

Wrapping up

In this deep dive you have sent commands for 3 ROS interfaces, topics, services and actions. You’ve also learnt about when and why commands fail and how to query and interpret command states and error codes.

Cleaning up

If you want to delete the commands you send in the guide, you can make an API call(s) with the uuid of each command you’d like to delete. Dont forget to replace <command-uuid> with the uuid of each command you would like to delete:

curl --request DELETE "localhost:8000/commands/<command-uuid>" \
        --header "content-type: application/json" \
        --header "air-api-key: $AIR_API_KEY"